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
1 change: 1 addition & 0 deletions packages/core/lib/v3/understudy/page.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1209,6 +1209,7 @@ export class Page {
* Supports iframe hop notation with '>>' (e.g., 'iframe#checkout >> .submit-btn').
*
* @param selector CSS selector to wait for (supports '>>' for iframe hops)
* @param options
* @param options.state Element state to wait for: 'attached' | 'detached' | 'visible' | 'hidden' (default: 'visible')
* @param options.timeout Maximum time to wait in milliseconds (default: 30000)
* @param options.pierceShadow Whether to search inside shadow DOM (default: true)
Expand Down
87 changes: 87 additions & 0 deletions packages/docs/v3/references/page.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -564,6 +564,58 @@ await page.waitForLoadState(state: LoadState, timeoutMs?: number): Promise<void>
**Default:** `15000`
</ParamField>

### waitForSelector()

Wait for an element matching the selector to reach a specific state in the DOM. Uses a MutationObserver for efficiency, pierces shadow DOM by default, and supports iframe hops when needed.

```typescript
await page.waitForSelector(
selector: string,
options?: {
state?: "attached" | "detached" | "visible" | "hidden";
timeout?: number;
pierceShadow?: boolean;
}
): Promise<boolean>
```

<ParamField path="selector" type="string" required>
CSS selector or XPath expression to wait for. Supports iframe hops (e.g., `/html/div/iframe/html/div/button`).
</ParamField>

<ParamField path="options" type="object" optional>
Optional wait configuration.

<Expandable title="properties">
<ParamField path="state" type="'attached' | 'detached' | 'visible' | 'hidden'">
Element state to wait for.

**Options:**
- `"attached"` - Element is present in DOM (even if hidden)
- `"detached"` - Element is removed from DOM
- `"visible"` - Element is visible
- `"hidden"` - Element is hidden

**Default:** `"visible"`
</ParamField>

<ParamField path="timeout" type="number">
Maximum time to wait in milliseconds before timing out.

**Default:** `30000`
</ParamField>

<ParamField path="pierceShadow" type="boolean">
Whether to search inside open and closed shadow DOM boundaries.

**Default:** `true`
</ParamField>
</Expandable>
</ParamField>

**Returns:** `true` when the condition is met.

**Throws:** Error if timeout is reached before the condition is met.

## Events

Expand Down Expand Up @@ -743,6 +795,41 @@ await page.goto("https://spa-app.com", {
await page.waitForLoadState("domcontentloaded");
```

</Tab>
<Tab title="Wait for Selector">

```typescript
// Wait for element to be visible (default)
await page.waitForSelector("#submit-btn");

// Wait for element to appear with custom timeout
await page.waitForSelector(".loading-spinner", {
state: "visible",
timeout: 10000
});

// Wait for element to be removed from DOM
await page.waitForSelector(".loading-spinner", {
state: "detached"
});

// Wait for element to become hidden
await page.waitForSelector(".modal", {
state: "hidden"
});

// Wait for element inside an iframe
await page.waitForSelector("iframe#checkout >> .pay-button");

// Wait for element in shadow DOM (enabled by default)
await page.waitForSelector("#shadow-button", {
pierceShadow: true
});

// Wait for element with XPath
await page.waitForSelector("/html/div/button");
```

</Tab>
<Tab title="Viewport">

Expand Down
2 changes: 1 addition & 1 deletion packages/server/src/types/model.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ export const AISDK_PROVIDERS = [
"perplexity",
"ollama",
"vertex",
"bedrock"
"bedrock",
] as const;
export type AISDKProvider = (typeof AISDK_PROVIDERS)[number];

Expand Down
Loading