Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Appium cannot type value into html input controlled by React >= 15.6.0 #9002

Open
ikhilko opened this issue Aug 9, 2017 · 35 comments

Comments

Projects
None yet
@ikhilko
Copy link

commented Aug 9, 2017

The problem

Appium cannot type value into html input controlled by React (webview in hybrid app). The problem appears after React 15.6.0 release, when was changed implementation of controlled inputs.

Environment

  • Appium version (or git revision) that exhibits the issue: 1.6.6-beta.3
  • Last Appium version that did not exhibit the issue (if applicable):
  • Desktop OS/version used to run Appium: Mac
  • Node.js version (unless using Appium.app|exe): v8.2.1
  • Mobile platform/version under test: IOS 10.3.1
  • Real device or emulator/simulator: Real device
  • Appium CLI or Appium.app|exe: CLI

Details

The issue is not about appium itself, it relates appium-remote-debugger. The problem is about how appium (and selenium itself) programmatically type value into inputs. The same issue was fixed in cypress-io/cypress#536. React wount fix it facebook/react#10347. Possible workaround cypress-io/cypress#536 (comment).

Thanks in advance.

@dpgraham

This comment has been minimized.

Copy link
Contributor

commented Aug 9, 2017

Thanks for reporting this @ikhilko

What happens when you try inputting values? Does it get automatically cleared or does it throw an exception?

@ikhilko

This comment has been minimized.

Copy link
Author

commented Aug 10, 2017

@dpgraham appium itself worked without exceptions. Value is presented in input and in attribute "value" but React's onChange handler doesn't fired.
So as I can understand appium setted "value" attribute and then fired onchange event with that value. But at that case React will prevent onChange to be called, because attribute "value" and value in event are the same. So the problem was introduced in latest versions of React, but it should work that way because when you typing value from keyboard it will fire onchange event but not set programatically value to "value" attributes.

@amedvedjev

This comment has been minimized.

Copy link

commented Aug 10, 2017

@ikhilko just wonder how about Paste event. It should trigger also onChange...

@ikhilko

This comment has been minimized.

Copy link
Author

commented Aug 10, 2017

@amedvedjev all events from user-side will work right. Doesn't work automation because the way of typing value used in automation (appium|selenium) differs from native behaviour of browser.
The difference is that automation first called element.value = value and then fire event with that value. The native behaviour is just fire event and value magicaly setted to element's attribute "value".
React listen to change event from input and doesn't fire own onChange in case when value from input's attribute value is equal to value from event.

@dpgraham

This comment has been minimized.

Copy link
Contributor

commented Aug 10, 2017

I don't know if we can fix this on the Appium/Selenium side, unless we applied a React-specific workaround.

Do you know if there's a way to force the onChange event, like using a JS execution script on the element?

@ikhilko

This comment has been minimized.

Copy link
Author

commented Aug 11, 2017

@dpgraham possible React-specific workaround cypress-io/cypress#536 (comment) , but looks like it will not work in future React 16 and up versions..

@dilankadon

This comment has been minimized.

Copy link

commented Aug 17, 2017

@dpgraham,
Q. What happens when you try inputting values? Does it get automatically cleared or does it throw an exception?

A. The value gets cleared automatically.
using 1.6.6.beta.4

@ikhilko

This comment has been minimized.

Copy link
Author

commented Sep 5, 2017

@dpgraham Is it any possibilities to fix it?

@dpgraham dpgraham added the Bug label Sep 5, 2017

@dpgraham

This comment has been minimized.

Copy link
Contributor

commented Sep 5, 2017

I'm not sure if it's something that we'll a fix for on the Appium side in the near future, unless somebody wants to submit a PR.

By the sounds of this, it's not just an Appium issue but also a Selenium issue. My advice would be to open a ticket with the React team and find out if there's a way to programmatically force an onChange event.

@amedvedjev

This comment has been minimized.

@jlipps

This comment has been minimized.

Copy link
Member

commented Sep 7, 2017

using Simulate seems like a good idea, and could probably be triggered by executeScript

@ikhilko

This comment has been minimized.

Copy link
Author

commented Sep 18, 2017

@jdeff So, your suggestion is to bundle React test-utils into production build of application to be able to execute "Simulate" function at DOM element? Sounds really not working for us..

@dpgraham

This comment has been minimized.

Copy link
Contributor

commented Sep 18, 2017

@ikhilko Couldn't you have just have a dev build that bundles react-test-utils?

@ikhilko

This comment has been minimized.

Copy link
Author

commented Oct 12, 2017

@dpgraham sorry, we are testing prod app. Not a great solution for us

@dhartleycs

This comment has been minimized.

Copy link

commented Nov 14, 2017

An ugly and slow workaround for iOS Safari execution - change your iOSDriver context to native, tap on the field and then press the "Close" button. This seems to trigger the event.

@gvozdeva-ira

This comment has been minimized.

Copy link

commented Jan 29, 2018

@ikhilko try this solution https://github.com/vitalyq/react-trigger-change , it works for me

@iwiiiwi

This comment has been minimized.

Copy link

commented Jan 30, 2018

@gvozdeva-ira Thanks, it also works for me.

@serhatbolsu

This comment has been minimized.

Copy link

commented Mar 14, 2018

This is still an issue if you are testing an react mobile web page. Is anyone has a solution ?

  • Android is ok,
  • Selenium is ok, from web view
  • iOS Safari, is writing the field but not triggering action change
@LolaYan

This comment has been minimized.

Copy link

commented Jul 3, 2018

This is still an issue. Is anyone has a solution ?

@simonh26

This comment has been minimized.

Copy link

commented Jul 30, 2018

Are there any solutions for this?

@tmonautoguy

This comment has been minimized.

Copy link

commented Aug 1, 2018

Definitely still an issue.

@simonh26

This comment has been minimized.

Copy link

commented Aug 1, 2018

@tmonautoguy

This comment has been minimized.

Copy link

commented Aug 1, 2018

@simonh26 did you try out @dhartleycs work around? Im trying it out now.

@simonh26

This comment has been minimized.

Copy link

commented Aug 1, 2018

@gvozdeva-ira

This comment has been minimized.

Copy link

commented Aug 2, 2018

This work around which work for me and fix the issue:
webFile = requests.get('https://unpkg.com/react-trigger-change/dist/react-trigger-change.js')
self.driver.execute_script(webFile.text)
self.driver.find_element(*self._search_input_locator).send_keys('some text')
search_input = self.driver.find_element(*self._search_input_locator)
self.driver.execute_script("reactTriggerChange(arguments[0]);", search_input)

@mykola-mokhnach mykola-mokhnach removed the Bug label Aug 2, 2018

@simonh26

This comment has been minimized.

Copy link

commented Aug 2, 2018

@gvozdeva-ira thanks this work around is working for us.

@silicakes

This comment has been minimized.

Copy link

commented Aug 2, 2018

@gvozdeva-ira - I've tried the same thing, however it removes the value I've set for the input.

For those using webdirever / browserstack / appium along with a webapp

The reason, with webdriver/browserstack at least, is that setValue, doesn't trigger changes in the state.

Using a webapp inside iphone also has its own issues with moveToObject, focus and other methods that doesn't play well together since they don't force the keyboard out.

A missing keyboard will cause appium to error with something like: Appium error: An unknown server-side error occurred while processing the command. Original error: Error Domain=com.facebook.WebDriverAgent Code=1 "Keyboard is not present" UserInfo={NSLocalizedDescription=Keyboard is not present}.

Here's a quick workaround for those with webdriverio/appium/browserstack/jasmine:

app index.tsx

(window as any).reactTriggerChange = (el: HTMLElement) => {...}

testSuite.js

  browser.execute(() => {
      let el = document.querySelector('#mainSearchInput');
      // forcing value to always return our custom string
      Object.defineProperty(el,"value", {
        get: () => 'My search term'
      })
      reactTriggerChange(el);
      return true;
    });
@CoryKniefel

This comment has been minimized.

Copy link

commented Aug 6, 2018

@gvozdeva-ira thank you, worked for me

@dheeraj1988

This comment has been minimized.

Copy link

commented Aug 30, 2018

I had a similar issue. When i try using sendKeys() on username field, it works fine. But as soon as I click on password field to use sendKeys() again, it clears the value in username field. Then I tried doing the same thing with JavascriptExecutor and I was able to enter values in both fields but clicking on login button after that clears the value.

Problem was react context is not firing onChange() event as we interact with the DOM using javascript. So, I had to manually do that. After so much of hassle, I found a good easy solution.

Setter .value= is not working as we wanted because React library overrides input value setter but we can call the function directly on the input as context.

var nativeInputValueSetter = Object.getOwnPropertyDescriptor(window.HTMLInputElement.prototype, "value").set;
var event = new Event('input', { bubbles: true});

nativeInputValueSetter.call(document.getElementById("username-field-id").dispatchEvent(ev);, 'username-input');
document.getElementById("username-field-id").dispatchEvent(event);

For textarea element you should use prototype of HTMLTextAreaElement class.

@mykola-mokhnach

This comment has been minimized.

Copy link
Collaborator

commented Oct 3, 2018

Linking some theory on this topic https://appiumpro.com/editions/36

@wswebcreation

This comment has been minimized.

Copy link

commented Nov 15, 2018

I have the same issue with a Hybrid App, and on Safari on iOS. The site is build with jQuery. I've been able to solve this with the following code, but it's not a nice workaround.

// some WebdriverIO code
browser.setValue('#username', 'test-user');
triggerOnChange('#username');

/**
 * Trigger the onChange on an element
 *
 * @param {string} selector the selector
 */
function triggerOnChange(selector) {
    if (browser.isIOS) {
        browser.execute((elementSelector) => {
            const event = new Event('change');
            document.querySelector(elementSelector).dispatchEvent(event);
        },selector);
    }
}
@nshoes

This comment has been minimized.

Copy link

commented Mar 12, 2019

Ran into this today as well while setting up Appium with our React app inside iOS. Input value visually changes but components state does not. I did not hit this issue on Android. Appium 1.11.1.

@vutukuriswathi

This comment has been minimized.

Copy link

commented Mar 27, 2019

I also ran in in to the similar issue when i am testing hybrid app webviews on iOS using appium 1.10.0.I tried the below options in my java code but no use.
JavascriptExecutor jsExecutor = (JavascriptExecutor) MobileHelper.getIosDriver();
jsExecutor.executeScript("$(arguments[0]).change();", toPort());

Can some one please help me how to resolve using appium on iOS?

Appreicated your help.

@PMudra

This comment has been minimized.

Copy link

commented Apr 2, 2019

Edit: Might only be of concern when using webdriver:

I was looking for a workaround for this issue today. For me a combination of different solutions worked.

Custom command:

function setNativeValue(element, value) {
  const { set: valueSetter } = Object.getOwnPropertyDescriptor(element, 'value') || {};
  const prototype = Object.getPrototypeOf(element);
  const { set: prototypeValueSetter } = Object.getOwnPropertyDescriptor(prototype, 'value') || {};

  if (prototypeValueSetter && valueSetter !== prototypeValueSetter) {
    prototypeValueSetter.call(element, value);
  } else if (valueSetter) {
    valueSetter.call(element, value);
  } else {
    throw new Error('The given element does not have a value setter');
  }
  element.dispatchEvent(new Event('input', { bubbles: true }));
}

function customCommand(value) {
  if (driver.isIOS) {
    browser.execute(setNativeValue, this, value);
  } else {
    this.setValue(value);
  }
}

browser.addCommand('setNativeValue', customCommand, true);

Usage example:

$('input[type=email]').setNativeValue('example@test.com');

References:

@ckzgraphics

This comment has been minimized.

Copy link

commented Jun 25, 2019

This comment seemed to do the trick as well!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
You can’t perform that action at this time.