硒在条带化信用卡输入中发送密钥的顺序不正确

Vaf*_*iik 7 selenium google-chrome selenium-chromedriver stripe-payments

将键发送到带有硒的输入字段后,结果不符合预期-键插入顺序不正确。

例如send_keys('4242424242424242')->结果为“ 4224242424242424”

编辑:在某些机器上,我仅随机观察到此问题,每10次尝试就有1次。在另一台机器上是10/10

这是专门针对Stripe付款表单而发生的+我仅在Chrome 69版中才看到此问题(在以前的版本中可以正常运行)

这可以在示例Stripe网站上轻松复制:https//stripe.github.io/elements-examples/

样本python代码:

from selenium import webdriver
driver = webdriver.Chrome()
driver.get('https://stripe.github.io/elements-examples/')
driver.switch_to.frame(driver.find_element_by_tag_name('iframe'))  # First iframe
cc_input = driver.find_element_by_css_selector('input[name="cardnumber"]')
cc_input.send_keys('4242424242424242')
Run Code Online (Sandbox Code Playgroud)

结果: 在此处输入图片说明

我可以通过稍微延迟一点一点地发送密钥来通过此操作-但这也不是100%可靠的(而且速度非常慢)

我不确定这是否是硒(3.14.1)/ chromedriver(2.41.578737)的问题,或者我做错了什么。

有什么想法吗?

Ben*_*nno 4

我们在 MacOS 和 Ubuntu 18.04 上以及使用 Protractor 5.4.1 和相同版本的 selenium 和 chromedriver 的 CI 服务器上遇到了完全相同的问题。从 Chrome 69 开始它就开始失败,在 v70 中更糟。

更新 - 工作(暂时)

经过进一步的调查,我记得 React 倾向于覆盖更改/输入事件,并且信用卡输入、ccv 输入等中的值是从 React 组件状态渲染的,而不仅仅是从输入值渲染的。所以我开始寻找,发现What is the best way to trigger onchange event in React js

我们的测试正在运行(目前):

//Example credit input
function creditCardInput (): ElementFinder {
  return element(by.xpath('//input[contains(@name, "cardnumber")]'))
}

/// ... snippet of our method ...
await ensureCreditCardInputIsReady()
await stripeInput(creditCardInput, ccNumber)
await stripeInput(creditCardExpiry, ccExpiry)
await stripeInput(creditCardCvc, ccCvc)
await browser.wait(this.hasCreditCardZip(), undefined, 'Should have a credit card zip')
await stripeInput(creditCardZip, ccZip)
await browser.switchTo().defaultContent()
/// ... snip ...

async function ensureCreditCardInputIsReady (): Promise<void> {
  await browser.wait(ExpectedConditions.presenceOf(paymentIFrame()), undefined, 'Should have a payment iframe')
  await browser.switchTo().frame(await paymentIFrame().getWebElement())
  await browser.wait(
    ExpectedConditions.presenceOf(creditCardInput()),
    undefined,
    'Should have a credit card input'
  )
}

/**
 * SendKeys for the Stripe gateway was having issues in Chrome since version 69. Keys were coming in out of order,
 * which resulted in failed tests.
 */
async function stripeInput (inputElement: Function, value: string): Promise<void> {
  await browser.executeScript(`
      var nativeInputValueSetter = Object.getOwnPropertyDescriptor(window.HTMLInputElement.prototype, "value").set;
          nativeInputValueSetter.call(arguments[0], '${value}');
      var inputEvent = new Event('input', { bubbles: true});
          arguments[0].dispatchEvent(inputEvent);
        `, inputElement()
  )
  await browser.sleep(100)
  const typedInValue = await inputElement().getWebElement().getAttribute('value')

  if (typedInValue.replace(/\s/g, '') === value) {
    return
  }

  throw new Error(`Failed set '${typedInValue}' on ${inputElement}`)
}
Run Code Online (Sandbox Code Playgroud)

以前的想法(只是偶尔有效):

我已经使用https://stripe.com/docs/stripe-js/elements/quickstart设置了一个最小的重现,并且当测试按顺序运行但不是并行运行时它会成功(我们认为由于切换到iframe)。

我们的解决方案是类似的,尽管我们通过观察测试注意到 input.clear() 不适用于 iframe 中使用的电话输入。

这仍然偶尔会失败,但频率要低得多。

/**
 * Types a value into an input field, and checks if the value of the input
 * matches the expected value. If not, it attempts for `maxAttempts` times to
 * type the value into the input again.
 *
 * This works around an issue with ChromeDriver where sendKeys() can send keys out of order,
 * so a string like "0260" gets typed as "0206" for example.
 *
 * It also works around an issue with IEDriver where sendKeys() can press the SHIFT key too soon
 * and cause letters or numbers to be converted to their SHIFT variants, "6" gets typed as "^", for example.
 */
export async function slowlyTypeOutField (
  value: string,
  inputElement: Function,
  maxAttempts = 20
): Promise<void> {
  for (let attemptNumber = 0; attemptNumber < maxAttempts; attemptNumber++) {
    if (attemptNumber > 0) {
      await browser.sleep(100)
    }

    /*
      Executing a script seems to be a lot more reliable in setting these flaky fields than using the sendKeys built-in
      method. However, I struggled in finding out which JavaScript events Stripe listens to. So we send the last key to
      the input field to trigger all events we need.
     */
    const firstPart = value.substring(0, value.length - 1)
    const secondPart = value.substring(value.length - 1, value.length)
    await browser.executeScript(`
        arguments[0].focus();
        arguments[0].value = "${firstPart}";
      `,
      inputElement()
    )
    await inputElement().sendKeys(secondPart)
    const typedInValue = await inputElement().getAttribute('value')

    if (typedInValue === value) {
      return
    }

    console.log(`Tried to set value ${value}, but instead set ${typedInValue} on ${inputElement}`)
  }

  throw new Error(`Failed after ${maxAttempts} attempts to set value on ${inputElement}`)
}
Run Code Online (Sandbox Code Playgroud)