All checks were successful
CI / Lint, typecheck, test (push) Successful in 6m24s
CI / Build container images (push) Has been skipped
CI / Auth e2e pack (push) Successful in 8m34s
CI / Import/export fidelity gate (push) Successful in 1m1s
CI / Build container images (pull_request) Successful in 1m13s
CI / Lint, typecheck, test (pull_request) Successful in 6m14s
CI / Auth e2e pack (pull_request) Successful in 8m31s
CI / Import/export fidelity gate (pull_request) Successful in 58s
CD / Build and push images (push) Successful in 19s
CD / Deploy to Test (push) Successful in 14s
CD / Smoke tests against Test (push) Successful in 1m18s
CD / Promote to Int (push) Successful in 11s
In hidden (and later enforced) mode, catalog-listed controls whose only purpose is enabling a violation are not rendered while their saved value is compliant (the four master switches, the Nextcloud backup block); value-listed selects keep only their compliant choices (registration mode, new-page classification, upload policy, SVG policy). Every affected section shows one accessible policy note (i18n de+en) so policy is distinguishable from missing features. A value that was already violating is surfaced exactly like in marked — never silently hidden. The API stays unchanged; enforcement is #246. e2e: hidden half of the marking pack (rows disappear, note visible, already-violating row stays marked, axe WCAG A/AA clean) — verified live locally; CI runs it against a second api (VS_NFD_MODE=hidden, same database) behind its own static server. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01AUtYMxwTCMHG9mVHnwbFg8
101 lines
4.7 KiB
TypeScript
101 lines
4.7 KiB
TypeScript
import AxeBuilder from '@axe-core/playwright';
|
|
import { expect, test } from '@playwright/test';
|
|
|
|
import { contextForUser } from './helpers';
|
|
|
|
/**
|
|
* VS-NfD marking pack (issue #244): in mode `marked`, catalog-listed
|
|
* controls carry an accessible deviation marking that follows the CURRENT
|
|
* (unsaved) value; in mode `off` nothing is marked anywhere. The api's
|
|
* mode is deploy-level, so each half runs only against the matching stack:
|
|
* CI runs the marked half (VS_NFD_MODE=marked on the e2e api), local full
|
|
* runs against a default stack cover the off half.
|
|
*/
|
|
const BASE_URL = process.env.E2E_BASE_URL ?? 'http://localhost:5173';
|
|
const MODE = process.env.E2E_VS_NFD_MODE ?? 'off';
|
|
|
|
test('mode marked: card, checkbox marking, and point-of-choice marking', async ({ browser }) => {
|
|
test.skip(MODE !== 'marked', 'needs an api started with VS_NFD_MODE=marked');
|
|
const context = await contextForUser(browser, BASE_URL, 'fixture-admin');
|
|
const page = await context.newPage();
|
|
await page.goto('/admin');
|
|
await page.waitForLoadState('networkidle');
|
|
|
|
// The profile card shows mode and violation count (issue #243).
|
|
await expect(page.locator('.vs-nfd-summary')).toBeVisible();
|
|
|
|
// feeds.enabled defaults to true = violating: its toggle row is marked,
|
|
// and the marking is part of the control's accessible description.
|
|
const feedsInput = page.locator('input[aria-describedby="vs-nfd-mark-feeds.enabled"]');
|
|
await expect(feedsInput).toBeVisible();
|
|
await expect(page.locator('#vs-nfd-mark-feeds\\.enabled')).toBeVisible();
|
|
|
|
// Point of choice: the registration-mode marking follows the UNSAVED
|
|
// select value — compliant choice clears it, violating choice brings it
|
|
// back, no save in between.
|
|
const regField = page.locator('label.field', {
|
|
has: page.locator('select[name="auth.registrationMode"]'),
|
|
});
|
|
const regSelect = regField.locator('select');
|
|
await regSelect.selectOption('open');
|
|
await expect(regField.locator('.vs-nfd-mark')).toBeVisible();
|
|
await regSelect.selectOption('closed');
|
|
await expect(regField.locator('.vs-nfd-mark')).toHaveCount(0);
|
|
await regSelect.selectOption('open');
|
|
await expect(regField.locator('.vs-nfd-mark')).toBeVisible();
|
|
|
|
await context.close();
|
|
});
|
|
|
|
test('mode hidden: rows disappear, notes mark the hiding, a11y clean', async ({ browser }) => {
|
|
test.skip(MODE !== 'hidden', 'needs an api started with VS_NFD_MODE=hidden');
|
|
const context = await contextForUser(browser, BASE_URL, 'fixture-admin');
|
|
const page = await context.newPage();
|
|
await page.goto('/admin');
|
|
await page.waitForLoadState('networkidle');
|
|
|
|
// feeds.enabled defaults to true = ALREADY violating: never silently
|
|
// hidden — the row stays visible with its marking.
|
|
await expect(page.locator('#vs-nfd-mark-feeds\\.enabled')).toBeVisible();
|
|
|
|
// Master switches: compliant (false) rows disappear, violating (true)
|
|
// rows stay — derive the expectation from the actual instance settings,
|
|
// the seed state differs between local and CI databases.
|
|
const settings = (await (
|
|
await context.request.get(`${BASE_URL}/api/v1/admin/settings`)
|
|
).json()) as Record<string, boolean>;
|
|
const masterKeys = ['api.enabled', 'mcp.enabled', 'feeds.enabled', 'plugins.enabled'] as const;
|
|
const visible = masterKeys.filter((key) => settings[key] === true).length;
|
|
expect(visible).toBeLessThan(masterKeys.length); // at least one row is hidden
|
|
await expect(page.locator('.api-opt-in__label')).toHaveCount(visible);
|
|
await expect(page.locator('.vs-nfd-hidden-note').first()).toBeVisible();
|
|
|
|
// Value-listed control: the compliant registration mode keeps only its
|
|
// compliant choice (seed leaves it open = violating? then all options).
|
|
const regSelect = page.locator('select[name="auth.registrationMode"]');
|
|
const regField = page.locator('label.field', { has: regSelect });
|
|
const optionCount = await regSelect.locator('option').count();
|
|
const marked = await regField.locator('.vs-nfd-mark').count();
|
|
// Exactly one of the two treatments applies, never a silent third state.
|
|
expect(optionCount === 1 || marked === 1).toBe(true);
|
|
|
|
// The hidden state and the policy note pass the axe WCAG A/AA scan.
|
|
const results = await new AxeBuilder({ page }).withTags(['wcag2a', 'wcag2aa']).analyze();
|
|
expect(results.violations).toEqual([]);
|
|
|
|
await context.close();
|
|
});
|
|
|
|
test('mode off: no card, no markings', async ({ browser }) => {
|
|
test.skip(MODE !== 'off', 'covers the default stack only');
|
|
const context = await contextForUser(browser, BASE_URL, 'fixture-admin');
|
|
const page = await context.newPage();
|
|
await page.goto('/admin');
|
|
await page.waitForLoadState('networkidle');
|
|
|
|
await expect(page.locator('.vs-nfd-summary')).toHaveCount(0);
|
|
await expect(page.locator('.vs-nfd-mark')).toHaveCount(0);
|
|
|
|
await context.close();
|
|
});
|