Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 16 additions & 1 deletion docs-site/docs/guides/wait-engine.md
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,22 @@ getWait().waitForText(By.cssSelector("h1"), "Welcome back");
### Attribute value

```java
getWait().waitForAttributeContains(By.id("status"), "class", "active");
getWait().waitForAttributeContains(By.id("status"), "class", "active"); // substring
getWait().waitForAttribute(By.id("status"), "aria-expanded", "true"); // exact match
```

### Text matches (regex)

```java
// Wait until the element's visible text matches a regular expression
getWait().waitForTextMatches(By.cssSelector(".total"), "\\$\\d+\\.\\d{2}");
```

### URL matches (regex)

```java
getWait().waitForUrlContains("/orders"); // substring
getWait().waitForUrlMatches(".*/orders/\\d+"); // regular expression
```

### DOM staleness
Expand Down
39 changes: 39 additions & 0 deletions src/main/java/com/seleniumboot/wait/WaitEngine.java
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
import org.openqa.selenium.support.ui.WebDriverWait;

import java.time.Duration;
import java.util.regex.Pattern;

/**
* Centralized explicit wait handler.
Expand Down Expand Up @@ -90,6 +91,31 @@ public static WebElement waitForAttributeContains(By locator, String attribute,
return DriverManager.getDriver().findElement(locator);
}

/**
* Waits until the element's attribute equals {@code value} exactly.
* Use {@link #waitForAttributeContains(By, String, String)} for a substring match.
*
* <pre>
* WaitEngine.waitForAttribute(By.id("status"), "aria-expanded", "true");
* </pre>
*/
public static WebElement waitForAttribute(By locator, String attribute, String value) {
createWait().until(ExpectedConditions.attributeToBe(locator, attribute, value));
return DriverManager.getDriver().findElement(locator);
}

/**
* Waits until the element's visible text matches the given regular expression.
*
* <pre>
* WaitEngine.waitForTextMatches(By.cssSelector(".total"), "\\$\\d+\\.\\d{2}");
* </pre>
*/
public static WebElement waitForTextMatches(By locator, String textRegex) {
createWait().until(ExpectedConditions.textMatches(locator, Pattern.compile(textRegex)));
return DriverManager.getDriver().findElement(locator);
}

// ----------------------------------------------------------
// Navigation
// ----------------------------------------------------------
Expand All @@ -104,6 +130,19 @@ public static boolean waitForUrlContains(String partialUrl) {
.until(ExpectedConditions.urlContains(partialUrl));
}

/**
* Waits until the current URL matches the given regular expression.
* Use {@link #waitForUrlContains(String)} for a simple substring match.
*
* <pre>
* WaitEngine.waitForUrlMatches(".*&#47;orders&#47;\\d+");
* </pre>
*/
public static boolean waitForUrlMatches(String urlRegex) {
return createWait()
.until(ExpectedConditions.urlMatches(urlRegex));
}

public static void waitForPageLoad() {
createWait().until(driver ->
"complete".equals(((JavascriptExecutor) driver)
Expand Down
92 changes: 92 additions & 0 deletions src/test/java/com/seleniumboot/unit/WaitEngineTest.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
package com.seleniumboot.unit;

import com.seleniumboot.config.SeleniumBootConfig;
import com.seleniumboot.driver.DriverManager;
import com.seleniumboot.internal.SeleniumBootContext;
import com.seleniumboot.wait.WaitEngine;
import org.mockito.MockedStatic;
import org.openqa.selenium.By;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.WebElement;
import org.testng.annotations.AfterMethod;
import org.testng.annotations.BeforeMethod;
import org.testng.annotations.Test;

import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.mockStatic;
import static org.mockito.Mockito.when;
import static org.testng.Assert.*;

/**
* Unit tests for the newer {@link WaitEngine} conditions
* ({@code waitForAttribute}, {@code waitForUrlMatches}, {@code waitForTextMatches}).
*
* <p>Uses a mocked {@link WebDriver} whose state already satisfies each condition,
* so {@code WebDriverWait} succeeds on the first poll — no real browser required.
*/
public class WaitEngineTest {

private WebDriver mockDriver;
private MockedStatic<DriverManager> driverManagerMock;
private MockedStatic<SeleniumBootContext> contextMock;

@BeforeMethod
public void setup() {
mockDriver = mock(WebDriver.class);

driverManagerMock = mockStatic(DriverManager.class);
driverManagerMock.when(DriverManager::getDriver).thenReturn(mockDriver);

// Supply a config so createWait() can read timeouts.explicit
SeleniumBootConfig.Timeouts timeouts = new SeleniumBootConfig.Timeouts();
timeouts.setExplicit(2);
SeleniumBootConfig config = new SeleniumBootConfig();
config.setTimeouts(timeouts);

contextMock = mockStatic(SeleniumBootContext.class);
contextMock.when(SeleniumBootContext::getConfig).thenReturn(config);
}

@AfterMethod
public void teardown() {
driverManagerMock.close();
contextMock.close();
}

// ── waitForAttribute (exact match) ─────────────────────────────────────────

@Test
public void waitForAttribute_returnsElement_whenAttributeMatchesExactly() {
By locator = By.id("status");
WebElement element = mock(WebElement.class);
when(mockDriver.findElement(locator)).thenReturn(element);
when(element.getAttribute("aria-expanded")).thenReturn("true");

WebElement result = WaitEngine.waitForAttribute(locator, "aria-expanded", "true");

assertSame(result, element);
}

// ── waitForUrlMatches (regex) ──────────────────────────────────────────────

@Test
public void waitForUrlMatches_returnsTrue_whenUrlMatchesRegex() {
when(mockDriver.getCurrentUrl()).thenReturn("https://shop.test/orders/42");

assertTrue(WaitEngine.waitForUrlMatches(".*/orders/\\d+"));
}

// ── waitForTextMatches (regex) ─────────────────────────────────────────────

@Test
public void waitForTextMatches_returnsElement_whenTextMatchesRegex() {
By locator = By.cssSelector(".total");
WebElement element = mock(WebElement.class);
when(mockDriver.findElement(locator)).thenReturn(element);
when(element.getText()).thenReturn("$19.99");

WebElement result = WaitEngine.waitForTextMatches(locator, "\\$\\d+\\.\\d{2}");

assertSame(result, element);
}
}
Loading