Moving slider with Cypress

Viewed 8873

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.

5 Answers

The answer is very and very simple. I found the solution coincidentally pressing enter key for my another test(date picker) and realized that pressing left or right arrow keys works for slider. You can achieve the same result using props as well. The only thing you need to do is to add this dependency: cypress-react-selector and following instructions here: cypress-react-selector

Example of using {rightarrow}

it("using arrow keys", () => {
  cy.visit("localhost:3000");

  const currentValue  = 20000;
  const targetValue = 35000;
  const increment = 500;
  const steps = (targetValue - currentValue) / increment;
  const arrows = '{rightarrow}'.repeat(steps); 

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

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

})

@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}"
);

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();
});

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)

  })
})

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 }]
  })
Related