我有一个表单,我想在 Java 中使用 Cucumber 和 Selenium Webdriver 来自动化 - 在这个表单中,我们有一个从 Stripe 使用的卡片元素。我们称之为 div,剩下的由 stripe 完成。我不确定这是否是 iFrame,但是当我使用
Hooks.driver.findElement(By.xpath("xpathOfTheCardNumberField")).sendKeys("123");
命令,它不与其交互,并在控制台日志中返回“无法定位元素”错误。
我已经要求我们的前端也许尝试向字段添加一些 ID 或名称标签,但他告诉我他无法与卡元素内部字段的标记进行交互,只能与卡元素本身进行交互 - 作为 Stripe处理其他一切。
随附卡片元素的图片,以及相关卡片元素的标记。
是否可以让 Selenium 与该元素交互?
非常感谢任何帮助。 卡元前端
为了完整性,对 Fabio 接受的答案进行了额外的跟进。
self.browser = webdriver.Chrome()
# fill in the other fields as usual (i.e. self.browser.find_element_by_id(...))
# When you arrive at the iframe for the card number, exp. date, CVC, and zip:
self.browser.switch_to.frame(frame_reference=self.browser.find_element(By.XPATH, '//iframe[@name="__privateStripeFrame3"]'))
# This switches to the iframe, which Selenium can now start selecting elements from.
# The remaining form elements can be found by name
self.browser.find_element_by_name('cardnumber').send_keys('4242 4242 4242 4242')
# And find the other elements the same way as above (exp-date, cvc, postal).
# Finally, switch back to the default content to select the submit button
self.browser.switch_to.default_content()
self.browser.find_element_by_tag_name('button').click()
我自己实际上已经解决了这个问题,所以我将回答这个问题并关闭它,以防其他人遇到问题。
我认为这是一种通用方法,可以用于任何 iframe,而不仅仅是 Stripe。
首先,您必须告诉您的网络驱动程序将框架切换到您尝试访问的 iframe:
Hooks.driver.switchTo().frame(Hooks.driver.findElement(By.xpath("xpathOfIframe")));
然后,您可以为该 iframe 中的内容创建 webElements,并与它们交互:
WebElement creditcardNumber = Hooks.driver.findElement(By.name("cardnumber"));
creditcardNumber.sendKeys("1234567890000066");
当前的 Stripe 配置将卡号、到期日期 (exp-date) 和 cvc 放在不同的 iframe 中。与以前不同,所有内容都在一个 iframe 中,并且上面的解决方案有效。
我只需转移到一个 iframe 即可更新卡信息。然后Stripe发生了变化,前端开发人员更新了支付网页。然后出现了到期日期和 CVC 的“无法定位元素...”错误。同样,我询问开发人员是否可以为 3 个输入输入 ID 或名称。他说,他做不到,Stripe 就做出了支付屏幕。
通常,当您在 Firefox 或 Chrome 中检查信用卡信息时,您将看到输入属于哪个 iframe。
在我们的网页中,卡号位于 __privateStripeFrame5 中。 exp-date 位于 __privateStripeFrame6 中,cvc 位于 __privateStripeFrame7 中。
这是解决我使用 selenium python 更新/测试卡信息时遇到的困境的代码(java 有类似的方法/函数):
# has_zip_code - True or False
# added params for flexibility
# exp_date - expiration date
# cvc_val - 3-digit number
def enter_card_details_and_submit(driver, card_num, exp_date, cvc_val, has_zip_code):
#print("\n{} -------------------------------------------\n".format(PI_WIN))
print("Use card number ... {}".format(card_num))
print(card_num)
driver.switch_to.frame(frame_reference=wait_and_get_elem_by(driver, By.NAME, "__privateStripeFrame5"))
card_num_text = wait_and_get_elem_by(driver, By.NAME, "cardnumber")
card_num_text.click()
time.sleep(1)
card_num_text.send_keys(card_num)
driver.switch_to.default_content()
print("\nEnter expiry month/year ...")
iframe6 = wait_and_get_elem_by(driver, By.NAME, "__privateStripeFrame6")
driver.switch_to.frame(iframe6)
exp_dt = wait_and_get_elem_by(driver, By.NAME, "exp-date")
exp_dt.click()
time.sleep(1)
exp_dt.send_keys(exp_date)
driver.switch_to.default_content()
print("\nEnter CVC ...")
driver.switch_to.frame(frame_reference=wait_and_get_elem_by(driver, By.NAME, "__privateStripeFrame7"))
cvc = wait_and_get_elem_by(driver, By.NAME, "cvc")
cvc.click()
time.sleep(1)
cvc.send_keys(cvc_val)
.
.
.
wait_and_get_elem_by() 是一个内部函数,用于查找元素,但其中包含 wait() 函数。您可以使用 find_element_by_name()。
卡号、到期日期和 CVC 使用 selenium (python) 更新:
对我来说,这现在有效。我发现 Card 上的 Name 不是 iframe,必须通过 chrome.webdriver 找到它
driver.switch_to.frame(frame_reference=driver.find_element(By.XPATH, '/html/body/div[1]/div/main[2]/section/div/div[2]/div/article/div/form/fieldset[3]/div[2]/div/div/div/iframe'))
creditNumber = driver.find_element_by_name("cardnumber").send_keys("4242 4242 4242 4242")
time.sleep(1)
driver.switch_to.default_content()
driver.switch_to.frame(frame_reference=driver.find_element(By.XPATH, '/html/body/div[1]/div/main[2]/section/div/div[2]/div/article/div/form/fieldset[3]/div[3]/div[2]/div[1]/div/div/iframe'))
expiryDate = driver.find_element_by_name("exp-date").send_keys("1223")
driver.switch_to.default_content()
driver.switch_to.frame(frame_reference=driver.find_element(By.XPATH, '/html/body/div[1]/div/main[2]/section/div/div[2]/div/article/div/form/fieldset[3]/div[3]/div[2]/div[2]/div/div/iframe'))
cvc = driver.find_element_by_name("cvc").send_keys("345")
driver.switch_to.default_content()
time.sleep(1)
nameOnCard = driver.find_element_by_name("name_on_card").send_keys("Testing")
主要问题是在多个 iframe 中导航。 Stripe 的支付页面旨在增强安全性,通常使用多个嵌套或动态加载的 iframe,这使得直接查找元素变得困难。
这是对我有用的解决方案:
列出所有 iframe:我首先列出页面上的所有 iframe,以确定哪个 iframe 包含所需的元素。 切换到正确的 Iframe:我遍历所有 iframe 以找到包含 cardNumber 输入字段的 iframe。 等待可见性:我确保 iframe 和元素在与它们交互之前已完全加载且可见。 这是我使用的通用代码:
public void inputPaymentDetails(String numCard, String dataCard, String cvcCard, String nameCard) 抛出异常 { 尝试 { // 确保所有元素都已加载 线程.sleep(4000); // 如果可能的话替换为 WebDriverWait 以获得更好的处理
driver.switchTo().defaultContent(); // Switch back to the main context
List<WebElement> iframes = driver.findElements(By.tagName("iframe"));
System.out.println("Total iframes found: " + iframes.size());
for (int i = 0; i < iframes.size(); i++) {
try {
WebElement iframe = iframes.get(i);
System.out.println("Checking iframe index: " + i);
// Switch to current iframe
driver.switchTo().frame(iframe);
// Try to find the card number field in the current iframe
List<WebElement> elements = driver.findElements(By.xpath("//*[@id='cardNumber']"));
if (elements.size() > 0) {
System.out.println("'cardNumber' field found in iframe index: " + i);
break;
} else {
System.out.println("'cardNumber' field NOT found in iframe index: " + i);
}
driver.switchTo().defaultContent(); // Switch back to the main content if not found
} catch (NoSuchElementException e) {
System.err.println("Error while checking iframe index: " + i + " - " + e.getMessage());
}
}
// Interact with the found card number field
WebElement cardNumberField = driver.findElement(By.id("cardNumber"));
if (cardNumberField != null) {
System.out.println("Found 'cardNumber' field, entering the card number...");
cardNumberField.sendKeys(numCard);
} else {
System.out.println("'cardNumber' field not found within the iframe.");
}
// Repeat similar steps for expiration date and CVC fields if they are in different iframes
// Switch back to main content
driver.switchTo().defaultContent();
// Input name on card
WebElement nameField = driver.findElement(By.name("name_on_card"));
if (nameField != null) {
System.out.println("Found 'name_on_card' field, entering the cardholder's name...");
nameField.sendKeys(nameCard);
} else {
System.out.println("'name_on_card' field not found in the main context.");
}
// Optionally click the submit button or take further actions
} catch (NoSuchElementException e) {
System.err.println("Error: Element not found - " + e.getMessage());
} catch (Exception e) {
System.err.println("Error during the execution of inputPaymentDetails method: " + e.getMessage());
}
}