* Improve admin notifications page UI * Stabilize notifications page E2E bootstrap wait * Fix PR Playwright diff head ref * Guard MyWashStart timers after test teardown * Stabilize self-serve wash E2E timing * Refresh edge gateway fixture runtime state * Run Playwright E2E on GitHub-hosted runners * Run all frontend CI on GitHub-hosted runners * Increase hosted full E2E parallelism * Stabilize full E2E validation * Preserve superuser gateway navigation label * Add targeted Playwright dispatch workflow * Integrate targeted Playwright dispatch into tests workflow --------- Co-authored-by: Jeppe Bundgaard <jb@truckwash.dk>
120 lines
5.5 KiB
JavaScript
120 lines
5.5 KiB
JavaScript
import { readFileSync } from "node:fs";
|
|
import { join } from "node:path";
|
|
import { describe, expect, it } from "vitest";
|
|
|
|
const root = process.cwd();
|
|
const workflowSource = () => readFileSync(join(root, ".github/workflows/tests.yml"), "utf8");
|
|
const networkFixtureSource = () => readFileSync(join(root, "tests/e2e/support/network.js"), "utf8");
|
|
|
|
describe("Playwright full E2E workflow grouping", () => {
|
|
it("orders full-suite matrix dimensions by browser, device, then role", () => {
|
|
const source = workflowSource();
|
|
|
|
expect(source).toContain("name: E2E-full-${{ matrix.browser_label }}-${{ matrix.device }}-${{ matrix.role }}");
|
|
expect(source).toContain("browser: [chromium, webkit, firefox]");
|
|
expect(source).toContain("device: [mobile, desktop, tablet]");
|
|
expect(source).toContain("role: [superuser, admin, customer, subuser]");
|
|
});
|
|
|
|
it("uses browser-device-role artifact namespaces and generated test lists", () => {
|
|
const source = workflowSource();
|
|
|
|
expect(source).toContain(
|
|
"PLAYWRIGHT_ARTIFACT_NAMESPACE: e2e-full-${{ matrix.browser }}-${{ matrix.device }}-${{ matrix.role }}"
|
|
);
|
|
expect(source).toContain(
|
|
"name: playwright-report-full-${{ matrix.browser }}-${{ matrix.device }}-${{ matrix.role }}"
|
|
);
|
|
expect(source).toContain(
|
|
"output/playwright/test-lists/${{ matrix.browser }}-${{ matrix.device }}-${{ matrix.role }}.txt"
|
|
);
|
|
});
|
|
|
|
it("keeps full-suite runner pressure bounded and diagnosable", () => {
|
|
const source = workflowSource();
|
|
|
|
expect(source).toMatch(
|
|
/e2e-full:[\s\S]*?max-parallel: \$\{\{ fromJSON\(github\.event_name == 'workflow_dispatch' && github\.event\.inputs\.runner == 'github-hosted' && '12' \|\| '2'\) \}\}/u
|
|
);
|
|
expect(source).toMatch(/e2e-full:[\s\S]*?PLAYWRIGHT_WORKERS: 1/u);
|
|
expect(source).toMatch(/e2e-full:[\s\S]*?PLAYWRIGHT_VIDEO_MODE: off/u);
|
|
expect(source).toContain("scripts/ci/with-systemd-inhibit.sh");
|
|
expect(source).toContain("scripts/ci/runner-diagnostics.sh");
|
|
});
|
|
|
|
it("diffs pull request changed-area tests against the PR head instead of the merge commit", () => {
|
|
const source = workflowSource();
|
|
|
|
expect(source).toContain("PR_HEAD_SHA: ${{ github.event.pull_request.head.sha }}");
|
|
expect(source).toMatch(
|
|
/if \[\[ "\$EVENT_NAME" == "pull_request" && -n "\$PR_BASE_SHA" \]\]; then[\s\S]*?head_ref="\$PR_HEAD_SHA"/u
|
|
);
|
|
expect(source).toContain('echo "head=$head_ref" >> "$GITHUB_OUTPUT"');
|
|
});
|
|
|
|
it("keeps PR E2E runner pressure bounded and diagnosable", () => {
|
|
const source = workflowSource();
|
|
|
|
expect(source).toMatch(/e2e-pr:[\s\S]*?runs-on: ubuntu-24\.04/u);
|
|
expect(source).toMatch(/e2e-pr:[\s\S]*?max-parallel: 4/u);
|
|
expect(source).toMatch(/e2e-pr:[\s\S]*?PLAYWRIGHT_WORKERS: 1/u);
|
|
expect(source).toMatch(/e2e-pr:[\s\S]*?PLAYWRIGHT_VIDEO_MODE: on-first-retry/u);
|
|
expect(source).toMatch(/e2e-pr:[\s\S]*?--env PLAYWRIGHT_WORKERS="\$PLAYWRIGHT_WORKERS"/u);
|
|
expect(source).toMatch(/e2e-pr:[\s\S]*?--env PLAYWRIGHT_VIDEO_MODE="\$PLAYWRIGHT_VIDEO_MODE"/u);
|
|
});
|
|
|
|
it("supports GitHub-hosted manual runners while keeping PR E2E on Ubuntu", () => {
|
|
const source = workflowSource();
|
|
|
|
expect(source).toContain("github.event.inputs.runner == 'github-hosted'");
|
|
expect(source).toContain('["ubuntu-latest"]');
|
|
expect(source).toContain('["self-hosted","Linux","X64","pleno","frontend"]');
|
|
expect(source).toMatch(/e2e-targeted:[\s\S]*?runs-on: ubuntu-24\.04/u);
|
|
expect(source).toMatch(/e2e-pr:[\s\S]*?runs-on: ubuntu-24\.04/u);
|
|
expect(source).toMatch(/e2e-full:[\s\S]*?frontend","docker"\]/u);
|
|
});
|
|
|
|
it("supports targeted manual Playwright reruns on GitHub-hosted runners", () => {
|
|
const source = workflowSource();
|
|
|
|
expect(source).toContain("workflow_dispatch:");
|
|
expect(source).toContain("targeted-then-full");
|
|
expect(source).toContain("target_specs:");
|
|
expect(source).toContain("target_projects:");
|
|
expect(source).toContain("e2e-targeted:");
|
|
expect(source).toContain("matrix:");
|
|
expect(source).toContain("project: ${{ fromJSON(inputs.target_projects || '[\"chromium-desktop\"]') }}");
|
|
expect(source).toMatch(/e2e-targeted:[\s\S]*?runs-on: ubuntu-24\.04/u);
|
|
expect(source).toContain('npx playwright test "${args[@]}"');
|
|
expect(source).toContain('"$spec_path" != tests/e2e/*');
|
|
expect(source).toMatch(/e2e-pr:[\s\S]*?needs: \[build-and-unit, e2e-targeted\]/u);
|
|
});
|
|
|
|
it("uses a machine-wide Playwright port lock across self-hosted runner processes", () => {
|
|
const source = workflowSource();
|
|
|
|
expect(source.match(/PLAYWRIGHT_PORT_LOCK_ROOT:-\/tmp\/pleno-playwright-port-locks/gu)).toHaveLength(3);
|
|
expect(source.match(/chmod 1777 "\$lock_root"/gu)).toHaveLength(3);
|
|
expect(source).not.toContain("${RUNNER_TEMP:-/tmp}/pleno-playwright-port-locks");
|
|
});
|
|
|
|
it("allows CI to reduce Playwright video artifact pressure", () => {
|
|
const source = readFileSync(join(root, "playwright.config.ts"), "utf8");
|
|
|
|
expect(source).toContain("PLAYWRIGHT_VIDEO_MODE");
|
|
expect(source).toContain('"retain-on-failure"');
|
|
expect(source).toContain('"on-first-retry"');
|
|
});
|
|
|
|
it("serves Font Awesome from local fixtures so external stylesheets cannot block page load", () => {
|
|
const source = networkFixtureSource();
|
|
|
|
expect(source).toContain("fixtures/fontawesome/6.7.1");
|
|
expect(source).toContain("FONT_AWESOME_CSS");
|
|
expect(source).toContain("FONT_AWESOME_WEBFONTS");
|
|
expect(source).toContain('contentType: "text/css"');
|
|
expect(source).toContain('contentType: "font/woff2"');
|
|
expect(source).toContain("body: FONT_AWESOME_CSS");
|
|
});
|
|
});
|