In EarlGrey, what's the non-polling way to wait for an element to appear? - xctest

Currently I wait for an element to appear like this:
let populated = GREYCondition(name: "Wait for UICollectionView to populate", block: { _ in
var errorOrNil: NSError?
EarlGrey().selectElementWithMatcher(collectionViewMatcher)
.assertWithMatcher(grey_notNil(), error: &errorOrNil)
let success = (errorOrNil == nil)
return success
}).waitWithTimeout(20.0)
GREYAssertTrue(populated, reason: "Failed to populate UICollectionView in 20 seconds")
Which polls constantly for 20 seconds for collection view to populate. Is there a better, non-polling way of achieving this?

EarlGrey recommends using its synchronization for waiting for elements rather than using sleeps or conditional checks like waits wherever possible.
EarlGrey has a variable kGREYConfigKeyInteractionTimeoutDuration value in GREYConfiguration that is set to 30 seconds and states -
* Configuration that holds timeout duration (in seconds) for action and assertions. Actions or
* assertions that are not scheduled within this time will fail due to timeout.
Since you're waiting for 20 seconds for your check, you can instead simply change it to -
EarlGrey().selectElementWithMatcher(collectionViewMatcher)
.assertWithMatcher(grey_notNil(), error: &errorOrNil)
and it'll be populated without a timeout.

I like to connect Earl Grey with basic XCTest and I've come up with this simple solution to problem of waiting for elements:
app.webViews.buttons["logout()"].waitForExistence(timeout: 5)
app.webViews.buttons["logout()"].tap()

Related

How do I wait for an element to disappear in Cypress?

I'd like to preface this by saying that I really tried looking here and anywhere else for an answer but without a succes.
Here is the problem:
I'm trying to automate a test for a web application that is loading a bigger amount of data and therefore has to be loading for a while. I have to wait for the page to load to do the next step and I'm trying to figure how to wait for the loading gif to be complete so that my tests can continue. The gif is a basic spinning thingy.
I would like to avoid using implicit wait time (cy.wait()) that is really only the last resort if noone is able to solve this.
What I found o far are these two functions:
cy.waitFor - this seems to be used mostly in situations, where you are waiting for an element to apper - I've tried this in different scenarios and works perfcetly but I havent been able to apply it here.
cy.waitUntil - this seems to be the thing I'm looking for but there is one huge problem. This function seems to have a timeout and I haven't been able to change it any other way than by changing timeouts globally for all the functions. If I set the global timeout to some longer period (minute +), then it works exactly how I would want it to work but obviously I dont really want to have such a long timeout for everything.
The way I see it, there are two possible solutions to this problem>
to change/turn off the timeout for the waitUntil function.
2)Or to somehow make it work with waitFor, because there seems to be no implicit timeout there and it just kind of waits forever for something to happen.
here is the code snippet of the situation:
cy.get('#load-data-button').click() // this clicks the button that stars the loading proces
cy.waitUntil(() => cy.get('body > div > my-app > billing > div > div > div.text-center > img').should('not.be.visible',) ) // this is the function that waits until the loading gif is invisible
I would be eternaly grateful for a solution, because unfortunatelly no one at my company is really a Cypress expert so they are not able to help.
Could you try something like:
cy.get([loading-spinner-identifier]).should("not.be.visible", { timeout: 60000 });
cy.get([an element that should now be visible]).should("be.visible");
It's a bit rough and ready, but I've used it when waiting for spinners to finish up. This waits a minute to see the spinner isn't there and then checks to see that something I'm expecting is there. I'd had varying success with this, but it has helped in some situations.
You can indeed use cy.waitUntil(). As you can see in the documentation of that package (https://www.npmjs.com/package/cypress-wait-until#arguments), the timeout that the function has is just the default (5000 ms) of an argument you can change it. You can even change how often cypress checks the condition you want, so if you do something like:
cy.waitUntil(() => cy.get('body > div > my-app > billing > div > div > div.text-center > img').should('not.be.visible'), {
errorMsg: 'The loading time was too long even for this crazy thing!',
timeout: 300000,
interval: 5000
});
...it will try for 300 seconds (5 minutes) each 5 seconds (just an example with very long timeout).
Also, maybe you can consider to wait until some element is visible after that loading spinner, instead of checking if it dissapeared. If you want to specifically test it, is fine, but if some element appears or becomes interactable after the loading, it could happen that is still not in the state you want, for a fraction of a second, after the spinner is not visible. If that is the case, I would avoid the checking of the spinner dissapearing in first place, unless it adds value to your test, and I would just wait for that element to be in the state you want (for example wait until some input is not disabled, element is visible, text appeared somewhere...).
Add positive and negative assertion to make sure loading spinner is visible.
Default Time is 4s for assert statement.
You can increase defaultTime to wait for assertion to validate by adding configuration in cypress.json
{
...
"defaultCommandTimeout": 4000,
...
}
const waitForSpinner = () => {
// First, make sure the loading indicator shows up (positive assertion)
cy.get('[data-qa="qa-waiting-spinner"]').should('be.visible')
// Then Assert it goes away (negative assertion)
cy.get('[data-qa="qa-waiting-spinner"]').should('not.be.visible')
}

JMeter - Avoid threads abrupt shutdown

I have a testPlan that has several transacion controllers (that I called UserJourneys) and each one is composed by some samplers (JourneySteps).
The problem I'm facing is that once the test duration is over, Jmeter kills all the threads and does not take into consideration if they are in the middle of a UserJourney (transaction controller) or not.
On some of these UJs I do some important stuff that needs to be done before the user logs in again, otherwise the next iterations (new test run) will fail.
The question is: Is there a way to tell to JMeter that it needs to wait every thread reach the end of its flow/UJ/TransactionController before killing it?
Thanks in advance!
This is not possible as of version 5.1.1, you should request an enhancement at:
https://jmeter.apache.org/issues.html
The solution is to add as first child of Thread Group a Flow Control Action containing a JSR223 PreProcessor:
The JSR223 PreProcessor will contain this groovy code:
import org.apache.jorphan.util.JMeterStopTestException;
long startDate = vars["TESTSTART.MS"].toLong();
long now = System.currentTimeMillis();
String testDuration = Parameters;
if ((now - startDate) >= testDuration.toLong()) {
log.info("Test duration "+testDuration+" reached");
throw new JMeterStopTestException("Test duration "+testDuration+"reached ");
} else {
log.info("Test duration "+testDuration+" not reached yet");
}
And be configured like this:
Finally you can set the property testDuration in millis on command line using:
-JtestDuration=3600000
If you'd like to learn more about JMeter and performance testing this book can help you.

How to change timeout in JDI Test Automation Framework

I need to change timeout for waiting particular webElements. For all of others default is pretty fine. Default timeout could be changed in settings:
timeout.wait.element=10
How to change timeout only for given elements?
For setting specific timeout on element you can use
textField.setWaitTimeout(10);
textField.input("TEXT");
But if you also has some waiting criteria and want to do something after it becomes true you can use
String text = textField.wait(w -> w.getText(), t -> t.equals("test"), 10);
or textField.wait(w -> w.getText().equals("test"), 10); if you just want to wait something
For setting specific implicit timeout on element you can use #WaitTimeout(sec)
#WaitTimeout(sec) — set sec seconds implicit wait for the element
or if you just want to wait until element is present in DOM:
someWebElement.waitFor(10).displayed();

Will the Implicit wait will move on if findElement action is complete?

Implicit Wait : If wait is set, it will wait for specified amount of time for each findElement/findElements call. It will throw an exception if action is not complete.
Assume we set implicit wait to 10 secs. My question is will selenium move on to next step if findElement action is complete before 10 secs?
Yes. Setting implicit wait causes the driver object to wait for the set time if the element it is looking for is not found immediately. The driver object keeps polling the DOM every 500 milliseconds until it finds the element or the time-out expires.
This is the explanation from official Selenium documentation page:
An implicit wait is to tell WebDriver to poll the DOM for a certain
amount of time when trying to find an element or elements if they are
not immediately available. The default setting is 0. Once set, the
implicit wait is set for the life of the WebDriver object instance.
So, to answer your question in short, yes it continues with executing next steps as soon as it finds the element(s) it is looking for. You may also understand that to be the case from a simple experiment like #sircapsalot has shown in his answer.
Answer:
Yes. It will continue with the next step if it finds the element before the implicit timeout is hit.
Proof of concept:
#Test
public void test29800926() {
driver.get("http://ddavison.io/tests/getting-started-with-selenium.htm");
driver.manage().timeouts().implicitlyWait(30000, TimeUnit.MILLISECONDS);
System.out.println(driver.findElement(By.id("click")).getText());
}
Instead of waiting the total of 30 seconds that I set the implicit wait to (30000ms / 1000 = 30sec), it found it immediately and continued to print the text of the element.

Does Selenium implicit wait always take the entire wait time or can it finish sooner?

Does Selenium implicit wait always take the entire wait time or can it finish sooner? If I set the implicit wait to 10 seconds, could a call to .findElement finish in a few seconds or would it always take the entire 10 seconds?
This page implies that it waits the full 10 seconds, which is very confusing because its not what the javadoc implies.
The following code comment from WebDriver.java implies that its a polling action which can finish sooner than the implicit timeout is defined at. BUT, the last sentence in the comment really throws a wrench into that belief and makes me not totally sure about it. If it is actually polling, then how would it "adversely affect test time", since it wouldn't go the entire implicit wait duration?
/**
* from WebDriver.java
* Specifies the amount of time the driver should wait when searching for an element if
* it is not immediately present.
* <p/>
* When searching for a single element, the driver should poll the page until the
* element has been found, or this timeout expires before throwing a
* {#link NoSuchElementException}. When searching for multiple elements, the driver
* should poll the page until at least one element has been found or this timeout has
* expired.
* <p/>
* Increasing the implicit wait timeout should be used judiciously as it will have an
* adverse effect on test run time, especially when used with slower location
* strategies like XPath.
*
* #param time The amount of time to wait.
* #param unit The unit of measure for {#code time}.
* #return A self reference.
*/
Timeouts implicitlyWait(long time, TimeUnit unit);
Also, if anyone can provide information on how often the default "polling" occurs?
It can finish once it was able to find the element. If not it does throws the error and stops. The poll time is again very specific to the driver implementation ( not Java bindings , but the driver part, example: FireFox extension, Safari Extension etc.)
As I have mentioned here, these are very specific to the driver implementation. All driver related calls goes via execute method.
I'm putting up the gist over of the execute method (you can find the full source here):
protected Response execute(String driverCommand, Map<String, ?> parameters) {
Command command = new Command(sessionId, driverCommand, parameters);
Response response;
long start = System.currentTimeMillis();
String currentName = Thread.currentThread().getName();
Thread.currentThread().setName(
String.format("Forwarding %s on session %s to remote", driverCommand, sessionId));
try {
log(sessionId, command.getName(), command, When.BEFORE);
response = executor.execute(command);
log(sessionId, command.getName(), command, When.AFTER);
if (response == null) {
return null;
}
//other codes
}
The line:
response = executor.execute(command);
says the whole story. executor is of type CommandExecutor, so all calls goes to the specific driver class like ChromeCommandExecutor,SafariDriverCommandExecutor, which has their own handling.
So the polling is upto the driver implementation.
If you want to specify the polling time, then you should probably start using Explicit Waits.
As mentioned the code comment:
* When searching for a single element, the driver should poll the page until the
* element has been found, or this timeout expires before throwing a
* {#link NoSuchElementException}.
Its going to wait till that element present, or timeout occurs.
For example, If you set Implicit wait as 10 seconds, .findElement is going to wait maximum of 10 seconds for that element. Suppose that element available in the DOM in 5 seconds, then it will come out of "wait" and start executing next step.
Hope this clarifies.
To my knowledge polling period is not 0.5 seconds with implicit wait. It is the case with explicit wait. Explicit wait polls the DOM every 500ms. Implicit wait, if the element is not found on page load waits for the specified time and then checks again after time is run out. If not found it throws an error