web-dev-qa-db-ja.com

Seleniumは、Stripeクレジットカード入力でキーの順序が正しくありません

Seleniumを使用して入力フィールドにキーを送信した後、結果は期待どおりではありません。キーが誤った順序で挿入されます。

例えば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')

結果:enter image description here

少し遅れてキーを1つずつ送信することで、これをパスすることができますが、これも100%信頼できるわけではありません(さらに非常に遅い)

これがSelenium(3.14.1)/ chromedriver(2.41.578737)の問題なのか、それとも何か問題があるのか​​わかりません。

何かアイデアはありますか?

8
Vafliik

MacOSとUbuntu18.04だけでなく、分度器5.4.1と同じバージョンのSeleniumとchromedriverを備えたCIサーバーでもまったく同じ問題が発生しています。 Chrome 69以降、v70ではさらに悪化しました。

更新-動作中(今のところ)

さらに詳しく調べた結果、Reactは変更/入力イベントをオーバーライドする傾向があり、クレジットカード入力、ccv入力などの値はReact入力値だけでなく、コンポーネントの状態。そこで調べ始めたところ、 react jsでonchangeイベントをトリガーする最良の方法は何ですか

私たちのテストは(今のところ)機能しています:

//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}`)
}

前のアイデア(たまにしか機能しませんでした):

https://stripe.com/docs/stripe-js/elements/quickstart を使用して最小限の再現をセットアップしました。テストを順次実行すると成功しますが、並列ではありません(フォーカスが原因だと思います)。/blurはiframeに切り替えるときに問題になります)。

私たちのソリューションは似ていますが、テストを見て、input.clear()がiframeで使用されるtel入力で機能しないことに気づきました。

これはまだ時々失敗しますが、はるかに少ない頻度です。

/**
 * 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}`)
}
3
Benno

多分私の解決策は誰かのために役立つでしょう:

sendKeys(" 4242424242424242")を使用しました
cvcフィールドについても同じ

文字列の前にスペースを入れると、実際にはセレニドで機能します+ chrome + Java

私はubuntu14.04で同様の問題に直面しました、次のトリックは私を助けました。それ以来、問題は発生していません。まず、通常のsend_keysメソッドを使用しました。次に、実行スクリプトを呼び出して値を更新しました

    input_data = "someimputdata"
    some_xpath = "//*[contains(@id,'input_fax.number_')]"
    element = web_driver_obj.find_element_by_xpath(some_xpath)
    element.clear()
    element.send_keys(input_data)
    web_driver_obj.execute_script("arguments[0].value = '{0}';".format(input_data), element)
0

編集

@Bennoに感謝します-彼の答えは正しかったです。彼のJSに基づいて、私のために働いたpythonソリューションを追加します

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"]')
value = "4242424242424242"
driver.execute_script('''
 input = arguments[0];
 var nativeInputValueSetter = Object.getOwnPropertyDescriptor(window.HTMLInputElement.prototype, "value").set; 
 nativeInputValueSetter.call(input, "{}");
 var eventCard = new Event("input", {{bubbles: true}});
 input.dispatchEvent(eventCard);
 '''.format(value), cc_input)
driver.switch_to.default_content()
driver.quit()

数時間試した後、私はあきらめて、これが本当にランダムな問題であるという事実を受け入れ、回避策を実行しました。

  1. 更新する必要がない場合は、Chromeバージョン<69のままにします
  2. 最新のChromeをテストするために、Reactソリューションを使用します

私が見つけたもの

この問題は主にMacOSで発生し、Windowsではほとんど発生しませんでした(おそらく他の要因が関係しています。これは単なる観察です)

フォームへの入力を100回繰り返して実験を実行しました。

Mac-68回の失敗

Windows-6回の失敗

クッキー/ローカル履歴(コメントで示唆されているように)は問題ではないようです。 Webドライバーは、Cookieやローカルストレージを使用せずに、常にブラウザの「クリーンな」インスタンスを生成しました。

0
Vafliik

送信したい入力要素と文字列を受け取る独自の汎用SendKeysメソッドを作成できます。このメソッドは、文字列を個々の文字に分割してから、各文字でSeleniumsendkeysメソッドを使用します。

0
Jordan Saunders