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

10 changes: 8 additions & 2 deletions config/neodymium.properties
Original file line number Diff line number Diff line change
Expand Up @@ -115,11 +115,17 @@
#############################
# The following properties are taken into account if you use functions form the SelenideUtils class.
#
# How often should Selenide try to match a condition if the element is affected by staleness
# How often should the $safe function try to match a condition if the element is affected by staleness
# neodymium.selenideAddons.staleElement.retry.count = 3
#
# How long should Selenide wait between retries in case of element staleness
# How long should the $safe function wait between retries in case of element staleness
# neodymium.selenideAddons.staleElement.retry.timeout = 500
#
# How long should the optionalWait(While|Until)Condition functions wait until it retries the condition
# neodymium.selenideAddons.optional.retry.pollingIntervall= 3000
#
# After which times should the optionalWait(While|Until)Condition functions stop retrying
# neodymium.selenideAddons.optional.retry.timeout = 30000

#############################
#
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,14 @@ public interface NeodymiumConfiguration extends Mutable
@DefaultValue("500")
public int staleElementRetryTimeout();

@Key("neodymium.selenideAddons.optional.retry.pollingIntervall")
@DefaultValue("3000")
public long optionalElementRetryPollingIntervall();

@Key("neodymium.selenideAddons.optional.retry.timeout")
@DefaultValue("30000")
public long optionalElementRetryTimeout();

@Key("neodymium.javaScriptUtils.timeout")
@DefaultValue("2000")
public int javaScriptTimeout();
Expand Down
163 changes: 163 additions & 0 deletions src/main/java/com/xceptance/neodymium/util/SelenideAddons.java
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package com.xceptance.neodymium.util;

import static com.codeborne.selenide.Condition.not;
import static com.codeborne.selenide.Selenide.$$;
import static com.codeborne.selenide.Selenide.open;
import static com.codeborne.selenide.Selenide.sleep;
Expand Down Expand Up @@ -473,4 +474,166 @@ public static void openHtmlContentWithCurrentWebDriver(String htmlContent)
String encodedStuff = Base64.getEncoder().encodeToString(htmlContent.getBytes());
open("data:text/html;charset=utf-8;base64," + encodedStuff);
}

/**
* Waits until an optional element matches a condition. This function will return false if the element does not
* match the given condition or can not be found in the given timeframe. This method will use the default optional
* retry timeout.
* <p>
* The following settings can be configured within the Neodymium configuration to tune the retry behavior:
* </p>
* <ul>
* <li>*
* <li>neodymium.selenideAddons.optional.retry.timeout (default 2000ms pause between retries)</li>
* </ul>
*
* @param element
* the element to match
* @param condition
* the condition for the element
* @return if the element did match the condition within the given retries
*/
public static boolean optionalWaitUntilCondition(SelenideElement element, Condition condition)
{
return optionalWaitUntilCondition(element, condition, null, null);
}

/**
* Waits until an optional element matches a condition. This function will return false if the element does not
* match the given condition or can not be found in the given timeframe.
* <p>
* The following settings can be configured within the Neodymium configuration to tune the retry behavior:
* </p>
* <ul>
* <li>neodymium.selenideAddons.optional.retry.count (default 5 retries)</li>
* </ul>
*
* @param element
* the element to match
* @param condition
* the condition for the element
* @param maxWaitingTime
* the maximum amount of time to wait
* @return if the element did match the condition within the given retries
*/
public static boolean optionalWaitUntilCondition(SelenideElement element, Condition condition, long maxWaitingTime)
{
return optionalWaitUntilCondition(element, condition, maxWaitingTime, null);
}

/**
* Waits until an optional element matches a condition. This function will return false if the element does not
* match the given condition or can not be found in the given timeframe.
* <p>
* The following settings can be configured within the Neodymium configuration to tune the retry behavior:
* </p>
* <ul>
* <li>neodymium.selenideAddons.optional.retry.count (default 5 retries)</li>
* </ul>
*
* @param element
* the element to match
* @param condition
* the condition for the element
* @param maxWaitingTime
* the maximum amount of time to wait
* @param pollingInterval
* the amount of time to wait in between retries
* @return if the element did match the condition within the given retries
*/
public static boolean optionalWaitUntilCondition(SelenideElement element, Condition condition, Long maxWaitingTime, Long pollingInterval)
{
if (maxWaitingTime == null)
{
maxWaitingTime = Neodymium.configuration().optionalElementRetryTimeout();
}
if (pollingInterval == null)
{
pollingInterval = Neodymium.configuration().optionalElementRetryPollingIntervall();
}

boolean result = false;
final long start = System.currentTimeMillis();
while (!result && ((System.currentTimeMillis() - start) < maxWaitingTime))
{
if (element.has(condition))
{
result = true;
break;
}
Selenide.sleep(pollingInterval);
}
return result;
}

/**
* Waits while an optional element matches a condition. This function will return false if the element does match
* the given condition or can not be found after the given timeframe. This method will use the default optional
* retry timeout.
* <p>
* The following settings can be configured within the Neodymium configuration to tune the retry behavior:
* </p>
* <ul>
* <li>*
* <li>neodymium.selenideAddons.optional.retry.timeout (default 2000ms pause between retries)</li>
* </ul>
*
* @param element
* the element to match
* @param condition
* the condition for the element
* @return if the element did stop matching the condition within the given retries
*/
public static boolean optionalWaitWhileCondition(SelenideElement element, Condition condition)
{
return optionalWaitUntilCondition(element, not(condition), null, null);
}

/**
* Waits while an optional element matches a condition. This function will return false if the element does match
* the given condition or can not be found after the given timeframe.
* <p>
* The following settings can be configured within the Neodymium configuration to tune the retry behavior:
* </p>
* <ul>
* <li>neodymium.selenideAddons.optional.retry.count (default 5 retries)</li>
* </ul>
*
* @param element
* the element to match
* @param condition
* the condition for the element
* @param maxWaitingTime
* the maximum amount of time to wait
* @return if the element did stop matching the condition within the given retries
*/
public static boolean optionalWaitWhileCondition(SelenideElement element, Condition condition, long maxWaitingTime)
{
return optionalWaitUntilCondition(element, not(condition), maxWaitingTime, null);
}

/**
* Waits while an optional element matches a condition. This function will return false if the element does match
* the given condition or can not be found after the given timeframe.
* <p>
* The following settings can be configured within the Neodymium configuration to tune the retry behavior:
* </p>
* <ul>
* <li>neodymium.selenideAddons.optional.retry.count (default 5 retries)</li>
* </ul>
*
* @param element
* the element to match
* @param condition
* the condition for the element
* @param maxWaitingTime
* the maximum amount of time to wait
* @param pollingInterval
* the amount of time to wait in between retries
* @return if the element did stop matching the condition within the given retries
*/
public static boolean optionalWaitWhileCondition(SelenideElement element, Condition condition, long maxWaitingTime, long pollingInterval)
{
return optionalWaitUntilCondition(element, not(condition), maxWaitingTime, pollingInterval);
}
}
167 changes: 167 additions & 0 deletions src/test/java/com/xceptance/neodymium/util/SelenideAddonsTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
import java.util.List;
import java.util.concurrent.atomic.AtomicInteger;

import org.apache.commons.lang3.Range;
import org.junit.Assert;
import org.junit.Test;
import org.junit.runner.RunWith;
Expand Down Expand Up @@ -640,4 +641,170 @@ public void testOpenHtmlContentWithCurrentWebDriver()
SelenideAddons.openHtmlContentWithCurrentWebDriver(textHtml);
Assert.assertEquals(text, $("body").getText());
}

private static final long CUSTOM_MAX_WAITING_TIME = 20000;

private static final long CUSTOM_POLLING_INTERVAL = 5000;

private static final String PRIVACY_DIALOG_SELECTOR = "#privacy-message";

private void validateRuntimeIsExpected(Runnable runnable, long expectedTime, long pollingInterval)
{
final long startTime = new Date().getTime();
runnable.run();
final long endTime = new Date().getTime();

final long runtime = endTime - startTime;
// check that runtime of the wait until method was as long as expected
Assert.assertTrue("Runtime was not within the expected range", Range.between(expectedTime, expectedTime + pollingInterval / 2)
.contains(runtime));
}

@Test
public void testOptionalWaitUntil()
{
openBlogPage();

validateRuntimeIsExpected(() -> {
// wait until the optional privacy dialog is visible
boolean isVisible = SelenideAddons.optionalWaitUntilCondition($(PRIVACY_DIALOG_SELECTOR), visible, CUSTOM_MAX_WAITING_TIME);
Assert.assertTrue("the privacy message dialog was not found within the timeframe", isVisible);
},
0,
Neodymium.configuration().optionalElementRetryPollingIntervall());

validateRuntimeIsExpected(() -> {
// try wait until the optional privacy dialog is hidden, which will fail
boolean isHidden = SelenideAddons.optionalWaitUntilCondition($(PRIVACY_DIALOG_SELECTOR), hidden, CUSTOM_MAX_WAITING_TIME);
// check that the is false as is expected
Assert.assertFalse("the privacy message dialog was unexpectedly hidden during the timeframe", isHidden);
},
CUSTOM_MAX_WAITING_TIME,
Neodymium.configuration().optionalElementRetryPollingIntervall());
}

@Test
public void testOptionalWaitWhile()
{
openBlogPage();

validateRuntimeIsExpected(() -> {
// wait until the optional privacy dialog is not visible anymore, which will fail
boolean isHidden = SelenideAddons.optionalWaitWhileCondition($(PRIVACY_DIALOG_SELECTOR), visible, CUSTOM_MAX_WAITING_TIME);
// check that the result is false as expected
Assert.assertFalse("the privacy message dialog was unexpectedly hidden during the timeframe", isHidden);
},
CUSTOM_MAX_WAITING_TIME,
Neodymium.configuration().optionalElementRetryPollingIntervall());

// click the opt out button
$(PRIVACY_DIALOG_SELECTOR).find(".btn-link").click();

validateRuntimeIsExpected(() -> {
// wait until the privacy dialog is hidden
boolean isHidden = SelenideAddons.optionalWaitWhileCondition($(PRIVACY_DIALOG_SELECTOR), visible, CUSTOM_MAX_WAITING_TIME);
Assert.assertTrue("the privacy message dialog remained visible during the timeframe", isHidden);
},
0,
Neodymium.configuration().optionalElementRetryPollingIntervall());
}

@Test
public void testOptionalWaitUntilWithDefaultTimeout()
{
openBlogPage();

validateRuntimeIsExpected(() -> {
// wait until the optional privacy dialog is visible
boolean isVisible = SelenideAddons.optionalWaitUntilCondition($(PRIVACY_DIALOG_SELECTOR), visible);
Assert.assertTrue("the privacy message dialog was not found within the timeframe", isVisible);
},
0,
Neodymium.configuration().optionalElementRetryPollingIntervall());

validateRuntimeIsExpected(() -> {
// try wait until the optional privacy dialog is hidden, which will fail
boolean isHidden = SelenideAddons.optionalWaitUntilCondition($(PRIVACY_DIALOG_SELECTOR), hidden);
// check that the is false as is expected
Assert.assertFalse("the privacy message dialog was unexpectedly hidden during the timeframe", isHidden);
},
Neodymium.configuration().optionalElementRetryTimeout(),
Neodymium.configuration().optionalElementRetryPollingIntervall());
}

@Test
public void testOptionalWaitWhileWithDefaultTimeout()
{
openBlogPage();

validateRuntimeIsExpected(() -> {
// wait until the optional privacy dialog is not visible anymore, which will fail
boolean isHidden = SelenideAddons.optionalWaitWhileCondition($(PRIVACY_DIALOG_SELECTOR), visible);
// check that the result is false as expected
Assert.assertFalse("the privacy message dialog was unexpectedly hidden during the timeframe", isHidden);
},
Neodymium.configuration().optionalElementRetryTimeout(),
Neodymium.configuration().optionalElementRetryPollingIntervall());

// click the opt out button
$(PRIVACY_DIALOG_SELECTOR).find(".btn-link").click();

validateRuntimeIsExpected(() -> {
// wait until the privacy dialog is hidden
boolean isHidden = SelenideAddons.optionalWaitWhileCondition($(PRIVACY_DIALOG_SELECTOR), visible);
Assert.assertTrue("the privacy message dialog remained visible during the timeframe", isHidden);
},
0,
Neodymium.configuration().optionalElementRetryPollingIntervall());
}

@Test
public void testOptionalWaitUntilWithTimeoutAndPollingInterval()
{
openBlogPage();

validateRuntimeIsExpected(() -> {
// wait until the optional privacy dialog is visible
boolean isVisible = SelenideAddons.optionalWaitUntilCondition($(PRIVACY_DIALOG_SELECTOR), visible, CUSTOM_MAX_WAITING_TIME,
CUSTOM_POLLING_INTERVAL);
Assert.assertTrue("the privacy message dialog was not found within the timeframe", isVisible);
},
0,
CUSTOM_POLLING_INTERVAL);

validateRuntimeIsExpected(() -> {
// try wait until the optional privacy dialog is hidden, which will fail
boolean isHidden = SelenideAddons.optionalWaitUntilCondition($(PRIVACY_DIALOG_SELECTOR), hidden, CUSTOM_MAX_WAITING_TIME, CUSTOM_POLLING_INTERVAL);
// check that the is false as is expected
Assert.assertFalse("the privacy message dialog was unexpectedly hidden during the timeframe", isHidden);
},
CUSTOM_MAX_WAITING_TIME,
CUSTOM_POLLING_INTERVAL);
}

@Test
public void testOptionalWaitWhileWithTimeoutAndPollingInterval()
{
openBlogPage();

validateRuntimeIsExpected(() -> {
// wait until the optional privacy dialog is not visible anymore, which will fail
boolean isHidden = SelenideAddons.optionalWaitWhileCondition($(PRIVACY_DIALOG_SELECTOR), visible, CUSTOM_MAX_WAITING_TIME, CUSTOM_POLLING_INTERVAL);
// check that the result is false as expected
Assert.assertFalse("the privacy message dialog was unexpectedly hidden during the timeframe", isHidden);
},
CUSTOM_MAX_WAITING_TIME,
CUSTOM_POLLING_INTERVAL);

// click the opt out button
$(PRIVACY_DIALOG_SELECTOR).find(".btn-link").click();

validateRuntimeIsExpected(() -> {
// wait until the privacy dialog is hidden
boolean isHidden = SelenideAddons.optionalWaitWhileCondition($(PRIVACY_DIALOG_SELECTOR), visible, CUSTOM_MAX_WAITING_TIME, CUSTOM_POLLING_INTERVAL);
Assert.assertTrue("the privacy message dialog remained visible during the timeframe", isHidden);
},
0,
CUSTOM_POLLING_INTERVAL);
}
}