Skip to content

Commit 2b2756a

Browse files
SisyphusZhengDevBot
andauthored
test(www,e2e): harden E2E selectors and cover the full public IA (#1232, B2.10) (#1306)
* test(www,e2e): harden E2E selectors and cover the full public IA (#1232, B2.10) M19: replace ad-hoc shadow-piercing and styling-class selectors with user-visible semantics (roles, accessible names, landmarks, stable routes) across the www e2e suite; the search island gains real dialog/region/input ARIA semantics instead of test-only hooks. Structural probes that test the DSD/shadow contract itself keep structural queries by design. M20: www/e2e/public-routes.spec.ts derives the full public route list mechanically from the built sitemap at collection time and smoke-covers every public route (status, locale lang, main landmark, visible h1, no uncaught page error) on all three CI engines via the browser-smoke legs; a fail-closed cross-check requires every content-graph article route (both locales) to appear in the sitemap. Playwright reporters are CI-visible (github annotations + html report + failure artifact upload), and the fullyParallel/workers contradiction in www/e2e is resolved (2 on CI, 50% locally); the serial fixture/starter configs document their coherence. * chore(adapter-vite): refresh request-time fixture lockfile to 0.44.0-beta.1 workspace links The committed lock still referenced 0.43.3 workspace links; the fixture build rewrites it to the current package line. Committed separately from the #1232 change set to keep the drift visible. --------- Co-authored-by: DevBot <devbot@openelement.dev>
1 parent bddbb9d commit 2b2756a

22 files changed

Lines changed: 361 additions & 301 deletions

.github/workflows/autoflow-ci.yml

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,23 @@ jobs:
7979
run: ./node_modules/.bin/playwright install --with-deps chromium firefox webkit
8080
- name: AutoFlow3 CI gate
8181
run: deno task autoflow:ci
82+
# #1232 (B2.10): e2e failures must be inspectable — the 'github'
83+
# reporter annotates the run inline, and this step publishes the
84+
# Playwright HTML report + per-test traces/screenshots on failure.
85+
- name: Upload Playwright failure artifacts
86+
if: failure()
87+
# v7.0.1
88+
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a
89+
with:
90+
name: playwright-failure-artifacts-${{ github.run_id }}-${{ github.run_attempt }}
91+
path: |
92+
www/e2e/test-results
93+
www/e2e/playwright-report
94+
e2e/starter-smoke/test-results
95+
packages/adapter-vite/__fixtures__/request-time/e2e/test-results
96+
packages/adapter-vite/__fixtures__/ui-dogfood/e2e/test-results
97+
if-no-files-found: ignore
98+
retention-days: 14
8299

83100
# Issue #628 (first slice): the generated dist/server artifact must boot
84101
# under plain Node, not just under the Deno CLI (#969). The runtime floor

deno.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -112,7 +112,7 @@
112112
"check:visual-baselines": "deno run --allow-read tools/check-visual-baseline-duplicates.ts",
113113
"test:e2e:browsers": "deno run -A npm:@playwright/test@1.59.1 test --config www/e2e/playwright.config.ts",
114114
"test:e2e:install": "deno run -A npm:playwright@1.59.1 install chromium",
115-
"test:e2e:browser-smoke": "deno run -A npm:@playwright/test@1.59.1 test --config www/e2e/playwright.config.ts --grep \"DSD Layers|Layout Island Shell|Island Script Loading|Theme Toggle|Theme initialization|data-signal bindings|SSR/hydration mismatch degradation|light-mode in-place activation|router guards on browser history traversal|reflect: true static props\" --project",
115+
"test:e2e:browser-smoke": "deno run -A npm:@playwright/test@1.59.1 test --config www/e2e/playwright.config.ts --grep \"DSD Layers|Layout Island Shell|Island Script Loading|Theme Toggle|Theme initialization|data-signal bindings|SSR/hydration mismatch degradation|light-mode in-place activation|router guards on browser history traversal|reflect: true static props|Public IA route coverage\" --project",
116116
"test:e2e:browsers:install": "deno run -A npm:playwright@1.59.1 install chromium firefox webkit",
117117
"fixture:request-time:build": "cd packages/adapter-vite/__fixtures__/request-time && deno run --config ../../../../deno.json --allow-read --allow-write --allow-net --allow-env --allow-ffi --allow-sys --allow-run ../../src/cli/build.ts",
118118
"fixture:static-only:build": "cd packages/adapter-vite/__fixtures__/static-only && rm -rf dist && deno run --config ../../../../deno.json --allow-read --allow-write --allow-net --allow-env --allow-ffi --allow-sys --allow-run ../../src/cli/build.ts",

docs/current/BROWSER_BASELINE.md

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,10 @@ The supported behavior is verified in the Chromium, Firefox, and WebKit
1212
Playwright projects by `www/e2e/dsd-layers.spec.ts`. CI runs the full E2E suite
1313
on Chromium and the DSD/island-hydration/theme smoke subset
1414
(`test:e2e:browser-smoke firefox`, `test:e2e:browser-smoke webkit`) on Firefox
15-
and WebKit. The
15+
and WebKit. Since #1232 (B2.10) that smoke subset also includes the full
16+
public information architecture route coverage
17+
(`www/e2e/public-routes.spec.ts`, routes enumerated mechanically from the
18+
built sitemap), so every public page renders on all three gated engines. The
1619
default build emits no inline DSD fallback, so a strict CSP does not need
1720
`unsafe-inline` for DSD.
1821

e2e/starter-smoke/playwright.config.ts

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,16 +24,20 @@ export default defineConfig({
2424
// dev.spec.ts targets the vite dev server (playwright.dev.config.ts), not
2525
// the production `start` server this config boots.
2626
testIgnore: 'dev.spec.ts',
27+
// Serial by design (#1232): one packed starter serves one app on one port,
28+
// so fullyParallel and workers agree on sequential execution.
2729
fullyParallel: false,
2830
forbidOnly: !!process.env.CI,
2931
retries: process.env.CI ? 2 : 0,
3032
workers: 1,
31-
reporter: 'list',
33+
// CI-visible reporting (#1232): 'github' annotates failures on the run.
34+
reporter: process.env.CI ? [['list'], ['github']] : 'list',
3235
timeout: 60_000,
3336

3437
use: {
3538
baseURL,
3639
trace: 'on-first-retry',
40+
screenshot: 'only-on-failure',
3741
},
3842

3943
webServer: {

packages/adapter-vite/__fixtures__/request-time/deno.lock

Lines changed: 5 additions & 5 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

packages/adapter-vite/__fixtures__/request-time/e2e/playwright.config.ts

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,16 +19,20 @@ const baseURL = `http://127.0.0.1:${PORT}`;
1919
export default defineConfig({
2020
testDir: '.',
2121
testMatch: '*.spec.ts',
22+
// Serial by design (#1232): one fixture server owns one port and delegates
23+
// to dist/server, so fullyParallel and workers agree on sequential runs.
2224
fullyParallel: false,
2325
forbidOnly: !!process.env.CI,
2426
retries: process.env.CI ? 2 : 0,
2527
workers: 1,
26-
reporter: 'list',
28+
// CI-visible reporting (#1232): 'github' annotates failures on the run.
29+
reporter: process.env.CI ? [['list'], ['github']] : 'list',
2730
timeout: 60_000,
2831

2932
use: {
3033
baseURL,
3134
trace: 'on-first-retry',
35+
screenshot: 'only-on-failure',
3236
},
3337

3438
webServer: {

packages/adapter-vite/__fixtures__/ui-dogfood/e2e/playwright.config.ts

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,16 +20,20 @@ const baseURL = `http://127.0.0.1:${PORT}`;
2020
export default defineConfig({
2121
testDir: '.',
2222
testMatch: '*.spec.ts',
23+
// Serial by design (#1232): one fixture server owns one port, so
24+
// fullyParallel and workers agree on sequential execution.
2325
fullyParallel: false,
2426
forbidOnly: !!process.env.CI,
2527
retries: process.env.CI ? 2 : 0,
2628
workers: 1,
27-
reporter: 'list',
29+
// CI-visible reporting (#1232): 'github' annotates failures on the run.
30+
reporter: process.env.CI ? [['list'], ['github']] : 'list',
2831
timeout: 60_000,
2932

3033
use: {
3134
baseURL,
3235
trace: 'on-first-retry',
36+
screenshot: 'only-on-failure',
3337
},
3438

3539
webServer: {

www/app/islands/open-search.tsx

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -66,14 +66,15 @@ export default class OpenSearch extends OpenElement {
6666
</button>
6767

6868
<div class='overlay' hidden onClick={this.closeSearchOnBackdrop}>
69-
<div class='panel'>
69+
<div class='panel' role='dialog' aria-modal='true' aria-label='Search'>
7070
<input
7171
type='text'
7272
class='search-input'
73+
aria-label='Search documentation'
7374
placeholder='Search documentation...'
7475
onInput={this.searchFromInput}
7576
/>
76-
<div class='results'>
77+
<div class='results' role='region' aria-label='Search results' aria-live='polite'>
7778
<div class='empty'>Type at least 2 characters to search</div>
7879
</div>
7980
</div>

www/e2e/accessibility-performance.spec.ts

Lines changed: 9 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -74,10 +74,11 @@ test.describe('Accessibility', () => {
7474

7575
test('layout footer labels do not create skipped heading levels', async ({ page }) => {
7676
await page.goto('/');
77-
await expect(page.locator('open-layout footer h4')).toHaveCount(0);
78-
const footer = page.locator('open-layout footer.app-footer');
77+
// The shell footer is a contentinfo landmark; its links and any stray
78+
// headings are user-visible semantics, so query by role, not class.
79+
const footer = page.getByRole('contentinfo');
7980
await expect(footer).toHaveCount(1);
80-
await expect(footer.locator('h1, h2, h3, h4, h5, h6')).toHaveCount(0);
81+
await expect(footer.getByRole('heading')).toHaveCount(0);
8182
await expect(footer.getByRole('link', { name: 'GitHub' })).toHaveAttribute(
8283
'href',
8384
'https://github.com/open-element/openelement',
@@ -128,9 +129,9 @@ test.describe('Accessibility', () => {
128129
});
129130
expect(hasDelegatesFocus).toBe(true);
130131

131-
// Verify the toggle contains a button (focusable target)
132-
const hasButton = await page.locator('open-theme-toggle >> button').count();
133-
expect(hasButton).toBeGreaterThan(0);
132+
// Verify the toggle button is a focusable target with an accessible
133+
// name (role locators pierce the component's open shadow root).
134+
await expect(page.getByRole('button', { name: 'Toggle theme' })).toBeVisible();
134135
}
135136
});
136137

@@ -199,8 +200,8 @@ test.describe('Performance', () => {
199200
await page.waitForFunction(() => {
200201
const layout = document.querySelector('open-layout');
201202
// The compiled app shell is a light-root static artifact. It is usable
202-
// before upgrade when its provenance marker and rendered body exist.
203-
return layout?.hasAttribute('data-oe-light') && !!layout.querySelector('.app-layout');
203+
// before upgrade when its provenance marker and main landmark exist.
204+
return layout?.hasAttribute('data-oe-light') && !!layout.querySelector('main');
204205
});
205206

206207
// Filter out known non-critical errors (e.g., analytics, CDN, external CDN integrity mismatch)

www/e2e/cinematic-home.spec.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,8 @@ test.describe('Cinematic homepage', () => {
1414

1515
test('renders a transparent theme-aware logo linked to the current locale home', async ({ page }) => {
1616
await page.goto('/zh/guide/getting-started');
17-
const logo = page.locator('open-layout').locator('a.logo');
17+
// The logo is the site-name link inside the banner landmark.
18+
const logo = page.getByRole('banner').getByRole('link', { name: 'openElement' });
1819
await expect(logo).toBeVisible();
1920
await expect(logo).toHaveAttribute('href', '/zh');
2021
await expect.poll(() => logo.evaluate((element) => getComputedStyle(element).backgroundImage))

0 commit comments

Comments
 (0)