Неправильный порядок отправки ключей Selenium при вводе данных с кредитной карты Stripe - PullRequest
0 голосов
/ 02 октября 2018

После отправки ключей в поле ввода с селеном результат не соответствует ожидаемому - ключи вставлены в неправильном порядке.

например, send_keys ('4242424242424242') -> результат - "4224242424242424"

РЕДАКТИРОВАТЬ: На некоторых машинах я наблюдаю проблему только случайно, 1 случай из 10 попыток.На другой машине это 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

Я могу передать это, послав ключи по одному с небольшой задержкой - но это также не на 100% надежно (плюс ужасно медленно)

Я не уверен, что этопроблема с селеном (3.14.1) / chromedriver (2.41.578737) или если я делаю что-то не так.

Есть идеи, пожалуйста?

Ответы [ 3 ]

0 голосов
/ 23 октября 2018

У нас точно такая же проблема на MacOS и Ubuntu 18.04, а также на нашем CI-сервере с транспортиром 5.4.1 и той же версией селена и хромедривера.Он начал давать сбой только после Chrome 69, хуже в v70.

Обновление - Работает (на данный момент)

После долгих дальнейших исследований я вспомнил, что React имеет тенденцию кпереопределить события изменения / ввода, а также то, что значения на входе кредитной карты, входе ccv и т. д. отображаются из состояния компонента реакции, а не только из входного значения.Поэтому я начал искать и нашел Каков наилучший способ вызвать событие onchange в реакции 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}`)
}

Предыдущая идея (работала только изредка):

Я настроил минимальное воспроизведение, используя https://stripe.com/docs/stripe-js/elements/quickstart, и он успешен, когда тесты выполняются последовательно, но не параллельно (мы думаем, чтодля фокусировки / размытия при переключении на iframes).

Наше решение аналогично, хотя мы наблюдали за просмотром тестов, что input.clear () не работал на входах tel, которые используются в 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}`)
}
0 голосов
/ 06 марта 2019

Я столкнулся с подобной проблемой в Ubuntu 14.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 голосов
/ 03 октября 2018

Редактировать

Большое спасибо @Benno - его ответ был правильным.Я просто добавлю решение Python, которое работало для меня, на основе его JS

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 </li>
  2. . Чтобы протестировать последнюю версию Chrome, я буду использовать решение React

Что я узнал

Проблема проявлялась в основном на MacOS, довольно редко на Windows (скорее всего, в игре есть и другие факторы, это всего лишьнаблюдение)

Я провел эксперимент с 100 повторениями заполнения формы.

Mac - 68 сбоев

Windows - 6 сбоев

Файлы cookie / локальная история (как указано в комментариях) , похоже, не являются проблемой.Веб-драйвер всегда порождал «чистый» экземпляр браузера, без файлов cookie или локального хранилища.

...