Skip to content

Commit

Permalink
#1136 add method $.shouldBe(condition, timeout)
Browse files Browse the repository at this point in the history
... as a replacement for $.waitUntil(condition, timeout)
  • Loading branch information
asolntsev committed Dec 8, 2020
1 parent 66bd7d9 commit 04f8be3
Show file tree
Hide file tree
Showing 5 changed files with 164 additions and 6 deletions.
52 changes: 48 additions & 4 deletions src/main/java/com/codeborne/selenide/SelenideElement.java
Expand Up @@ -17,6 +17,7 @@
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.FileNotFoundException;
import java.time.Duration;

/**
* Wrapper around {@link WebElement} with additional methods like
Expand Down Expand Up @@ -359,6 +360,13 @@ public interface SelenideElement extends WebElement, WrapsDriver, WrapsElement,
@CanIgnoreReturnValue
SelenideElement shouldHave(Condition... condition);

/**
* Wait until given element meets given condition (with given timeout)
*/
@Nonnull
@CanIgnoreReturnValue
SelenideElement shouldHave(Condition condition, Duration timeout);

/**
* <p>Synonym for {@link #should(com.codeborne.selenide.Condition...)}. Useful for better readability.</p>
* <p>For example: {@code
Expand All @@ -372,6 +380,13 @@ public interface SelenideElement extends WebElement, WrapsDriver, WrapsElement,
@CanIgnoreReturnValue
SelenideElement shouldBe(Condition... condition);

/**
* Wait until given element meets given condition (with given timeout)
*/
@Nonnull
@CanIgnoreReturnValue
SelenideElement shouldBe(Condition condition, Duration timeout);

/**
* <p>Checks that given element does not meet given conditions.</p>
*
Expand All @@ -393,6 +408,13 @@ public interface SelenideElement extends WebElement, WrapsDriver, WrapsElement,
@CanIgnoreReturnValue
SelenideElement shouldNot(Condition... condition);

/**
* Wait until given element meets given condition (with given timeout)
*/
@Nonnull
@CanIgnoreReturnValue
SelenideElement shouldNot(Condition condition, Duration timeout);

/**
* <p>Synonym for {@link #shouldNot(com.codeborne.selenide.Condition...)}. Useful for better readability.</p>
* <p>For example: {@code
Expand All @@ -406,6 +428,13 @@ public interface SelenideElement extends WebElement, WrapsDriver, WrapsElement,
@CanIgnoreReturnValue
SelenideElement shouldNotHave(Condition... condition);

/**
* Wait until given element does NOT meet given condition (with given timeout)
*/
@Nonnull
@CanIgnoreReturnValue
SelenideElement shouldNotHave(Condition condition, Duration timeout);

/**
* <p>Synonym for {@link #shouldNot(com.codeborne.selenide.Condition...)}. Useful for better readability.</p>
* <p>For example: {@code
Expand All @@ -419,6 +448,13 @@ public interface SelenideElement extends WebElement, WrapsDriver, WrapsElement,
@CanIgnoreReturnValue
SelenideElement shouldNotBe(Condition... condition);

/**
* Wait until given element does NOT meet given condition (with given timeout)
*/
@Nonnull
@CanIgnoreReturnValue
SelenideElement shouldNotBe(Condition condition, Duration timeout);

/**
* <p>Wait until given element meets given conditions.</p>
*
Expand All @@ -427,10 +463,12 @@ public interface SelenideElement extends WebElement, WrapsDriver, WrapsElement,
*
* @param condition e.g. enabled, visible, text() and so on
* @param timeoutMilliseconds timeout in milliseconds.
* @see com.codeborne.selenide.commands.ShouldBe
* @see com.codeborne.selenide.commands.WaitUntil
* @deprecated use {@link #shouldBe(Condition, Duration)} or @deprecated use {@link #shouldHave(Condition, Duration)}
*/
@Nonnull
@CanIgnoreReturnValue
@Deprecated
SelenideElement waitUntil(Condition condition, long timeoutMilliseconds);

/**
Expand All @@ -442,10 +480,12 @@ public interface SelenideElement extends WebElement, WrapsDriver, WrapsElement,
* @param condition e.g. enabled, visible, text() and so on
* @param timeoutMilliseconds timeout in milliseconds.
* @param pollingIntervalMilliseconds interval in milliseconds, when checking condition
* @see com.codeborne.selenide.commands.ShouldBe
* @see com.codeborne.selenide.commands.WaitUntil
* @deprecated use {@link #shouldBe(Condition, Duration)} or @deprecated use {@link #shouldHave(Condition, Duration)}
*/
@Nonnull
@CanIgnoreReturnValue
@Deprecated
SelenideElement waitUntil(Condition condition, long timeoutMilliseconds, long pollingIntervalMilliseconds);

/**
Expand All @@ -456,10 +496,12 @@ public interface SelenideElement extends WebElement, WrapsDriver, WrapsElement,
*
* @param condition e.g. enabled, visible, text() and so on
* @param timeoutMilliseconds timeout in milliseconds.
* @see com.codeborne.selenide.commands.ShouldNotBe
* @see com.codeborne.selenide.commands.WaitWhile
* @deprecated use {@link #shouldBe(Condition, Duration)} or @deprecated use {@link #shouldHave(Condition, Duration)}
*/
@Nonnull
@CanIgnoreReturnValue
@Deprecated
SelenideElement waitWhile(Condition condition, long timeoutMilliseconds);

/**
Expand All @@ -471,10 +513,12 @@ public interface SelenideElement extends WebElement, WrapsDriver, WrapsElement,
* @param condition e.g. enabled, visible, text() and so on
* @param timeoutMilliseconds timeout in milliseconds.
* @param pollingIntervalMilliseconds interval in milliseconds, when checking condition
* @see com.codeborne.selenide.commands.ShouldNotBe
* @see com.codeborne.selenide.commands.WaitWhile
* @deprecated use {@link #shouldBe(Condition, Duration)} or @deprecated use {@link #shouldHave(Condition, Duration)}
*/
@Nonnull
@CanIgnoreReturnValue
@Deprecated
SelenideElement waitWhile(Condition condition, long timeoutMilliseconds, long pollingIntervalMilliseconds);

/**
Expand Down
3 changes: 2 additions & 1 deletion src/main/java/com/codeborne/selenide/commands/Util.java
Expand Up @@ -6,6 +6,7 @@
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import javax.annotation.ParametersAreNonnullByDefault;
import java.time.Duration;
import java.util.ArrayList;
import java.util.List;

Expand Down Expand Up @@ -35,7 +36,7 @@ static List<Condition> argsToConditions(@Nullable Object[] args) {
conditions.add((Condition) arg);
else if (arg instanceof Condition[])
conditions.addAll(asList((Condition[]) arg));
else if (!(arg instanceof String || arg instanceof Long))
else if (!(arg instanceof String || arg instanceof Long || arg instanceof Duration))
throw new IllegalArgumentException("Unknown parameter: " + arg);
}
return conditions;
Expand Down
16 changes: 16 additions & 0 deletions src/main/java/com/codeborne/selenide/impl/Arguments.java
@@ -1,7 +1,10 @@
package com.codeborne.selenide.impl;

import javax.annotation.CheckReturnValue;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import java.util.Arrays;
import java.util.Optional;

public class Arguments {
private final Object[] args;
Expand All @@ -24,4 +27,17 @@ public <T> T nth(int index) {
public int length() {
return args == null ? 0 : args.length;
}

@CheckReturnValue
@Nonnull
public <T> Optional<T> ofType(@Nonnull Class<T> klass) {
if (args == null) return Optional.empty();

for (Object arg : args) {
if (klass.isAssignableFrom(arg.getClass()))
//noinspection unchecked
return Optional.of((T) arg);
}
return Optional.empty();
}
}
Expand Up @@ -19,7 +19,9 @@
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.time.Duration;
import java.util.HashSet;
import java.util.Optional;
import java.util.Set;

import static com.codeborne.selenide.AssertionMode.SOFT;
Expand Down Expand Up @@ -163,7 +165,11 @@ static boolean shouldRetryAfterError(Throwable e) {

@CheckReturnValue
private long getTimeoutMs(Method method, Arguments arguments) {
return isWaitCommand(method) ? arguments.nth(1) : config().timeout();
Optional<Duration> duration = arguments.ofType(Duration.class);

return duration.map(Duration::toMillis).orElseGet(() ->
isWaitCommand(method) ? arguments.nth(1) : config().timeout()
);
}

@CheckReturnValue
Expand Down
@@ -0,0 +1,91 @@
package integration.errormessages;

import com.codeborne.selenide.Configuration;
import com.codeborne.selenide.SelenideElement;
import com.codeborne.selenide.ex.ElementNotFound;
import com.codeborne.selenide.ex.ElementShould;
import com.codeborne.selenide.ex.ElementShouldNot;
import integration.IntegrationTest;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;

import java.time.Duration;

import static com.codeborne.selenide.Condition.text;
import static com.codeborne.selenide.Condition.visible;
import static com.codeborne.selenide.Selenide.$;
import static com.codeborne.selenide.Selenide.$$;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.fail;

final class ShouldMethodWithTimeoutFailsOnTest extends IntegrationTest {
@BeforeEach
void openPage() {
givenHtml(
"<ul>Hello to:",
"<li class='the-expanse detective'>Miller <label>detective</label></li>",
"<li class='the-expanse missing'>Julie Mao</li>",
"<li class='the-expanse hidden' style='display: none;'>Julie Mao</li>",
"</ul>"
);
Configuration.timeout = 1;
}

@Test
void should_have_with_timeout() {
SelenideElement element = $(".detective");

try {
element.shouldHave(text("Müller"), Duration.ofMillis(43));
fail("Expected ElementNotFound");
}
catch (ElementShould expected) {
assertThat(expected).hasMessageStartingWith("Element should have text 'Müller' {.detective}");
assertThat(expected).hasMessageContaining("Timeout: 43 ms.");
}
}

@Test
void should_be_with_timeout() {
SelenideElement element = $$("ul .nonexistent").get(1);

try {
element.shouldBe(visible, Duration.ofMillis(44));
fail("Expected ElementNotFound");
}
catch (ElementNotFound expected) {
assertThat(expected).hasMessageStartingWith("Element not found {ul .nonexistent[1]}");
assertThat(expected).hasMessageContaining("Expected: visible");
assertThat(expected).hasMessageContaining("Timeout: 44 ms.");
}
}

@Test
void should_not_be_with_timeout() {
SelenideElement element = $(".detective").shouldBe(visible);

try {
element.shouldNotBe(visible, Duration.ofMillis(46));
fail("Expected ElementNotFound");
}
catch (ElementShouldNot expected) {
assertThat(expected).hasMessageStartingWith("Element should not be visible {.detective}");
assertThat(expected).hasMessageContaining("Actual value: visible:true");
assertThat(expected).hasMessageContaining("Timeout: 46 ms.");
}
}

@Test
void should_not_have_with_timeout() {
SelenideElement element = $(".detective").shouldBe(visible);

try {
element.shouldNotHave(text("Miller"), Duration.ofMillis(45));
fail("Expected ElementNotFound");
}
catch (ElementShouldNot expected) {
assertThat(expected).hasMessageStartingWith("Element should not have text 'Miller' {.detective}");
assertThat(expected).hasMessageContaining("Timeout: 45 ms.");
}
}
}

0 comments on commit 04f8be3

Please sign in to comment.