From 581a0727de10ee01ed120f402b4dc86a574dc9e9 Mon Sep 17 00:00:00 2001 From: csd113 Date: Thu, 1 Oct 2026 07:23:04 -0700 Subject: [PATCH 1/2] Stabilize native quote navigation release checks --- tests/e2e/public-reading-polish.spec.ts | 21 ++++++++++++++++++++- 1 file changed, 20 insertions(+), 1 deletion(-) diff --git a/tests/e2e/public-reading-polish.spec.ts b/tests/e2e/public-reading-polish.spec.ts index 4e879df6..438031a8 100644 --- a/tests/e2e/public-reading-polish.spec.ts +++ b/tests/e2e/public-reading-polish.spec.ts @@ -375,6 +375,23 @@ test('search post numbers and quotes open their thread context', async ({ page, await page.goBack(); await expect(page.locator('#board-search-input')).toHaveValue('unique-search-needle'); if (['chromium', 'webkit', 'firefox'].includes(testInfo.project.name)) { + // Check each modifier independently so the foreground gesture's Shift + // cannot hide a broken Control/Command passthrough guard. + const intercepted = await page.locator(`#p${reply} a.quotelink`).evaluate(link => { + return ['ctrlKey', 'metaKey'].map(modifier => { + let preventedByApp: boolean | undefined; + document.addEventListener('click', event => { + preventedByApp = event.defaultPrevented; + // Inspect application handling, then cancel synthetic navigation. + event.preventDefault(); + }, { once: true }); + link.dispatchEvent(new MouseEvent('click', { + bubbles: true, cancelable: true, [modifier]: true, + })); + return preventedByApp; + }); + }); + expect(intercepted).toEqual([false, false]); await page.evaluate(() => { document.addEventListener('click', event => { if (!(event.target instanceof Element) || !event.target.closest('a.quotelink')) return; @@ -386,7 +403,9 @@ test('search post numbers and quotes open their thread context', async ({ page, }); const [opened] = await Promise.all([ page.context().waitForEvent('page'), - page.locator(`#p${reply} a.quotelink`).click({ modifiers: ['ControlOrMeta'] }), + // Open the tab in the foreground: Chromium can leave a traced background + // tab uninitialized even after its document response has arrived. + page.locator(`#p${reply} a.quotelink`).click({ modifiers: ['ControlOrMeta', 'Shift'] }), ]).finally(async () => { noteEvent('modified-quote-click', JSON.stringify(await page.evaluate(() => (window as Window & { auditQuoteClick?: unknown }).auditQuoteClick))); }); From b6861a012bb9429ce1454a38b74d7e1e71b96b80 Mon Sep 17 00:00:00 2001 From: csd113 Date: Thu, 1 Oct 2026 07:55:29 -0700 Subject: [PATCH 2/2] Remove browser Actions checks and enforce plain branch names --- .github/workflows/e2e.yml | 210 -------------------------------------- AGENTS.md | 4 + CHANGELOG.md | 4 +- README.md | 2 +- tests/e2e/README.md | 26 ++--- 5 files changed, 18 insertions(+), 228 deletions(-) delete mode 100644 .github/workflows/e2e.yml create mode 100644 AGENTS.md diff --git a/.github/workflows/e2e.yml b/.github/workflows/e2e.yml deleted file mode 100644 index 759f7987..00000000 --- a/.github/workflows/e2e.yml +++ /dev/null @@ -1,210 +0,0 @@ -name: Browser E2E - -on: - pull_request: - branches: [main, master] - push: - branches: [main, master] - merge_group: - workflow_dispatch: - inputs: - mode: - description: Browser suite mode - type: choice - options: - - normal - - deep - default: normal - schedule: - # Weekly deep audit pass (all projects, expensive diagnostics). - - cron: "41 7 * * 6" - -permissions: - contents: read - -concurrency: - group: e2e-${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }} - cancel-in-progress: true - -env: - CARGO_INCREMENTAL: 0 - CARGO_TERM_COLOR: always - CARGO_NET_RETRY: 10 - RUST_BACKTRACE: 1 - -jobs: - # --------------------------------------------------------------------------- - # Normal regression mode: maintained suite on the primary engines. One job per - # project so wall time stays bounded; retries stay at the repository default - # of zero and any strict-diagnostics finding fails its project. - # --------------------------------------------------------------------------- - regression: - name: Regression (${{ matrix.project }}) - if: ${{ github.event_name != 'schedule' && !(github.event_name == 'workflow_dispatch' && inputs.mode == 'deep') }} - runs-on: ubuntu-24.04 - timeout-minutes: 45 - - strategy: - fail-fast: false - matrix: - include: - - project: chromium - browser: chromium - - project: webkit - browser: webkit - - project: firefox - browser: firefox - - project: chromium-nojs - browser: chromium - - steps: - - name: Checkout - uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 - - - name: Install Rust toolchain - uses: dtolnay/rust-toolchain@4360b52568e2003a75bf9bc1d59f33a8e3fc893c # stable - with: - toolchain: 1.91.0 - - - name: Cache Rust artifacts - uses: Swatinem/rust-cache@6323deb102c322ba6fcbdcafc7e3dddab59af2b6 # v2 - with: - shared-key: e2e-regression - key: ${{ matrix.project }} - - - name: Install Node.js - uses: actions/setup-node@v6 - with: - node-version: 22 - cache: npm - - - name: Install browser dependencies - run: | - npm ci - npx playwright install --with-deps ${{ matrix.browser }} - - - name: Configure headless audio output - run: | - sudo apt-get update - sudo apt-get install -y --no-install-recommends pulseaudio pulseaudio-utils - pulseaudio --start --exit-idle-time=-1 - pactl load-module module-null-sink sink_name=rustchan_ci rate=48000 - pactl set-default-sink rustchan_ci - pactl list short sinks - - - name: Build RustChan - run: cargo build --locked --bin rustchan-cli - - - name: Run maintained browser suite - env: - CI: true - RUSTCHAN_E2E_SKIP_BUILD: 1 - run: >- - npx playwright test - --project=${{ matrix.project }} - --workers=2 - --max-failures=5 - - - name: Upload failure evidence - if: failure() - uses: actions/upload-artifact@v7 - with: - name: playwright-${{ matrix.project }}-failure - path: | - playwright-report/ - test-results/e2e/ - if-no-files-found: warn - retention-days: 7 - - # --------------------------------------------------------------------------- - # Deep audit mode: every project, including the emulated mobile and Firefox - # no-JavaScript configurations, with bounded tracing/screenshot diagnostics. - # Run manually (mode=deep) or by the weekly schedule. This is deliberately not - # part of the routine pull-request pass. - # --------------------------------------------------------------------------- - deep-audit: - name: Deep audit (${{ matrix.project }}) - if: ${{ github.event_name == 'schedule' || (github.event_name == 'workflow_dispatch' && inputs.mode == 'deep') }} - runs-on: ubuntu-24.04 - timeout-minutes: 90 - - strategy: - fail-fast: false - matrix: - include: - - project: chromium - browser: chromium - - project: chromium-nojs - browser: chromium - - project: webkit - browser: webkit - - project: mobile-webkit - browser: webkit - - project: firefox - browser: firefox - - project: mobile-firefox - browser: firefox - - project: firefox-nojs - browser: firefox - - steps: - - name: Checkout - uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 - - - name: Install Rust toolchain - uses: dtolnay/rust-toolchain@4360b52568e2003a75bf9bc1d59f33a8e3fc893c # stable - with: - toolchain: 1.91.0 - - - name: Cache Rust artifacts - uses: Swatinem/rust-cache@6323deb102c322ba6fcbdcafc7e3dddab59af2b6 # v2 - with: - shared-key: e2e-deep - key: ${{ matrix.project }} - - - name: Install Node.js - uses: actions/setup-node@v6 - with: - node-version: 22 - cache: npm - - - name: Install browser dependencies - run: | - npm ci - npx playwright install --with-deps ${{ matrix.browser }} - - - name: Configure headless audio output - run: | - sudo apt-get update - sudo apt-get install -y --no-install-recommends pulseaudio pulseaudio-utils - pulseaudio --start --exit-idle-time=-1 - pactl load-module module-null-sink sink_name=rustchan_ci rate=48000 - pactl set-default-sink rustchan_ci - pactl list short sinks - - - name: Build RustChan - run: cargo build --locked --bin rustchan-cli - - - name: Run deep browser audit - env: - CI: true - RUSTCHAN_E2E_SKIP_BUILD: 1 - RUSTCHAN_E2E_DEEP_DIAGNOSTICS: 1 - RUSTCHAN_AUDIT_OUTPUT: output/playwright/deep-audit/${{ matrix.project }} - run: >- - npx playwright test - --project=${{ matrix.project }} - --workers=2 - - - name: Upload deep audit evidence - if: always() - uses: actions/upload-artifact@v7 - with: - name: deep-audit-${{ matrix.project }} - # The deep run writes everything under RUSTCHAN_AUDIT_OUTPUT; the - # report and result JSON are bounded, and failure traces are bounded - # by the deep artifact budget. - path: | - output/playwright/deep-audit/${{ matrix.project }}/ - if-no-files-found: warn - retention-days: 14 diff --git a/AGENTS.md b/AGENTS.md new file mode 100644 index 00000000..60fd1a81 --- /dev/null +++ b/AGENTS.md @@ -0,0 +1,4 @@ +# Repository instructions + +- Never create or push a branch whose name contains `codex`, regardless of case. +- Use plain, descriptive branch names. diff --git a/CHANGELOG.md b/CHANGELOG.md index bd4e3542..26125054 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -47,9 +47,9 @@ upgrades, public posting and reading improvements, and verified native software ### Testing and maintenance -- Restored version-controlled Playwright infrastructure and browser CI, with normal regression and weekly/manual deep modes. Runtime data, browser profiles, screenshots, reports, traces and disposable audit evidence remain ignored. +- Restored version-controlled Playwright infrastructure for local normal regression and deep audit runs. Removed browser checks and scheduled browser audits from GitHub Actions. Runtime data, browser profiles, screenshots, reports, traces and disposable audit evidence remain ignored. - Added administrator authorization, updater signing/archive/snapshot/rollback/recovery, readiness privacy, all-theme/narrow-screen, keyboard focus and no-JavaScript regressions. Release candidates receive strict Rust lint, locked dependency policy, packaging and browser validation. -- Stabilized fixture teardown, backup completion and completed-redirect submission assertions and session-revocation fixtures that retain and verify the old session without unrelated polling, preserving test budgets and all-theme coverage. Linux browser CI provides a virtual audio output for genuine native playback checks. Firefox uses an isolated application identity for the macOS 27 upstream Playwright launch defect, with launch regressions and a version guard. +- Stabilized fixture teardown, backup completion and completed-redirect submission assertions and session-revocation fixtures that retain and verify the old session without unrelated polling, preserving test budgets and all-theme coverage. Native quote-navigation checks open foreground tabs and independently verify Control/Command passthrough. Firefox uses an isolated application identity for the macOS 27 upstream Playwright launch defect, with launch regressions and a version guard. - Coordinated CLI test subprocess spawning to prevent Linux fork/exec descriptor inheritance from causing `ETXTBSY`, while preserving parallel test execution. Guarded native updater helpers with Unix configuration so check-only Windows builds remain warning-free. Kept configuration documentation compatible with Rust 1.91 strict lint and refreshed the yanked `yoke-derive` lockfile entry. ## RustChan 1.5.0 — 2026-09-28 diff --git a/README.md b/README.md index a28d8a2b..7f7693a4 100644 --- a/README.md +++ b/README.md @@ -219,7 +219,7 @@ cargo clippy --locked --workspace --all-targets --all-features cargo test --locked --workspace --all-features ``` -Browser-test infrastructure and its npm files are local-only and are not included in a fresh clone. Keep browser reports and temporary screenshots out of commits; the demonstration screenshots in `docs/screenshots/` are published documentation. See [CONTRIBUTING.md](CONTRIBUTING.md) for contribution guidance. +Browser tests run locally; their harness and npm files are tracked, but GitHub Actions does not run browser checks. Keep browser reports and temporary screenshots out of commits; the demonstration screenshots in `docs/screenshots/` are published documentation. See [CONTRIBUTING.md](CONTRIBUTING.md) for contribution guidance. ## Documentation diff --git a/tests/e2e/README.md b/tests/e2e/README.md index d53d8419..20a5c113 100644 --- a/tests/e2e/README.md +++ b/tests/e2e/README.md @@ -1,8 +1,8 @@ # RustChan Playwright E2E Suite -The maintained Playwright harness belongs in version control: `playwright.config.ts`, -`package.json` / `package-lock.json`, `tests/e2e/**`, and -`.github/workflows/e2e.yml`. Generated run evidence stays ignored: browser +The maintained local Playwright harness belongs in version control: `playwright.config.ts`, +`package.json` / `package-lock.json`, and `tests/e2e/**`. +Browser tests do not run in GitHub Actions. Generated run evidence stays ignored: browser reports, `test-results/`, the `output/playwright/` runtime tree (including preserved debug roots and audit output), traces, videos, screenshots, storage state, and the rebranded-Firefox workaround bundle. Never force-add generated @@ -45,8 +45,7 @@ Two supported modes share one configuration; retries stay at **zero** in both. ### Normal regression mode -Routine CI runs the maintained suite per engine, one project per job, with a -failure cap. Locally the closest equivalent is: +Run the maintained local suite per engine with a failure cap: ```sh RUSTCHAN_E2E_SKIP_BUILD=1 npx playwright test --project=chromium --workers=2 --max-failures=5 @@ -56,8 +55,8 @@ RUSTCHAN_E2E_SKIP_BUILD=1 npx playwright test --project=chromium-nojs --workers= ``` `npm run test:e2e:ci` is the bounded Chromium shorthand (`--max-failures=5`). -The tracked workflow `.github/workflows/e2e.yml` runs these four projects on -pull requests, pushes, and merge groups; evidence is uploaded only on failure. +These commands run locally; pull requests, pushes, and merge groups do not +trigger browser checks. ### Deep audit mode @@ -75,10 +74,8 @@ npm run test:e2e:deep `npm run test:e2e:deep` is the all-projects matrix (`chromium`, `chromium-nojs`, `webkit`, `mobile-webkit`, `firefox`, `mobile-firefox`, -`firefox-nojs`). The workflow exposes it as `workflow_dispatch` input -`mode=deep` and as the weekly scheduled pass. Deep diagnostics are expensive; -they are deliberately not part of the routine pull-request pass. A single -project can be run deeply with an explicit `--project`. +`firefox-nojs`). Deep diagnostics are expensive and run only when requested +locally. A single project can be run deeply with an explicit `--project`. ## Firefox on macOS 27 (Playwright issue #42768) @@ -119,7 +116,7 @@ Firefox/mobile/no-JS launch probes pass. ## Maintained source, fixtures and disposable evidence Track the TypeScript specs/helpers/configs, helper `.mjs` scripts, npm manifest -and lockfile, and browser workflow. Required small sanitized regression fixtures +and lockfile. Required small sanitized regression fixtures belong in `tests/e2e/fixtures/`; most current fixtures are generated deterministically inside each isolated runtime by `helpers.ts`. Never copy runtime databases, authentication state or downloaded browser bundles into source fixtures. @@ -377,9 +374,8 @@ The retained local npm shorthand runs a bounded Chromium pass with two workers a npm run test:e2e:ci ``` -The tracked browser workflow is `.github/workflows/e2e.yml` (normal regression -projects on PR/push, deep audit on `workflow_dispatch mode=deep` and the weekly -schedule). Failure diagnostics retain traces, screenshots, videos, server logs, +The browser suite runs locally; there is no GitHub Actions browser workflow or weekly +schedule. Failure diagnostics retain traces, screenshots, videos, server logs, and browser errors; retries are disabled. ## Full Local Matrix