cypress-react-selector cy.react should retry
bahmutov opened this issue · 8 comments
When using https://github.com/abhinaba-ghosh/cypress-react-selector noticed cy.react
command does not retry finding the element.
Workaround - use cy.get
or cy.contains
to first confirm the component is present by finding the DOM, the fetch the component using cy.react
Test in cypress/component/advanced/react-book-example/src/components/ProductsList.spec.js
it('retries until component is found', () => {
// or the command times out
const products = [
{ id: 1, name: 'First item' },
{ id: 2, name: 'Second item' },
]
const response = {
json: cy.stub().resolves({
products,
}),
}
cy.stub(window, 'fetch')
.withArgs('http://myapi.com/products')
// simulate slow load by delaying the response
.resolves(Cypress.Promise.resolve(response).delay(1000))
mount(<ProductsList />)
// use https://github.com/abhinaba-ghosh/cypress-react-selector
// to find DOM elements by React component constructor name, props, or state
cy.waitForReact(1000, '#cypress-root')
// cy.react should requery the elements until
// the assertions that follow it are satisfied,
// or, if there are no assertions, that an element is found
cy.react('AProduct').should('have.length', 2)
})
Screenshot - notice that 2 components appear after 1 second, but by then the test has already failed
To recreate the problem:
- clone the repo
- install dependencies with
npm i
- start Cypress
npx cypress open
- open test cypress/component/advanced/react-book-example/src/components/ProductsList.spec.js
- uncomment the test above
Hi @bahmutov , need some help to understand the cypress promises better. I am following the cypress-xpath
retry logic. But, it is throwing error like:
export const react = (subject, component, props, state) => {
cy.log(`Finding ${getIdentifierLogs(component, props, state)}`);
const getNodes = () => {
let elements;
return cy.window({ log: false }).then((window) => {
// some node calculations
return nodes;
});
};
const resolveValue = () => {
return Cypress.Promise.try(getNodes).then((value) => {
return cy.verifyUpcomingAssertions(value, options, {
onRetry: resolveValue,
});
});
};
return resolveValue().then((value) => {
return value;
});
};
Got the solution:
const resultPromise = resolveValue().then((value) => {
return value;
});
return cy.wrap(resultPromise);
super
@bahmutov , but basically it is not retrying getNodes()
at all. It is waiting for the timeout, but not retrying the window
function. Can you please suggest?
cy.wrap
is not a solution I think. So, I. removed it. Here is the code and respective error now. Handling promises is getting tricky here.
export const react = (subject, component, props, state, options = {}) => {
if (subject === null) {
throw new Error(`Previous component found null.`);
}
const getNodes = () => {
return cy.window({ log: false }).then((window) => {
let elements;
if (!window.resq) {
throw new Error(
'[cypress-react-selector] not loaded yet. did you forget to run cy.waitForReact()?'
);
}
if (subject) {
elements = window.resq.resq$$(component, subject[0]);
} else {
elements = window.resq.resq$$(component);
}
if (props) {
elements = elements.byProps(props);
}
if (state) {
elements = elements.byState(state);
}
if (!elements.length) {
console.log(
`Component not found ${getIdentifierLogs(component, props, state)}`
);
return null;
}
let nodes = [];
elements.forEach((elm) => {
var node = elm.node,
isFragment = elm.isFragment;
if (isFragment) {
nodes = nodes.concat(node);
} else {
nodes.push(node);
}
});
return nodes;
});
};
const resolveValue = () => {
return Cypress.Promise.try(getNodes).then((value) => {
return cy.verifyUpcomingAssertions(value, options, {
onRetry: resolveValue,
});
});
};
return resolveValue().then((value) => {
return value;
});
};
Error output:
I think you should move cy.window
and .then
out of the retry - because you do want to get the window object ones, and then only retry the .resq.resq$$
part again and again
works