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
53 changes: 53 additions & 0 deletions docs-site/docs/recipes/download-and-verify-a-pdf.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
---
description: "Download and verify a PDF in Selenium: DownloadManager waits for the file to appear in the download directory, handling partial downloads, so you can assert it exists and is non-empty."
id: download-and-verify-a-pdf
title: Download and verify a PDF
sidebar_label: Download & verify a PDF
---

# Download and verify a PDF

Click a download link, wait for the file to land, and assert it's a real PDF. `DownloadManager` polls the download directory and ignores partial (`.crdownload`) files until they're complete — no `Thread.sleep()`.

```java title="InvoiceTest.java"
import com.seleniumboot.browser.DownloadManager;
import com.seleniumboot.test.BaseTest;
import java.io.File;
import org.openqa.selenium.By;
import org.testng.Assert;
import org.testng.annotations.Test;

public class InvoiceTest extends BaseTest {

@Test
public void downloadsInvoicePdf() {
open("/invoices/123");
$("#download-pdf").click();

// Dynamic filename (timestamped) → wait for any file, up to 15s:
File pdf = DownloadManager.waitForAnyFile(15);

Assert.assertTrue(pdf.getName().endsWith(".pdf"));
Assert.assertTrue(pdf.length() > 0); // non-empty
}
}
```

Known filename? Wait for it by name instead:

```java
File pdf = DownloadManager.waitForFile("invoice-123.pdf", 15);
```

Set the download directory in config (defaults to `./target/downloads`):

```yaml title="selenium-boot.yml"
browser:
downloadDir: ./target/downloads
```

:::tip Clean state between tests
Call `DownloadManager.clearDownloads()` in `@BeforeMethod` so a stale file from a previous run can't satisfy the wait.
:::

**Deeper reference:** [DownloadManager](/docs/guides/download-manager) — waiting strategies, partial-download handling, and cleanup.
41 changes: 41 additions & 0 deletions docs-site/docs/recipes/handle-iframes.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
---
description: "Handle iframes in Selenium without manual switchTo() bookkeeping: Selenium Boot's withinFrame runs your actions inside the frame and restores the previous context automatically, even when nested."
id: handle-iframes
title: Handle iframes
sidebar_label: Handle iframes
---

# Handle iframes

Interacting with content inside an `<iframe>` normally means `driver.switchTo().frame(...)` and remembering to switch back. `BasePage`'s `withinFrame(...)` does the bookkeeping for you: it switches in, runs your action, and restores the previous context — even when nested.

```java title="CheckoutPage.java"
import com.seleniumboot.test.BasePage;
import org.openqa.selenium.By;

public class CheckoutPage extends BasePage {

public void payWithCard(String number) {
withinFrame(By.id("payment-iframe"), () -> {
type(By.id("card-number"), number);
click(By.id("pay"));
});
// Back in the main document automatically here.
}
}
```

Nesting is safe — inner frames restore to their **parent** frame, not the top document:

```java
withinFrame(By.id("outer-iframe"), () -> {
withinFrame(By.id("inner-iframe"), () -> {
click(By.id("confirm"));
});
click(By.id("next")); // still inside outer-iframe
});
```

Prefer to target a frame by index or by its `name`/`id` attribute? Use `withinFrameIndex(int, ...)` or `withinFrameName(String, ...)`.

**Deeper reference:** [BasePage](/docs/guides/base-page) — page-object base class and its frame helpers.
50 changes: 50 additions & 0 deletions docs-site/docs/recipes/handle-shadow-dom.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
---
description: "Handle Shadow DOM in Selenium: Selenium Boot pierces shadow roots with shadowFind, shadowClick, and shadowType, and traverses nested web components with shadowPierce — no manual JavaScript."
id: handle-shadow-dom
title: Handle Shadow DOM
sidebar_label: Handle Shadow DOM
---

# Handle Shadow DOM

Elements inside a web component's **shadow root** aren't reachable with ordinary CSS from the page. `BasePage` gives you helpers that pierce the shadow boundary for you — no hand-written JavaScript.

```java title="SettingsPage.java"
import com.seleniumboot.test.BasePage;
import org.openqa.selenium.By;

public class SettingsPage extends BasePage {

public void updateEmail(String email) {
// Host element <my-form>, then a selector scoped to its shadow root:
shadowType(By.cssSelector("my-form"), "#email", email);
shadowClick(By.cssSelector("my-form"), "#save");
}

public String bannerText() {
return shadowGetText(By.cssSelector("my-banner"), ".message");
}
}
```

**Nested** shadow roots (a component inside a component) — traverse them with `shadowPierce(...)`, passing the host selector at each level:

```java
// <checkout-flow> → shadow → <payment-widget> → shadow → #pay-btn
WebElement payBtn = shadowPierce("checkout-flow", "payment-widget", "#pay-btn");
payBtn.click();
```

Guard optional shadow content without a `try/catch` — `shadowExists(...)` never throws:

```java
if (shadowExists(By.cssSelector("my-form"), ".error")) {
// handle validation error
}
```

:::caution CSS only
Shadow-root selectors must be **CSS** — XPath cannot cross a shadow boundary.
:::

**Deeper reference:** [BasePage](/docs/guides/base-page) — page-object base class and its Shadow DOM helpers.
39 changes: 39 additions & 0 deletions docs-site/docs/recipes/upload-a-file.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
---
description: "Upload a file in Selenium without OS dialogs: Selenium Boot's upload(By, path) sets the file input directly, with paths resolved from the classpath or an absolute location."
id: upload-a-file
title: Upload a file
sidebar_label: Upload a file
---

# Upload a file

Set a file `<input type="file">` directly — no brittle OS file-picker automation. `BasePage` exposes an `upload(By, path)` helper:

```java title="UploadPage.java"
import com.seleniumboot.test.BasePage;
import org.openqa.selenium.By;

public class UploadPage extends BasePage {

public void attachResume(String path) {
upload(By.id("file-input"), path); // sets the input's value directly
click(By.id("submit"));
}
}
```

```java
// From your test:
new UploadPage().attachResume("testfiles/resume.pdf"); // classpath-relative
new UploadPage().attachResume("/absolute/path/to/photo.png");
```

- The path may be **classpath-relative** (e.g. `testfiles/resume.pdf`) or **absolute**. Selenium Boot resolves it and fails fast with a clear error if the file doesn't exist.
- Works with hidden/off-screen inputs — it sets the input value, so no visible dialog is required.
- For a **multiple** upload, pass separated paths to the input as your app expects, or call the helper per file.

:::tip
Uploads go through a page object because `upload(...)` is a `BasePage` helper. Keep the file under `src/test/resources` so the classpath-relative path works on any machine and in CI.
:::

**Deeper reference:** [BasePage](/docs/guides/base-page) — the base class for page objects and its helpers.
10 changes: 10 additions & 0 deletions docs-site/sidebars.js
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,16 @@ const sidebars = {
'guides/scenario-context',
],
},
{
type: 'category',
label: 'Recipes',
items: [
'recipes/upload-a-file',
'recipes/download-and-verify-a-pdf',
'recipes/handle-iframes',
'recipes/handle-shadow-dom',
],
},
{
type: 'category',
label: 'CI / CD',
Expand Down
Loading