@motion-proto/live-tokens 0.74.0 → 0.76.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/.claude/skills/live-tokens-check-compliance/SKILL.md +33 -38
- package/.claude/skills/live-tokens-create-component/SKILL copy.md +196 -0
- package/.claude/skills/live-tokens-create-component/SKILL.md +198 -146
- package/.claude/skills/live-tokens-create-component/references/contract-tests.md +95 -52
- package/.claude/skills/live-tokens-create-component/references/intrinsics.md +2 -2
- package/.claude/skills/live-tokens-create-component/references/linked-siblings.md +2 -2
- package/.claude/skills/live-tokens-create-component/references/sketch-mode.md +12 -12
- package/.claude/skills/live-tokens-create-component/references/token-naming.md +2 -1
- package/.claude/skills/live-tokens-create-page/SKILL.md +187 -0
- package/.claude/skills/live-tokens-create-page/references/interaction-sources.md +66 -0
- package/.claude/skills/live-tokens-create-page/references/layout-sources.md +87 -0
- package/.claude/skills/live-tokens-create-theme/SKILL.md +52 -48
- package/.claude/skills/live-tokens-create-theme/references/design-directions.md +1 -1
- package/.claude/skills/live-tokens-fix-findings/SKILL.md +69 -60
- package/.claude/skills/live-tokens-pick-component/SKILL.md +64 -79
- package/.claude/skills/live-tokens-set-colors/SKILL.md +47 -38
- package/.claude/skills/live-tokens-set-geometry/SKILL.md +58 -35
- package/.claude/skills/live-tokens-set-geometry/references/geometry-anchors.md +5 -3
- package/.claude/skills/live-tokens-set-type/SKILL.md +30 -30
- package/CHANGELOG.md +138 -0
- package/README.md +17 -7
- package/bin/check-component.mjs +91 -19
- package/bin/check-page.mjs +102 -21
- package/bin/cli.mjs +87 -113
- package/bin/contractRunner.mjs +945 -0
- package/bin/create.mjs +1 -1
- package/bin/lib/catalogue.mjs +37 -30
- package/bin/lib/findings.mjs +46 -15
- package/bin/lib/report.mjs +3 -3
- package/bin/lib/tokenVocabulary.mjs +4 -4
- package/bin/migrate-routes.mjs +5 -5
- package/bin/migrate.mjs +4 -4
- package/bin/save-theme.mjs +8 -9
- package/bin/set-colors.mjs +9 -11
- package/bin/set-geometry.mjs +7 -7
- package/bin/set-type.mjs +5 -7
- package/bin/setup-claude.mjs +110 -0
- package/dist-plugin/{chunk-W6Y4BWFB.js → chunk-6WGFOJXO.js} +22 -0
- package/dist-plugin/{chunk-7VRTBGJT.js → chunk-PDNL4NC5.js} +9 -2
- package/dist-plugin/{chunk-V3YF6CGT.js → chunk-SWXRVZKT.js} +43 -1
- package/dist-plugin/{dataPaths-BhWzd5cL.d.cts → dataPaths-BpIK_Obx.d.cts} +1 -0
- package/dist-plugin/{dataPaths-BhWzd5cL.d.ts → dataPaths-BpIK_Obx.d.ts} +1 -0
- package/dist-plugin/index.cjs +310 -147
- package/dist-plugin/index.d.cts +1 -1
- package/dist-plugin/index.d.ts +1 -1
- package/dist-plugin/index.js +218 -122
- package/dist-plugin/migrateData/index.cjs +66 -2
- package/dist-plugin/migrateData/index.d.cts +1 -1
- package/dist-plugin/migrateData/index.d.ts +1 -1
- package/dist-plugin/migrateData/index.js +3 -3
- package/dist-plugin/setColors/index.cjs +61 -4
- package/dist-plugin/setColors/index.d.cts +1 -1
- package/dist-plugin/setColors/index.d.ts +1 -1
- package/dist-plugin/setColors/index.js +5 -5
- package/dist-plugin/setGeometry/index.cjs +107 -44
- package/dist-plugin/setGeometry/index.d.cts +5 -5
- package/dist-plugin/setGeometry/index.d.ts +5 -5
- package/dist-plugin/setGeometry/index.js +51 -45
- package/dist-plugin/setType/index.cjs +15 -0
- package/dist-plugin/setType/index.d.cts +1 -1
- package/dist-plugin/setType/index.d.ts +1 -1
- package/dist-plugin/setType/index.js +1 -1
- package/dist-plugin/tokensCssMigrations/index.d.cts +1 -1
- package/dist-plugin/tokensCssMigrations/index.d.ts +1 -1
- package/dist-plugin/tokensCssMigrations/index.js +1 -1
- package/package.json +35 -8
- package/src/app/site.css +19 -9
- package/src/editor/bootstrap.ts +2 -0
- package/src/editor/component-editor/CollapsibleSectionEditor.svelte +4 -4
- package/src/editor/component-editor/DialogEditor.svelte +4 -4
- package/src/editor/component-editor/MenuSelectEditor.svelte +4 -1
- package/src/editor/component-editor/SegmentedControlEditor.svelte +6 -1
- package/src/editor/component-editor/TabBarEditor.svelte +1 -1
- package/src/editor/component-editor/TableEditor.svelte +2 -2
- package/src/editor/component-editor/scaffolding/TokenLayout.svelte +6 -3
- package/src/editor/component-editor/scaffolding/VariantGroup.svelte +41 -20
- package/src/editor/core/components/adjustAliases.ts +59 -45
- package/src/editor/core/components/aliasKinds.ts +9 -5
- package/src/editor/core/preview/themePreview.ts +9 -2
- package/src/editor/core/sketch/sketchLayer.ts +22 -0
- package/src/editor/core/store/editorStore.ts +10 -1
- package/src/editor/core/themes/buildColors.ts +3 -3
- package/src/editor/core/themes/liveStateStream.ts +26 -0
- package/src/editor/core/themes/migrations/2026-09-07-stroke-role-renames.ts +52 -0
- package/src/editor/core/themes/migrations/index.ts +2 -0
- package/src/editor/core/themes/themeDocumentSync.ts +22 -11
- package/src/editor/core/themes/themeService.ts +9 -2
- package/src/editor/pages/ComponentEditorPage.svelte +17 -1
- package/src/editor/pages/liveTokensEditorHandle.ts +23 -0
- package/src/editor/skill-atlas/SkillAtlas.svelte +75 -573
- package/src/editor/skill-atlas/TreeCanvas.svelte +263 -0
- package/src/editor/skill-atlas/TreeNodeCard.svelte +136 -53
- package/src/editor/skill-atlas/edges.ts +31 -0
- package/src/editor/skill-atlas/skillSources.generated.ts +20 -19
- package/src/editor/skill-atlas/skillTrees.ts +19 -3840
- package/src/editor/skill-atlas/trees/check-compliance.ts +183 -0
- package/src/editor/skill-atlas/trees/create-component.ts +275 -0
- package/src/editor/skill-atlas/trees/create-page.ts +320 -0
- package/src/editor/skill-atlas/trees/create-theme.ts +255 -0
- package/src/editor/skill-atlas/trees/fix-findings.ts +469 -0
- package/src/editor/skill-atlas/trees/pick-component.ts +300 -0
- package/src/editor/skill-atlas/trees/set-colors.ts +148 -0
- package/src/editor/skill-atlas/trees/set-geometry.ts +136 -0
- package/src/editor/skill-atlas/trees/set-type.ts +142 -0
- package/src/editor/skill-atlas/types.ts +3 -4
- package/src/editor/skill-atlas/wireLayout.ts +287 -0
- package/src/live-tokens/data/themes/autumn.json +15 -15
- package/src/live-tokens/data/themes/halloween.json +15 -15
- package/src/live-tokens/data/themes/midnight-study.json +15 -15
- package/src/live-tokens/data/themes/ocean.json +15 -15
- package/src/live-tokens/data/themes/royal-velvet.json +15 -15
- package/src/live-tokens/data/themes/sketchy.json +15 -15
- package/src/live-tokens/data/themes/spring-meadow.json +15 -15
- package/src/live-tokens/data/themes/sunset.json +15 -15
- package/src/system/components/Badge.svelte +7 -0
- package/src/system/components/Button.svelte +7 -0
- package/src/system/components/Callout.svelte +10 -6
- package/src/system/components/Card.svelte +23 -4
- package/src/system/components/CodeSnippet.svelte +4 -3
- package/src/system/components/CollapsibleSection.svelte +23 -8
- package/src/system/components/CornerBadge.svelte +6 -0
- package/src/system/components/Dialog.svelte +13 -6
- package/src/system/components/IconButton.svelte +9 -0
- package/src/system/components/Image.svelte +8 -0
- package/src/system/components/ImageLightbox.svelte +6 -0
- package/src/system/components/InlineEditActions.svelte +7 -0
- package/src/system/components/Input.svelte +7 -0
- package/src/system/components/MenuSelect.svelte +7 -0
- package/src/system/components/Notification.svelte +7 -0
- package/src/system/components/Panel.svelte +6 -0
- package/src/system/components/ProgressBar.svelte +5 -0
- package/src/system/components/RadioButton.svelte +11 -5
- package/src/system/components/SectionDivider.svelte +8 -0
- package/src/system/components/SegmentedControl.svelte +6 -0
- package/src/system/components/SideNavigation.svelte +6 -0
- package/src/system/components/Slider.svelte +7 -4
- package/src/system/components/TabBar.svelte +15 -9
- package/src/system/components/Table.svelte +8 -3
- package/src/system/components/Toggle.svelte +4 -4
- package/src/system/components/Tooltip.svelte +6 -0
- package/src/testing-js/chunk-AO7EZHYV.js +776 -0
- package/src/testing-js/chunk-AO7EZHYV.js.map +1 -0
- package/src/testing-js/chunk-FAFOAWYL.js +39 -0
- package/src/testing-js/chunk-FAFOAWYL.js.map +1 -0
- package/src/testing-js/chunk-L73N4NSO.js +23 -0
- package/src/testing-js/chunk-L73N4NSO.js.map +1 -0
- package/src/testing-js/chunk-LXR3MN6N.js +3063 -0
- package/src/testing-js/chunk-LXR3MN6N.js.map +1 -0
- package/src/testing-js/chunk-ZMSX6CXR.js +53 -0
- package/src/testing-js/chunk-ZMSX6CXR.js.map +1 -0
- package/src/testing-js/component-alias.contract.js +81 -0
- package/src/testing-js/component-alias.contract.js.map +1 -0
- package/src/testing-js/component-editor.contract.js +62 -0
- package/src/testing-js/component-editor.contract.js.map +1 -0
- package/src/testing-js/component-render.contract.js +568 -0
- package/src/testing-js/component-render.contract.js.map +1 -0
- package/src/testing-js/index.d.ts +293 -0
- package/src/testing-js/index.js +222 -0
- package/src/testing-js/index.js.map +1 -0
- package/src/testing-js/registry.contract.js +39 -0
- package/src/testing-js/registry.contract.js.map +1 -0
- package/src/testing-js/vitest-BE6uGF31.d.ts +73 -0
- package/src/testing-js/vitest.d.ts +3 -0
- package/src/testing-js/vitest.js +13 -0
- package/src/testing-js/vitest.js.map +1 -0
- package/template/README.md +13 -0
- package/template/_gitignore +6 -0
- package/template/package.json +3 -1
- package/template/src/pages/Home.svelte +4 -18
- package/.claude/skills/live-tokens-build-page/SKILL.md +0 -103
- package/.claude/skills/live-tokens-build-page/references/layout-sources.md +0 -48
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../testing/component-render.contract.ts"],"sourcesContent":["import fs from 'node:fs';\nimport path from 'node:path';\nimport { expect, test, type Locator, type Page } from '@playwright/test';\nimport { openComponentsEditor } from './support/editor';\nimport { selectedContracts } from './contracts';\nimport { ContractHarness } from './support/contractHarness';\n\n// Each test boots its own editor and asserts only over its own component's\n// aliases, so the 25 component tests carry no shared state between them.\ntest.describe.configure({ mode: 'parallel' });\n\ninterface AliasCase {\n component: string;\n variable: string;\n}\n\nfunction discoverDefaultAliases(): AliasCase[] {\n const root = path.resolve(process.env.LIVE_TOKENS_DATA_DIR ?? 'src/live-tokens/data', 'component-configs');\n const cases: AliasCase[] = [];\n for (const directory of fs.readdirSync(root).sort()) {\n const file = path.join(root, directory, 'default.json');\n if (!fs.existsSync(file)) continue;\n const data = JSON.parse(fs.readFileSync(file, 'utf8')) as {\n component?: string;\n aliases?: Record<string, unknown>;\n };\n const component = data.component ?? directory;\n for (const variable of Object.keys(data.aliases ?? {}).sort()) {\n cases.push({ component, variable });\n }\n }\n return cases;\n}\n\nconst requestedComponent = process.env.LIVE_TOKENS_COMPONENT;\nconst aliasCases = discoverDefaultAliases()\n .filter(({ component }) => !requestedComponent || component === requestedComponent);\nconst aliasesByComponent = new Map<string, string[]>();\nfor (const { component, variable } of aliasCases) {\n const variables = aliasesByComponent.get(component) ?? [];\n variables.push(variable);\n aliasesByComponent.set(component, variables);\n}\n\ninterface ProbeResult {\n covered: string[];\n unchanged: string[];\n}\n\n/**\n * Exercise each standard token selector through the same click path a designer\n * uses. The rendered-property probe below deliberately writes root variables\n * directly so it can try arbitrary CSS values; this companion gate proves the\n * editor control itself reaches the store and the document root.\n */\nasync function exerciseVisibleTokenControls(\n page: Page,\n componentAliases: string[],\n exercised: Set<string>,\n): Promise<void> {\n const group = page.locator('.variant-group:visible');\n const settle = () => page.evaluate(() => new Promise<void>((resolve) =>\n requestAnimationFrame(() => requestAnimationFrame(() => resolve()))));\n await settle();\n // A disabled or locked selector (a hover surface stood down while the tint\n // is on) opens a dropdown with nothing to pick from.\n const variables = await group.locator('.ui-token-selector:visible[data-token-variable]:not(.disabled):not(.locked)')\n .evaluateAll((elements) => [...new Set(elements\n .map((element) => (element as HTMLElement).dataset.tokenVariable)\n .filter((value): value is string => !!value))]);\n\n for (const variable of variables) {\n if (!componentAliases.includes(variable) || exercised.has(variable)) continue;\n const selector = group.locator(`.ui-token-selector:visible[data-token-variable=\"${variable}\"]:not(.disabled):not(.locked)`).first();\n if (await selector.count() === 0) continue;\n const before = await page.evaluate((name) =>\n document.documentElement.style.getPropertyValue(name).trim(), variable);\n\n const optionSelector = [\n '.ui-ts-dropdown .static-chip:not(.active):not(:disabled)',\n '.ui-ts-dropdown .ui-option-item:not(.active):not(:disabled)',\n '.ui-ts-dropdown .font-size-row:not(.active):not(:disabled)',\n ].join(', ');\n let after = before;\n let foundOption = false;\n // Some semantic options can resolve to the same CSS as the current alias.\n // Try several distinct visible choices before treating the control as dead.\n for (let attempt = 0; attempt < 4 && after === before; attempt++) {\n await settle();\n await selector.locator('.ui-ts-trigger').click();\n const options = selector.locator(optionSelector);\n const count = await options.count();\n if (count === 0) {\n // Close an unsupported dropdown before the traversal moves on. The\n // final coverage assertion reports its precise variable.\n await selector.locator('.ui-ts-trigger').click();\n break;\n }\n foundOption = true;\n await options.nth(attempt % count).click();\n await settle();\n after = await page.evaluate((name) =>\n document.documentElement.style.getPropertyValue(name).trim(), variable);\n }\n if (!foundOption) continue;\n expect(after, `${variable} did not write through its editor control`).not.toBe(before);\n exercised.add(variable);\n }\n}\n\n/** Split padding exposes four side selectors and a merge action instead of a\n * selector for the fallback parent value. Merge each visible composite, then\n * exercise the newly rendered parent selector through the standard path. */\nasync function exerciseVisibleSplitPaddingControls(\n page: Page,\n componentAliases: string[],\n exercised: Set<string>,\n): Promise<void> {\n const group = page.locator('.variant-group:visible');\n const variables = await group.locator('.merge-btn:visible[data-padding-parent-variable]')\n .evaluateAll((elements) => [...new Set(elements\n .map((element) => (element as HTMLElement).dataset.paddingParentVariable)\n .filter((value): value is string => !!value))]);\n\n for (const variable of variables) {\n if (!componentAliases.includes(variable) || exercised.has(variable)) continue;\n const merge = group.locator(`.merge-btn:visible[data-padding-parent-variable=\"${variable}\"]`).first();\n if (await merge.count()) await merge.click();\n }\n if (variables.length > 0) {\n await page.evaluate(() => new Promise<void>((resolve) =>\n requestAnimationFrame(() => requestAnimationFrame(() => resolve()))));\n await exerciseVisibleTokenControls(page, componentAliases, exercised);\n }\n}\n\n/** A single semantic control may intentionally own several CSS variables.\n * Every listed variable must change before the component traversal can\n * advance. */\n/** Returns the gate inputs it toggled, so the caller can put each one back\n * after probing: a gate left on locks rows behind it (a hover surface stood\n * down behind a tint), and those rows still have to be exercised in the\n * views that follow. */\nasync function exerciseVisibleCompositeControls(\n page: Page,\n componentAliases: string[],\n exercised: Set<string>,\n): Promise<Locator[]> {\n const toggled: Locator[] = [];\n const group = page.locator('.variant-group:visible');\n const controls = group.locator('[data-token-variables]:visible');\n for (let index = 0; index < await controls.count(); index++) {\n const control = controls.nth(index);\n const variables = ((await control.getAttribute('data-token-variables')) ?? '')\n .split(/\\s+/)\n .filter((variable) => componentAliases.includes(variable) && !exercised.has(variable));\n if (variables.length === 0) continue;\n const before = await page.evaluate((names) => Object.fromEntries(names.map((name) => [name,\n document.documentElement.style.getPropertyValue(name).trim(),\n ])), variables);\n // A gate is a checkbox, a radio, or a Toggle, which is a `role=\"switch\"`.\n const input = control.locator('input[type=\"checkbox\"], input[type=\"radio\"], [role=\"switch\"]').first();\n if (await input.count() === 0) continue;\n await input.click();\n toggled.push(input);\n await page.evaluate(() => new Promise<void>((resolve) =>\n requestAnimationFrame(() => requestAnimationFrame(() => resolve()))));\n const after = await page.evaluate((names) => Object.fromEntries(names.map((name) => [name,\n document.documentElement.style.getPropertyValue(name).trim(),\n ])), variables);\n for (const variable of variables) {\n expect(after[variable], `${variable} did not write through its composite control`)\n .not.toBe(before[variable]);\n exercised.add(variable);\n }\n }\n return toggled;\n}\n\n/** Component-owned gradients use a composite editor instead of a token\n * selector. Pick a real stop colour through its nested palette control and\n * assert the owning component variable—not the scratch stop variable—writes. */\nasync function exerciseVisibleGradientControls(\n page: Page,\n componentAliases: string[],\n exercised: Set<string>,\n): Promise<void> {\n const group = page.locator('.variant-group:visible');\n const editors = group.locator('.gradient-editor:visible[data-token-variable]');\n for (let index = 0; index < await editors.count(); index++) {\n const editor = editors.nth(index);\n const variable = await editor.getAttribute('data-token-variable');\n if (!variable || !componentAliases.includes(variable) || exercised.has(variable)) continue;\n const before = await page.evaluate((name) =>\n document.documentElement.style.getPropertyValue(name).trim(), variable);\n\n // Promote an empty/flat background to Solid so its stop picker is present,\n // then select a non-active invariant colour through the real palette UI.\n const solid = editor.getByRole('radio', { name: 'Solid' });\n if (await solid.count() && !await solid.isChecked()) await solid.check({ force: true });\n const picker = editor.locator('.picker-slot .ui-token-selector').first();\n await picker.locator('.ui-ts-trigger').click({ force: true });\n const swatch = picker.locator('.static-chip:not(.active):not(:disabled)').first();\n if (await swatch.count() === 0) continue;\n await swatch.click({ force: true });\n\n await expect.poll(() => page.evaluate((name) =>\n document.documentElement.style.getPropertyValue(name).trim(), variable),\n { message: `${variable} did not write through its gradient editor` })\n .not.toMatch(new RegExp(`^${escapeRegExp(before)}$`));\n exercised.add(variable);\n }\n}\n\nfunction escapeRegExp(value: string): string {\n return value.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\$&');\n}\n\n/**\n * Exercise every property control visible for the current standardized\n * VariantGroup view. The preview and property panel are deliberately siblings:\n * only `.tabs-preview` is fingerprinted, so a selector repaint cannot satisfy\n * the runtime-component contract.\n */\nasync function probeCurrentView(\n page: Page,\n componentAliases: string[],\n forcedVariables: string[] = [],\n provenVariables: string[] = [],\n): Promise<ProbeResult> {\n return page.evaluate(async ({ allAliases, forced, proven }) => {\n const group = Array.from(document.querySelectorAll<HTMLElement>('.variant-group'))\n .find((element) => element.offsetParent !== null);\n const preview = group?.querySelector<HTMLElement>('.tabs-preview');\n if (!group || !preview) return { covered: [], unchanged: [] };\n\n const visibleVariables = new Set<string>();\n if (forced.length > 0) {\n for (const variable of forced) {\n if (allAliases.includes(variable)) visibleVariables.add(variable);\n }\n } else {\n for (const element of group.querySelectorAll<HTMLElement>('[data-token-variable]')) {\n const variable = element.dataset.tokenVariable;\n if (element.offsetParent !== null && variable && allAliases.includes(variable)) {\n visibleVariables.add(variable);\n }\n }\n for (const element of group.querySelectorAll<HTMLElement>('[data-token-variables]')) {\n if (element.offsetParent === null) continue;\n for (const variable of (element.dataset.tokenVariables ?? '').split(/\\s+/)) {\n if (allAliases.includes(variable)) visibleVariables.add(variable);\n }\n }\n }\n\n // Split padding's four override slots are intentionally hidden controls.\n // The standardized padding control owns them, so include their default-file\n // variables whenever their base padding row is visible.\n for (const variable of [...visibleVariables]) {\n if (!variable.endsWith('-padding')) continue;\n for (const side of ['top', 'right', 'bottom', 'left']) {\n const sideVariable = `${variable}-${side}`;\n if (allAliases.includes(sideVariable)) visibleVariables.add(sideVariable);\n }\n }\n\n // A property already observed repainting cannot become less reactive in a\n // later view, and it is already recorded as both seen and covered. Probing\n // it again in every remaining view is the traversal's dominant cost: a\n // component with 47 views pays for its whole alias set 47 times over.\n for (const variable of proven) visibleVariables.delete(variable);\n if (visibleVariables.size === 0) return { covered: [], unchanged: [] };\n const properties = [\n 'display', 'visibility', 'opacity', 'position', 'zIndex',\n 'color', 'background', 'backgroundColor', 'backgroundImage', 'backgroundSize', 'backgroundPosition',\n 'border', 'borderColor', 'borderWidth', 'borderStyle',\n 'borderTopColor', 'borderRightColor', 'borderBottomColor', 'borderLeftColor',\n 'borderTopWidth', 'borderRightWidth', 'borderBottomWidth', 'borderLeftWidth',\n 'borderRadius', 'borderTopLeftRadius', 'borderTopRightRadius',\n 'borderBottomRightRadius', 'borderBottomLeftRadius',\n 'outline', 'outlineColor', 'outlineWidth', 'boxShadow',\n 'filter', 'backdropFilter', 'mixBlendMode',\n 'fontFamily', 'fontSize', 'fontWeight', 'fontStyle', 'lineHeight', 'letterSpacing',\n 'textAlign', 'textDecoration', 'textTransform', 'textShadow', 'whiteSpace',\n 'padding', 'paddingTop', 'paddingRight', 'paddingBottom', 'paddingLeft',\n 'margin', 'marginTop', 'marginRight', 'marginBottom', 'marginLeft',\n 'gap', 'rowGap', 'columnGap',\n 'width', 'minWidth', 'maxWidth', 'height', 'minHeight', 'maxHeight',\n 'top', 'right', 'bottom', 'left',\n 'transform', 'transformOrigin', 'translate', 'rotate', 'scale',\n 'justifyContent', 'justifyItems', 'justifySelf', 'alignContent', 'alignItems', 'alignSelf',\n 'gridTemplateColumns', 'gridTemplateRows', 'gridColumn', 'gridRow', 'flex', 'flexDirection',\n 'overflow', 'overflowX', 'overflowY', 'clipPath', 'objectFit', 'objectPosition',\n 'fill', 'stroke', 'strokeWidth', 'strokeDasharray', 'strokeDashoffset',\n 'cursor', 'pointerEvents', 'content',\n 'transitionProperty', 'transitionDuration', 'transitionTimingFunction', 'transitionDelay',\n 'scrollbarColor', 'scrollbarWidth',\n ] as const;\n\n const ignoredAttribute = /^(class|style|data-|aria-)/;\n const stableAttributes = (element: Element) => Array.from(element.attributes)\n .filter((attribute) => !ignoredAttribute.test(attribute.name))\n .map((attribute) => `${attribute.name}=${attribute.value}`)\n .sort()\n .join(';');\n\n const pseudoSelectorsFor = (variable: string) => {\n const pseudoSelectors = ['::before', '::after'];\n if (variable.includes('placeholder')) pseudoSelectors.push('::placeholder');\n if (variable.includes('scrollbar')) {\n pseudoSelectors.push(\n '::-webkit-scrollbar',\n '::-webkit-scrollbar-track',\n '::-webkit-scrollbar-thumb',\n );\n }\n return pseudoSelectors;\n };\n\n // Fixed overlays are portaled to body and are therefore outside the\n // preview subtree. Dialog roles let the harness include them without a\n // component-specific selector or fixture.\n const fingerprintNodes = () => {\n const roots = [preview, ...document.querySelectorAll<HTMLElement>('[role=\"dialog\"]')];\n return roots.flatMap((root) => [root, ...root.querySelectorAll<HTMLElement>('*')]);\n };\n\n const nodeFingerprint = (element: HTMLElement, index: number, pseudoSelectors: string[]) => {\n const style = getComputedStyle(element);\n const rect = element.getBoundingClientRect();\n const values = properties.map((property) => style[property]).join('|');\n const pseudoValues = pseudoSelectors.map((selector) => {\n const pseudo = getComputedStyle(element, selector);\n return properties.map((property) => pseudo[property]).join('|');\n }).join('~');\n return [\n index,\n element.tagName,\n values,\n pseudoValues,\n `${rect.x},${rect.y},${rect.width},${rect.height}`,\n `${element.scrollWidth},${element.scrollHeight}`,\n stableAttributes(element),\n ].join('~');\n };\n\n const fingerprint = (variable: string) => {\n const pseudoSelectors = pseudoSelectorsFor(variable);\n return fingerprintNodes().map((element, index) =>\n nodeFingerprint(element, index, pseudoSelectors));\n };\n\n /** Proving a repaint takes one difference, so stop at the node that moved\n * instead of reading every remaining node's computed style. Each node\n * costs 58 properties across the element and its pseudo-elements, and this\n * walk runs once per candidate value per alias, so it is the probe's\n * dominant cost. Only the negative case has to read everything. */\n const movedFrom = (variable: string, baseline: string[]) => {\n const pseudoSelectors = pseudoSelectorsFor(variable);\n const nodes = fingerprintNodes();\n if (nodes.length !== baseline.length) return true;\n for (let index = 0; index < nodes.length; index++) {\n if (nodeFingerprint(nodes[index], index, pseudoSelectors) !== baseline[index]) return true;\n }\n return false;\n };\n\n const alternateValues = (variable: string, current: string): string[] => {\n const values: string[] = [];\n const add = (...candidates: string[]) => {\n for (const candidate of candidates) {\n if (candidate !== current && !values.includes(candidate)) values.push(candidate);\n }\n };\n if (/(surface|color|text|border)(?!-width)|-icon$|outline-color/.test(variable)) {\n add('rgb(1, 2, 3)', 'rgb(253, 127, 3)');\n }\n if (variable.includes('font-family')) add('monospace', 'serif');\n if (variable.includes('font-weight')) add('900', '200');\n if (variable.includes('line-height')) add('2.75', '0.75');\n if (variable.includes('letter-spacing')) add('7px', '-2px');\n if (variable.includes('shadow')) add('rgb(1, 2, 3) 7px 9px 0 3px', 'none');\n if (variable.includes('blur')) add('blur(7px)', 'none');\n if (variable.includes('gradient') || variable.endsWith('-background')) {\n add('linear-gradient(90deg, rgb(1, 2, 3), rgb(253, 127, 3))', 'none');\n }\n if (variable.includes('duration') || variable.includes('transition')) add('9s', '0s');\n if (variable.includes('opacity')) add('0.17', '0.83');\n if (variable.includes('display')) add(current === 'none' ? 'block' : 'none', 'contents');\n if (variable.includes('align')) add('right', 'center', 'flex-end');\n if (variable.includes('position')) add('above-label', 'below-description', 'center');\n if (variable.includes('button-variant')) add('danger', 'outline');\n if (variable.includes('shimmer')) {\n add('linear-gradient(90deg, transparent, rgb(255 255 255 / 80%), transparent)', 'none');\n }\n if (/(padding|margin|radius|width|height|size|gap|offset|inset|thickness)/.test(variable)) {\n add('37px', '3px');\n }\n if (variable.includes('scale')) add('1.73', '0.53');\n add('__live_tokens_invalid_probe__', '37px', 'rgb(1, 2, 3)', 'none', '2');\n return values;\n };\n\n const root = document.documentElement;\n const covered: string[] = [];\n const unchanged: string[] = [];\n // Fast-forward in-flight transitions so the fingerprint samples end states.\n // Reading mid-interpolation makes consecutive samples differ on their own,\n // which reports a dead property as covered. Transition shorthands stay\n // untouched so the duration and easing aliases remain observable.\n const settle = async () => {\n await new Promise<void>((resolve) => requestAnimationFrame(() => resolve()));\n for (const animation of document.getAnimations()) animation.finish();\n };\n\n // Remove animation noise from the fingerprint. The contract concerns the\n // end-state projection, not interpolation frames.\n const freeze = document.createElement('style');\n freeze.dataset.reactivityProbe = 'true';\n freeze.textContent = '*,*::before,*::after{animation:none!important;caret-color:transparent!important}';\n document.head.appendChild(freeze);\n await document.fonts?.ready;\n await settle();\n\n for (const variable of visibleVariables) {\n // A split padding control edits its base plus all materialized sides as\n // one standardized transformation. Probe that same transformation so a\n // base fallback is not incorrectly reported dead merely because the\n // active theme already contains side overrides.\n const mutatedVariables = [variable];\n if (variable.endsWith('-padding')) {\n for (const side of ['top', 'right', 'bottom', 'left']) {\n const sideVariable = `${variable}-${side}`;\n if (allAliases.includes(sideVariable)) mutatedVariables.push(sideVariable);\n }\n }\n const previous = mutatedVariables.map((name) => ({\n name,\n value: root.style.getPropertyValue(name),\n priority: root.style.getPropertyPriority(name),\n }));\n const current = getComputedStyle(root).getPropertyValue(variable).trim();\n const baseline = fingerprint(variable);\n let changed = false;\n for (const candidate of alternateValues(variable, current)) {\n for (const name of mutatedVariables) root.style.setProperty(name, candidate, 'important');\n // CSS updates synchronously. One animation frame also gives the few\n // JavaScript-backed components (SVG/filter bridges) time to reconcile.\n await settle();\n if (movedFrom(variable, baseline)) {\n changed = true;\n break;\n }\n }\n for (const prior of previous) {\n if (prior.value) root.style.setProperty(prior.name, prior.value, prior.priority);\n else root.style.removeProperty(prior.name);\n }\n await settle();\n (changed ? covered : unchanged).push(variable);\n }\n\n freeze.remove();\n return { covered, unchanged };\n }, { allAliases: componentAliases, forced: forcedVariables, proven: provenVariables });\n}\n\nasync function selectComponent(page: Page, component: string): Promise<void> {\n await page.evaluate(async (id) => {\n const editor = window.__liveTokensEditor;\n if (!editor) throw new Error('window.__liveTokensEditor is not present on the components route');\n editor.selectComponent(id);\n await new Promise<void>((resolve) => requestAnimationFrame(() =>\n requestAnimationFrame(() => resolve())));\n }, component);\n await page.locator('.variant-group').filter({ visible: true }).waitFor();\n}\n\nfor (const [component, aliases] of aliasesByComponent) {\n test(`${component} repaints every property in its standardized runtime preview`, async ({ page }) => {\n test.setTimeout(600_000);\n await openComponentsEditor(page);\n const covered = new Set<string>();\n const seen = new Set<string>();\n const unchanged = new Set<string>();\n const controlExercised = new Set<string>();\n\n const probe = async (componentAliases: string[], forced: string[] = []) => {\n const remaining = forced.length > 0\n ? forced.filter((variable) => !covered.has(variable))\n : [];\n if (forced.length > 0 && remaining.length === 0) return;\n\n // Exercise standardized preview toggles (zoom, hover enablement, optional\n // content) when retrying a property that is dormant in the default mode.\n if (forced.length > 0) {\n const unchecked = page.locator(\n '.variant-group:visible .tabs-preview input[type=\"checkbox\"]:not(:checked):not(:disabled), '\n + '.variant-group:visible .properties-header input[type=\"checkbox\"]:not(:checked):not(:disabled)',\n );\n for (let index = 0; index < await unchecked.count(); index++) {\n await unchecked.nth(index).click({ force: true });\n }\n }\n\n // Portaled dialogs sit outside `.tabs-preview`; hover their first action\n // so hover-only chrome aliases are painted too. Role-based discovery keeps\n // this independent of any component name or DOM implementation class.\n const dialogAction = page.locator('[role=\"dialog\"] button:not(:disabled)').first();\n if (await dialogAction.count()) await dialogAction.hover({ force: true });\n\n await exerciseVisibleTokenControls(page, componentAliases, controlExercised);\n const gates = await exerciseVisibleCompositeControls(page, componentAliases, controlExercised);\n // A gate that just opened may have revealed a row (the tint colour).\n await exerciseVisibleTokenControls(page, componentAliases, controlExercised);\n await exerciseVisibleSplitPaddingControls(page, componentAliases, controlExercised);\n await exerciseVisibleGradientControls(page, componentAliases, controlExercised);\n const result = await probeCurrentView(page, componentAliases, remaining, [...covered]);\n for (const gate of gates) {\n if (await gate.count()) await gate.click();\n }\n result.covered.forEach((variable) => covered.add(variable));\n result.covered.forEach((variable) => seen.add(variable));\n result.unchanged.forEach((variable) => {\n seen.add(variable);\n if (!covered.has(variable)) unchanged.add(variable);\n });\n };\n\n const traverseComponent = async (\n componentId: string,\n componentAliases: string[],\n forced: string[] = [],\n ) => {\n await selectComponent(page, componentId);\n\n const visitViews = async () => {\n if (forced.length > 0 && forced.every((variable) => covered.has(variable))) return;\n const sizeSelect = page.locator('.variant-group:visible .preview-actions select').first();\n const sizeValues = await sizeSelect.count()\n ? await sizeSelect.locator('option').evaluateAll((options) =>\n options.map((option) => (option as HTMLOptionElement).value))\n : [''];\n\n for (const sizeValue of sizeValues) {\n const currentSizeSelect = page.locator('.variant-group:visible .preview-actions select').first();\n if (sizeValue && await currentSizeSelect.count()) await currentSizeSelect.selectOption(sizeValue);\n\n const variantTabs = page.locator('.variant-group:visible .variant-tabs .variant-tab-btn');\n const variantLabels = await variantTabs.count()\n ? await variantTabs.allTextContents()\n : [''];\n\n for (let variantIndex = 0; variantIndex < variantLabels.length; variantIndex++) {\n if (variantLabels[variantIndex]) {\n await page.locator('.variant-group:visible .variant-tabs .variant-tab-btn')\n .nth(variantIndex)\n .click({ force: true });\n }\n\n await probe(componentAliases, forced);\n const primaryTabs = page.locator(\n '.variant-group:visible .tabs-states-block > .tabs-selectors:first-of-type .state-tab-btn',\n );\n const primaryLabels = await primaryTabs.allTextContents();\n for (let primaryIndex = 0; primaryIndex < primaryLabels.length; primaryIndex++) {\n await page.locator(\n '.variant-group:visible .tabs-states-block > .tabs-selectors:first-of-type .state-tab-btn',\n ).nth(primaryIndex).click({ force: true });\n await probe(componentAliases, forced);\n\n const secondaryTabs = page.locator(\n '.variant-group:visible .tabs-states-block > .tabs-selectors.substrip .state-tab-btn',\n );\n const secondaryLabels = await secondaryTabs.allTextContents();\n for (let secondaryIndex = 0; secondaryIndex < secondaryLabels.length; secondaryIndex++) {\n await page.locator(\n '.variant-group:visible .tabs-states-block > .tabs-selectors.substrip .state-tab-btn',\n ).nth(secondaryIndex).click({ force: true });\n await probe(componentAliases, forced);\n }\n }\n }\n }\n };\n\n await visitViews();\n if (forced.length === 0 || forced.every((variable) => covered.has(variable))) return;\n\n // Open any dialog-capable preview and repeat the same registry traversal.\n const dialogTrigger = page.locator(\n '.variant-group:visible .tabs-preview [aria-haspopup=\"dialog\"][aria-expanded=\"false\"]',\n ).first();\n if (await dialogTrigger.count()) {\n await dialogTrigger.click({ force: true });\n await page.locator('[role=\"dialog\"]').waitFor();\n await visitViews();\n }\n\n // Exercise every option of standardized canvas selects. This covers modes\n // such as Input type and optional Notification actions without any\n // per-component fixture or test case.\n const canvasSelects = page.locator('.variant-group:visible .tabs-preview .canvas-toolbar select');\n for (let selectIndex = 0; selectIndex < await canvasSelects.count(); selectIndex++) {\n const values = await canvasSelects.nth(selectIndex).locator('option').evaluateAll((options) =>\n options.map((option) => (option as HTMLOptionElement).value));\n for (const value of values) {\n if (forced.every((variable) => covered.has(variable))) return;\n await canvasSelects.nth(selectIndex).selectOption(value);\n await visitViews();\n }\n }\n\n // Expanded/collapsed previews expose motion aliases whose computed style\n // is different in each direction.\n const expanders = page.locator(\n '.variant-group:visible .tabs-preview [aria-expanded]:not([aria-haspopup=\"dialog\"])',\n );\n for (let index = 0; index < await expanders.count(); index++) {\n if (forced.every((variable) => covered.has(variable))) return;\n await expanders.nth(index).click({ force: true });\n await visitViews();\n }\n\n // A default-off hover gate only paints while the runtime element itself\n // matches :hover. Forced preview classes intentionally bypass that gate,\n // so retry unresolved properties while hovering each visible preview node.\n // This stays component-agnostic and runs only for the small unresolved set.\n // The traversal above leaves a forced state selected, and its preview\n // class outranks the gated `:hover` rule, so return to the first state.\n const firstStateTab = page.locator(\n '.variant-group:visible .tabs-states-block > .tabs-selectors:first-of-type .state-tab-btn',\n ).first();\n if (await firstStateTab.count()) await firstStateTab.click({ force: true });\n const hoverTargets = page.locator('.variant-group:visible .tabs-preview *:visible');\n for (let index = 0; index < await hoverTargets.count(); index++) {\n if (forced.every((variable) => covered.has(variable))) return;\n // The node list was read once; a probe above may have re-rendered it.\n try {\n await hoverTargets.nth(index).hover({ force: true, timeout: 2_000 });\n } catch {\n continue;\n }\n await probe(componentAliases, forced);\n }\n };\n\n await traverseComponent(component, aliases);\n\n // Retry every property not yet observed repainting. Composite controls,\n // pseudo-elements, optional content, interaction modes and portaled dialogs\n // are all discovered from standard DOM semantics; there is no component\n // exception table.\n const unresolved = aliases.filter((variable) => !covered.has(variable));\n if (unresolved.length > 0) await traverseComponent(component, aliases, unresolved);\n\n const notExposed = aliases.filter((variable) => !seen.has(variable));\n const notReactive = aliases.filter((variable) => seen.has(variable) && !covered.has(variable));\n const notControlExercised = aliases.filter((variable) => !controlExercised.has(variable));\n\n expect(notExposed, `Properties not exposed by a standardized component view:\\n${notExposed.join('\\n')}`)\n .toEqual([]);\n expect(notReactive, `Properties whose rendered preview did not change:\\n${notReactive.join('\\n')}`)\n .toEqual([]);\n expect(notControlExercised, `Properties not exercised through a rendered editor control:\\n${notControlExercised.join('\\n')}`)\n .toEqual([]);\n expect(covered.size).toBe(aliases.length);\n });\n}\n\ntest('component discovery covers every alias exactly once', () => {\n const perComponent = [...aliasesByComponent.values()].reduce((sum, a) => sum + a.length, 0);\n expect(aliasCases.length).toBeGreaterThan(0);\n expect(perComponent).toBe(aliasCases.length);\n});\n\n// Obligation 6. The traversal above proves every property repaints something in\n// the preview; these pin the mapping, so a repaint elsewhere cannot stand in for\n// the part and CSS property the property is meant to drive.\nfor (const contract of await selectedContracts()) {\n test(`${contract.id} paints each declared property on its declared part`, async ({ page }) => {\n test.setTimeout(180_000);\n const harness = await ContractHarness.open(page, contract);\n expect(await harness.assertProperties()).toBeGreaterThan(0);\n });\n}\n"],"mappings":"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","names":["root"]}
|
|
@@ -0,0 +1,293 @@
|
|
|
1
|
+
import { PlaywrightTestProject, PlaywrightTestConfig } from '@playwright/test';
|
|
2
|
+
import { L as LiveTokensTestingConfig } from './vitest-BE6uGF31.js';
|
|
3
|
+
export { C as COMPONENTS_PATH_ENV, a as COMPONENT_ENV, R as ResolvedTestingConfig, T as TESTING_CONFIG_FILE, V as VitestConfigOptions, c as createVitestConfig, d as defineTestingConfig, b as devServerCommand, r as resolveTestingConfig } from './vitest-BE6uGF31.js';
|
|
4
|
+
import 'vite';
|
|
5
|
+
import 'vitest/config';
|
|
6
|
+
|
|
7
|
+
interface PlaywrightConfigOptions extends LiveTokensTestingConfig {
|
|
8
|
+
/** Project root the settings resolve against. Default `process.cwd()`. */
|
|
9
|
+
root?: string;
|
|
10
|
+
/** Projects that run beside the contract project. */
|
|
11
|
+
extraProjects?: PlaywrightTestProject[];
|
|
12
|
+
/** Where projects that declare no `testDir` of their own look. */
|
|
13
|
+
testDir?: string;
|
|
14
|
+
}
|
|
15
|
+
/**
|
|
16
|
+
* The contract project, its dev server, and the data isolation the two share.
|
|
17
|
+
*
|
|
18
|
+
* Isolation runs here rather than in a global setup because the config is the
|
|
19
|
+
* first thing every process in the run evaluates: the environment it leaves
|
|
20
|
+
* behind reaches the workers and the dev server, and nothing in the run can
|
|
21
|
+
* observe the state before it.
|
|
22
|
+
*/
|
|
23
|
+
declare function createPlaywrightConfig(options?: PlaywrightConfigOptions): Promise<PlaywrightTestConfig>;
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* The plugin reads this name ahead of its own options and
|
|
27
|
+
* `live-tokens.config.json`, so setting it redirects every server-side write —
|
|
28
|
+
* the data tree, `tokens.generated.css`, and `fonts.css` — into the copy.
|
|
29
|
+
*/
|
|
30
|
+
declare const TEST_DATA_DIR_ENV = "LIVE_TOKENS_TEST_DATA_DIR";
|
|
31
|
+
/** What the suites and the harness read component configs from in the runner's
|
|
32
|
+
* own process. It names the same directory and is a separate variable because
|
|
33
|
+
* it answers a different question: where a reader looks, not where the server
|
|
34
|
+
* is confined. */
|
|
35
|
+
declare const DATA_DIR_ENV = "LIVE_TOKENS_DATA_DIR";
|
|
36
|
+
interface IsolatedData {
|
|
37
|
+
dataDir: string;
|
|
38
|
+
/** True when this process made the copy and owns removing it. */
|
|
39
|
+
created: boolean;
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
* Copy `sourceDataDir` into a unique temporary directory and point both the
|
|
43
|
+
* server and the runner at it. Idempotent across processes: Playwright loads
|
|
44
|
+
* the config again in every worker, and each inherits the parent's environment,
|
|
45
|
+
* so only the first call copies anything.
|
|
46
|
+
*/
|
|
47
|
+
declare function isolateDataDir(sourceDataDir: string): IsolatedData;
|
|
48
|
+
|
|
49
|
+
declare const PORT_ENV = "LIVE_TOKENS_TEST_PORT";
|
|
50
|
+
/**
|
|
51
|
+
* The port the dev server and `baseURL` share. Cached in the environment so
|
|
52
|
+
* every Playwright worker, which loads the config again in its own process,
|
|
53
|
+
* reads the port the main process settled on.
|
|
54
|
+
*/
|
|
55
|
+
declare function resolvePort(preferred?: number): number;
|
|
56
|
+
|
|
57
|
+
type ContractRule = 'contract-listed' | 'contract-render' | 'contract-alias' | 'contract-persist' | 'contract-theme' | 'contract-preview' | 'contract-sketch';
|
|
58
|
+
/**
|
|
59
|
+
* A failed obligation, carrying the rule id the CLI reports it under. A defect
|
|
60
|
+
* fixture names the rule it expects, and `check-component --tests` maps a
|
|
61
|
+
* reporter error onto a finding.
|
|
62
|
+
*/
|
|
63
|
+
declare class ContractViolation extends Error {
|
|
64
|
+
readonly rule: ContractRule;
|
|
65
|
+
readonly component: string;
|
|
66
|
+
constructor(rule: ContractRule, component: string, message: string);
|
|
67
|
+
}
|
|
68
|
+
interface Inapplicable {
|
|
69
|
+
applicable: false;
|
|
70
|
+
reason: string;
|
|
71
|
+
}
|
|
72
|
+
declare function isInapplicable(value: unknown): value is Inapplicable;
|
|
73
|
+
interface PartLocator {
|
|
74
|
+
/** Resolved inside the preview stage unless `portal` is set. */
|
|
75
|
+
selector: string;
|
|
76
|
+
/** Read computed style from this pseudo-element of the part, e.g.
|
|
77
|
+
* `'::after'`. Chromium's `getComputedStyle` only tolerates the bare form
|
|
78
|
+
* (`'after'`) for `::before`/`::after`; use the full `::` syntax so a
|
|
79
|
+
* future pseudo-element (`::placeholder`, `::marker`, ...) works the same
|
|
80
|
+
* way without a silent fallback to the host element's own style. */
|
|
81
|
+
pseudo?: string;
|
|
82
|
+
/** Rendered outside the preview stage. Resolved against the document. */
|
|
83
|
+
portal?: boolean;
|
|
84
|
+
}
|
|
85
|
+
type PartDeclaration = string | PartLocator;
|
|
86
|
+
/**
|
|
87
|
+
* An editor control the preview is gated behind: a canvas-toolbar checkbox, an
|
|
88
|
+
* optional-content switch. `check` sets a checkbox to a state and is safe to
|
|
89
|
+
* repeat; `value` picks a select option; neither clicks once.
|
|
90
|
+
*/
|
|
91
|
+
interface ControlStep {
|
|
92
|
+
kind: 'control';
|
|
93
|
+
/** Selector inside the visible variant group. */
|
|
94
|
+
selector: string;
|
|
95
|
+
check?: boolean;
|
|
96
|
+
value?: string;
|
|
97
|
+
}
|
|
98
|
+
type SetupStep = InteractionAction | ControlStep;
|
|
99
|
+
/** Which VariantGroup, which state tab, and what has to happen first. */
|
|
100
|
+
interface View {
|
|
101
|
+
/** Variant tab label. Omitted when the editor renders one variant. */
|
|
102
|
+
variant?: string;
|
|
103
|
+
/** State tab label. Omitted when the editor renders no state strip. */
|
|
104
|
+
state?: string;
|
|
105
|
+
/** Run after the tabs are selected. Portaled and toolbar-gated parts exist
|
|
106
|
+
* only once these have run. */
|
|
107
|
+
setup?: SetupStep[];
|
|
108
|
+
}
|
|
109
|
+
/** Part key -> CSS property -> the design token that must drive it. */
|
|
110
|
+
type PaintMap = Record<string, Record<string, string>>;
|
|
111
|
+
interface PropertyExpectation extends View {
|
|
112
|
+
paints: PaintMap;
|
|
113
|
+
}
|
|
114
|
+
interface StateExpectation extends View {
|
|
115
|
+
state: string;
|
|
116
|
+
/** Class the root part carries while the editor previews this state. */
|
|
117
|
+
forceClass?: string;
|
|
118
|
+
/** Part key -> attributes it must carry. A null value requires the absence. */
|
|
119
|
+
attributes?: Record<string, Record<string, string | null>>;
|
|
120
|
+
/** What the state repaints, as computed-value equality against its token. */
|
|
121
|
+
paints?: PaintMap;
|
|
122
|
+
}
|
|
123
|
+
/**
|
|
124
|
+
* `token` and `opacity` drive the token selector for `variable`. `gradient`
|
|
125
|
+
* drives the gradient editor for `variable`. `literal` drives `control` and
|
|
126
|
+
* reads `variable` back off the root, which is where an intrinsic lands: the
|
|
127
|
+
* editors write those into the alias bucket. `config` drives `control` and
|
|
128
|
+
* reads `configKey` out of the config bucket, which holds editor metadata that
|
|
129
|
+
* never reaches `:root`.
|
|
130
|
+
*/
|
|
131
|
+
type PersistenceShape = 'token' | 'literal' | 'opacity' | 'gradient' | 'config';
|
|
132
|
+
interface PersistenceCase extends View {
|
|
133
|
+
shape: PersistenceShape;
|
|
134
|
+
/** The alias the value is read back from. Omitted for `config`. */
|
|
135
|
+
variable?: string;
|
|
136
|
+
/** Selector inside the visible variant group, for `literal` and `config`. */
|
|
137
|
+
control?: string;
|
|
138
|
+
/** The config-bucket key, for `config`. */
|
|
139
|
+
configKey?: string;
|
|
140
|
+
/** Where the persisted value must land once the page has reloaded. */
|
|
141
|
+
observe: {
|
|
142
|
+
part: string;
|
|
143
|
+
css: string;
|
|
144
|
+
};
|
|
145
|
+
}
|
|
146
|
+
interface PersistenceExpectation {
|
|
147
|
+
cases: PersistenceCase[];
|
|
148
|
+
/** The alias Reset must return to the value the server holds for it. It has
|
|
149
|
+
* to be one a case above moves, so the saved value and the value the
|
|
150
|
+
* component booted with are different and Reset can be seen to choose. */
|
|
151
|
+
resetVariable: string;
|
|
152
|
+
}
|
|
153
|
+
interface ThemeExpectation extends View {
|
|
154
|
+
/** Theme slug shown in the Theme Picker, e.g. `ocean`. */
|
|
155
|
+
theme: string;
|
|
156
|
+
/** Variables whose root value the theme must move. */
|
|
157
|
+
changed: string[];
|
|
158
|
+
/**
|
|
159
|
+
* Variables the theme must leave exactly as they were. Two theme JSON files
|
|
160
|
+
* can name the identical design token (e.g. both `--border-neutral`) and
|
|
161
|
+
* still resolve to different rendered colors, because many color primitives
|
|
162
|
+
* are computed per theme from a palette/seed even when the alias string is
|
|
163
|
+
* unchanged — comparing the two files' alias *names* does not establish
|
|
164
|
+
* invariance. Pick `unchanged` candidates from token families a theme never
|
|
165
|
+
* recolors: `--shadow-*`, `--icon-size-*`, `--font-size-*`,
|
|
166
|
+
* `--line-height-*`, `--font-family`/`--font-mono`, and the spacing/radius/
|
|
167
|
+
* border-width scale steps (`--space-*`, `--radius-*`, `--border-width-*`)
|
|
168
|
+
* are all fixed constants, not palette-derived — verify a candidate's
|
|
169
|
+
* *resolved* value is identical between the two theme files before trusting
|
|
170
|
+
* it, not just that the alias name matches.
|
|
171
|
+
*/
|
|
172
|
+
unchanged: string[];
|
|
173
|
+
/** Variables the theme's own component config aliases, and the design token
|
|
174
|
+
* each must resolve to under it. */
|
|
175
|
+
aliasedTo: Record<string, string>;
|
|
176
|
+
/** Where the theme's value must land in the rendered preview. */
|
|
177
|
+
observe: {
|
|
178
|
+
part: string;
|
|
179
|
+
css: string;
|
|
180
|
+
variable: string;
|
|
181
|
+
};
|
|
182
|
+
}
|
|
183
|
+
type InteractionAction = {
|
|
184
|
+
kind: 'press';
|
|
185
|
+
part: string;
|
|
186
|
+
key: string;
|
|
187
|
+
} | {
|
|
188
|
+
kind: 'click';
|
|
189
|
+
part: string;
|
|
190
|
+
} | {
|
|
191
|
+
kind: 'type';
|
|
192
|
+
part: string;
|
|
193
|
+
text: string;
|
|
194
|
+
} | {
|
|
195
|
+
kind: 'dragTo';
|
|
196
|
+
part: string;
|
|
197
|
+
along: string;
|
|
198
|
+
fraction: number;
|
|
199
|
+
};
|
|
200
|
+
type InteractionOutcome = {
|
|
201
|
+
kind: 'attribute';
|
|
202
|
+
part: string;
|
|
203
|
+
name: string;
|
|
204
|
+
value: string;
|
|
205
|
+
} | {
|
|
206
|
+
kind: 'valueMoves';
|
|
207
|
+
part: string;
|
|
208
|
+
direction: 'up' | 'down';
|
|
209
|
+
} | {
|
|
210
|
+
kind: 'valueBecomes';
|
|
211
|
+
part: string;
|
|
212
|
+
value: string;
|
|
213
|
+
} | {
|
|
214
|
+
kind: 'valueChanges';
|
|
215
|
+
part: string;
|
|
216
|
+
} | {
|
|
217
|
+
kind: 'valueHolds';
|
|
218
|
+
part: string;
|
|
219
|
+
}
|
|
220
|
+
/** Whether the part takes focus. The only outcome a button with no state
|
|
221
|
+
* attribute has: a disabled one refuses it and an enabled one takes it. */
|
|
222
|
+
| {
|
|
223
|
+
kind: 'focused';
|
|
224
|
+
part: string;
|
|
225
|
+
value: boolean;
|
|
226
|
+
};
|
|
227
|
+
interface InteractionCase extends View {
|
|
228
|
+
name: string;
|
|
229
|
+
action: InteractionAction;
|
|
230
|
+
expect: InteractionOutcome;
|
|
231
|
+
}
|
|
232
|
+
interface InteractionExpectation {
|
|
233
|
+
/** The part carrying the interactive role. */
|
|
234
|
+
part: string;
|
|
235
|
+
role: string;
|
|
236
|
+
cases: InteractionCase[];
|
|
237
|
+
}
|
|
238
|
+
interface SketchPartExpectation {
|
|
239
|
+
part: string;
|
|
240
|
+
/** The design token `--sketch-fill` must resolve to on this part. */
|
|
241
|
+
fill?: string;
|
|
242
|
+
/** The design token `--sketch-stroke` must resolve to on this part. */
|
|
243
|
+
stroke?: string;
|
|
244
|
+
}
|
|
245
|
+
interface SketchExpectation extends View {
|
|
246
|
+
/** Sketchstyle id the paint is asserted under. */
|
|
247
|
+
style: string;
|
|
248
|
+
parts: SketchPartExpectation[];
|
|
249
|
+
}
|
|
250
|
+
interface ComponentContract {
|
|
251
|
+
id: string;
|
|
252
|
+
origin: 'system' | 'custom';
|
|
253
|
+
parts: Record<string, PartDeclaration>;
|
|
254
|
+
/** Part key of the component's own root element in the preview. */
|
|
255
|
+
root: string;
|
|
256
|
+
/** The view every obligation opens with. */
|
|
257
|
+
view?: View;
|
|
258
|
+
states: StateExpectation[] | Inapplicable;
|
|
259
|
+
properties: PropertyExpectation[];
|
|
260
|
+
/**
|
|
261
|
+
* Aliases no paint map can pin, each with the reason. A token consumed
|
|
262
|
+
* inside `calc()` or handed to a gradient function never appears verbatim in
|
|
263
|
+
* a computed style, so the probe cannot read it back — that is the only
|
|
264
|
+
* class this belongs to. A token a sibling alias merely happens to carry
|
|
265
|
+
* the same rendered value through (a base padding token behind four
|
|
266
|
+
* per-side overrides, say) is not in this class: the per-side entries pin
|
|
267
|
+
* their own aliases, not the base one, and a paint map that drives the base
|
|
268
|
+
* token directly and observes the per-side CSS property can pin it too. Put
|
|
269
|
+
* that here and Wave 4's coverage JSON records the base token as covered
|
|
270
|
+
* when nothing asserts the link between it and what actually painted.
|
|
271
|
+
*/
|
|
272
|
+
uncovered?: Record<string, string>;
|
|
273
|
+
persistence: PersistenceExpectation;
|
|
274
|
+
theme: ThemeExpectation;
|
|
275
|
+
interaction: InteractionExpectation | Inapplicable;
|
|
276
|
+
sketch: SketchExpectation | Inapplicable;
|
|
277
|
+
}
|
|
278
|
+
declare function requiresInteraction(role: string | null): boolean;
|
|
279
|
+
declare function partLocator(contract: ComponentContract, key: string): PartLocator;
|
|
280
|
+
|
|
281
|
+
declare const shippedContracts: ComponentContract[];
|
|
282
|
+
/** Names the module `selectedContracts` loads a custom component's
|
|
283
|
+
* `ComponentContract[]` from. The suite files are static Playwright entry
|
|
284
|
+
* points with no argument to receive one through, so an env var plus a
|
|
285
|
+
* dynamic import is the only route in. */
|
|
286
|
+
declare const CONTRACTS_MODULE_ENV = "LIVE_TOKENS_CONTRACTS_MODULE";
|
|
287
|
+
/** The shipped list plus whatever `LIVE_TOKENS_CONTRACTS_MODULE` names,
|
|
288
|
+
* unfiltered — what a "no contract for this id" error lists as declared. */
|
|
289
|
+
declare function allContracts(contracts?: ComponentContract[]): Promise<ComponentContract[]>;
|
|
290
|
+
/** The contracts a run covers, narrowed by `LIVE_TOKENS_COMPONENT`. */
|
|
291
|
+
declare function selectedContracts(contracts?: ComponentContract[]): Promise<ComponentContract[]>;
|
|
292
|
+
|
|
293
|
+
export { CONTRACTS_MODULE_ENV, type ComponentContract, type ContractRule, ContractViolation, type ControlStep, DATA_DIR_ENV, type Inapplicable, type InteractionAction, type InteractionCase, type InteractionExpectation, type InteractionOutcome, type IsolatedData, LiveTokensTestingConfig, PORT_ENV, type PaintMap, type PartDeclaration, type PartLocator, type PersistenceCase, type PersistenceExpectation, type PersistenceShape, type PlaywrightConfigOptions, type PropertyExpectation, type SetupStep, type SketchExpectation, type SketchPartExpectation, type StateExpectation, TEST_DATA_DIR_ENV, type ThemeExpectation, type View, allContracts, createPlaywrightConfig, isInapplicable, isolateDataDir, partLocator, requiresInteraction, resolvePort, selectedContracts, shippedContracts };
|
|
@@ -0,0 +1,222 @@
|
|
|
1
|
+
import {
|
|
2
|
+
CONTRACTS_MODULE_ENV,
|
|
3
|
+
ContractViolation,
|
|
4
|
+
allContracts,
|
|
5
|
+
isInapplicable,
|
|
6
|
+
partLocator,
|
|
7
|
+
requiresInteraction,
|
|
8
|
+
selectedContracts,
|
|
9
|
+
shippedContracts
|
|
10
|
+
} from "./chunk-LXR3MN6N.js";
|
|
11
|
+
import {
|
|
12
|
+
createVitestConfig
|
|
13
|
+
} from "./chunk-FAFOAWYL.js";
|
|
14
|
+
import {
|
|
15
|
+
COMPONENTS_PATH_ENV,
|
|
16
|
+
COMPONENT_ENV,
|
|
17
|
+
TESTING_CONFIG_FILE,
|
|
18
|
+
defineTestingConfig,
|
|
19
|
+
devServerCommand,
|
|
20
|
+
resolveTestingConfig
|
|
21
|
+
} from "./chunk-ZMSX6CXR.js";
|
|
22
|
+
|
|
23
|
+
// src/testing/playwright.ts
|
|
24
|
+
import path2 from "path";
|
|
25
|
+
import { fileURLToPath } from "url";
|
|
26
|
+
import {
|
|
27
|
+
defineConfig,
|
|
28
|
+
devices
|
|
29
|
+
} from "@playwright/test";
|
|
30
|
+
|
|
31
|
+
// src/testing/isolation.ts
|
|
32
|
+
import fs from "fs";
|
|
33
|
+
import os from "os";
|
|
34
|
+
import path from "path";
|
|
35
|
+
var TEST_DATA_DIR_ENV = "LIVE_TOKENS_TEST_DATA_DIR";
|
|
36
|
+
var DATA_DIR_ENV = "LIVE_TOKENS_DATA_DIR";
|
|
37
|
+
var SESSION_FILES = /* @__PURE__ */ new Set(["_working.json", "_active.json", "_production.json"]);
|
|
38
|
+
var removed = false;
|
|
39
|
+
function registerCleanup(dataDir) {
|
|
40
|
+
const remove = () => {
|
|
41
|
+
if (removed) return;
|
|
42
|
+
removed = true;
|
|
43
|
+
fs.rmSync(dataDir, { recursive: true, force: true });
|
|
44
|
+
};
|
|
45
|
+
process.once("exit", remove);
|
|
46
|
+
process.once("SIGINT", remove);
|
|
47
|
+
process.once("SIGTERM", remove);
|
|
48
|
+
}
|
|
49
|
+
function isolateDataDir(sourceDataDir) {
|
|
50
|
+
const existing = process.env[TEST_DATA_DIR_ENV];
|
|
51
|
+
if (existing) {
|
|
52
|
+
const dataDir2 = path.resolve(existing);
|
|
53
|
+
if (!fs.existsSync(dataDir2)) {
|
|
54
|
+
throw new Error(`${TEST_DATA_DIR_ENV} names ${dataDir2}, which does not exist`);
|
|
55
|
+
}
|
|
56
|
+
process.env[DATA_DIR_ENV] = dataDir2;
|
|
57
|
+
return { dataDir: dataDir2, created: false };
|
|
58
|
+
}
|
|
59
|
+
const source = path.resolve(sourceDataDir);
|
|
60
|
+
if (!fs.existsSync(source)) {
|
|
61
|
+
throw new Error(
|
|
62
|
+
`No data directory at ${source}. Set \`dataDir\` in live-tokens.testing.ts when the project keeps its live-tokens data somewhere else.`
|
|
63
|
+
);
|
|
64
|
+
}
|
|
65
|
+
const dataDir = fs.realpathSync(fs.mkdtempSync(path.join(os.tmpdir(), "live-tokens-contract-")));
|
|
66
|
+
fs.cpSync(source, dataDir, { recursive: true });
|
|
67
|
+
for (const entry of fs.readdirSync(dataDir, { recursive: true })) {
|
|
68
|
+
if (SESSION_FILES.has(path.basename(entry))) {
|
|
69
|
+
fs.rmSync(path.join(dataDir, entry), { force: true });
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
process.env[TEST_DATA_DIR_ENV] = dataDir;
|
|
73
|
+
process.env[DATA_DIR_ENV] = dataDir;
|
|
74
|
+
registerCleanup(dataDir);
|
|
75
|
+
return { dataDir, created: true };
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
// src/testing/port.ts
|
|
79
|
+
import { execFileSync } from "child_process";
|
|
80
|
+
var PORT_ENV = "LIVE_TOKENS_TEST_PORT";
|
|
81
|
+
var PREFERRED_PORT = 4173;
|
|
82
|
+
var PROBE = `
|
|
83
|
+
const net = require('net');
|
|
84
|
+
const listen = (port) => new Promise((resolve, reject) => {
|
|
85
|
+
const server = net.createServer();
|
|
86
|
+
server.once('error', reject);
|
|
87
|
+
server.listen(port, '127.0.0.1', () => {
|
|
88
|
+
const { port: bound } = server.address();
|
|
89
|
+
server.close(() => resolve(bound));
|
|
90
|
+
});
|
|
91
|
+
});
|
|
92
|
+
// process.stdout.write, not console.log: the parent's FORCE_COLOR makes
|
|
93
|
+
// console.log wrap a number in ANSI escapes.
|
|
94
|
+
listen(Number(process.argv[1])).catch(() => listen(0)).then((port) => process.stdout.write(String(port)));
|
|
95
|
+
`;
|
|
96
|
+
function resolvePort(preferred) {
|
|
97
|
+
const cached = process.env[PORT_ENV];
|
|
98
|
+
if (cached) return Number(cached);
|
|
99
|
+
const port = preferred ?? Number(execFileSync(process.execPath, ["-e", PROBE, String(PREFERRED_PORT)], {
|
|
100
|
+
encoding: "utf-8"
|
|
101
|
+
}).trim());
|
|
102
|
+
if (!Number.isInteger(port) || port <= 0) {
|
|
103
|
+
throw new Error(`Could not settle on a dev-server port (read ${port})`);
|
|
104
|
+
}
|
|
105
|
+
process.env[PORT_ENV] = String(port);
|
|
106
|
+
return port;
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
// src/testing/playwright.ts
|
|
110
|
+
var HOST = "127.0.0.1";
|
|
111
|
+
var CONTRACT_TEST_DIR = path2.dirname(fileURLToPath(import.meta.url));
|
|
112
|
+
async function createPlaywrightConfig(options = {}) {
|
|
113
|
+
const settings = resolveTestingConfig(options, options.root);
|
|
114
|
+
const { dataDir } = isolateDataDir(settings.dataDir);
|
|
115
|
+
const port = resolvePort(settings.port);
|
|
116
|
+
const baseURL = `http://${HOST}:${port}`;
|
|
117
|
+
process.env[COMPONENTS_PATH_ENV] = settings.componentsPath;
|
|
118
|
+
if (settings.contractsModule) process.env[CONTRACTS_MODULE_ENV] = settings.contractsModule;
|
|
119
|
+
const requested = process.env[COMPONENT_ENV];
|
|
120
|
+
if (requested) {
|
|
121
|
+
const matches = await selectedContracts();
|
|
122
|
+
if (matches.length === 0) {
|
|
123
|
+
const all = await allContracts();
|
|
124
|
+
throw new Error(
|
|
125
|
+
`${COMPONENT_ENV}=${requested} names a component with no contract. Declared: ${all.map((contract) => contract.id).sort().join(", ")}`
|
|
126
|
+
);
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
return defineConfig({
|
|
130
|
+
testDir: options.testDir ?? CONTRACT_TEST_DIR,
|
|
131
|
+
// The component suites carry their serial and parallel intent per file:
|
|
132
|
+
// `component-editor.contract.ts` runs each component's save and reset
|
|
133
|
+
// cycles in order against one data tree. Turning this on would interleave
|
|
134
|
+
// them.
|
|
135
|
+
fullyParallel: false,
|
|
136
|
+
// One worker is the safe default: the specs share a dev server and a data
|
|
137
|
+
// directory. A run that can afford more passes a worker count on the
|
|
138
|
+
// command line.
|
|
139
|
+
workers: 1,
|
|
140
|
+
timeout: 3e4,
|
|
141
|
+
expect: { timeout: 5e3 },
|
|
142
|
+
// A shared-server suite times out under CI load in ways it never does
|
|
143
|
+
// locally, and without a retry budget one wobble aborts a tagged release.
|
|
144
|
+
retries: process.env.CI ? 2 : 0,
|
|
145
|
+
// Absolute and rooted at the consumer project, not the config file's own
|
|
146
|
+
// directory: `check-component --tests`'s generated config lives in a
|
|
147
|
+
// temporary directory that gets removed on completion, and a relative
|
|
148
|
+
// `outputDir` would put every trace and screenshot in there too.
|
|
149
|
+
outputDir: path2.join(settings.root, "test-results/playwright"),
|
|
150
|
+
reporter: process.env.CI ? [["line"], ["html", { open: "never" }]] : "list",
|
|
151
|
+
use: {
|
|
152
|
+
baseURL,
|
|
153
|
+
// Unset, both of these are unbounded: a locator that never resolves waits
|
|
154
|
+
// out the whole test timeout and reports nothing about where it stopped.
|
|
155
|
+
actionTimeout: 1e4,
|
|
156
|
+
navigationTimeout: 3e4,
|
|
157
|
+
trace: "on-first-retry",
|
|
158
|
+
screenshot: "only-on-failure",
|
|
159
|
+
// `retain-on-failure` still records every passing test and throws the
|
|
160
|
+
// file away, which costs ~28% CPU. Match the trace policy instead.
|
|
161
|
+
video: "on-first-retry"
|
|
162
|
+
},
|
|
163
|
+
projects: [
|
|
164
|
+
...options.extraProjects ?? [],
|
|
165
|
+
{
|
|
166
|
+
name: "contract",
|
|
167
|
+
testDir: CONTRACT_TEST_DIR,
|
|
168
|
+
// `.ts` in this repo, `.js` once tsup compiles the shipped build into
|
|
169
|
+
// `src/testing-js`; matching both here needs no build-time swap.
|
|
170
|
+
testMatch: "**/component-*.contract.{ts,js}",
|
|
171
|
+
// `.tabs-preview` now caps its sticky band at 50vh, which is enough for
|
|
172
|
+
// image, panel, card and sidenavigation to pass alone at 1280x720. A
|
|
173
|
+
// full parallel run still fails panel's gradient radio there, so the
|
|
174
|
+
// band can cover a control at some scroll positions. Until that is
|
|
175
|
+
// fixed the suite runs taller. See docs/contract-test-defects.md.
|
|
176
|
+
use: { ...devices["Desktop Chrome"], viewport: { width: 1280, height: 900 } }
|
|
177
|
+
}
|
|
178
|
+
],
|
|
179
|
+
webServer: {
|
|
180
|
+
command: devServerCommand(settings.devCommand, HOST, port),
|
|
181
|
+
// Otherwise defaults to the config file's own directory. A generated
|
|
182
|
+
// config that lives outside the consumer root (`check-component
|
|
183
|
+
// --tests`'s temporary config) would run `npm run dev` from wherever
|
|
184
|
+
// that happens to be instead.
|
|
185
|
+
cwd: settings.root,
|
|
186
|
+
url: baseURL,
|
|
187
|
+
// A server this run did not start carries none of these variables, so it
|
|
188
|
+
// would write the project's own data tree.
|
|
189
|
+
reuseExistingServer: false,
|
|
190
|
+
timeout: 12e4,
|
|
191
|
+
env: {
|
|
192
|
+
...process.env,
|
|
193
|
+
[TEST_DATA_DIR_ENV]: dataDir,
|
|
194
|
+
[DATA_DIR_ENV]: dataDir
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
});
|
|
198
|
+
}
|
|
199
|
+
export {
|
|
200
|
+
COMPONENTS_PATH_ENV,
|
|
201
|
+
COMPONENT_ENV,
|
|
202
|
+
CONTRACTS_MODULE_ENV,
|
|
203
|
+
ContractViolation,
|
|
204
|
+
DATA_DIR_ENV,
|
|
205
|
+
PORT_ENV,
|
|
206
|
+
TESTING_CONFIG_FILE,
|
|
207
|
+
TEST_DATA_DIR_ENV,
|
|
208
|
+
allContracts,
|
|
209
|
+
createPlaywrightConfig,
|
|
210
|
+
createVitestConfig,
|
|
211
|
+
defineTestingConfig,
|
|
212
|
+
devServerCommand,
|
|
213
|
+
isInapplicable,
|
|
214
|
+
isolateDataDir,
|
|
215
|
+
partLocator,
|
|
216
|
+
requiresInteraction,
|
|
217
|
+
resolvePort,
|
|
218
|
+
resolveTestingConfig,
|
|
219
|
+
selectedContracts,
|
|
220
|
+
shippedContracts
|
|
221
|
+
};
|
|
222
|
+
//# sourceMappingURL=index.js.map
|