Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

#148 Added optionalWaitUntilCondition and optionalWaitWhileCondition #191

Conversation

georgkunze
Copy link

#148 Added optionalWaitUntilCondition and optionalWaitWhileCondition function.

@georgkunze georgkunze requested a review from oomelianchuk June 3, 2021 15:54
Copy link
Contributor

@oomelianchuk oomelianchuk left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Would it also make sense to allow users to pass their own waiting time to the method, analog to Selenide $(element).waitUntil(condition, waitingTime)?

Comment on lines 503 to 513
try
{
if (element.has(condition))
{
result = true;
break;
}
}
catch (ElementNotFound e)
{
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You don't need to catch the ElementNotFound exception here because element.has(condition) doesn't assert anything but just checks if the condition matches.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done

Comment on lines 543 to 553
try
{
if (element.has(not(condition)))
{
result = true;
break;
}
}
catch (ElementNotFound e)
{
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You don't need to catch the ElementNotFound exception here because element.has(condition) doesn't assert anything but just checks if the condition matches.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done

openBlogPage();
SelenideElement privaceDialog = $("#privacy-message");
boolean isVisible = SelenideAddons.optionalWaitUntilCondition(privaceDialog, visible);
assertTrue(isVisible);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please, add a message to the assertion, so that in case of test failure, its cause could be easily found

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done

long startTime = new Date().getTime();
boolean isHidden = SelenideAddons.optionalWaitUntilCondition(privaceDialog, hidden);
long endTime = new Date().getTime();
assertFalse(isHidden);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please, add a message to the assertion, so that in case of test failure, its cause could be easily found

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done

long startTime = new Date().getTime();
boolean isHidden = SelenideAddons.optionalWaitWhileCondition(privaceDialog, visible);
long endTime = new Date().getTime();
assertFalse(isHidden);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please, add a message to the assertion, so that in case of test failure, its cause could be easily found

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done

boolean isHidden = SelenideAddons.optionalWaitWhileCondition(privaceDialog, visible);
long endTime = new Date().getTime();
assertFalse(isHidden);
Assert.assertTrue(endTime - startTime > Neodymium.configuration().optionalElementRetryTimeout());
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please, add a message to the assertion, so that in case of test failure, its cause could be easily found

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done

boolean isHidden = SelenideAddons.optionalWaitUntilCondition(privaceDialog, hidden);
long endTime = new Date().getTime();
assertFalse(isHidden);
Assert.assertTrue(endTime - startTime > Neodymium.configuration().optionalElementRetryTimeout());
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

use static import like you did in assertTrue(isVisible);

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done

boolean isHidden = SelenideAddons.optionalWaitWhileCondition(privaceDialog, visible);
long endTime = new Date().getTime();
assertFalse(isHidden);
Assert.assertTrue(endTime - startTime > Neodymium.configuration().optionalElementRetryTimeout());
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

use static import like you did in assertTrue(isHidden);

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done

boolean isHidden = SelenideAddons.optionalWaitUntilCondition(privaceDialog, hidden);
long endTime = new Date().getTime();
assertFalse(isHidden);
Assert.assertTrue(endTime - startTime > Neodymium.configuration().optionalElementRetryTimeout());
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If we know, that the SelenideAddons.optionalWaitUntilCondition(privaceDialog, hidden); should return false, then we can expect, that the method should take maximal waiting time -> Neodymium.configuration().optionalElementRetryTimeout() * Neodymium.configuration().optionalElementRetryCount();. This means, we can improve assert with following code:

Suggested change
Assert.assertTrue(endTime - startTime > Neodymium.configuration().optionalElementRetryTimeout());
int waitingTime = Neodymium.configuration().optionalElementRetryTimeout() * Neodymium.configuration().optionalElementRetryCount();
Assert.assertTrue(Range.between(waitingTime, waitingTime + Neodymium.configuration().optionalElementRetryTimeout()).contains(Math.toIntExact(endTime - startTime)));

where Range comes from org.apache.commons.lang3 package.
What do you think?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done

boolean isHidden = SelenideAddons.optionalWaitWhileCondition(privaceDialog, visible);
long endTime = new Date().getTime();
assertFalse(isHidden);
Assert.assertTrue(endTime - startTime > Neodymium.configuration().optionalElementRetryTimeout());
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If we know, that the SelenideAddons.optionalWaitWhileCondition(privaceDialog, visible); should return false, then we can expect, that the method should take maximal waiting time -> Neodymium.configuration().optionalElementRetryTimeout() * Neodymium.configuration().optionalElementRetryCount();. This means, we can improve assert with following code:

Suggested change
Assert.assertTrue(endTime - startTime > Neodymium.configuration().optionalElementRetryTimeout());
int waitingTime = Neodymium.configuration().optionalElementRetryTimeout() * Neodymium.configuration().optionalElementRetryCount();
Assert.assertTrue(Range.between(waitingTime, waitingTime + Neodymium.configuration().optionalElementRetryTimeout()).contains(Math.toIntExact(endTime - startTime)));

where Range comes from org.apache.commons.lang3 package.
What do you think?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done

@georgkunze georgkunze requested a review from oomelianchuk June 4, 2021 12:28
Copy link
Contributor

@oomelianchuk oomelianchuk left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sorry, I formulated the comment poorly

Would it also make sense to allow users to pass their own waiting time to the method, analog to Selenide $(element).waitUntil(condition, waitingTime)?

What I wanted to say is, that it would be great to have another method, which would allow to pass the waiting time. These methods would help if the user needs different waiting times at different spots. At the same time, the user should be able to configure the default waiting time (similar to $(element).should(condition))You have adjusted the optionalWaitUntilCondition and optionalWaitWhileCondition to accept waiting time as a parameter and now you can create wrapper methods, which would pass the Neodymium.configuration().optionalElementRetryTimeout() as a waiting time. So we would have:

  • optionalWaitWhileCondition(SelenideElement element, Condition condition, long waitingTime) analog to $(element).waitWhile(condition, waitingTime)
  • optionalWaitUntilCondition(SelenideElement element, Condition condition, long waitingTime) analog to $(element).waitUntil(condition, waitingTime)
  • optionalWaitWhileCondition(SelenideElement element, Condition condition) analog to $(element).shouldNot(condition)
  • optionalWaitUntilCondition(SelenideElement element, Condition condition) analog to $(element).should(condition)

Copy link
Contributor

@oomelianchuk oomelianchuk left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

BTW, it would be great, if you could separate the test code into smaller sections using newlines. In this case, the test code would be easier to read and therefore to support. The comments are also appreciated :)

long startTime = new Date().getTime();
boolean isHidden = SelenideAddons.optionalWaitWhileCondition(privaceDialog, visible);
long endTime = new Date().getTime();
assertFalse("the privacy message dialog was unexpectedly during within the timeframe", isHidden);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
assertFalse("the privacy message dialog was unexpectedly during within the timeframe", isHidden);
assertFalse("the privacy message dialog was unexpectedly hidden during the timeframe", isHidden);

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Don't you agree with the suggested changes? Why?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sorry, did not see. Should be in now.

long startTime = new Date().getTime();
boolean isHidden = SelenideAddons.optionalWaitWhileCondition(privaceDialog, visible, customWaitingTime);
long endTime = new Date().getTime();
assertFalse("the privacy message dialog was unexpectedly during within the timeframe", isHidden);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
assertFalse("the privacy message dialog was unexpectedly during within the timeframe", isHidden);
assertFalse("the privacy message dialog was unexpectedly hidden within the timeframe", isHidden);

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Don't you agree with the suggested changes? Why?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sorry, did not see. Should be in now.

Comment on lines 579 to 591
boolean result = false;
int counter = 0;
while (counter < Neodymium.configuration().optionalElementRetryCount())
{
counter++;
if (element.has(not(condition)))
{
result = true;
break;
}
Selenide.sleep(waitingTime);
}
return result;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think, we can reuse the optionalWaitUntilCondition method here because not(condition) is also a condition

Suggested change
boolean result = false;
int counter = 0;
while (counter < Neodymium.configuration().optionalElementRetryCount())
{
counter++;
if (element.has(not(condition)))
{
result = true;
break;
}
Selenide.sleep(waitingTime);
}
return result;
return optionalWaitUntilCondition(element, not(condition), waitingTime);

@georgkunze georgkunze requested a review from oomelianchuk June 7, 2021 09:15
Copy link
Contributor

@oomelianchuk oomelianchuk left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think the changes are good now. Great job!

Copy link
Contributor

@occupant23 occupant23 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@georgkunze Please take care of the comments

@@ -5,6 +5,8 @@
import static com.codeborne.selenide.Condition.hidden;
import static com.codeborne.selenide.Condition.visible;
import static com.codeborne.selenide.Selenide.$;
import static org.junit.Assert.assertFalse;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please remove the static imports for assertFalse and assertTrue and call the functions like

Assert.assert...

Until now we use this scheme at most of our test cases and I would like to have the same approach for similar things.


// check that runtime of the wait until method was as long as expected
int waitingTime = customWaitingTime * Neodymium.configuration().optionalElementRetryCount();
Assert.assertTrue("Runtime was shorter than expected", Range.between(waitingTime, waitingTime + customWaitingTime)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please adjust the error message to something like
Runtime was not within the expected range
since the runtime can also be longer.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please refactor this into one private validation method in order to avoid code duplication throughout the 4 test cases. Try to perform the same validation. The other test cases also check with Range.contains

openBlogPage();

// wait until the optional privacy dialog is visible
SelenideElement privaceDialog = $("#privacy-message");
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

privacyDialog

Copy link
Contributor

@occupant23 occupant23 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@georgkunze Please take care of the comments

openBlogPage();

// wait until the optional privacy dialog is not visible anymore, which will fail
SelenideElement privaceDialog = $("#privacy-message").shouldBe(visible);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

privacyDialog

openBlogPage();

// wait until the optional privacy dialog is visible
SelenideElement privaceDialog = $("#privacy-message");
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

privacyDialog

openBlogPage();

// wait until the optional privacy dialog is not visible anymore, which will fail
SelenideElement privaceDialog = $("#privacy-message").shouldBe(visible);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

privacyDialog

@georgkunze georgkunze requested a review from occupant23 June 11, 2021 14:37
@oomelianchuk
Copy link
Contributor

I just thought it might be more convenient for the user if we let them enter a full waiting time instead of a polling interval. What do you think?

@occupant23 occupant23 self-assigned this Jun 14, 2021
@occupant23
Copy link
Contributor

occupant23 commented Jun 17, 2021

I just thought it might be more convenient for the user if we let them enter a full waiting time instead of a polling interval. What do you think?

@oomelianchuk and @georgkunze I haven't spotted this yet. I just tested the formal side of the code until now. But this sounds very intriguing.
@georgkunze Please adjust the logic accordingly. The polling time can be computed out of the total waiting time and the number of retries.

@occupant23
Copy link
Contributor

@georg final decision maxWaitingTime and pollingInterval you can implement a function similar to JavaScriptUtils.until

@oomelianchuk
Copy link
Contributor

I think the changes are great now. The only thing I personally would like to change is the type of maxWaitingTime and pollingInterval. Selenide mostly requires waiting time in type long, so it would be more convenient to be able to pass the same type to the optional wait methods. WDYT?

@georgkunze
Copy link
Author

I think the changes work. There is nothing to add from my side.

@occupant23
Copy link
Contributor

I think the changes are great now. The only thing I personally would like to change is the type of maxWaitingTime and pollingInterval. Selenide mostly requires waiting time in type long, so it would be more convenient to be able to pass the same type to the optional wait methods. WDYT?

@oomelianchuk Good point I adjusted this. Additionally, I will set up a separate issue to refacture the other timings.

@occupant23 occupant23 merged commit 9dccc40 into develop Jul 5, 2021
@occupant23 occupant23 deleted the #148-Create-a-function-to-stabilize-test-that-are-flaky-due-to-page-features-that-are-not-relevant-for-the-test branch July 5, 2021 09:23
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Create a function to stabilize test that are flaky due to page features that are not relevant for the test
3 participants