Files
pbakaus_impeccable/tests/framework-fixtures/vite8-sveltekit-stateful/fixture.json
T
Paul BakausandClaude Code 39f233ac24 fix: hydrate attribute-bound each values and guard style directives
Addresses two cursor review findings:

- {#each} bodies whose bound values appear in attributes (href={link.href},
  src={item.img}) now record attr slots; the browser hydrates them from the
  rendered attribute so component previews no longer mount with empty links.
  Single-expression attributes hydrate exactly; mixed values stay unhydrated
  as before. A new slot classifier also refuses shapes that would crash a
  shallow hydration item (deep paths, method calls, bare item renders) and
  routes them to source-preview mode instead.
- Style directives now run the mixed loop/outer identifier check before the
  free-identifier param check, so style:width={base + r.pct} falls back
  instead of minting a broken param.

Tests: attr-slot analysis units, crashy/lossy fallback units, an attribute-
bound anchor in the stateful SvelteKit fixture asserted through accept, and
a mountedDomProbe e2e hook that reads the hydrated href off the mounted
variant DOM (verified to fail when hydration is disabled).

AI-assisted (Claude Code).

Co-Authored-By: Claude Code <noreply@anthropic.com>
2026-07-28 15:33:16 -07:00

145 lines
3.4 KiB
JSON

{
"name": "Vite 8 + SvelteKit stateful page",
"config": {
"files": [
"src/app.html"
],
"insertBefore": "</body>",
"commentSyntax": "html"
},
"sourceFiles": [
"DESIGN.md",
"src/app.html",
"src/routes/+page.svelte",
"src/routes/+layout.svelte",
"svelte.config.js",
"vite.config.js"
],
"generatedFiles": [],
"wrapCases": [
{
"name": "wraps hero title through Svelte component preview",
"args": {
"classes": "hero-title",
"tag": "h1"
},
"expectedFile": "node_modules/.impeccable-live/wraptest0/manifest.json",
"expectedSourceFile": "src/routes/+page.svelte",
"expectedPreviewMode": "svelte-component"
},
{
"name": "wraps the each-block list through Svelte component preview",
"args": {
"classes": "expense-list",
"tag": "ul"
},
"expectedFile": "node_modules/.impeccable-live/wraptest1/manifest.json",
"expectedSourceFile": "src/routes/+page.svelte",
"expectedPreviewMode": "svelte-component"
}
],
"runtime": {
"styling": "plain-css",
"install": [
"npm",
"install",
"--no-audit",
"--no-fund",
"--loglevel=error"
],
"devCommand": [
"npx",
"vite",
"dev",
"--host",
"127.0.0.1"
],
"readyPattern": "Local:\\s+https?://[^:]+:(\\d+)",
"readyTimeoutMs": 120000,
"steer": false,
"pickSelector": "ul.expense-list",
"pickPosition": {
"x": 10,
"y": 10
},
"variantSequence": [
3,
1,
2
],
"acceptedSourcePattern": "<ul[^>]*class=\"[^\"]*\\bexpense-list\\b",
"assertSourceContains": [
"{#each expenses as expense, i}",
"{expense.name}",
"{expense.amount}",
"href={expense.doc}"
],
"preActions": [
{
"type": "click",
"selector": "[data-testid='add-expense']"
},
{
"type": "wait",
"selector": "[data-testid='expense-row'][data-index='0']"
},
{
"type": "click",
"selector": "[data-testid='add-expense']"
},
{
"type": "wait",
"selector": "[data-testid='expense-row'][data-index='1']"
},
{
"type": "click",
"selector": "[data-testid='add-expense']"
},
{
"type": "wait",
"selector": "[data-testid='expense-row'][data-index='2']"
}
],
"stateProbe": {
"textSelector": "[data-testid='open-count']",
"expectedText": "3 offen",
"windowProperty": "__impeccableStatefulMounts",
"expectedWindowValue": 1,
"expectWindowUnchanged": true
},
"paramsScenario": {
"variant": 2,
"rangeLabel": "Lead",
"rangeValue": 1.8,
"stepsLabel": "Density",
"stepsOptionLabel": "Snug",
"expectSourceContains": [
"line-height: 1.8",
"letter-spacing: 0.01em"
],
"expectSourceMissing": [
"letter-spacing: 0.14em"
]
},
"componentFailureScenarios": {
"variant": 2,
"storageLoss": false
},
"probe": {
"expectLiveInit": true,
"expectConsoleClean": true
},
"mountedDomProbe": [
{
"selector": "ul.expense-list a.expense-doc",
"attr": "href",
"expect": "/receipts/snack"
},
{
"selector": "ul.expense-list strong.expense-name",
"expect": "Design snack"
}
]
}
}