mirror of
https://github.com/outbackdingo/optimclaw.git
synced 2026-08-25 14:53:34 +00:00
* feat(e2e): extensions tab tests, CI parallelization, and 3 bug fixes ## E2E test coverage - Add tests/e2e/scenarios/test_extensions.py with 57 tests covering all extensions tab flows: installed WASM tool/MCP/channel cards, configure modal (open, fields, cancel, save, OAuth, error), auth card (token, OAuth, submit, cancel, error, multi-extension coexistence), activate flow, install/remove flows, WASM channel stepper states, and tab reload behaviour. All network calls intercepted via page.route() — no real binaries or external registries needed. - Expand tests/e2e/helpers.py with 50+ new CSS selectors for the extensions tab UI. - Add tests/e2e/README.md documentation on the page.route() mocking pattern, LIFO handler ordering, and page.evaluate() injection. ## CI parallelization - Split .github/workflows/e2e.yml into a build job (compile once, upload artifact) and a 3-way parallel test matrix (core / features / extensions), matching the pattern in test.yml. Reduces wall-clock time from ~15–20 min serial to ~10–12 min. Adds an e2e roll-up job for branch protection. ## Bug fixes in app.js (found via test-driven code review) - Fix null crash: renderExtensionCard() called ext.tools.length without a null guard; add ext.tools && check (regression: test_ext_tools_null). - Fix modal UX: submitConfigureModal() closed the overlay before checking success, making failures unrecoverable without reopening; close only on success, re-enable buttons and keep modal open on failure (regression: test_configure_modal_stays_open_on_save_failure). - Fix URL injection: all window.open() calls for server-supplied auth_url now go through openOAuthUrl() which rejects non-HTTPS schemes (regression: test_oauth_url_injection_blocked). Co-Authored-By: Claude Sonnet 4.6 <[email protected]> * refactor(e2e): prune extensions tests 57→46 by merging redundant setups Merge 11 tests that shared identical fixture+navigation overhead: - Group A: 3 empty-state tests → test_extensions_empty_tab_layout - Group B: card_renders absorbs ext_tools_list_shown (same _WASM_TOOL fixture) - Group B: auth_dot_unauthed + unauthed_shows_configure_btn → test_installed_wasm_tool_unauthed_state - Group D: installed + configured states → test_wasm_channel_setup_states (identical UI) - Group D: failed_state + stepper_failed_circle → test_wasm_channel_failed_renders - Group G: 5 field badge tests → test_configure_modal_field_variants (4 fields, one pass) - Group H: submit_success + enter_key_submits → test_auth_card_submit_success Coverage preserved: all assertions kept, no unique behaviors removed. Extensions CI job estimated to drop from ~7 min to ~5 min. Co-Authored-By: Claude Sonnet 4.6 <[email protected]> * fix(e2e): fix configure_input selector scoping in merged field variants test modal.locator(".configure-modal input[type='password']") scoped the absolute selector inside .configure-modal, effectively searching for a nested .configure-modal which never exists → count() == 0. Use page.locator() instead, consistent with all other tests in the file. Co-Authored-By: Claude Sonnet 4.6 <[email protected]> * fix(e2e): address PR review comments — replace fixed sleeps with deterministic waits - Remove unnecessary wait_for_timeout(1000) from test_remove_cancelled_keeps_card (window.confirm = () => false is synchronous; DOM is unchanged when click() returns) - Replace wait_for_timeout(800) with wait_for_function() for window._lastOpenedUrl checks in configure_modal_save_oauth and activate_with_auth_url_opens_popup - Replace wait_for_timeout(300) with nth(1).wait_for(visible) in test_auth_card_multiple_extensions_coexist - Remove wait_for_timeout(800/300) in test_auth_card_submit_empty_noop and test_auth_completed_sse_dismisses_card (both check synchronous JS side-effects) - Add comment in test_oauth_url_injection_blocked explaining why timeout is kept (negative assertion — cannot use wait_for_function for absence of event) Co-Authored-By: Claude Sonnet 4.6 <[email protected]> * fix(e2e): address remaining PR review comments - Remove unused `import pytest` from test_extensions.py - Fix unawaited coroutine bug: convert lambda route handlers to async def in test_extensions_tab_reloads_on_revisit and test_auth_completed_sse_triggers_extensions_reload (lambda r: r.fulfill(...) returns an unawaited coroutine; requests silently fell through to real server) - Fix README.md example to use async def handler (same bug in docs) - Harden openOAuthUrl() in app.js: use URL constructor instead of .startsWith() so non-string server-supplied values (objects, null, etc.) are safely rejected rather than throwing TypeError Co-Authored-By: Claude Sonnet 4.6 <[email protected]> * fix(e2e): address second round of PR review comments - Add timeout-minutes to CI build job to prevent hung workflows - Use parsed.href instead of raw url in openOAuthUrl for safety - Remove unused MessageEvent variable in auth_completed test - Replace wait_for_timeout(800) with expect_response in activate test - Replace wait_for_timeout(300) with tab panel wait_for in reload test [skip-regression-check] Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]> --------- Co-authored-by: Claude Sonnet 4.6 <[email protected]>
100 lines
3.0 KiB
YAML
100 lines
3.0 KiB
YAML
name: E2E Tests
|
|
on:
|
|
schedule:
|
|
- cron: "0 6 * * 1" # Weekly Monday 6 AM UTC
|
|
workflow_dispatch:
|
|
pull_request:
|
|
paths:
|
|
- "src/channels/web/**"
|
|
- "tests/e2e/**"
|
|
|
|
jobs:
|
|
# ── Step 1: compile once ──────────────────────────────────────────────────
|
|
build:
|
|
name: Build ironclaw (libsql)
|
|
runs-on: ubuntu-latest
|
|
timeout-minutes: 30
|
|
steps:
|
|
- uses: actions/checkout@v6
|
|
|
|
- uses: dtolnay/rust-toolchain@stable
|
|
|
|
- uses: actions/cache@v4
|
|
with:
|
|
path: |
|
|
target
|
|
~/.cargo/registry
|
|
key: e2e-${{ runner.os }}-${{ hashFiles('Cargo.lock') }}
|
|
|
|
- name: Build
|
|
run: cargo build --no-default-features --features libsql
|
|
|
|
- name: Upload binary
|
|
uses: actions/upload-artifact@v4
|
|
with:
|
|
name: ironclaw-e2e-binary
|
|
path: target/debug/ironclaw
|
|
retention-days: 1
|
|
|
|
# ── Step 2: run test slices in parallel ───────────────────────────────────
|
|
test:
|
|
name: E2E (${{ matrix.group }})
|
|
needs: build
|
|
runs-on: ubuntu-latest
|
|
timeout-minutes: 30
|
|
strategy:
|
|
fail-fast: false
|
|
matrix:
|
|
include:
|
|
- group: core
|
|
files: "tests/e2e/scenarios/test_connection.py tests/e2e/scenarios/test_chat.py tests/e2e/scenarios/test_sse_reconnect.py tests/e2e/scenarios/test_html_injection.py"
|
|
- group: features
|
|
files: "tests/e2e/scenarios/test_skills.py tests/e2e/scenarios/test_tool_approval.py"
|
|
- group: extensions
|
|
files: "tests/e2e/scenarios/test_extensions.py"
|
|
steps:
|
|
- uses: actions/checkout@v6
|
|
|
|
- name: Download binary
|
|
uses: actions/download-artifact@v4
|
|
with:
|
|
name: ironclaw-e2e-binary
|
|
path: target/debug/
|
|
|
|
- name: Make binary executable
|
|
run: chmod +x target/debug/ironclaw
|
|
|
|
- uses: actions/setup-python@v5
|
|
with:
|
|
python-version: "3.12"
|
|
|
|
- name: Install E2E dependencies
|
|
run: |
|
|
cd tests/e2e
|
|
pip install -e .
|
|
playwright install --with-deps chromium
|
|
|
|
- name: Run E2E tests (${{ matrix.group }})
|
|
run: pytest ${{ matrix.files }} -v --timeout=120
|
|
|
|
- name: Upload screenshots on failure
|
|
if: failure()
|
|
uses: actions/upload-artifact@v4
|
|
with:
|
|
name: e2e-screenshots-${{ matrix.group }}
|
|
path: tests/e2e/screenshots/
|
|
if-no-files-found: ignore
|
|
|
|
# ── Roll-up for branch protection ────────────────────────────────────────
|
|
e2e:
|
|
name: E2E Tests
|
|
runs-on: ubuntu-latest
|
|
if: always()
|
|
needs: [test]
|
|
steps:
|
|
- run: |
|
|
if [[ "${{ needs.test.result }}" != "success" ]]; then
|
|
echo "One or more E2E jobs failed"
|
|
exit 1
|
|
fi
|