问题
I am in a situation where I need to use for loop and If else block in cypress
Scenario: Once I login to an application, I need to read an element's text which is rounded in the below screenshot.
This element will appear within 20-90 seconds after I log in, when I refreshed the screen. so I need to write something like this, wait for element, if it appears reads the text and returns the value, if not wait for 10 seconds reload the page and do the process again.
function waitAndreload() {
for (let i = 0; i < 10; i++) {
cy.get("#ele").then(ele => {
if (ele.text()) {
return ele.text();
} else {
cy.wait(10000);
cy.reload();
}
});
}
}
How to write this in cypress, as cypress won't support if-else or for loops
回答1:
Below is a technical solution, but first is want to explain what I believe is a better solution.
Adding to @dwelle comment, it seems like what you're trying to do is not a best-practice in terms of the the design of the test. Tests should be designed to be deterministic and should control all relevant inputs that may affect the expected result.
More specifically, is this text something that a real user should see and use, or only something that the developers put for debugging or testing purposes? If it's for a real user, then what determines if it should appear or not? (Is it purely random?, If so, see below) if it's for testing or debugging purposes, talk to the developers and come up with a better solution in which you can control whether this text appears or not directly. If it's something that the user should see and it's not random, then consider what conditions should be met in order for the text to appear, and design the test in a way that makes this condition true, either by controlling the actual necessary preconditions or by using mocks to simulate that condition. Again, I recommend that you consult with the developers to help you find the best approach.
In case it is "purely" random, then ask the developers to provide a way for you to specify the seed of the random generator, and then you'll be able to control it too.
As promised, in case you still want the technical solution for the specific problem, without redesigning the test, then there trick is to use recursion. Something like this:
function getEnvironment() {
function getEnvironmentInternal(retires) {
if (retires == 0)
throw "text didn't appear after the specified retires";
return ele.text().then(text => {
if(text)
return cy.wrap(text);
cy.wait(10000);
cy.reload();
return getEnvironmentInternal(retires-1);
});
)};
return getEnvironmentInternal(10);
}
// usage:
getEnvironment().then(text => {
// do something with text...
}
回答2:
I wrote my own helper command for checking repeatedly until condition is fulfilled.
/**
* Waits until call to cb() resolves to something truthy.
*
* @param message {string} Error message on timeout
* @param cb {() => Cypress Command "promise"}
* Callback for checking if condition is met should return cypress command cy.xxxxxx.then()
* which resolves to undefined if polling should continue. Throwing an error aborts before
* waiting for timeout to complete.
*/
Cypress.Commands.add('waitFor', (message, cb, errorReporterCb = null, timeoutMs = 5000) => {
const startTime = new Date().getTime();
const giveupTime = startTime + timeoutMs;
const startTimeout = 5;
const ctx = {};
const errorReporter =
errorReporterCb ||
(err => {
throw err;
});
function checkCb(timeout) {
const currentTime = new Date().getTime();
if (currentTime > giveupTime) {
const err = new Error(`Timeout while waiting for (${currentTime - startTime}ms): ${message}`);
errorReporter(err, ctx);
} else {
cy.wait(timeout);
return cb(ctx).then(result => {
if (result === undefined || result === false) {
return checkCb(timeout * 2); // always wait twice as long as the last time
} else {
return result;
}
});
}
}
return checkCb(startTimeout);
});
With this you can implement polling loop like:
cy.waitFor(
'reload page until #ele contain text',
() => cy.reload().get("#ele").then(ele => ele.text() ? ele.text() : undefined),
null, 60000);
回答3:
I would say using a for loop for something like this and refreshing is an anti-pattern. It looks like you're waiting for the text to show up in the element, not the element itself.
If so, can you stub the response to the server so it comes back right away? If that doesn't work, just do a cy.wait('@<whatever you aliased your response as>')
until the call is completed
回答4:
So it seems you just want to wait that element is appeared and then take text value.
So something like cy.get('#ele', {timeout: 60000}).should('exist').invoke('text').then(text => ...work with text value)
Assertions in cypress have built-in retry mechanism, so if it fails before timeout expire - it will retry previous command.
来源:https://stackoverflow.com/questions/58770726/using-for-loop-and-if-else-in-cypress