'Moving slider with Cypress

I've got a Slider component from rc-slider and I need Cypress to set the value of it.

<Slider
 min={5000}
 max={40000}
 step={500}
 value={this.state.input.amount}
 defaultValue={this.state.input.amount}
 className="sliderBorrow"
 onChange={(value) => this.updateInput("amount",value)} 
 data-cy={"input-slider"}
 />

This is my Cypress code:

it.only("Changing slider", () => {
    cy.visit("/");
    cy.get(".sliderBorrow")
      .invoke("val", 23000)
      .trigger("change")
      .click({ force: true })
  });

What I've tried so far does not work. Starting point of slider is 20000, and after test runs it goes to 22000, no matter what value I pass, any number range.

Looks like it used to work before, How do interact correctly with a range input (slider) in Cypress? but not anymore.



Solution 1:[1]

@darkseid's answer helped guide me reach an optimal solution.

There are two steps

  1. Click the slider's circle, to move the current focus on the slider.
  2. Press the keyboard arrow buttons to reach your desired value.

My slider jumps between values on the sliders, therefore this method would work. (I am using Ion range slider)

This method doesn't require any additional depedency.

slider image

// Move the focus to slider, by clicking on the slider's circle element
cy.get(".irs-handle.single").click({ multiple: true, force: true });
// Press right arrow two times
cy.get(".irs-handle.single").type(
  "{rightarrow}{rightarrow}"
);

Solution 2:[2]

You might be able to tackle this using Application actions, provided you are able to modify the app source code slightly.

Application actions give the test a hook into the app that can be used to modify the internal state of the app.

I tested it with a Function component exposing setValue from the useState() hook.
You have used a Class component, so I guess you would expose this.updateInput() instead, something like

  if (window.Cypress) {
    window.app = { updateInput: this.updateInput };
  }

App: index.js

import React, { useState } from 'react';
import { render } from 'react-dom';
import './style.css';

import Slider from 'rc-slider';
import 'rc-slider/assets/index.css';

function App() {

  const [value, setValue] = useState(20000);

  // Expose the setValue() method so that Cypress can set the app state
  if (window.Cypress) {
    window.app = { setValue };
  }

  return (
    <div className="App">
      <Slider
        min={5000}
        max={40000}
        step={500}
        value={value}
        defaultValue={value}
        className="sliderBorrow"
        onChange={val => setValue(val)}
        data-cy={"input-slider"}
      />
      <div style={{ marginTop: 40 }}><b>Selected Value: </b>{value}</div>
    </div>
  );
}

render(<App />, document.getElementById('root'));

Test: slider.spec.js

The easiest way I found assert the value in the test is to use the aria-valuenow attribute of the slider handle, but you may have another way of testing that the value has visibly changed on the page.

describe('Slider', () => {

  it("Changing slider", () => {
    cy.visit("localhost:3000");

    cy.get('.rc-slider-handle')
      .should('have.attr', 'aria-valuenow', 20000)

    cy.window().then(win => {
      win.app.setValue(35000);
    })

    cy.get('.rc-slider-handle')
      .should('have.attr', 'aria-valuenow', 35000)

  })
})

Solution 3:[3]

I got this to work with the popular react-easy-swipe:

cy.get('[data-cy=week-picker-swipe-container]')
  .trigger('touchstart', {
    touches: [{ pageY: 0, pageX: 0 }]
  })
  .trigger('touchmove', {
    touches: [{ pageY: 0, pageX: -30 }]
  })

Solution 4:[4]

For whoever comes across this with Material UI/MUI 5+ Sliders:

First off, this github issue and comment might be useful: https://github.com/cypress-io/cypress/issues/1570#issuecomment-606445818. I tried changing the value by accessing the input with type range that is used underneath in the slider, but for me that did not do the trick.

My solution with MUI 5+ Slider:

<Slider
  disabled={false}
  step={5}
  marks
  data-cy="control-percentage"
  name="control-percentage"
  defaultValue={0}
  onChange={(event, newValue) =>
    //Handle change
  }
/>

What is important here is the enabled marks property. This allowed me to just click straight on the marks in the cypress test, which of course can also be abstracted to a support function.

cy.get('[data-cy=control-percentage]').within(() => {
  // index 11 represents 55 in this case, depending on your step setting.
  cy.get('span[data-index=11]').click();
});

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 Rahul Makhija
Solution 2 Richard Matsen
Solution 3 Fabricio Cunha
Solution 4 KeukenkastjeXYZ