-
Notifications
You must be signed in to change notification settings - Fork 3.2k
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
can't trigger 'onChange' for an input type='range' rendered by React #1570
Comments
Can confirm, |
ok, so I've found sort of a workaround. Workaround would be: use |
I am also running into the same issue. |
I'm encountering the same issue. Using I've been playing around with a number of combinations using So far, I have this piece of code that sets and changes the value of the input in the DOM, and it moves the range slider (the circle knob), but the input still doesn't recognize that there's an interaction: cy.get('input[type=range]').eq(0)
.then(($el) => {
cy.wrap($el)
.click(200, 15)
.invoke('val', 2)
.trigger('change', { force: true })
.invoke('attr', 'value', 2)
}); I've tried Does anyone know a good way of triggering an |
Same issue here. Holler if anyone knows any workarounds. Code: handleSlider = event => {
this.setState({
value: event.target.value,
});
};
<StyledSliderValue>
<SliderValue data-test="weeds-slider-value">{this.state.value} per acre</SliderValue>
</StyledSliderValue>
<SliderInput
onChange={this.handleSlider}
type="range"
min="0"
max="1000"
step="100"
value={this.state.value}
data-test="weeds-slider"
/> Cypress test: it('updates value on slider', ()=> {
cy.get('[data-test="weeds-slider"]')
.invoke('val', 500)
.trigger('input')
.click()
cy.get('[data-test="weeds-slider-value"]')
.should('have.text', '500 per acre')
}) In Cypress, I can see the slider knob move to the center, but the value does not change. In my app, everything works fine. I've tried |
I have put together a workaround for this issue that does not require switching to the There are two parts:
That works like this: // React overrides the DOM node's setter, so get the original, as per the linked Stack Overflow
const nativeInputValueSetter = Object.getOwnPropertyDescriptor(window.HTMLInputElement.prototype, 'value').set;
describe('Range', () => {
it('Updates the value and UI when changing a range input', () => {
cy.get('input[type="range"]').then(($range) => {
// get the DOM node
const range = $range[0];
// set the value manually
nativeInputValueSetter.call(range, 15);
// now dispatch the event
range.dispatchEvent(new Event('change', { value: 15, bubbles: true }));
});
});
}); I'm fairly new to the cypress ecosystem, so perhaps someone can do this in a more cypress-y way, but this solves the problem. |
thank you @davemyersworld! that fixed the issue for me!! |
@jennifer-shehane @annezhou920 @davemyersworld <div class="horizontal-slider" style="position: relative;">
<div class="bar bar-0" style="position: absolute; left: 0px; right: 225px; will-change: left, right;"></div>
<div class="bar bar-1" style="position: absolute; left: 0px; right: 0.0139758px; will-change: left, right;"></div>
<div class="bar bar-2" style="position: absolute; left: 224.986px; right: 0px; will-change: left, right;"></div>
<div class="handle handle-0 " tabindex="0" role="slider" aria-valuenow="162.08" aria-valuemin="162.08"
aria-valuemax="9177.64" style="position: absolute; z-index: 1; left: 0px;"></div>
<div class="handle handle-1 " tabindex="0" role="slider" aria-valuenow="9177.08" aria-valuemin="162.08"
aria-valuemax="9177.64" style="position: absolute; z-index: 2; left: 224.986px;"></div>
</div> cypress step cy.get('#content-section div.horizontal-slider')
.find('div.handle.handle-1')
.invoke('attr', 'style', "position: absolute; z-index: 1; left: 150.025px; will-change: left;")
.trigger('change')
cy.get('#content-section div.horizontal-slider')
.find('div.handle.handle-0')
.invoke('attr', 'style', "position: absolute; z-index: 1; left: 56.025px; will-change: left;")
.click() |
✋ Also effected by this same issue:
HTML<label for="font-size">Font Size</label>
<input type="range" id="font-size" min="0" max="3" step="1" value="1"> Cypresscy.get('#font-size')
.invoke('val', 0)
.trigger('change') Workaround 🎉I have implemented @davemyersworld's workaround, and can confirm it works! 👍 I've taken his example a wee further and abstracted a higher order function to perform the input value change. Here is the Cypress test to change and test the input range value from 0 to 1 to 2 to 3: // TODO: Remove `.then()` workaround and replace with commented steps when
// issue is resolved: https://github.com/cypress-io/cypress/issues/1570
const nativeInputValueSetter = Object.getOwnPropertyDescriptor(
window.HTMLInputElement.prototype,
'value'
).set
const changeRangeInputValue = $range => value => {
nativeInputValueSetter.call($range[0], value)
$range[0].dispatchEvent(new Event('change', { value, bubbles: true }))
}
describe('Font Settings', () => {
it('User can adjust font settings', () => {
cy.visit('/settings')
cy.contains('Sample text.')
.should('have.css', 'font-size')
.should('eq', '24px')
cy.get('#font-size')
// .invoke('val', 0)
// .trigger('change')
.then(input => changeRangeInputValue(input)(0))
cy.contains('Sample text.')
.should('have.css', 'font-size')
.should('eq', '18px')
cy.get('#font-size')
// .invoke('val', 1)
// .trigger('change')
.then(input => changeRangeInputValue(input)(1))
cy.contains('Sample text.')
.should('have.css', 'font-size')
.should('eq', '24px')
cy.get('#font-size')
// .invoke('val', 2)
// .trigger('change')
.then(input => changeRangeInputValue(input)(2))
cy.contains('Sample text.')
.should('have.css', 'font-size')
.should('eq', '44px')
cy.get('#font-size')
// .invoke('val', 3)
// .trigger('change')
.then(input => changeRangeInputValue(input)(3))
cy.contains('Sample text.')
.should('have.css', 'font-size')
.should('eq', '50px')
}
} Looking forward to replacing workaround when a fix is available. Thanks for triaging @jennifer-shehane |
This might help anyone coming here looking for answers but this helped us: cy.get('[type="range"]')
.first()
.invoke('val', 25)
.trigger('change', { data: '25' }) |
Same issue. I was put on track to solving it with the short article "Simulate React On-Change On Controlled Components": https://hustle.bizongo.in/simulate-react-on-change-on-controlled-components-baa336920e04 (Which I since see is the same sol'n as in the SO reference cited above.) |
It looks like this issue has been solved, but I wanted to put this here in case anyone comes across this in google like I did today - I was able to register a custom command in in the Cypress.Commands.add("controlledInputChange", (input, value) => {
const nativeInputValueSetter = Object.getOwnPropertyDescriptor(
window.HTMLInputElement.prototype,
'value'
).set
const changeInputValue = inputToChange => newValue => {
nativeInputValueSetter.call(inputToChange[0], newValue)
inputToChange[0].dispatchEvent(new Event('change', { newValue, bubbles: true }))
}
return cy.get(input).then(input => changeInputValue(input)(value))
}) This is used in the test as: cy.get('input[type="range"]').eq(0).then(input => {
value = 10.toString() // or whatever number, but it needs to be a passed in as a string
cy.controlledInputChange(input, value)
}) |
Need some help with the material-ui slider: I didn't manage to move the slider with some value of offset (sideways like) Current Cypress it('Slider test', () => {
cy.visit('https://material-ui.com/components/slider/')
cy.get(
'.jss407 > .MuiGrid-container > .MuiGrid-grid-xs-true > .MuiSlider-root > .MuiSlider-thumb',
)
.focus()
.trigger('mousedown')
.trigger('mousemove', 0, 20, { which: 1 })
.trigger('mouseup')
})
}) Versions:
Perhaps someone can help? |
*** The issue is resolved by the code below: *** - Thx to Vinod Mathew@k.vinodmathew_gitlab *** it.only('Slider test', () => {
cy.viewport(1900, 1000);
cy.visit('https://material-ui.com/components/slider/')
cy.get('[aria-labelledby="discrete-slider"]').first()
.trigger('mousedown', { which: 1 }, { force: true })
.trigger('mousemove', 881, 288, { force: true })
.trigger('mouseup') |
Has anyone managed to program the workaround with Typescript? I'm getting always a Type Error - Illegal invocation for this: const nativeInputValueSetter = Object.getOwnPropertyDescriptor(
window.HTMLInputElement.prototype,
'value'
)?.set
const changeRangeInputValue = ($range: JQuery<HTMLElement>) => (value: number) => {
nativeInputValueSetter?.call($range[0], value)
$range[0].dispatchEvent(new Event('change', { value, bubbles: true }))
} |
Hi @Elshaikh are you available to solve the slider? i have similar slider type with you and cant make the slider move <div data-testid="range-slider-handle" class="crang-slider-handle" tabindex="0" aria-valuemax="12345" aria-valuemin="0" aria-valuenow="0" draggable="false" role="slider" style="position: absolute; z-index: 0; cursor: grab; user-select: none; touch-action: none; transform: translate(-8px, -6px);">
<span data-label="12" class="crang-slider-handle-label is-default" style="padding: 0px 15px;">$0</span>
</div> |
I was also running into this issue and had to use beausmith's workaround (adapted from Dave Myers'). I had to make a couple further adjustments, so I'll share them here, hoping they can save other people some time. const nativeInputValueSetter = Object.getOwnPropertyDescriptor(
window.HTMLInputElement.prototype,
"value"
).set;
const changeRangeInputValue = ($range) => (value) => {
return new Promise((resolve) => {
nativeInputValueSetter.call($range[0], value);
$range[0].dispatchEvent(new Event("input", { value, bubbles: true }));
$range[0].dispatchEvent(new Event("change", { value, bubbles: true }));
resolve();
});
}; What changed:
Hopefully I didn't misunderstand any Cypress or DOM details - but the above solution, adapted from @beausmith 's, worked well for me, letting me test sliders on my project. |
Thanks @eFFeeMMe, that works great! |
Here's a variation on @ryanfiller 's solution that is implemented as a child command for a more succinct API (I think): Usage cy.get('.slider-input').controlledInputChange('5000000') Command Cypress.Commands.add('controlledInputChange', { prevSubject: 'element'}, (input, value) => {
const nativeInputValueSetter = Object.getOwnPropertyDescriptor(
window.HTMLInputElement.prototype,
'value'
).set
const changeInputValue = inputToChange => newValue => {
nativeInputValueSetter.call(inputToChange[0], newValue)
inputToChange[0].dispatchEvent(new Event('change', { newValue, bubbles: true }))
}
return cy.get(input).then(input => changeInputValue(input)(value))
}) |
@afraser I've tried your method for a React field but doesn't work for me. I'm filling in a bunch of fields (which does work) but there's a button that activates when they are all filled it which isn't happening. I've been told this button works off the 'onChange 'event of the fields so it should work right? Thanks |
@alexp1980 hrm... not sure what to tell you. I'm still pretty new to cypress myself and this is working for me when it comes to setting a slider input value. My other controlled inputs however don't even need it. I just use Maybe try adding logging to your component to verify that your values are/aren't being set. If they are, then something else is going on with your button. |
has anyone found the typescript version of this workaround? |
@JonathanAbdon I couldn't solve it until now :( |
@davemyersworld saviour you are!! Many thanks and very much appreciated for the below: // React overrides the DOM node's setter, so get the original, as per the linked Stack Overflow
const nativeInputValueSetter = Object.getOwnPropertyDescriptor(window.HTMLInputElement.prototype, 'value').set;
describe('Range', () => {
it('Updates the value and UI when changing a range input', () => {
cy.get('input[type="range"]').then(($range) => {
// get the DOM node
const range = $range[0];
// set the value manually
nativeInputValueSetter.call(range, 15);
// now dispatch the event
range.dispatchEvent(new Event('change', { value: 15, bubbles: true }));
});
});
}); |
@cjoecker, @JonathanAbdon this works with typescript -> #1570 (comment) |
It doesn't work for TypeScript. I received the error: So I've solved my problem with this issue for TypeScript using the following code: Cypress.Commands.add('setSliderValue', { prevSubject: 'element' },
(subject, value) => {
const element = subject[0]
const nativeInputValueSetter = Object.getOwnPropertyDescriptor(
window.HTMLInputElement.prototype,
'value'
)?.set
nativeInputValueSetter?.call(element, value)
element.dispatchEvent(new Event('input', { bubbles: true }))
}
)
declare namespace Cypress {
interface Chainable {
setSliderValue(value: number): Chainable<void>
}
} And use it in the test: cy.get('input[name="sliderComponentInput"]').setSliderValue(25) |
@RobertoPegoraro, this worked for me, thanks! |
@davemyersworld Can't thank you enough for this solution!! |
|
This one really helped me navigate around this. My app is built using nextjs and whenever Id use cypress triggers to slide the range, it wouls reset back to zero. Thank you for this |
@sbasinger Thank you from 2024 |
Current behavior:
Changes the slider value.
Doesn't trigger the onChange handler.
Desired behavior:
should trigger the onChange handler
Steps to reproduce:
Set up a React app with an input type='range' and an onChange handler
Try to trigger the onChange event from cypress.
Versions
"cypress": "^2.1.0"
"react": "^15.4.2"
The text was updated successfully, but these errors were encountered: