test: establish frontend gate taxonomy
This commit is contained in:
@@ -0,0 +1,24 @@
|
|||||||
|
# Test and evidence taxonomy
|
||||||
|
|
||||||
|
Each gate is blocking in its declared scope. Failures are not downgraded with
|
||||||
|
`continue-on-error` or warning-only scripts.
|
||||||
|
|
||||||
|
| Level | Command | Evidence |
|
||||||
|
| --- | --- | --- |
|
||||||
|
| runtime schema | `pnpm test:runtime-schema` | `artifacts/tests/runtime-schema.xml` |
|
||||||
|
| unit | `pnpm test:unit` | `artifacts/tests/unit.xml` |
|
||||||
|
| component | `pnpm test:component` | `artifacts/tests/component.xml` |
|
||||||
|
| integration | `pnpm test:integration` | `artifacts/tests/integration.xml` |
|
||||||
|
| end-to-end | `pnpm test:e2e` | `artifacts/tests/e2e/` |
|
||||||
|
| accessibility | `pnpm test:a11y` | `artifacts/tests/a11y.json` |
|
||||||
|
|
||||||
|
A control is verified only when a positive fixture passes and its deliberately
|
||||||
|
failing negative fixture is rejected. Generated evidence is retained by CI;
|
||||||
|
the repository tracks only the evidence directory structure.
|
||||||
|
|
||||||
|
Promotion is an AND graph:
|
||||||
|
|
||||||
|
1. merge gates
|
||||||
|
2. merge gates plus release gates
|
||||||
|
3. release gates plus rollback/runbook drills
|
||||||
|
4. production promotion plus eligible field Web Vitals evidence
|
||||||
+17
-2
@@ -13,18 +13,33 @@
|
|||||||
"build": "vite build && node scripts/generate-build-manifest.mjs",
|
"build": "vite build && node scripts/generate-build-manifest.mjs",
|
||||||
"preview": "vite preview",
|
"preview": "vite preview",
|
||||||
"check:types": "tsc --allowJs --checkJs --noEmit",
|
"check:types": "tsc --allowJs --checkJs --noEmit",
|
||||||
"check:types:fixture": "tsc --allowJs --checkJs --noEmit --target ES2022 --module NodeNext --moduleResolution NodeNext tests/fixtures/typecheck/invalid-port-call.js"
|
"check:types:fixture": "tsc --allowJs --checkJs --noEmit --target ES2022 --module NodeNext --moduleResolution NodeNext tests/fixtures/typecheck/invalid-port-call.js",
|
||||||
|
"test:runtime-schema": "vitest run tests/runtime-schema --reporter=default --reporter=junit --outputFile.junit=artifacts/tests/runtime-schema.xml --passWithNoTests",
|
||||||
|
"test:unit": "vitest run tests/unit --reporter=default --reporter=junit --outputFile.junit=artifacts/tests/unit.xml",
|
||||||
|
"test:component": "vitest run tests/component --reporter=default --reporter=junit --outputFile.junit=artifacts/tests/component.xml",
|
||||||
|
"test:integration": "vitest run tests/integration --reporter=default --reporter=junit --outputFile.junit=artifacts/tests/integration.xml",
|
||||||
|
"test:e2e": "playwright test",
|
||||||
|
"test:a11y": "playwright test --grep @a11y",
|
||||||
|
"test:all": "pnpm test:runtime-schema && pnpm test:unit && pnpm test:component && pnpm test:integration"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"react": "19.2.8",
|
"react": "19.2.8",
|
||||||
"react-dom": "19.2.8"
|
"react-dom": "19.2.8"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
|
"@axe-core/playwright": "4.12.1",
|
||||||
|
"@playwright/test": "1.62.0",
|
||||||
|
"@testing-library/jest-dom": "7.0.0",
|
||||||
|
"@testing-library/react": "16.3.2",
|
||||||
|
"@testing-library/user-event": "14.6.1",
|
||||||
"@types/node": "24.13.3",
|
"@types/node": "24.13.3",
|
||||||
"@types/react": "19.2.8",
|
"@types/react": "19.2.8",
|
||||||
"@types/react-dom": "19.2.3",
|
"@types/react-dom": "19.2.3",
|
||||||
"@vitejs/plugin-react": "6.0.4",
|
"@vitejs/plugin-react": "6.0.4",
|
||||||
|
"jsdom": "29.1.1",
|
||||||
|
"msw": "2.15.0",
|
||||||
"typescript": "7.0.2",
|
"typescript": "7.0.2",
|
||||||
"vite": "8.1.5"
|
"vite": "8.1.5",
|
||||||
|
"vitest": "4.1.10"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,23 @@
|
|||||||
|
import { defineConfig, devices } from "@playwright/test";
|
||||||
|
|
||||||
|
export default defineConfig({
|
||||||
|
testDir: "./tests/e2e",
|
||||||
|
outputDir: "./artifacts/tests/e2e/results",
|
||||||
|
reporter: [
|
||||||
|
["list"],
|
||||||
|
["html", { outputFolder: "./artifacts/tests/e2e/report", open: "never" }],
|
||||||
|
],
|
||||||
|
use: {
|
||||||
|
baseURL: "http://127.0.0.1:5173",
|
||||||
|
trace: "retain-on-failure",
|
||||||
|
screenshot: "only-on-failure",
|
||||||
|
},
|
||||||
|
webServer: {
|
||||||
|
command: "pnpm dev --host 127.0.0.1",
|
||||||
|
url: "http://127.0.0.1:5173",
|
||||||
|
reuseExistingServer: !process.env.CI,
|
||||||
|
},
|
||||||
|
projects: [
|
||||||
|
{ name: "chromium", use: { ...devices["Desktop Chrome"] } },
|
||||||
|
],
|
||||||
|
});
|
||||||
Generated
+1231
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,6 @@
|
|||||||
|
allowBuilds:
|
||||||
|
msw: true
|
||||||
|
minimumReleaseAgeExclude:
|
||||||
|
- '@playwright/test@1.62.0'
|
||||||
|
- playwright-core@1.62.0
|
||||||
|
- playwright@1.62.0
|
||||||
@@ -0,0 +1,17 @@
|
|||||||
|
// @vitest-environment jsdom
|
||||||
|
|
||||||
|
import { render, screen } from "@testing-library/react";
|
||||||
|
import { describe, expect, it } from "vitest";
|
||||||
|
|
||||||
|
function TestShell() {
|
||||||
|
return <main aria-label="application shell">ready</main>;
|
||||||
|
}
|
||||||
|
|
||||||
|
describe("component test level", () => {
|
||||||
|
it("renders an accessible application shell", () => {
|
||||||
|
render(<TestShell />);
|
||||||
|
expect(screen.getByRole("main", { name: "application shell" })).toHaveTextContent(
|
||||||
|
"ready",
|
||||||
|
);
|
||||||
|
});
|
||||||
|
});
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
import { expect, test } from "@playwright/test";
|
||||||
|
|
||||||
|
test("boots the public app shell", async ({ page }) => {
|
||||||
|
await page.goto("/");
|
||||||
|
await expect(page.getByRole("heading", { level: 1 })).toHaveText(
|
||||||
|
"Clean Architecture Frontend",
|
||||||
|
);
|
||||||
|
});
|
||||||
@@ -0,0 +1,23 @@
|
|||||||
|
import { HttpResponse, http } from "msw";
|
||||||
|
import { setupServer } from "msw/node";
|
||||||
|
import { afterAll, afterEach, beforeAll, describe, expect, it } from "vitest";
|
||||||
|
|
||||||
|
const server = setupServer(
|
||||||
|
http.get("https://example.test/health", () =>
|
||||||
|
HttpResponse.json({ success: true, data: { status: "ok" } }),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
beforeAll(() => server.listen({ onUnhandledRequest: "error" }));
|
||||||
|
afterEach(() => server.resetHandlers());
|
||||||
|
afterAll(() => server.close());
|
||||||
|
|
||||||
|
describe("integration test level", () => {
|
||||||
|
it("uses MSW to isolate the HTTP boundary", async () => {
|
||||||
|
const response = await fetch("https://example.test/health");
|
||||||
|
await expect(response.json()).resolves.toEqual({
|
||||||
|
success: true,
|
||||||
|
data: { status: "ok" },
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
import "@testing-library/jest-dom/vitest";
|
||||||
|
import { cleanup } from "@testing-library/react";
|
||||||
|
import { afterEach } from "vitest";
|
||||||
|
|
||||||
|
afterEach(() => {
|
||||||
|
cleanup();
|
||||||
|
});
|
||||||
@@ -0,0 +1,13 @@
|
|||||||
|
import { describe, expect, it, vi } from "vitest";
|
||||||
|
|
||||||
|
import { systemClock } from "../../src/application/ports/clock-port.js";
|
||||||
|
|
||||||
|
describe("systemClock", () => {
|
||||||
|
it("resolves after the requested duration", async () => {
|
||||||
|
vi.useFakeTimers();
|
||||||
|
const sleeper = systemClock.sleep(250);
|
||||||
|
await vi.advanceTimersByTimeAsync(250);
|
||||||
|
await expect(sleeper).resolves.toBeUndefined();
|
||||||
|
vi.useRealTimers();
|
||||||
|
});
|
||||||
|
});
|
||||||
@@ -0,0 +1,15 @@
|
|||||||
|
import { defineConfig } from "vitest/config";
|
||||||
|
|
||||||
|
export default defineConfig({
|
||||||
|
test: {
|
||||||
|
globals: false,
|
||||||
|
setupFiles: ["./tests/setup.js"],
|
||||||
|
restoreMocks: true,
|
||||||
|
clearMocks: true,
|
||||||
|
mockReset: true,
|
||||||
|
testTimeout: 10_000,
|
||||||
|
coverage: {
|
||||||
|
reporter: ["text", "json-summary"],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
Reference in New Issue
Block a user