What is the recommended approach for dealing with a debounced input in Cypress E2E tests.
For example, let’s assume I have the following component that uses lodash/debounce
import React, { useState, useCallback } from 'react';
import _ from 'lodash';
const DebouncedInput = () => {
const [query, setQuery] = useState("");
const [searchResult, setSearchResult] = useState("");
// The debounced function
const searchApi = useCallback(
_.debounce(async (inputValue) => {
setSearchResult(`User searched for: ${inputValue}`);
}, 200),
[]
);
const handleChange = (e) => {
setQuery(e.target.value);
searchApi(e.target.value);
};
return (
<div>
<input
type="text"
value={query}
onChange={handleChange}
placeholder="Type something..."
/>
{searchResult && <p>{searchResult}</p>}
</div>
);
};
export default DebouncedInput;
How would I go about making sure that my Cypress tests won’t be flaky?
In my mind there are two ways:
- Dealing with cy.clock() and using cy.tick()
cy.get('[data-cy="foo"]').type('hey').tick(250)
- Taking advantage of
cy.type
and thedelay
option
cy.get('[data-cy="foo"]').type('hey', {delay: 250});
I tried both approaches and seem to work. But I am not sure if there is actually a recommended way to do this or if one apporach is better than the other.
2
Answers
You can use cy.wait(The same delay period of the used debounce).
Since your
debounce()
is on the firing of thesearchApi()
function, the first example.type('hey').tick(250)
will fire only once in the testThe lodash code uses
setTimeout()
internally soclock()/tick()
will ensure the debounce does not fire until you tick, by which time all chars will be typed into the input.But
.type('hey', {delay: 250})
limits the rate at which characters are typed, so at delay of 250 you will firesearchApi()
for every character in the typed string.At the moment, all you are doing is echoing the typed value to the page. I assume that you will actually perform a search call.
To test that your debounce is effective, add an intercept at the top of the test.
tick() method
delay option
Without a searchAPI call, you would need to spy on the
setSearchResult
function to see how many calls were made.Without cy.intercept() on an API call
Technically @Alaa MH has a workable solution, but the example he gives isn’t terrific.
If there is no API call, you can use
cy.wait()
because the waiting time is always going to be just a little longer than the debounce time (add a little for app internal javascript to process.However, using
clock()/tick()
is the safest option, though.