Working with SPAs
Single-page applications load content dynamically without full page reloads. Claudezilla provides tools to wait for specific conditions and detect changes in dynamic pages. ## Wait-For Patterns `firefox_wait_for` blocks until a condition is met on the page. It supports three mutually exclusive conditions: ### Wait for a Selector Wait until a CSS element appears in the DOM: ```js // Wait for a modal to appear firefox_wait_for({ selector: ".modal-dialog" }) // Wait for results to load firefox_wait_for({ selector: "#search-results .result-item" }) // Wait for a loading spinner to disappear (negate with :not or check absence after) firefox_wait_for({ selector: ".content:not(.loading)" }) ``` ### Wait for Text Wait until specific text appears in `document.body.innerText`: ```js // Wait for a success message firefox_wait_for({ text: "Your order has been placed" }) // Wait for data to render firefox_wait_for({ text: "Showing 1-10 of" }) ``` ### Wait for URL Wait until `window.location.href` matches a glob pattern. Useful for SPA route changes and redirects: ```js // Wait for navigation to dashboard after login firefox_type({ selector: "#password", text: "..." }) firefox_click({ selector: "#login-btn" }) firefox_wait_for({ url: "**/dashboard" }) // Wait for a specific route firefox_wait_for({ url: "**/users/*/profile" }) ``` ### Timeout All wait conditions accept an optional `timeout` (default: 10000ms): ```js // Give a slow API response more time firefox_wait_for({ selector: "#report-table", timeout: 30000 }) ``` If the timeout is reached, an error is returned rather than silently proceeding. ## Page State Diffs Use `firefox_get_page_state` and `firefox_diff_page_state` together to detect what changed after an action. ### Capture-Act-Diff Pattern ```js // 1. Capture the "before" state const before = firefox_get_page_state({ tabId: 42 }) // 2. Perform an action firefox_click({ selector: "#load-more" }) firefox_wait_for({ selector: ".item:nth-child(11)" }) // 3. Capture the "after" state const after = firefox_get_page_state({ tabId: 42 }) // 4. Diff the two snapshots firefox_diff_page_state({ before: before, after: after }) ``` The diff returns added, removed, and changed elements across headings, links, buttons, and inputs: ```json { "added": { "links": [ { "text": "Page 2", "href": "/results?page=2" } ], "buttons": [ { "text": "Load More", "selector": "#load-more-2" } ] }, "removed": { "buttons": [ { "text": "Load More", "selector": "#load-more" } ] }, "changed": {} } ``` ### Use Cases - **Form submission verification**: Diff before/after submitting to confirm success messages appeared - **Navigation effects**: Verify that clicking a link changed the expected page elements - **Regression detection**: Compare page state across test runs to catch missing elements ## Common SPA Patterns ### Login Flow ```js firefox_create_window({ url: "https://app.example.com/login" }) firefox_wait_for({ selector: "#email" }) firefox_type({ selector: "#email", text: "user@example.com" }) firefox_type({ selector: "#password", text: "..." }) firefox_click({ selector: "button[type=submit]" }) // Wait for the SPA to route to dashboard firefox_wait_for({ url: "**/dashboard" }) ``` ### Infinite Scroll ```js // Load initial content firefox_create_window({ url: "https://app.example.com/feed" }) firefox_wait_for({ selector: ".feed-item" }) // Scroll to bottom to trigger load firefox_scroll({ y: 99999 }) // Wait for new items firefox_wait_for({ selector: ".feed-item:nth-child(21)" }) ``` ### Tab-Based Navigation ```js // Click a tab in the SPA firefox_click({ selector: "[data-tab='settings']" }) // Wait for the tab panel content to appear firefox_wait_for({ selector: "#settings-panel .form-group" }) ``` ### Waiting After Form Submission ```js const before = firefox_get_page_state({ tabId: 42 }) firefox_click({ selector: "#save-btn" }) // Wait for success toast or updated content firefox_wait_for({ text: "Changes saved" }) const after = firefox_get_page_state({ tabId: 42 }) firefox_diff_page_state({ before, after }) ``` ## Tips - Always `wait_for` after actions that trigger async updates. SPAs do not trigger page load events. - Prefer `firefox_get_page_state` over screenshots when you need to understand page structure — it is faster and does not require the screenshot mutex. - Use URL waiting for route changes, selector waiting for DOM changes, and text waiting for content that renders asynchronously. - Set appropriate timeouts for slow APIs. The default 10s is fine for most pages but may not be enough for complex reports or data-heavy views.Single-page applications load content dynamically without full page reloads. Claudezilla provides tools to wait for specific conditions and detect changes in dynamic pages.
Wait-For Patterns
Section titled “Wait-For Patterns”firefox_wait_for blocks until a condition is met on the page. It supports three mutually exclusive conditions:
Wait for a Selector
Section titled “Wait for a Selector”Wait until a CSS element appears in the DOM:
// Wait for a modal to appearfirefox_wait_for({ selector: ".modal-dialog" })
// Wait for results to loadfirefox_wait_for({ selector: "#search-results .result-item" })
// Wait for a loading spinner to disappear (negate with :not or check absence after)firefox_wait_for({ selector: ".content:not(.loading)" })Wait for Text
Section titled “Wait for Text”Wait until specific text appears in document.body.innerText:
// Wait for a success messagefirefox_wait_for({ text: "Your order has been placed" })
// Wait for data to renderfirefox_wait_for({ text: "Showing 1-10 of" })Wait for URL
Section titled “Wait for URL”Wait until window.location.href matches a glob pattern. Useful for SPA route changes and redirects:
// Wait for navigation to dashboard after loginfirefox_type({ selector: "#password", text: "..." })firefox_click({ selector: "#login-btn" })firefox_wait_for({ url: "**/dashboard" })
// Wait for a specific routefirefox_wait_for({ url: "**/users/*/profile" })Timeout
Section titled “Timeout”All wait conditions accept an optional timeout (default: 10000ms):
// Give a slow API response more timefirefox_wait_for({ selector: "#report-table", timeout: 30000})If the timeout is reached, an error is returned rather than silently proceeding.
Page State Diffs
Section titled “Page State Diffs”Use firefox_get_page_state and firefox_diff_page_state together to detect what changed after an action.
Capture-Act-Diff Pattern
Section titled “Capture-Act-Diff Pattern”// 1. Capture the "before" stateconst before = firefox_get_page_state({ tabId: 42 })
// 2. Perform an actionfirefox_click({ selector: "#load-more" })firefox_wait_for({ selector: ".item:nth-child(11)" })
// 3. Capture the "after" stateconst after = firefox_get_page_state({ tabId: 42 })
// 4. Diff the two snapshotsfirefox_diff_page_state({ before: before, after: after })The diff returns added, removed, and changed elements across headings, links, buttons, and inputs:
{ "added": { "links": [ { "text": "Page 2", "href": "/results?page=2" } ], "buttons": [ { "text": "Load More", "selector": "#load-more-2" } ] }, "removed": { "buttons": [ { "text": "Load More", "selector": "#load-more" } ] }, "changed": {}}Use Cases
Section titled “Use Cases”- Form submission verification: Diff before/after submitting to confirm success messages appeared
- Navigation effects: Verify that clicking a link changed the expected page elements
- Regression detection: Compare page state across test runs to catch missing elements
Common SPA Patterns
Section titled “Common SPA Patterns”Login Flow
Section titled “Login Flow”firefox_create_window({ url: "https://app.example.com/login" })firefox_wait_for({ selector: "#email" })
firefox_type({ selector: "#email", text: "user@example.com" })firefox_type({ selector: "#password", text: "..." })firefox_click({ selector: "button[type=submit]" })
// Wait for the SPA to route to dashboardfirefox_wait_for({ url: "**/dashboard" })Infinite Scroll
Section titled “Infinite Scroll”// Load initial contentfirefox_create_window({ url: "https://app.example.com/feed" })firefox_wait_for({ selector: ".feed-item" })
// Scroll to bottom to trigger loadfirefox_scroll({ y: 99999 })
// Wait for new itemsfirefox_wait_for({ selector: ".feed-item:nth-child(21)" })Tab-Based Navigation
Section titled “Tab-Based Navigation”// Click a tab in the SPAfirefox_click({ selector: "[data-tab='settings']" })
// Wait for the tab panel content to appearfirefox_wait_for({ selector: "#settings-panel .form-group" })Waiting After Form Submission
Section titled “Waiting After Form Submission”const before = firefox_get_page_state({ tabId: 42 })
firefox_click({ selector: "#save-btn" })
// Wait for success toast or updated contentfirefox_wait_for({ text: "Changes saved" })
const after = firefox_get_page_state({ tabId: 42 })firefox_diff_page_state({ before, after })- Always
wait_forafter actions that trigger async updates. SPAs do not trigger page load events. - Prefer
firefox_get_page_stateover screenshots when you need to understand page structure — it is faster and does not require the screenshot mutex. - Use URL waiting for route changes, selector waiting for DOM changes, and text waiting for content that renders asynchronously.
- Set appropriate timeouts for slow APIs. The default 10s is fine for most pages but may not be enough for complex reports or data-heavy views.