Cypress' type() not compatible with react 15.6's onChange?
arthens opened this issue ยท 13 comments
- Operating System: Mac os X and Linux
- Cypress Version: 0.19.2 (app) and 0.13.1 (cli)
- Browser/Browser Version: Chrome 58
Are you requesting a feature or reporting a bug?
Bug
Our cypress tests started to fail when we bumped react from 15.5.4 to 15.6.1. We debugged the problem and came to the conclusion that onChange
doesn't fire as it should when the input field is being manipulated using .type('some text')
.
cli + headless chrome: broken
app + real chrome: broken
user interacting with the browser: working
React component:
<input
className="input-text"
onChange={() => {
// this will executed with react 15.5.4, but not with 15.6.1
}}
value={this.props.value}
type="text"
data-stock-url />
and this is how we are manipulating it
cy.visit('https://...')
.get('[data-stock-url]').type('https://google.com/stock/123')
onChange doesn't fire anymore. The problem went away when we downgraded back to 15.5.4.
Not sure if related, but react-dom's CHANGELOG says:
which is suspicious. Is cypress' type()
relying on an implementation detail that is not true anymore?
Cypress doesn't rely on any specific JS frameworks' implementation. It's firing events at the lowest possible level.
We'll take a look at this. In 0.20.0
we have many fixes going out for cy.type
, so we'll first see if it's a new potential problem.
We use React internally on all of our projects. @chrisbreiding can you look at this?
The input logic in React now dedupe's change events so they don't fire more than once per value. It listens for both browser onChange
/onInput
events as well as set
s on the DOM node value
prop (when you update the value via javascript). This has the side effect of meaning that if you update the input's value manually input.value = 'foo'
then dispatch a ChangeEvent
with { target: input }
React will register both the set
and the event, see it's value is still `'foo', consider it a duplicate event and swallow it.
This works fine in normal cases because a "real" browser initiated event doesn't trigger set
s on the element.value
. You can bail out of this logic secretly by tagging the event you trigger with a simulated
flag and react will always fire the event. https://github.com/jquense/react/blob/9a93af4411a8e880bbc05392ccf2b195c97502d1/src/renderers/dom/client/eventPlugins/ChangeEventPlugin.js#L128
I can confirm this problem is happening.
We've been able to reproduce and fix it. We're going to release an 0.19.4
patch with just this fix in it.
Thank you @brian-mann
Thank you for your quick work. :)
Thanks for the timely info, @jquense! That helped a lot.
Fixed in 0.19.4
.
@brian-mann i'm having the same issue in a non-related project. Could you share the way you fixed this? I'm guessing you switched from input radio
to <button>
or <a>
tags? Or did you manage to keep the radios and get the onChange
event to fire on each click? Thanks in advance.
@brian-mann btw if you used my simulated
workaround I'd bring your use case up to on React team. That has been removed in v16.
We did use the simulated
hack just to get out the patch immediately (because we created several tests for it and they all pass)
However we thought about it later and have a "real" fix being implemented that's not React specific, and should actually fix the problem permanently...
This is from my notes:
To clarify I don't think the react team needs to make this change. We determined the exact situation where Cypress deviates from the way a real browser behaves. A real browser will NOT call the value
setter when changes the value from a default action. It will just "magically" update.
Since we call the setter (this is the difference). However as long as we call the original setter (that 3rd party code cannot possibly get notified of) we'll essentially reproduce the real browser behavior without needing any 3rd party library to change the way it works.
sounds great :)