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
6 changes: 5 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -55,4 +55,8 @@ The container entrypoint wraps commands in `xvfb-run`, which provides the virtua

## Exploration

`exploration/explore.py` is a helper for writing new tests: it navigates to a path (optionally logging in first) and dumps a screenshot plus the rendered page source so selectors can be inspected without running a full test.
`exploration/explore.py` is a helper for writing new tests: it navigates to a path (optionally logging in first) and dumps a screenshot plus the rendered page source so selectors can be inspected without running a full test. Pass `--click <selector>` (repeatable; CSS, or XPath if it starts with `//`) to click elements — e.g. open a menu or flip a toggle — before the capture:

```bash
uv run python exploration/explore.py --login user --click "#integration-menu-button"
```
27 changes: 23 additions & 4 deletions exploration/explore.py
Original file line number Diff line number Diff line change
@@ -1,12 +1,14 @@
"""
Exploration helper for writing Selenium tests.
Usage: python explore.py <url_path> [--login builder|user|admin]
Usage: python explore.py <url_path> [--login builder|user] [--click <selector> ...]

Navigates to TEST_DOMAIN+url_path, optionally logs in first,
then saves a screenshot and page source for inspection.
Navigates to TEST_DOMAIN+url_path, optionally logs in first, optionally
clicks elements (e.g. to open a menu or flip a toggle), then saves a
screenshot and page source for inspection.
"""

import os
import time
import argparse

from selenium.webdriver import Chrome
Expand Down Expand Up @@ -53,8 +55,14 @@ def wait_for_page(browser):
)


def click(browser, selector):
by = By.XPATH if selector.startswith(("//", "(")) else By.CSS_SELECTOR
element = WebDriverWait(browser, 10).until(EC.element_to_be_clickable((by, selector)))
element.click()
time.sleep(1) # let menus/animations settle before the capture


def save_outputs(browser):
wait_for_page(browser)
browser.save_screenshot(OUTPUT_SCREENSHOT)
with open(OUTPUT_HTML, "w", encoding="utf-8") as f:
f.write(browser.page_source)
Expand All @@ -68,6 +76,14 @@ def main():
parser = argparse.ArgumentParser()
parser.add_argument("path", nargs="?", default="", help="URL path to append to TEST_DOMAIN")
parser.add_argument("--login", choices=CREDENTIALS.keys(), help="Log in as this role first")
parser.add_argument(
"--click",
action="append",
default=[],
metavar="SELECTOR",
help="Click this element after the page loads, before capturing "
"(CSS selector, or XPath if it starts with //; repeatable, clicked in order)",
)
args = parser.parse_args()

domain = os.environ["TEST_DOMAIN"].rstrip("/")
Expand All @@ -78,6 +94,9 @@ def main():
if args.login:
login(browser, args.login)
browser.get(target)
wait_for_page(browser)
for selector in args.click:
click(browser, selector)
save_outputs(browser)
finally:
browser.quit()
Expand Down