@fairfox/polly 0.77.3 → 0.79.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cli/polly.js +46 -3
- package/dist/cli/polly.js.map +3 -3
- package/dist/src/background/index.js.map +3 -3
- package/dist/src/background/message-router.js.map +3 -3
- package/dist/src/client/index.js +137 -32
- package/dist/src/client/index.js.map +6 -5
- package/dist/src/client/wrapper.d.ts +39 -2
- package/dist/src/elysia/index.js +22 -3
- package/dist/src/elysia/index.js.map +5 -5
- package/dist/src/elysia/route-match.d.ts +13 -0
- package/dist/src/index.d.ts +1 -1
- package/dist/src/index.js +12 -2
- package/dist/src/index.js.map +7 -7
- package/dist/src/mesh.js +87 -46
- package/dist/src/mesh.js.map +12 -11
- package/dist/src/peer.js +7 -3
- package/dist/src/peer.js.map +6 -6
- package/dist/src/polly-ui/Badge.d.ts +5 -0
- package/dist/src/polly-ui/Button.d.ts +31 -6
- package/dist/src/polly-ui/Dropdown.d.ts +6 -0
- package/dist/src/polly-ui/Select.d.ts +11 -1
- package/dist/src/polly-ui/TextInput.d.ts +30 -0
- package/dist/src/polly-ui/index.css +10 -0
- package/dist/src/polly-ui/index.js +81 -32
- package/dist/src/polly-ui/index.js.map +10 -10
- package/dist/src/polly-ui/styles.css +10 -0
- package/dist/src/shared/adapters/index.js.map +3 -3
- package/dist/src/shared/lib/context-helpers.js.map +3 -3
- package/dist/src/shared/lib/mesh-client.d.ts +38 -0
- package/dist/src/shared/lib/mesh-signaling-client.d.ts +6 -5
- package/dist/src/shared/lib/mesh-state.d.ts +21 -0
- package/dist/src/shared/lib/message-bus.js.map +3 -3
- package/dist/src/shared/lib/peer-relay-adapter.d.ts +5 -0
- package/dist/src/shared/lib/peer-repo-server.d.ts +15 -0
- package/dist/src/shared/lib/resource.js +11 -2
- package/dist/src/shared/lib/resource.js.map +6 -6
- package/dist/src/shared/lib/state.d.ts +20 -0
- package/dist/src/shared/lib/state.js +11 -1
- package/dist/src/shared/lib/state.js.map +5 -5
- package/dist/src/shared/state/app-state.js +10 -1
- package/dist/src/shared/state/app-state.js.map +5 -5
- package/dist/tools/init/src/cli.js +23 -2
- package/dist/tools/init/src/cli.js.map +4 -4
- package/dist/tools/init/templates/pwa/package.json.template +1 -1
- package/dist/tools/init/templates/pwa/src/service-worker.ts.template +26 -15
- package/dist/tools/init/templates/pwa/src/shared-worker.ts.template +13 -3
- package/dist/tools/init/templates/pwa/tsconfig.json.template +2 -2
- package/dist/tools/init/templates/pwa/tsconfig.worker.json.template +17 -0
- package/dist/tools/test/src/browser/index.js +5 -2
- package/dist/tools/test/src/browser/index.js.map +3 -3
- package/dist/tools/test/src/contrast/index.js +20 -15
- package/dist/tools/test/src/contrast/index.js.map +3 -3
- package/dist/tools/test/src/e2e-cli/index.d.ts +10 -0
- package/dist/tools/test/src/e2e-cli/run-cli.d.ts +25 -0
- package/dist/tools/test/src/e2e-cli/with-temp-dir.d.ts +15 -0
- package/dist/tools/test/src/e2e-mesh/index.js +12 -7
- package/dist/tools/test/src/e2e-mesh/index.js.map +4 -4
- package/dist/tools/test/src/e2e-mesh/launch-peer.d.ts +7 -1
- package/dist/tools/test/src/e2e-relay/index.d.ts +12 -0
- package/dist/tools/test/src/e2e-relay/wait-for-relay-convergence.d.ts +27 -0
- package/dist/tools/test/src/e2e-relay/with-repo-server.d.ts +24 -0
- package/dist/tools/test/src/e2e-shared/assert.d.ts +18 -0
- package/dist/tools/test/src/e2e-shared/contract.d.ts +40 -0
- package/dist/tools/test/src/e2e-shared/index.d.ts +2 -0
- package/dist/tools/test/src/tiers/args.d.ts +23 -0
- package/dist/tools/test/src/tiers/cli.d.ts +2 -0
- package/dist/tools/test/src/tiers/cli.js +490 -0
- package/dist/tools/test/src/tiers/cli.js.map +16 -0
- package/dist/tools/test/src/tiers/detect.d.ts +12 -0
- package/dist/tools/test/src/tiers/discover.d.ts +2 -0
- package/dist/tools/test/src/tiers/engine.d.ts +3 -0
- package/dist/tools/test/src/tiers/index.d.ts +14 -0
- package/dist/tools/test/src/tiers/protocol.d.ts +10 -0
- package/dist/tools/test/src/tiers/reporter.d.ts +12 -0
- package/dist/tools/test/src/tiers/types.d.ts +94 -0
- package/dist/tools/test/src/tiers/worker.d.ts +2 -0
- package/dist/tools/test/src/tiers/worker.js +60 -0
- package/dist/tools/test/src/tiers/worker.js.map +12 -0
- package/dist/tools/verify/src/cli.js +322 -27
- package/dist/tools/verify/src/cli.js.map +13 -10
- package/dist/tools/verify/src/config.d.ts +10 -0
- package/dist/tools/verify/src/config.js.map +2 -2
- package/dist/tools/verify/src/stryker/index.js +20 -11
- package/dist/tools/verify/src/stryker/index.js.map +3 -3
- package/dist/tools/visualize/src/cli.js +8 -5
- package/dist/tools/visualize/src/cli.js.map +4 -4
- package/package.json +16 -6
|
@@ -2,9 +2,9 @@
|
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../tools/test/src/browser/harness.ts"],
|
|
4
4
|
"sourcesContent": [
|
|
5
|
-
"/**\n * Browser-side test harness for Polly applications.\n *\n * Provides describe/test/expect/done that run inside a Puppeteer-launched\n * browser tab and record results on window.__testResults for the Node-side\n * runner to collect. Matchers cover both value assertions and DOM element\n * assertions so that Preact component tests and WebRTC adapter tests use\n * the same harness.\n *\n * @example\n * ```typescript\n * import { describe, test, expect, done, flush, cleanup } from \"@fairfox/polly/test/browser\";\n *\n * const app = document.getElementById(\"app\")!;\n *\n * describe(\"my feature\", () => {\n * test(\"renders correctly\", async () => {\n * render(<MyComponent />, app);\n * await flush();\n * expect(app.querySelector(\"h1\")).toHaveTextContent(\"Hello\");\n * cleanup(app);\n * });\n * });\n *\n * done();\n * ```\n */\n\ninterface TestResult {\n name: string;\n passed: boolean;\n error?: string;\n}\n\nconst results: TestResult[] = [];\nconst suites: Array<{\n name: string;\n tests: Array<{ name: string; fn: () => Promise<void> | void }>;\n}> = [];\n\nexport function describe(name: string, fn: () => void): void {\n suites.push({ name, tests: [] });\n fn();\n}\n\nexport function test(name: string, fn: () => Promise<void> | void): void {\n const suite = suites[suites.length - 1];\n if (suite) {\n suite.tests.push({ name, fn });\n }\n}\n\nfunction assertElement(value: unknown): Element {\n if (!(value instanceof Element)) {\n throw new Error(`Expected an Element, got ${typeof value}: ${String(value)}`);\n }\n return value;\n}\n\nexport function expect<T>(actual: T) {\n return {\n // Value matchers\n toBe(expected: T) {\n if (actual !== expected) {\n throw new Error(`Expected ${JSON.stringify(expected)}, got ${JSON.stringify(actual)}`);\n }\n },\n toEqual(expected: T) {\n if (JSON.stringify(actual) !== JSON.stringify(expected)) {\n throw new Error(`Expected ${JSON.stringify(expected)}, got ${JSON.stringify(actual)}`);\n }\n },\n toContain(sub: string) {\n if (!String(actual).includes(sub)) {\n throw new Error(`Expected \"${String(actual)}\" to contain \"${sub}\"`);\n }\n },\n toBeTruthy() {\n if (!actual) throw new Error(`Expected truthy, got ${String(actual)}`);\n },\n toBeFalsy() {\n if (actual) throw new Error(`Expected falsy, got ${String(actual)}`);\n },\n toBeNull() {\n if (actual !== null) throw new Error(`Expected null, got ${String(actual)}`);\n },\n toBeDefined() {\n if (actual === undefined || actual === null) {\n throw new Error(`Expected value to be defined, got ${String(actual)}`);\n }\n },\n toBeUndefined() {\n if (actual !== undefined) {\n throw new Error(`Expected undefined, got ${JSON.stringify(actual)}`);\n }\n },\n toBeGreaterThan(expected: number) {\n if (typeof actual !== \"number\" || actual <= expected) {\n throw new Error(`Expected ${String(actual)} to be greater than ${expected}`);\n }\n },\n toHaveLength(expected: number) {\n const obj = actual;\n const len = obj && typeof obj === \"object\" && \"length\" in obj ? Number(obj.length) : -1;\n if (len !== expected) throw new Error(`Expected length ${expected}, got ${len}`);\n },\n toExist() {\n if (actual == null) throw new Error(`Expected value to exist, got ${String(actual)}`);\n },\n\n // DOM element matchers\n toHaveTextContent(expected: string) {\n const el = assertElement(actual);\n if (!el.textContent?.includes(expected)) {\n throw new Error(\n `Expected text content to include ${JSON.stringify(expected)}, got ${JSON.stringify(el.textContent)}`\n );\n }\n },\n toBeChecked() {\n const el = assertElement(actual);\n if (!(el instanceof HTMLInputElement) || !el.checked) {\n throw new Error(\"Expected element to be checked\");\n }\n },\n toBeDisabled() {\n const el = assertElement(actual);\n if (!el.hasAttribute(\"disabled\") && el.getAttribute(\"aria-disabled\") !== \"true\") {\n throw new Error(\"Expected element to be disabled\");\n }\n },\n toHaveValue(expected: string) {\n const el = assertElement(actual);\n const inputEl =\n el instanceof HTMLInputElement || el instanceof HTMLTextAreaElement ? el : null;\n if (!inputEl || inputEl.value !== expected) {\n throw new Error(\n `Expected value ${JSON.stringify(expected)}, got ${JSON.stringify(inputEl?.value ?? \"(not an input)\")}`\n );\n }\n },\n toHaveAttribute(name: string, value?: string) {\n const el = assertElement(actual);\n if (!el.hasAttribute(name)) {\n throw new Error(`Expected element to have attribute \"${name}\"`);\n }\n if (value !== undefined && el.getAttribute(name) !== value) {\n throw new Error(\n `Expected attribute \"${name}\" to be ${JSON.stringify(value)}, got ${JSON.stringify(el.getAttribute(name))}`\n );\n }\n },\n\n // .not variants\n not: {\n toBe(expected: T) {\n if (actual === expected) {\n throw new Error(`Expected value NOT to be ${JSON.stringify(expected)}`);\n }\n },\n toEqual(expected: T) {\n if (JSON.stringify(actual) === JSON.stringify(expected)) {\n throw new Error(`Expected value NOT to equal ${JSON.stringify(expected)}`);\n }\n },\n toContain(sub: string) {\n if (String(actual).includes(sub)) {\n throw new Error(`Expected \"${String(actual)}\" NOT to contain \"${sub}\"`);\n }\n },\n toBeNull() {\n if (actual === null) throw new Error(\"Expected value NOT to be null\");\n },\n toExist() {\n if (actual != null) throw new Error(`Expected value NOT to exist, got ${String(actual)}`);\n },\n toBeChecked() {\n const el = assertElement(actual);\n if (el instanceof HTMLInputElement && el.checked) {\n throw new Error(\"Expected element NOT to be checked\");\n }\n },\n toBeDisabled() {\n const el = assertElement(actual);\n if (el.hasAttribute(\"disabled\") || el.getAttribute(\"aria-disabled\") === \"true\") {\n throw new Error(\"Expected element NOT to be disabled\");\n }\n },\n toHaveAttribute(name: string) {\n const el = assertElement(actual);\n if (el.hasAttribute(name)) {\n throw new Error(`Expected element NOT to have attribute \"${name}\"`);\n }\n },\n },\n };\n}\n\n/**\n * Flush microtasks and pending DOM updates. Call after signal assignments\n * or render calls to give the reactive system and the browser a chance to\n * settle before asserting on the result.\n */\nexport function flush(ms = 50): Promise<void> {\n return new Promise((r) => setTimeout(r, ms));\n}\n\n/**\n * Clear a container's rendered content. Call at the end of each test to\n * prevent state leaking between tests. If you use Preact's render(), pass\n * the same container; the function calls render(null, container) if Preact\n * is available, otherwise sets innerHTML to \"\".\n */\nexport function cleanup(container: Element): void {\n container.innerHTML = \"\";\n}\n\n/**\n * Run all registered tests and report the tally to the Node-side runner.\n *\n * The runner injects `window.__pollyReport` (via Puppeteer's\n * `page.exposeFunction`) before navigating, so reporting is a single\n * push from the page to the runner — no polling, no waiting on a long-\n * running CDP call from the Node side. Polled `page.evaluate` was the\n * root cause of the intermittent `Runtime.callFunctionOn` stalls\n * tracked in polly#138.\n *\n * Call this at the end of every .browser.ts test file.\n */\nexport async function done(): Promise<void> {\n for (const suite of suites) {\n for (const t of suite.tests) {\n const fullName = `${suite.name} > ${t.name}`;\n try {\n await t.fn();\n results.push({ name: fullName, passed: true });\n } catch (err) {\n results.push({\n name: fullName,\n passed: false,\n error: err instanceof Error ? err.message : String(err),\n });\n }\n }\n }\n\n const report = (window as unknown as Record<string, unknown>)[\"__pollyReport\"];\n if (
|
|
5
|
+
"/**\n * Browser-side test harness for Polly applications.\n *\n * Provides describe/test/expect/done that run inside a Puppeteer-launched\n * browser tab and record results on window.__testResults for the Node-side\n * runner to collect. Matchers cover both value assertions and DOM element\n * assertions so that Preact component tests and WebRTC adapter tests use\n * the same harness.\n *\n * @example\n * ```typescript\n * import { describe, test, expect, done, flush, cleanup } from \"@fairfox/polly/test/browser\";\n *\n * const app = document.getElementById(\"app\")!;\n *\n * describe(\"my feature\", () => {\n * test(\"renders correctly\", async () => {\n * render(<MyComponent />, app);\n * await flush();\n * expect(app.querySelector(\"h1\")).toHaveTextContent(\"Hello\");\n * cleanup(app);\n * });\n * });\n *\n * done();\n * ```\n */\n\ninterface TestResult {\n name: string;\n passed: boolean;\n error?: string;\n}\n\nconst results: TestResult[] = [];\nconst suites: Array<{\n name: string;\n tests: Array<{ name: string; fn: () => Promise<void> | void }>;\n}> = [];\n\nexport function describe(name: string, fn: () => void): void {\n suites.push({ name, tests: [] });\n fn();\n}\n\nexport function test(name: string, fn: () => Promise<void> | void): void {\n const suite = suites[suites.length - 1];\n if (suite) {\n suite.tests.push({ name, fn });\n }\n}\n\nfunction assertElement(value: unknown): Element {\n if (!(value instanceof Element)) {\n throw new Error(`Expected an Element, got ${typeof value}: ${String(value)}`);\n }\n return value;\n}\n\nexport function expect<T>(actual: T) {\n return {\n // Value matchers\n toBe(expected: T) {\n if (actual !== expected) {\n throw new Error(`Expected ${JSON.stringify(expected)}, got ${JSON.stringify(actual)}`);\n }\n },\n toEqual(expected: T) {\n if (JSON.stringify(actual) !== JSON.stringify(expected)) {\n throw new Error(`Expected ${JSON.stringify(expected)}, got ${JSON.stringify(actual)}`);\n }\n },\n toContain(sub: string) {\n if (!String(actual).includes(sub)) {\n throw new Error(`Expected \"${String(actual)}\" to contain \"${sub}\"`);\n }\n },\n toBeTruthy() {\n if (!actual) throw new Error(`Expected truthy, got ${String(actual)}`);\n },\n toBeFalsy() {\n if (actual) throw new Error(`Expected falsy, got ${String(actual)}`);\n },\n toBeNull() {\n if (actual !== null) throw new Error(`Expected null, got ${String(actual)}`);\n },\n toBeDefined() {\n if (actual === undefined || actual === null) {\n throw new Error(`Expected value to be defined, got ${String(actual)}`);\n }\n },\n toBeUndefined() {\n if (actual !== undefined) {\n throw new Error(`Expected undefined, got ${JSON.stringify(actual)}`);\n }\n },\n toBeGreaterThan(expected: number) {\n if (typeof actual !== \"number\" || actual <= expected) {\n throw new Error(`Expected ${String(actual)} to be greater than ${expected}`);\n }\n },\n toHaveLength(expected: number) {\n const obj = actual;\n const len = obj && typeof obj === \"object\" && \"length\" in obj ? Number(obj.length) : -1;\n if (len !== expected) throw new Error(`Expected length ${expected}, got ${len}`);\n },\n toExist() {\n if (actual == null) throw new Error(`Expected value to exist, got ${String(actual)}`);\n },\n\n // DOM element matchers\n toHaveTextContent(expected: string) {\n const el = assertElement(actual);\n if (!el.textContent?.includes(expected)) {\n throw new Error(\n `Expected text content to include ${JSON.stringify(expected)}, got ${JSON.stringify(el.textContent)}`\n );\n }\n },\n toBeChecked() {\n const el = assertElement(actual);\n if (!(el instanceof HTMLInputElement) || !el.checked) {\n throw new Error(\"Expected element to be checked\");\n }\n },\n toBeDisabled() {\n const el = assertElement(actual);\n if (!el.hasAttribute(\"disabled\") && el.getAttribute(\"aria-disabled\") !== \"true\") {\n throw new Error(\"Expected element to be disabled\");\n }\n },\n toHaveValue(expected: string) {\n const el = assertElement(actual);\n const inputEl =\n el instanceof HTMLInputElement || el instanceof HTMLTextAreaElement ? el : null;\n if (!inputEl || inputEl.value !== expected) {\n throw new Error(\n `Expected value ${JSON.stringify(expected)}, got ${JSON.stringify(inputEl?.value ?? \"(not an input)\")}`\n );\n }\n },\n toHaveAttribute(name: string, value?: string) {\n const el = assertElement(actual);\n if (!el.hasAttribute(name)) {\n throw new Error(`Expected element to have attribute \"${name}\"`);\n }\n if (value !== undefined && el.getAttribute(name) !== value) {\n throw new Error(\n `Expected attribute \"${name}\" to be ${JSON.stringify(value)}, got ${JSON.stringify(el.getAttribute(name))}`\n );\n }\n },\n\n // .not variants\n not: {\n toBe(expected: T) {\n if (actual === expected) {\n throw new Error(`Expected value NOT to be ${JSON.stringify(expected)}`);\n }\n },\n toEqual(expected: T) {\n if (JSON.stringify(actual) === JSON.stringify(expected)) {\n throw new Error(`Expected value NOT to equal ${JSON.stringify(expected)}`);\n }\n },\n toContain(sub: string) {\n if (String(actual).includes(sub)) {\n throw new Error(`Expected \"${String(actual)}\" NOT to contain \"${sub}\"`);\n }\n },\n toBeNull() {\n if (actual === null) throw new Error(\"Expected value NOT to be null\");\n },\n toExist() {\n if (actual != null) throw new Error(`Expected value NOT to exist, got ${String(actual)}`);\n },\n toBeChecked() {\n const el = assertElement(actual);\n if (el instanceof HTMLInputElement && el.checked) {\n throw new Error(\"Expected element NOT to be checked\");\n }\n },\n toBeDisabled() {\n const el = assertElement(actual);\n if (el.hasAttribute(\"disabled\") || el.getAttribute(\"aria-disabled\") === \"true\") {\n throw new Error(\"Expected element NOT to be disabled\");\n }\n },\n toHaveAttribute(name: string) {\n const el = assertElement(actual);\n if (el.hasAttribute(name)) {\n throw new Error(`Expected element NOT to have attribute \"${name}\"`);\n }\n },\n },\n };\n}\n\n/**\n * Flush microtasks and pending DOM updates. Call after signal assignments\n * or render calls to give the reactive system and the browser a chance to\n * settle before asserting on the result.\n */\nexport function flush(ms = 50): Promise<void> {\n return new Promise((r) => setTimeout(r, ms));\n}\n\n/**\n * Clear a container's rendered content. Call at the end of each test to\n * prevent state leaking between tests. If you use Preact's render(), pass\n * the same container; the function calls render(null, container) if Preact\n * is available, otherwise sets innerHTML to \"\".\n */\nexport function cleanup(container: Element): void {\n container.innerHTML = \"\";\n}\n\n/**\n * Run all registered tests and report the tally to the Node-side runner.\n *\n * The runner injects `window.__pollyReport` (via Puppeteer's\n * `page.exposeFunction`) before navigating, so reporting is a single\n * push from the page to the runner — no polling, no waiting on a long-\n * running CDP call from the Node side. Polled `page.evaluate` was the\n * root cause of the intermittent `Runtime.callFunctionOn` stalls\n * tracked in polly#138.\n *\n * Call this at the end of every .browser.ts test file.\n */\nexport async function done(): Promise<void> {\n for (const suite of suites) {\n for (const t of suite.tests) {\n const fullName = `${suite.name} > ${t.name}`;\n try {\n await t.fn();\n results.push({ name: fullName, passed: true });\n } catch (err) {\n results.push({\n name: fullName,\n passed: false,\n error: err instanceof Error ? err.message : String(err),\n });\n }\n }\n }\n\n const report = (window as unknown as Record<string, unknown>)[\"__pollyReport\"];\n if (!isReportFunction(report)) {\n throw new Error(\n \"harness.done(): window.__pollyReport is not defined. \" +\n \"This harness must be driven by the Polly browser test runner.\"\n );\n }\n report(results);\n}\n\n/** The runner injects `window.__pollyReport`; all we can check is callability. */\nfunction isReportFunction(value: unknown): value is (r: TestResult[]) => void {\n return typeof value === \"function\";\n}\n\n/**\n * Wait until a predicate returns true, polling every intervalMs. Rejects\n * after timeoutMs.\n */\nexport async function waitFor(\n predicate: () => boolean | Promise<boolean>,\n timeoutMs = 5000,\n intervalMs = 25\n): Promise<void> {\n const deadline = Date.now() + timeoutMs;\n while (Date.now() < deadline) {\n if (await predicate()) return;\n await new Promise((r) => setTimeout(r, intervalMs));\n }\n throw new Error(`waitFor timed out after ${timeoutMs}ms`);\n}\n"
|
|
6
6
|
],
|
|
7
|
-
"mappings": ";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAkCA,IAAM,UAAwB,CAAC;AAC/B,IAAM,SAGD,CAAC;AAEC,SAAS,QAAQ,CAAC,MAAc,IAAsB;AAAA,EAC3D,OAAO,KAAK,EAAE,MAAM,OAAO,CAAC,EAAE,CAAC;AAAA,EAC/B,GAAG;AAAA;AAGE,SAAS,IAAI,CAAC,MAAc,IAAsC;AAAA,EACvE,MAAM,QAAQ,OAAO,OAAO,SAAS;AAAA,EACrC,IAAI,OAAO;AAAA,IACT,MAAM,MAAM,KAAK,EAAE,MAAM,GAAG,CAAC;AAAA,EAC/B;AAAA;AAGF,SAAS,aAAa,CAAC,OAAyB;AAAA,EAC9C,IAAI,EAAE,iBAAiB,UAAU;AAAA,IAC/B,MAAM,IAAI,MAAM,4BAA4B,OAAO,UAAU,OAAO,KAAK,GAAG;AAAA,EAC9E;AAAA,EACA,OAAO;AAAA;AAGF,SAAS,MAAS,CAAC,QAAW;AAAA,EACnC,OAAO;AAAA,IAEL,IAAI,CAAC,UAAa;AAAA,MAChB,IAAI,WAAW,UAAU;AAAA,QACvB,MAAM,IAAI,MAAM,YAAY,KAAK,UAAU,QAAQ,UAAU,KAAK,UAAU,MAAM,GAAG;AAAA,MACvF;AAAA;AAAA,IAEF,OAAO,CAAC,UAAa;AAAA,MACnB,IAAI,KAAK,UAAU,MAAM,MAAM,KAAK,UAAU,QAAQ,GAAG;AAAA,QACvD,MAAM,IAAI,MAAM,YAAY,KAAK,UAAU,QAAQ,UAAU,KAAK,UAAU,MAAM,GAAG;AAAA,MACvF;AAAA;AAAA,IAEF,SAAS,CAAC,KAAa;AAAA,MACrB,IAAI,CAAC,OAAO,MAAM,EAAE,SAAS,GAAG,GAAG;AAAA,QACjC,MAAM,IAAI,MAAM,aAAa,OAAO,MAAM,kBAAkB,MAAM;AAAA,MACpE;AAAA;AAAA,IAEF,UAAU,GAAG;AAAA,MACX,IAAI,CAAC;AAAA,QAAQ,MAAM,IAAI,MAAM,wBAAwB,OAAO,MAAM,GAAG;AAAA;AAAA,IAEvE,SAAS,GAAG;AAAA,MACV,IAAI;AAAA,QAAQ,MAAM,IAAI,MAAM,uBAAuB,OAAO,MAAM,GAAG;AAAA;AAAA,IAErE,QAAQ,GAAG;AAAA,MACT,IAAI,WAAW;AAAA,QAAM,MAAM,IAAI,MAAM,sBAAsB,OAAO,MAAM,GAAG;AAAA;AAAA,IAE7E,WAAW,GAAG;AAAA,MACZ,IAAI,WAAW,aAAa,WAAW,MAAM;AAAA,QAC3C,MAAM,IAAI,MAAM,qCAAqC,OAAO,MAAM,GAAG;AAAA,MACvE;AAAA;AAAA,IAEF,aAAa,GAAG;AAAA,MACd,IAAI,WAAW,WAAW;AAAA,QACxB,MAAM,IAAI,MAAM,2BAA2B,KAAK,UAAU,MAAM,GAAG;AAAA,MACrE;AAAA;AAAA,IAEF,eAAe,CAAC,UAAkB;AAAA,MAChC,IAAI,OAAO,WAAW,YAAY,UAAU,UAAU;AAAA,QACpD,MAAM,IAAI,MAAM,YAAY,OAAO,MAAM,wBAAwB,UAAU;AAAA,MAC7E;AAAA;AAAA,IAEF,YAAY,CAAC,UAAkB;AAAA,MAC7B,MAAM,MAAM;AAAA,MACZ,MAAM,MAAM,OAAO,OAAO,QAAQ,YAAY,YAAY,MAAM,OAAO,IAAI,MAAM,IAAI;AAAA,MACrF,IAAI,QAAQ;AAAA,QAAU,MAAM,IAAI,MAAM,mBAAmB,iBAAiB,KAAK;AAAA;AAAA,IAEjF,OAAO,GAAG;AAAA,MACR,IAAI,UAAU;AAAA,QAAM,MAAM,IAAI,MAAM,gCAAgC,OAAO,MAAM,GAAG;AAAA;AAAA,IAItF,iBAAiB,CAAC,UAAkB;AAAA,MAClC,MAAM,KAAK,cAAc,MAAM;AAAA,MAC/B,IAAI,CAAC,GAAG,aAAa,SAAS,QAAQ,GAAG;AAAA,QACvC,MAAM,IAAI,MACR,oCAAoC,KAAK,UAAU,QAAQ,UAAU,KAAK,UAAU,GAAG,WAAW,GACpG;AAAA,MACF;AAAA;AAAA,IAEF,WAAW,GAAG;AAAA,MACZ,MAAM,KAAK,cAAc,MAAM;AAAA,MAC/B,IAAI,EAAE,cAAc,qBAAqB,CAAC,GAAG,SAAS;AAAA,QACpD,MAAM,IAAI,MAAM,gCAAgC;AAAA,MAClD;AAAA;AAAA,IAEF,YAAY,GAAG;AAAA,MACb,MAAM,KAAK,cAAc,MAAM;AAAA,MAC/B,IAAI,CAAC,GAAG,aAAa,UAAU,KAAK,GAAG,aAAa,eAAe,MAAM,QAAQ;AAAA,QAC/E,MAAM,IAAI,MAAM,iCAAiC;AAAA,MACnD;AAAA;AAAA,IAEF,WAAW,CAAC,UAAkB;AAAA,MAC5B,MAAM,KAAK,cAAc,MAAM;AAAA,MAC/B,MAAM,UACJ,cAAc,oBAAoB,cAAc,sBAAsB,KAAK;AAAA,MAC7E,IAAI,CAAC,WAAW,QAAQ,UAAU,UAAU;AAAA,QAC1C,MAAM,IAAI,MACR,kBAAkB,KAAK,UAAU,QAAQ,UAAU,KAAK,UAAU,SAAS,SAAS,gBAAgB,GACtG;AAAA,MACF;AAAA;AAAA,IAEF,eAAe,CAAC,MAAc,OAAgB;AAAA,MAC5C,MAAM,KAAK,cAAc,MAAM;AAAA,MAC/B,IAAI,CAAC,GAAG,aAAa,IAAI,GAAG;AAAA,QAC1B,MAAM,IAAI,MAAM,uCAAuC,OAAO;AAAA,MAChE;AAAA,MACA,IAAI,UAAU,aAAa,GAAG,aAAa,IAAI,MAAM,OAAO;AAAA,QAC1D,MAAM,IAAI,MACR,uBAAuB,eAAe,KAAK,UAAU,KAAK,UAAU,KAAK,UAAU,GAAG,aAAa,IAAI,CAAC,GAC1G;AAAA,MACF;AAAA;AAAA,IAIF,KAAK;AAAA,MACH,IAAI,CAAC,UAAa;AAAA,QAChB,IAAI,WAAW,UAAU;AAAA,UACvB,MAAM,IAAI,MAAM,4BAA4B,KAAK,UAAU,QAAQ,GAAG;AAAA,QACxE;AAAA;AAAA,MAEF,OAAO,CAAC,UAAa;AAAA,QACnB,IAAI,KAAK,UAAU,MAAM,MAAM,KAAK,UAAU,QAAQ,GAAG;AAAA,UACvD,MAAM,IAAI,MAAM,+BAA+B,KAAK,UAAU,QAAQ,GAAG;AAAA,QAC3E;AAAA;AAAA,MAEF,SAAS,CAAC,KAAa;AAAA,QACrB,IAAI,OAAO,MAAM,EAAE,SAAS,GAAG,GAAG;AAAA,UAChC,MAAM,IAAI,MAAM,aAAa,OAAO,MAAM,sBAAsB,MAAM;AAAA,QACxE;AAAA;AAAA,MAEF,QAAQ,GAAG;AAAA,QACT,IAAI,WAAW;AAAA,UAAM,MAAM,IAAI,MAAM,+BAA+B;AAAA;AAAA,MAEtE,OAAO,GAAG;AAAA,QACR,IAAI,UAAU;AAAA,UAAM,MAAM,IAAI,MAAM,oCAAoC,OAAO,MAAM,GAAG;AAAA;AAAA,MAE1F,WAAW,GAAG;AAAA,QACZ,MAAM,KAAK,cAAc,MAAM;AAAA,QAC/B,IAAI,cAAc,oBAAoB,GAAG,SAAS;AAAA,UAChD,MAAM,IAAI,MAAM,oCAAoC;AAAA,QACtD;AAAA;AAAA,MAEF,YAAY,GAAG;AAAA,QACb,MAAM,KAAK,cAAc,MAAM;AAAA,QAC/B,IAAI,GAAG,aAAa,UAAU,KAAK,GAAG,aAAa,eAAe,MAAM,QAAQ;AAAA,UAC9E,MAAM,IAAI,MAAM,qCAAqC;AAAA,QACvD;AAAA;AAAA,MAEF,eAAe,CAAC,MAAc;AAAA,QAC5B,MAAM,KAAK,cAAc,MAAM;AAAA,QAC/B,IAAI,GAAG,aAAa,IAAI,GAAG;AAAA,UACzB,MAAM,IAAI,MAAM,2CAA2C,OAAO;AAAA,QACpE;AAAA;AAAA,IAEJ;AAAA,EACF;AAAA;AAQK,SAAS,KAAK,CAAC,KAAK,IAAmB;AAAA,EAC5C,OAAO,IAAI,QAAQ,CAAC,MAAM,WAAW,GAAG,EAAE,CAAC;AAAA;AAStC,SAAS,OAAO,CAAC,WAA0B;AAAA,EAChD,UAAU,YAAY;AAAA;AAexB,eAAsB,IAAI,GAAkB;AAAA,EAC1C,WAAW,SAAS,QAAQ;AAAA,IAC1B,WAAW,KAAK,MAAM,OAAO;AAAA,MAC3B,MAAM,WAAW,GAAG,MAAM,UAAU,EAAE;AAAA,MACtC,IAAI;AAAA,QACF,MAAM,EAAE,GAAG;AAAA,QACX,QAAQ,KAAK,EAAE,MAAM,UAAU,QAAQ,KAAK,CAAC;AAAA,QAC7C,OAAO,KAAK;AAAA,QACZ,QAAQ,KAAK;AAAA,UACX,MAAM;AAAA,UACN,QAAQ;AAAA,UACR,OAAO,eAAe,QAAQ,IAAI,UAAU,OAAO,GAAG;AAAA,QACxD,CAAC;AAAA;AAAA,IAEL;AAAA,EACF;AAAA,EAEA,MAAM,SAAU,OAA8C;AAAA,EAC9D,IAAI,
|
|
8
|
-
"debugId": "
|
|
7
|
+
"mappings": "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",
|
|
8
|
+
"debugId": "24287A6E9F4D05B664756E2164756E21",
|
|
9
9
|
"names": []
|
|
10
10
|
}
|
|
@@ -266,11 +266,12 @@ function splitFunctionArgs(inner) {
|
|
|
266
266
|
}
|
|
267
267
|
function parseRgb(inner) {
|
|
268
268
|
const { parts, alpha } = splitFunctionArgs(inner);
|
|
269
|
-
|
|
269
|
+
const [rToken, gToken, bToken] = parts;
|
|
270
|
+
if (rToken === undefined || gToken === undefined || bToken === undefined)
|
|
270
271
|
return null;
|
|
271
|
-
const r = clampChannel(parseComponent(
|
|
272
|
-
const g = clampChannel(parseComponent(
|
|
273
|
-
const b = clampChannel(parseComponent(
|
|
272
|
+
const r = clampChannel(parseComponent(rToken, 255));
|
|
273
|
+
const g = clampChannel(parseComponent(gToken, 255));
|
|
274
|
+
const b = clampChannel(parseComponent(bToken, 255));
|
|
274
275
|
if (![r, g, b].every(Number.isFinite))
|
|
275
276
|
return null;
|
|
276
277
|
return { r, g, b, a: parseAlpha(alpha) };
|
|
@@ -301,11 +302,12 @@ function hslToRgb(h, s, l) {
|
|
|
301
302
|
}
|
|
302
303
|
function parseHsl(inner) {
|
|
303
304
|
const { parts, alpha } = splitFunctionArgs(inner);
|
|
304
|
-
|
|
305
|
+
const [hToken, sToken, lToken] = parts;
|
|
306
|
+
if (hToken === undefined || sToken === undefined || lToken === undefined)
|
|
305
307
|
return null;
|
|
306
|
-
const h = Number.parseFloat(
|
|
307
|
-
const s = clamp(parseComponent(
|
|
308
|
-
const l = clamp(parseComponent(
|
|
308
|
+
const h = Number.parseFloat(hToken.replace(/deg$/, ""));
|
|
309
|
+
const s = clamp(parseComponent(sToken, 100) / 100, 0, 1);
|
|
310
|
+
const l = clamp(parseComponent(lToken, 100) / 100, 0, 1);
|
|
309
311
|
if (![h, s, l].every(Number.isFinite))
|
|
310
312
|
return null;
|
|
311
313
|
return { ...hslToRgb(h, s, l), a: parseAlpha(alpha) };
|
|
@@ -325,13 +327,13 @@ function parseColor(input) {
|
|
|
325
327
|
if (value.startsWith("#"))
|
|
326
328
|
return parseHex(value);
|
|
327
329
|
const fn = value.match(/^([a-z]+)\(([^)]*)\)$/i);
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
const
|
|
330
|
+
const [, fnName, fnInner] = fn ?? [];
|
|
331
|
+
if (fnName !== undefined && fnInner !== undefined) {
|
|
332
|
+
const name = fnName.toLowerCase();
|
|
331
333
|
if (name === "rgb" || name === "rgba")
|
|
332
|
-
return parseRgb(
|
|
334
|
+
return parseRgb(fnInner);
|
|
333
335
|
if (name === "hsl" || name === "hsla")
|
|
334
|
-
return parseHsl(
|
|
336
|
+
return parseHsl(fnInner);
|
|
335
337
|
}
|
|
336
338
|
return null;
|
|
337
339
|
}
|
|
@@ -386,7 +388,10 @@ function effectiveBackground(element) {
|
|
|
386
388
|
}
|
|
387
389
|
let result = { r: 255, g: 255, b: 255, a: 1 };
|
|
388
390
|
for (let i = stack.length - 1;i >= 0; i--) {
|
|
389
|
-
|
|
391
|
+
const layer = stack[i];
|
|
392
|
+
if (layer === undefined)
|
|
393
|
+
continue;
|
|
394
|
+
result = compositeOver(layer, result);
|
|
390
395
|
}
|
|
391
396
|
return result;
|
|
392
397
|
}
|
|
@@ -430,4 +435,4 @@ export {
|
|
|
430
435
|
assertContrast
|
|
431
436
|
};
|
|
432
437
|
|
|
433
|
-
//# debugId=
|
|
438
|
+
//# debugId=6107E968AC1112A564756E2164756E21
|
|
@@ -3,9 +3,9 @@
|
|
|
3
3
|
"sources": ["../tools/test/src/contrast/named-colors.ts", "../tools/test/src/contrast/index.ts"],
|
|
4
4
|
"sourcesContent": [
|
|
5
5
|
"// The complete CSS named-color keyword table (CSS Color Module Level 4),\n// keyed by lowercase name → `#rrggbb`. Parsed by `parseColor` so a consumer can\n// write `assertContrast(el, { against: \"white\" })` or feed a named color\n// directly. `transparent` is handled separately (it carries alpha 0); the rest\n// are fully opaque. `rebeccapurple` is included per CSS Color 4.\n//\n// Kept as data so `parseColor` stays transport-agnostic — no DOM round-trip\n// needed to resolve a name, so it works identically in Node and in a\n// `page.evaluate` bundle.\nexport const CSS_NAMED_COLORS: Readonly<Record<string, string>> = Object.freeze({\n aliceblue: \"#f0f8ff\",\n antiquewhite: \"#faebd7\",\n aqua: \"#00ffff\",\n aquamarine: \"#7fffd4\",\n azure: \"#f0ffff\",\n beige: \"#f5f5dc\",\n bisque: \"#ffe4c4\",\n black: \"#000000\",\n blanchedalmond: \"#ffebcd\",\n blue: \"#0000ff\",\n blueviolet: \"#8a2be2\",\n brown: \"#a52a2a\",\n burlywood: \"#deb887\",\n cadetblue: \"#5f9ea0\",\n chartreuse: \"#7fff00\",\n chocolate: \"#d2691e\",\n coral: \"#ff7f50\",\n cornflowerblue: \"#6495ed\",\n cornsilk: \"#fff8dc\",\n crimson: \"#dc143c\",\n cyan: \"#00ffff\",\n darkblue: \"#00008b\",\n darkcyan: \"#008b8b\",\n darkgoldenrod: \"#b8860b\",\n darkgray: \"#a9a9a9\",\n darkgreen: \"#006400\",\n darkgrey: \"#a9a9a9\",\n darkkhaki: \"#bdb76b\",\n darkmagenta: \"#8b008b\",\n darkolivegreen: \"#556b2f\",\n darkorange: \"#ff8c00\",\n darkorchid: \"#9932cc\",\n darkred: \"#8b0000\",\n darksalmon: \"#e9967a\",\n darkseagreen: \"#8fbc8f\",\n darkslateblue: \"#483d8b\",\n darkslategray: \"#2f4f4f\",\n darkslategrey: \"#2f4f4f\",\n darkturquoise: \"#00ced1\",\n darkviolet: \"#9400d3\",\n deeppink: \"#ff1493\",\n deepskyblue: \"#00bfff\",\n dimgray: \"#696969\",\n dimgrey: \"#696969\",\n dodgerblue: \"#1e90ff\",\n firebrick: \"#b22222\",\n floralwhite: \"#fffaf0\",\n forestgreen: \"#228b22\",\n fuchsia: \"#ff00ff\",\n gainsboro: \"#dcdcdc\",\n ghostwhite: \"#f8f8ff\",\n gold: \"#ffd700\",\n goldenrod: \"#daa520\",\n gray: \"#808080\",\n green: \"#008000\",\n greenyellow: \"#adff2f\",\n grey: \"#808080\",\n honeydew: \"#f0fff0\",\n hotpink: \"#ff69b4\",\n indianred: \"#cd5c5c\",\n indigo: \"#4b0082\",\n ivory: \"#fffff0\",\n khaki: \"#f0e68c\",\n lavender: \"#e6e6fa\",\n lavenderblush: \"#fff0f5\",\n lawngreen: \"#7cfc00\",\n lemonchiffon: \"#fffacd\",\n lightblue: \"#add8e6\",\n lightcoral: \"#f08080\",\n lightcyan: \"#e0ffff\",\n lightgoldenrodyellow: \"#fafad2\",\n lightgray: \"#d3d3d3\",\n lightgreen: \"#90ee90\",\n lightgrey: \"#d3d3d3\",\n lightpink: \"#ffb6c1\",\n lightsalmon: \"#ffa07a\",\n lightseagreen: \"#20b2aa\",\n lightskyblue: \"#87cefa\",\n lightslategray: \"#778899\",\n lightslategrey: \"#778899\",\n lightsteelblue: \"#b0c4de\",\n lightyellow: \"#ffffe0\",\n lime: \"#00ff00\",\n limegreen: \"#32cd32\",\n linen: \"#faf0e6\",\n magenta: \"#ff00ff\",\n maroon: \"#800000\",\n mediumaquamarine: \"#66cdaa\",\n mediumblue: \"#0000cd\",\n mediumorchid: \"#ba55d3\",\n mediumpurple: \"#9370db\",\n mediumseagreen: \"#3cb371\",\n mediumslateblue: \"#7b68ee\",\n mediumspringgreen: \"#00fa9a\",\n mediumturquoise: \"#48d1cc\",\n mediumvioletred: \"#c71585\",\n midnightblue: \"#191970\",\n mintcream: \"#f5fffa\",\n mistyrose: \"#ffe4e1\",\n moccasin: \"#ffe4b5\",\n navajowhite: \"#ffdead\",\n navy: \"#000080\",\n oldlace: \"#fdf5e6\",\n olive: \"#808000\",\n olivedrab: \"#6b8e23\",\n orange: \"#ffa500\",\n orangered: \"#ff4500\",\n orchid: \"#da70d6\",\n palegoldenrod: \"#eee8aa\",\n palegreen: \"#98fb98\",\n paleturquoise: \"#afeeee\",\n palevioletred: \"#db7093\",\n papayawhip: \"#ffefd5\",\n peachpuff: \"#ffdab9\",\n peru: \"#cd853f\",\n pink: \"#ffc0cb\",\n plum: \"#dda0dd\",\n powderblue: \"#b0e0e6\",\n purple: \"#800080\",\n rebeccapurple: \"#663399\",\n red: \"#ff0000\",\n rosybrown: \"#bc8f8f\",\n royalblue: \"#4169e1\",\n saddlebrown: \"#8b4513\",\n salmon: \"#fa8072\",\n sandybrown: \"#f4a460\",\n seagreen: \"#2e8b57\",\n seashell: \"#fff5ee\",\n sienna: \"#a0522d\",\n silver: \"#c0c0c0\",\n skyblue: \"#87ceeb\",\n slateblue: \"#6a5acd\",\n slategray: \"#708090\",\n slategrey: \"#708090\",\n snow: \"#fffafa\",\n springgreen: \"#00ff7f\",\n steelblue: \"#4682b4\",\n tan: \"#d2b48c\",\n teal: \"#008080\",\n thistle: \"#d8bfd8\",\n tomato: \"#ff6347\",\n turquoise: \"#40e0d0\",\n violet: \"#ee82ee\",\n wheat: \"#f5deb3\",\n white: \"#ffffff\",\n whitesmoke: \"#f5f5f5\",\n yellow: \"#ffff00\",\n yellowgreen: \"#9acd32\",\n});\n",
|
|
6
|
-
"/**\n * @fairfox/polly/test/contrast — WCAG contrast helpers for theme/dark-mode\n * tests (polly#141).\n *\n * Theme regressions are easy to ship and hard to catch, and hand-rolled checks\n * keep repeating the same mistakes: averaging RGB channels as a brightness\n * proxy (false passes for equal-luminance hues), reading a single\n * `backgroundColor` and skipping `rgba(0,0,0,0)` (silently dropping every\n * transparent-background element), and picking an arbitrary threshold. WCAG 2.1\n * has a defined answer — relative luminance, 4.5:1 for body text, 3:1 for UI\n * components and large text (SC 1.4.3 / 1.4.11) — and it belongs in one place.\n *\n * The pure functions (`parseColor`, `relativeLuminance`, `contrastRatio`) are\n * transport-agnostic: they work in Node and inside a Playwright/Puppeteer\n * `page.evaluate` bundle alike. `effectiveBackground` and `assertContrast` read\n * the live DOM and are meant for the harness side (or a `page.evaluate` where a\n * real `getComputedStyle` exists).\n */\n\nimport { CSS_NAMED_COLORS } from \"./named-colors\";\n\n/** r, g, b in 0–255; a (alpha) in 0–1. */\nexport interface RGBA {\n r: number;\n g: number;\n b: number;\n a: number;\n}\n\n/** Anything `contrastRatio`/`assertContrast` will accept for a color. */\nexport type ColorInput = string | RGBA | { r: number; g: number; b: number };\n\nconst clamp = (n: number, lo: number, hi: number): number => Math.min(hi, Math.max(lo, n));\nconst clampChannel = (n: number): number => clamp(Math.round(n), 0, 255);\n\n/** Parse one rgb/hsl numeric component that may be a percentage of `scale`. */\nfunction parseComponent(token: string, scale: number): number {\n const t = token.trim();\n if (t.endsWith(\"%\")) {\n return (Number.parseFloat(t) / 100) * scale;\n }\n return Number.parseFloat(t);\n}\n\n/** Parse an alpha token (`0.5` or `50%`) to 0–1. */\nfunction parseAlpha(token: string | undefined): number {\n if (token === undefined) return 1;\n const t = token.trim();\n const value = t.endsWith(\"%\") ? Number.parseFloat(t) / 100 : Number.parseFloat(t);\n return Number.isFinite(value) ? clamp(value, 0, 1) : 1;\n}\n\nfunction parseHex(input: string): RGBA | null {\n const hex = input.slice(1);\n const expand = (h: string): string =>\n h.length === 3 || h.length === 4\n ? h\n .split(\"\")\n .map((c) => c + c)\n .join(\"\")\n : h;\n const full = expand(hex);\n if (full.length !== 6 && full.length !== 8) return null;\n if (!/^[0-9a-fA-F]+$/.test(full)) return null;\n const r = Number.parseInt(full.slice(0, 2), 16);\n const g = Number.parseInt(full.slice(2, 4), 16);\n const b = Number.parseInt(full.slice(4, 6), 16);\n const a = full.length === 8 ? Number.parseInt(full.slice(6, 8), 16) / 255 : 1;\n return { r, g, b, a };\n}\n\n/** Split the inside of `fn(...)` into channel tokens and an optional alpha,\n * accepting both comma syntax (`r, g, b, a`) and modern slash syntax\n * (`r g b / a`). */\nfunction splitFunctionArgs(inner: string): { parts: string[]; alpha: string | undefined } {\n const [head, alphaTail] = inner.split(\"/\");\n const slashAlpha = alphaTail?.trim();\n const parts = (head ?? \"\")\n .trim()\n .split(/[\\s,]+/)\n .filter(Boolean);\n if (slashAlpha !== undefined) {\n return { parts, alpha: slashAlpha };\n }\n // Comma syntax folds alpha into the 4th part.\n if (parts.length === 4) {\n return { parts: parts.slice(0, 3), alpha: parts[3] };\n }\n return { parts, alpha: undefined };\n}\n\nfunction parseRgb(inner: string): RGBA | null {\n const { parts, alpha } = splitFunctionArgs(inner);\n if (parts.length < 3) return null;\n const r = clampChannel(parseComponent(parts[0] as string, 255));\n const g = clampChannel(parseComponent(parts[1] as string, 255));\n const b = clampChannel(parseComponent(parts[2] as string, 255));\n if (![r, g, b].every(Number.isFinite)) return null;\n return { r, g, b, a: parseAlpha(alpha) };\n}\n\n/** HSL→RGB per CSS. h in degrees, s & l in 0–1. */\nfunction hslToRgb(h: number, s: number, l: number): { r: number; g: number; b: number } {\n const hue = ((h % 360) + 360) % 360;\n const c = (1 - Math.abs(2 * l - 1)) * s;\n const x = c * (1 - Math.abs(((hue / 60) % 2) - 1));\n const m = l - c / 2;\n const [r1, g1, b1] = (() => {\n if (hue < 60) return [c, x, 0];\n if (hue < 120) return [x, c, 0];\n if (hue < 180) return [0, c, x];\n if (hue < 240) return [0, x, c];\n if (hue < 300) return [x, 0, c];\n return [c, 0, x];\n })();\n return {\n r: clampChannel((r1 + m) * 255),\n g: clampChannel((g1 + m) * 255),\n b: clampChannel((b1 + m) * 255),\n };\n}\n\nfunction parseHsl(inner: string): RGBA | null {\n const { parts, alpha } = splitFunctionArgs(inner);\n if (parts.length < 3) return null;\n const h = Number.parseFloat((parts[0] as string).replace(/deg$/, \"\"));\n const s = clamp(parseComponent(parts[1] as string, 100) / 100, 0, 1);\n const l = clamp(parseComponent(parts[2] as string, 100) / 100, 0, 1);\n if (![h, s, l].every(Number.isFinite)) return null;\n return { ...hslToRgb(h, s, l), a: parseAlpha(alpha) };\n}\n\n/**\n * Parse a CSS color string to `{ r, g, b, a }`, or `null` if unrecognised.\n * Handles `#rgb`, `#rgba`, `#rrggbb`, `#rrggbbaa`, `rgb()/rgba()` (comma or\n * space syntax, with optional `/ alpha`), `hsl()/hsla()`, `transparent`, and\n * the full set of CSS named colors. This is the parse `getComputedStyle`\n * output needs (always `rgb()`/`rgba()`) plus the literal forms a test author\n * is likely to pass to `against`.\n */\nexport function parseColor(input: string): RGBA | null {\n if (typeof input !== \"string\") return null;\n const value = input.trim();\n if (value === \"\") return null;\n const lower = value.toLowerCase();\n\n if (lower === \"transparent\") return { r: 0, g: 0, b: 0, a: 0 };\n\n const named = CSS_NAMED_COLORS[lower];\n if (named) return parseHex(named);\n\n if (value.startsWith(\"#\")) return parseHex(value);\n\n const fn = value.match(/^([a-z]+)\\(([^)]*)\\)$/i);\n if (fn) {\n const name = (fn[1] as string).toLowerCase();\n const inner = fn[2] as string;\n if (name === \"rgb\" || name === \"rgba\") return parseRgb(inner);\n if (name === \"hsl\" || name === \"hsla\") return parseHsl(inner);\n }\n return null;\n}\n\n/** Coerce a ColorInput to RGBA (assuming alpha 1 when absent), or null. */\nfunction toRGBA(input: ColorInput): RGBA | null {\n if (typeof input === \"string\") return parseColor(input);\n if (input && typeof input === \"object\" && \"r\" in input) {\n return { r: input.r, g: input.g, b: input.b, a: \"a\" in input ? (input as RGBA).a : 1 };\n }\n return null;\n}\n\n/**\n * WCAG relative luminance (0–1) of an opaque color. Alpha is ignored — composite\n * first (see `compositeOver`) if the color is translucent.\n */\nexport function relativeLuminance(color: { r: number; g: number; b: number }): number {\n const channel = (c: number): number => {\n const cs = c / 255;\n return cs <= 0.03928 ? cs / 12.92 : ((cs + 0.055) / 1.055) ** 2.4;\n };\n return 0.2126 * channel(color.r) + 0.7152 * channel(color.g) + 0.0722 * channel(color.b);\n}\n\n/**\n * WCAG contrast ratio (1–21) between two colors. Accepts CSS strings or RGB(A)\n * objects. Alpha is ignored; composite translucent colors over their background\n * first. Returns `NaN` if either input cannot be parsed.\n */\nexport function contrastRatio(a: ColorInput, b: ColorInput): number {\n const ca = toRGBA(a);\n const cb = toRGBA(b);\n if (!ca || !cb) return Number.NaN;\n const la = relativeLuminance(ca);\n const lb = relativeLuminance(cb);\n const lighter = Math.max(la, lb);\n const darker = Math.min(la, lb);\n return (lighter + 0.05) / (darker + 0.05);\n}\n\n/**\n * Alpha-composite `fg` over `bg` (the \"over\" operator), returning an opaque\n * color. `bg` is assumed opaque (its alpha is treated as 1).\n */\nexport function compositeOver(fg: RGBA, bg: RGBA): RGBA {\n const a = clamp(fg.a, 0, 1);\n return {\n r: clampChannel(fg.r * a + bg.r * (1 - a)),\n g: clampChannel(fg.g * a + bg.g * (1 - a)),\n b: clampChannel(fg.b * a + bg.b * (1 - a)),\n a: 1,\n };\n}\n\n/**\n * Resolve the effective (opaque) background an element renders against by\n * walking up the ancestor chain, compositing each translucent background over\n * the next, until an opaque one is reached. Elements with a fully transparent\n * background — most of them — are no longer silently skipped: they contribute\n * their (zero) alpha and the walk continues to the parent. Falls back to white\n * when the chain reaches the root without an opaque background.\n */\nexport function effectiveBackground(element: Element): RGBA {\n const view = element.ownerDocument?.defaultView;\n const getStyle = view?.getComputedStyle?.bind(view);\n const stack: RGBA[] = [];\n\n let node: Element | null = element;\n while (node && getStyle) {\n const parsed = parseColor(getStyle(node).backgroundColor || \"\");\n if (parsed && parsed.a > 0) {\n stack.push(parsed);\n if (parsed.a >= 1) break;\n }\n node = node.parentElement;\n }\n\n // Composite from the deepest opaque layer outward toward the element.\n let result: RGBA = { r: 255, g: 255, b: 255, a: 1 };\n for (let i = stack.length - 1; i >= 0; i--) {\n result = compositeOver(stack[i] as RGBA, result);\n }\n return result;\n}\n\nexport interface AssertContrastOptions {\n /** Minimum acceptable ratio. WCAG: 3 (UI/large text), 4.5 (body), 7 (AAA). */\n min: 3 | 4.5 | 7 | number;\n /**\n * Background to measure against. `\"effective\"` (default) walks the ancestor\n * chain via `effectiveBackground`; an explicit color (string or RGB) skips\n * the walk.\n */\n against?: \"effective\" | ColorInput;\n}\n\nexport interface ContrastResult {\n ratio: number;\n foreground: RGBA;\n background: RGBA;\n min: number;\n passes: boolean;\n}\n\nfunction describeElement(element: Element): string {\n const tag = element.tagName.toLowerCase();\n const id = element.id ? `#${element.id}` : \"\";\n const cls = element.getAttribute(\"class\");\n const classes = cls ? `.${cls.trim().split(/\\s+/).join(\".\")}` : \"\";\n return `${tag}${id}${classes}`;\n}\n\nconst fmt = (c: RGBA): string =>\n c.a >= 1 ? `rgb(${c.r}, ${c.g}, ${c.b})` : `rgba(${c.r}, ${c.g}, ${c.b}, ${c.a})`;\n\n/**\n * Assert an element's text meets a WCAG contrast minimum against its\n * background, throwing a message that names the element, both colors, and the\n * actual ratio. The element's own (possibly translucent) text color is\n * composited over the resolved background before measuring. Returns the\n * computed `ContrastResult` on success so callers can log or assert further.\n */\nexport function assertContrast(element: Element, options: AssertContrastOptions): ContrastResult {\n const view = element.ownerDocument?.defaultView;\n const getStyle = view?.getComputedStyle?.bind(view);\n if (!getStyle) {\n throw new Error(\"assertContrast: no getComputedStyle available on the element's document\");\n }\n\n const fgRaw = parseColor(getStyle(element).color || \"\");\n if (!fgRaw) {\n throw new Error(`assertContrast: could not parse the color of ${describeElement(element)}`);\n }\n\n const against = options.against ?? \"effective\";\n const background =\n against === \"effective\" ? effectiveBackground(element) : toRGBA(against as ColorInput);\n if (!background) {\n throw new Error(\n `assertContrast: could not parse the 'against' color for ${describeElement(element)}`\n );\n }\n\n const foreground = fgRaw.a < 1 ? compositeOver(fgRaw, background) : fgRaw;\n const ratio = contrastRatio(foreground, background);\n const passes = ratio >= options.min;\n\n if (!passes) {\n throw new Error(\n `Contrast ${ratio.toFixed(2)}:1 is below the required ${options.min}:1 for ` +\n `${describeElement(element)} — foreground ${fmt(foreground)} on background ${fmt(background)}.`\n );\n }\n\n return { ratio, foreground, background, min: options.min, passes };\n}\n"
|
|
6
|
+
"/**\n * @fairfox/polly/test/contrast — WCAG contrast helpers for theme/dark-mode\n * tests (polly#141).\n *\n * Theme regressions are easy to ship and hard to catch, and hand-rolled checks\n * keep repeating the same mistakes: averaging RGB channels as a brightness\n * proxy (false passes for equal-luminance hues), reading a single\n * `backgroundColor` and skipping `rgba(0,0,0,0)` (silently dropping every\n * transparent-background element), and picking an arbitrary threshold. WCAG 2.1\n * has a defined answer — relative luminance, 4.5:1 for body text, 3:1 for UI\n * components and large text (SC 1.4.3 / 1.4.11) — and it belongs in one place.\n *\n * The pure functions (`parseColor`, `relativeLuminance`, `contrastRatio`) are\n * transport-agnostic: they work in Node and inside a Playwright/Puppeteer\n * `page.evaluate` bundle alike. `effectiveBackground` and `assertContrast` read\n * the live DOM and are meant for the harness side (or a `page.evaluate` where a\n * real `getComputedStyle` exists).\n */\n\nimport { CSS_NAMED_COLORS } from \"./named-colors\";\n\n/** r, g, b in 0–255; a (alpha) in 0–1. */\nexport interface RGBA {\n r: number;\n g: number;\n b: number;\n a: number;\n}\n\n/** Anything `contrastRatio`/`assertContrast` will accept for a color. */\nexport type ColorInput = string | RGBA | { r: number; g: number; b: number };\n\nconst clamp = (n: number, lo: number, hi: number): number => Math.min(hi, Math.max(lo, n));\nconst clampChannel = (n: number): number => clamp(Math.round(n), 0, 255);\n\n/** Parse one rgb/hsl numeric component that may be a percentage of `scale`. */\nfunction parseComponent(token: string, scale: number): number {\n const t = token.trim();\n if (t.endsWith(\"%\")) {\n return (Number.parseFloat(t) / 100) * scale;\n }\n return Number.parseFloat(t);\n}\n\n/** Parse an alpha token (`0.5` or `50%`) to 0–1. */\nfunction parseAlpha(token: string | undefined): number {\n if (token === undefined) return 1;\n const t = token.trim();\n const value = t.endsWith(\"%\") ? Number.parseFloat(t) / 100 : Number.parseFloat(t);\n return Number.isFinite(value) ? clamp(value, 0, 1) : 1;\n}\n\nfunction parseHex(input: string): RGBA | null {\n const hex = input.slice(1);\n const expand = (h: string): string =>\n h.length === 3 || h.length === 4\n ? h\n .split(\"\")\n .map((c) => c + c)\n .join(\"\")\n : h;\n const full = expand(hex);\n if (full.length !== 6 && full.length !== 8) return null;\n if (!/^[0-9a-fA-F]+$/.test(full)) return null;\n const r = Number.parseInt(full.slice(0, 2), 16);\n const g = Number.parseInt(full.slice(2, 4), 16);\n const b = Number.parseInt(full.slice(4, 6), 16);\n const a = full.length === 8 ? Number.parseInt(full.slice(6, 8), 16) / 255 : 1;\n return { r, g, b, a };\n}\n\n/** Split the inside of `fn(...)` into channel tokens and an optional alpha,\n * accepting both comma syntax (`r, g, b, a`) and modern slash syntax\n * (`r g b / a`). */\nfunction splitFunctionArgs(inner: string): { parts: string[]; alpha: string | undefined } {\n const [head, alphaTail] = inner.split(\"/\");\n const slashAlpha = alphaTail?.trim();\n const parts = (head ?? \"\")\n .trim()\n .split(/[\\s,]+/)\n .filter(Boolean);\n if (slashAlpha !== undefined) {\n return { parts, alpha: slashAlpha };\n }\n // Comma syntax folds alpha into the 4th part.\n if (parts.length === 4) {\n return { parts: parts.slice(0, 3), alpha: parts[3] };\n }\n return { parts, alpha: undefined };\n}\n\nfunction parseRgb(inner: string): RGBA | null {\n const { parts, alpha } = splitFunctionArgs(inner);\n const [rToken, gToken, bToken] = parts;\n if (rToken === undefined || gToken === undefined || bToken === undefined) return null;\n const r = clampChannel(parseComponent(rToken, 255));\n const g = clampChannel(parseComponent(gToken, 255));\n const b = clampChannel(parseComponent(bToken, 255));\n if (![r, g, b].every(Number.isFinite)) return null;\n return { r, g, b, a: parseAlpha(alpha) };\n}\n\n/** HSL→RGB per CSS. h in degrees, s & l in 0–1. */\nfunction hslToRgb(h: number, s: number, l: number): { r: number; g: number; b: number } {\n const hue = ((h % 360) + 360) % 360;\n const c = (1 - Math.abs(2 * l - 1)) * s;\n const x = c * (1 - Math.abs(((hue / 60) % 2) - 1));\n const m = l - c / 2;\n const [r1, g1, b1] = (() => {\n if (hue < 60) return [c, x, 0];\n if (hue < 120) return [x, c, 0];\n if (hue < 180) return [0, c, x];\n if (hue < 240) return [0, x, c];\n if (hue < 300) return [x, 0, c];\n return [c, 0, x];\n })();\n return {\n r: clampChannel((r1 + m) * 255),\n g: clampChannel((g1 + m) * 255),\n b: clampChannel((b1 + m) * 255),\n };\n}\n\nfunction parseHsl(inner: string): RGBA | null {\n const { parts, alpha } = splitFunctionArgs(inner);\n const [hToken, sToken, lToken] = parts;\n if (hToken === undefined || sToken === undefined || lToken === undefined) return null;\n const h = Number.parseFloat(hToken.replace(/deg$/, \"\"));\n const s = clamp(parseComponent(sToken, 100) / 100, 0, 1);\n const l = clamp(parseComponent(lToken, 100) / 100, 0, 1);\n if (![h, s, l].every(Number.isFinite)) return null;\n return { ...hslToRgb(h, s, l), a: parseAlpha(alpha) };\n}\n\n/**\n * Parse a CSS color string to `{ r, g, b, a }`, or `null` if unrecognised.\n * Handles `#rgb`, `#rgba`, `#rrggbb`, `#rrggbbaa`, `rgb()/rgba()` (comma or\n * space syntax, with optional `/ alpha`), `hsl()/hsla()`, `transparent`, and\n * the full set of CSS named colors. This is the parse `getComputedStyle`\n * output needs (always `rgb()`/`rgba()`) plus the literal forms a test author\n * is likely to pass to `against`.\n */\nexport function parseColor(input: string): RGBA | null {\n if (typeof input !== \"string\") return null;\n const value = input.trim();\n if (value === \"\") return null;\n const lower = value.toLowerCase();\n\n if (lower === \"transparent\") return { r: 0, g: 0, b: 0, a: 0 };\n\n const named = CSS_NAMED_COLORS[lower];\n if (named) return parseHex(named);\n\n if (value.startsWith(\"#\")) return parseHex(value);\n\n const fn = value.match(/^([a-z]+)\\(([^)]*)\\)$/i);\n const [, fnName, fnInner] = fn ?? [];\n if (fnName !== undefined && fnInner !== undefined) {\n const name = fnName.toLowerCase();\n if (name === \"rgb\" || name === \"rgba\") return parseRgb(fnInner);\n if (name === \"hsl\" || name === \"hsla\") return parseHsl(fnInner);\n }\n return null;\n}\n\n/** Coerce a ColorInput to RGBA (assuming alpha 1 when absent), or null. */\nfunction toRGBA(input: ColorInput): RGBA | null {\n if (typeof input === \"string\") return parseColor(input);\n if (input && typeof input === \"object\" && \"r\" in input) {\n return { r: input.r, g: input.g, b: input.b, a: \"a\" in input ? input.a : 1 };\n }\n return null;\n}\n\n/**\n * WCAG relative luminance (0–1) of an opaque color. Alpha is ignored — composite\n * first (see `compositeOver`) if the color is translucent.\n */\nexport function relativeLuminance(color: { r: number; g: number; b: number }): number {\n const channel = (c: number): number => {\n const cs = c / 255;\n return cs <= 0.03928 ? cs / 12.92 : ((cs + 0.055) / 1.055) ** 2.4;\n };\n return 0.2126 * channel(color.r) + 0.7152 * channel(color.g) + 0.0722 * channel(color.b);\n}\n\n/**\n * WCAG contrast ratio (1–21) between two colors. Accepts CSS strings or RGB(A)\n * objects. Alpha is ignored; composite translucent colors over their background\n * first. Returns `NaN` if either input cannot be parsed.\n */\nexport function contrastRatio(a: ColorInput, b: ColorInput): number {\n const ca = toRGBA(a);\n const cb = toRGBA(b);\n if (!ca || !cb) return Number.NaN;\n const la = relativeLuminance(ca);\n const lb = relativeLuminance(cb);\n const lighter = Math.max(la, lb);\n const darker = Math.min(la, lb);\n return (lighter + 0.05) / (darker + 0.05);\n}\n\n/**\n * Alpha-composite `fg` over `bg` (the \"over\" operator), returning an opaque\n * color. `bg` is assumed opaque (its alpha is treated as 1).\n */\nexport function compositeOver(fg: RGBA, bg: RGBA): RGBA {\n const a = clamp(fg.a, 0, 1);\n return {\n r: clampChannel(fg.r * a + bg.r * (1 - a)),\n g: clampChannel(fg.g * a + bg.g * (1 - a)),\n b: clampChannel(fg.b * a + bg.b * (1 - a)),\n a: 1,\n };\n}\n\n/**\n * Resolve the effective (opaque) background an element renders against by\n * walking up the ancestor chain, compositing each translucent background over\n * the next, until an opaque one is reached. Elements with a fully transparent\n * background — most of them — are no longer silently skipped: they contribute\n * their (zero) alpha and the walk continues to the parent. Falls back to white\n * when the chain reaches the root without an opaque background.\n */\nexport function effectiveBackground(element: Element): RGBA {\n const view = element.ownerDocument?.defaultView;\n const getStyle = view?.getComputedStyle?.bind(view);\n const stack: RGBA[] = [];\n\n let node: Element | null = element;\n while (node && getStyle) {\n const parsed = parseColor(getStyle(node).backgroundColor || \"\");\n if (parsed && parsed.a > 0) {\n stack.push(parsed);\n if (parsed.a >= 1) break;\n }\n node = node.parentElement;\n }\n\n // Composite from the deepest opaque layer outward toward the element.\n let result: RGBA = { r: 255, g: 255, b: 255, a: 1 };\n for (let i = stack.length - 1; i >= 0; i--) {\n const layer = stack[i];\n if (layer === undefined) continue;\n result = compositeOver(layer, result);\n }\n return result;\n}\n\nexport interface AssertContrastOptions {\n /** Minimum acceptable ratio. WCAG: 3 (UI/large text), 4.5 (body), 7 (AAA). */\n min: 3 | 4.5 | 7 | number;\n /**\n * Background to measure against. `\"effective\"` (default) walks the ancestor\n * chain via `effectiveBackground`; an explicit color (string or RGB) skips\n * the walk.\n */\n against?: \"effective\" | ColorInput;\n}\n\nexport interface ContrastResult {\n ratio: number;\n foreground: RGBA;\n background: RGBA;\n min: number;\n passes: boolean;\n}\n\nfunction describeElement(element: Element): string {\n const tag = element.tagName.toLowerCase();\n const id = element.id ? `#${element.id}` : \"\";\n const cls = element.getAttribute(\"class\");\n const classes = cls ? `.${cls.trim().split(/\\s+/).join(\".\")}` : \"\";\n return `${tag}${id}${classes}`;\n}\n\nconst fmt = (c: RGBA): string =>\n c.a >= 1 ? `rgb(${c.r}, ${c.g}, ${c.b})` : `rgba(${c.r}, ${c.g}, ${c.b}, ${c.a})`;\n\n/**\n * Assert an element's text meets a WCAG contrast minimum against its\n * background, throwing a message that names the element, both colors, and the\n * actual ratio. The element's own (possibly translucent) text color is\n * composited over the resolved background before measuring. Returns the\n * computed `ContrastResult` on success so callers can log or assert further.\n */\nexport function assertContrast(element: Element, options: AssertContrastOptions): ContrastResult {\n const view = element.ownerDocument?.defaultView;\n const getStyle = view?.getComputedStyle?.bind(view);\n if (!getStyle) {\n throw new Error(\"assertContrast: no getComputedStyle available on the element's document\");\n }\n\n const fgRaw = parseColor(getStyle(element).color || \"\");\n if (!fgRaw) {\n throw new Error(`assertContrast: could not parse the color of ${describeElement(element)}`);\n }\n\n const against = options.against ?? \"effective\";\n const background = against === \"effective\" ? effectiveBackground(element) : toRGBA(against);\n if (!background) {\n throw new Error(\n `assertContrast: could not parse the 'against' color for ${describeElement(element)}`\n );\n }\n\n const foreground = fgRaw.a < 1 ? compositeOver(fgRaw, background) : fgRaw;\n const ratio = contrastRatio(foreground, background);\n const passes = ratio >= options.min;\n\n if (!passes) {\n throw new Error(\n `Contrast ${ratio.toFixed(2)}:1 is below the required ${options.min}:1 for ` +\n `${describeElement(element)} — foreground ${fmt(foreground)} on background ${fmt(background)}.`\n );\n }\n\n return { ratio, foreground, background, min: options.min, passes };\n}\n"
|
|
7
7
|
],
|
|
8
|
-
"mappings": "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
|
|
9
|
-
"debugId": "
|
|
8
|
+
"mappings": "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",
|
|
9
|
+
"debugId": "6107E968AC1112A564756E2164756E21",
|
|
10
10
|
"names": []
|
|
11
11
|
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fairfox/polly/test/e2e-cli — end-to-end test kit for the polly CLI.
|
|
3
|
+
*
|
|
4
|
+
* Drives `cli/polly.ts` from this checkout against throwaway temp projects:
|
|
5
|
+
* scaffold with `polly init`, wire the scaffold's `@fairfox/polly`
|
|
6
|
+
* dependency to the working tree, and build — proving the first thing a new
|
|
7
|
+
* user does works cold, without a published release on the path.
|
|
8
|
+
*/
|
|
9
|
+
export { POLLY_PKG_DIR, type RunOptions, type RunResult, runBun, runCli } from "./run-cli";
|
|
10
|
+
export { type TempDir, withTempDir } from "./with-temp-dir";
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fairfox/polly/test/e2e-cli — spawn the polly CLI from the working tree.
|
|
3
|
+
*
|
|
4
|
+
* The scripts invoke `cli/polly.ts` from this checkout, NOT a globally
|
|
5
|
+
* installed `polly`, so an init/build/quality e2e exercises the code under
|
|
6
|
+
* test rather than whatever happens to be on PATH. Output is captured (not
|
|
7
|
+
* inherited) so a script can assert on stdout/stderr and exit code.
|
|
8
|
+
*/
|
|
9
|
+
/** Absolute path to the polly package root (packages/polly). Scripts use
|
|
10
|
+
* this to override a scaffold's `@fairfox/polly` dependency at `file:` so
|
|
11
|
+
* the build resolves the working tree offline, not a published version. */
|
|
12
|
+
export declare const POLLY_PKG_DIR: string;
|
|
13
|
+
export interface RunResult {
|
|
14
|
+
exitCode: number;
|
|
15
|
+
stdout: string;
|
|
16
|
+
stderr: string;
|
|
17
|
+
}
|
|
18
|
+
export interface RunOptions {
|
|
19
|
+
cwd: string;
|
|
20
|
+
env?: Record<string, string>;
|
|
21
|
+
}
|
|
22
|
+
/** Spawn `bun <args>` in `cwd`, capturing stdout/stderr and the exit code. */
|
|
23
|
+
export declare function runBun(args: string[], options: RunOptions): Promise<RunResult>;
|
|
24
|
+
/** Spawn the working-tree polly CLI: `bun cli/polly.ts <args>`. */
|
|
25
|
+
export declare function runCli(args: string[], options: RunOptions): Promise<RunResult>;
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fairfox/polly/test/e2e-cli — a throwaway working directory.
|
|
3
|
+
*
|
|
4
|
+
* CLI scripts scaffold and build inside a fresh temp dir so each run starts
|
|
5
|
+
* from cold disk — the filesystem analogue of the mesh kit's fresh-profile
|
|
6
|
+
* guarantee — and leaves nothing behind.
|
|
7
|
+
*/
|
|
8
|
+
export interface TempDir {
|
|
9
|
+
/** Absolute path to the fresh directory. */
|
|
10
|
+
dir: string;
|
|
11
|
+
/** Remove the directory and everything under it. Idempotent. */
|
|
12
|
+
cleanup: () => void;
|
|
13
|
+
}
|
|
14
|
+
/** Create a fresh empty temp directory. */
|
|
15
|
+
export declare function withTempDir(prefix?: string): TempDir;
|
|
@@ -625,15 +625,19 @@ function decodeSignedEnvelope(bytes) {
|
|
|
625
625
|
// tools/test/src/e2e-mesh/keys.ts
|
|
626
626
|
function toBase64(bytes) {
|
|
627
627
|
let binary = "";
|
|
628
|
-
for (
|
|
629
|
-
binary += String.fromCharCode(
|
|
628
|
+
for (const byte of bytes) {
|
|
629
|
+
binary += String.fromCharCode(byte);
|
|
630
630
|
}
|
|
631
631
|
return btoa(binary);
|
|
632
632
|
}
|
|
633
633
|
function prebakeKeyringPair(peerIdA = "peer-a", peerIdB = "peer-b") {
|
|
634
634
|
const set = prebakeKeyringSet([peerIdA, peerIdB]);
|
|
635
|
+
const [peerA, peerB] = set.peers;
|
|
636
|
+
if (peerA === undefined || peerB === undefined) {
|
|
637
|
+
throw new Error("prebakeKeyringPair: expected exactly two peers");
|
|
638
|
+
}
|
|
635
639
|
return {
|
|
636
|
-
peers: [
|
|
640
|
+
peers: [peerA, peerB],
|
|
637
641
|
docKeyB64: set.docKeyB64
|
|
638
642
|
};
|
|
639
643
|
}
|
|
@@ -740,7 +744,8 @@ async function launchPeer(options) {
|
|
|
740
744
|
headless = process.env["HEADLESS"] !== "false",
|
|
741
745
|
consoleAllowlist = MESH_CONSOLE_ALLOWLIST,
|
|
742
746
|
readyTimeoutMs = 15000,
|
|
743
|
-
profileParent = resolve(tmpdir(), "polly-e2e")
|
|
747
|
+
profileParent = resolve(tmpdir(), "polly-e2e"),
|
|
748
|
+
readyStatus = "ready"
|
|
744
749
|
} = options;
|
|
745
750
|
if (!existsSync(profileParent))
|
|
746
751
|
mkdirSync(profileParent, { recursive: true });
|
|
@@ -771,7 +776,7 @@ async function launchPeer(options) {
|
|
|
771
776
|
let lastStatus = "";
|
|
772
777
|
while (Date.now() < deadline) {
|
|
773
778
|
lastStatus = await page.evaluate(() => document.querySelector("[data-e2e='status']")?.textContent ?? "");
|
|
774
|
-
if (lastStatus ===
|
|
779
|
+
if (lastStatus === readyStatus) {
|
|
775
780
|
ready = true;
|
|
776
781
|
break;
|
|
777
782
|
}
|
|
@@ -785,7 +790,7 @@ async function launchPeer(options) {
|
|
|
785
790
|
if (!ready) {
|
|
786
791
|
await browser.close();
|
|
787
792
|
rmSync(userDataDir, { recursive: true, force: true });
|
|
788
|
-
throw new Error(`launchPeer(${peerId}): consumer did not reach "
|
|
793
|
+
throw new Error(`launchPeer(${peerId}): consumer did not reach "${readyStatus}" within ${readyTimeoutMs}ms (last status: "${lastStatus}")`);
|
|
789
794
|
}
|
|
790
795
|
function assertNoUnexpectedConsole() {
|
|
791
796
|
const bad = consoleLines.filter((line) => !line.allowed && (line.level === "error" || line.level === "warn" || line.level === "warning"));
|
|
@@ -1204,4 +1209,4 @@ export {
|
|
|
1204
1209
|
MESH_CONSOLE_ALLOWLIST
|
|
1205
1210
|
};
|
|
1206
1211
|
|
|
1207
|
-
//# debugId=
|
|
1212
|
+
//# debugId=DBF39E19E602FCC464756E2164756E21
|