[{"data":1,"prerenderedAt":347},["ShallowReactive",2],{"navigation_docs":3,"-docs-architecture-testing":64,"-docs-architecture-testing-surround":342},[4,29,49],{"title":5,"path":6,"stem":7,"children":8,"page":28},"Getting Started","\u002Fdocs\u002Fgetting-started","docs\u002F1.getting-started",[9,14,19,24],{"title":10,"path":11,"stem":12,"icon":13},"Introduction","\u002Fdocs\u002Fgetting-started\u002Fintroduction","docs\u002F1.getting-started\u002F1.introduction","i-lucide-house",{"title":15,"path":16,"stem":17,"icon":18},"Installation","\u002Fdocs\u002Fgetting-started\u002Finstallation","docs\u002F1.getting-started\u002F2.installation","i-lucide-rocket",{"title":20,"path":21,"stem":22,"icon":23},"Configuration","\u002Fdocs\u002Fgetting-started\u002Fconfiguration","docs\u002F1.getting-started\u002F3.configuration","i-lucide-sliders-horizontal",{"title":25,"path":26,"stem":27,"icon":18},"Deployment","\u002Fdocs\u002Fgetting-started\u002Fdeployment","docs\u002F1.getting-started\u002F4.deployment",false,{"title":30,"path":31,"stem":32,"children":33,"page":28},"Architecture","\u002Fdocs\u002Farchitecture","docs\u002F2.architecture",[34,39,44],{"title":35,"path":36,"stem":37,"icon":38},"Layers","\u002Fdocs\u002Farchitecture\u002Flayers","docs\u002F2.architecture\u002F1.layers","i-lucide-layers",{"title":40,"path":41,"stem":42,"icon":43},"Multi-tenancy","\u002Fdocs\u002Farchitecture\u002Fmulti-tenancy","docs\u002F2.architecture\u002F2.multi-tenancy","i-lucide-building-2",{"title":45,"path":46,"stem":47,"icon":48},"Testing","\u002Fdocs\u002Farchitecture\u002Ftesting","docs\u002F2.architecture\u002F3.testing","i-lucide-flask-conical",{"title":50,"path":51,"stem":52,"children":53,"page":28},"Reference","\u002Fdocs\u002Freference","docs\u002F3.reference",[54,59],{"title":55,"path":56,"stem":57,"icon":58},"Commands","\u002Fdocs\u002Freference\u002Fcommands","docs\u002F3.reference\u002F1.commands","i-lucide-terminal",{"title":60,"path":61,"stem":62,"icon":63},"Decisions","\u002Fdocs\u002Freference\u002Fdecisions","docs\u002F3.reference\u002F2.decisions","i-lucide-git-branch",{"id":65,"title":45,"body":66,"description":335,"extension":336,"links":337,"meta":338,"navigation":339,"path":46,"seo":340,"stem":47,"__hash__":341},"docs\u002Fdocs\u002F2.architecture\u002F3.testing.md",{"type":67,"value":68,"toc":329},"minimark",[69,74,124,128,132,156,238,241,245,251,289,292,307,322,325],[70,71,73],"h2",{"id":72},"two-suites-one-boundary","Two suites, one boundary",[75,76,77,93],"table",{},[78,79,80],"thead",{},[81,82,83,87,90],"tr",{},[84,85,86],"th",{},"Suite",[84,88,89],{},"Files",[84,91,92],{},"Scope",[94,95,96,111],"tbody",{},[81,97,98,102,108],{},[99,100,101],"td",{},"Vitest",[99,103,104],{},[105,106,107],"code",{},"**\u002F*.test.ts",[99,109,110],{},"pure logic only — no DOM, no Supabase",[81,112,113,116,121],{},[99,114,115],{},"Playwright",[99,117,118],{},[105,119,120],{},"e2e\u002F*.spec.ts",[99,122,123],{},"full flows against a seeded local Supabase",[125,126,127],"p",{},"The boundary is the point. Unit tests that mock a database client mostly assert\nthat the mock was called, which is why the coverage gate below is credible.",[70,129,131],{"id":130},"the-100-gate","The 100% gate",[125,133,134,135,139,140,143,144,143,147,150,151,155],{},"Vitest enforces ",[136,137,138],"strong",{},"100% lines, branches, functions and statements"," — but only over\nthe pure-logic layer (",[105,141,142],{},"app\u002Futils",", ",[105,145,146],{},"server\u002Futils",[105,148,149],{},"layers\u002F**\u002Fserver\u002Futils",").\nPlatform-bound files are ",[152,153,154],"em",{},"excluded",", not mocked to hit a number:",[157,158,163],"pre",{"className":159,"code":160,"language":161,"meta":162,"style":162},"language-ts shiki shiki-themes material-theme-lighter material-theme material-theme-palenight","exclude: [\n  'app\u002Futils\u002Fdownload.ts',               \u002F\u002F browser DOM\n  'server\u002Futils\u002FrequireAdmin.ts',        \u002F\u002F H3 + Supabase\n  'layers\u002Femail\u002Fserver\u002Futils\u002Femails.ts', \u002F\u002F renders .vue templates\n]\n","ts","",[105,164,165,182,202,217,232],{"__ignoreMap":162},[166,167,170,174,178],"span",{"class":168,"line":169},"line",1,[166,171,173],{"class":172},"sBMFI","exclude",[166,175,177],{"class":176},"sMK4o",":",[166,179,181],{"class":180},"sTEyZ"," [\n",[166,183,185,188,192,195,198],{"class":168,"line":184},2,[166,186,187],{"class":176},"  '",[166,189,191],{"class":190},"sfazB","app\u002Futils\u002Fdownload.ts",[166,193,194],{"class":176},"'",[166,196,197],{"class":176},",",[166,199,201],{"class":200},"sHwdD","               \u002F\u002F browser DOM\n",[166,203,205,207,210,212,214],{"class":168,"line":204},3,[166,206,187],{"class":176},[166,208,209],{"class":190},"server\u002Futils\u002FrequireAdmin.ts",[166,211,194],{"class":176},[166,213,197],{"class":176},[166,215,216],{"class":200},"        \u002F\u002F H3 + Supabase\n",[166,218,220,222,225,227,229],{"class":168,"line":219},4,[166,221,187],{"class":176},[166,223,224],{"class":190},"layers\u002Femail\u002Fserver\u002Futils\u002Femails.ts",[166,226,194],{"class":176},[166,228,197],{"class":176},[166,230,231],{"class":200}," \u002F\u002F renders .vue templates\n",[166,233,235],{"class":168,"line":234},5,[166,236,237],{"class":180},"]\n",[125,239,240],{},"A gate that counts fake-mocked platform code reports 100% while testing nothing.\nThis one covers a smaller surface honestly.",[70,242,244],{"id":243},"playwright-projects","Playwright projects",[125,246,247,248,177],{},"A spec only runs once it's added to a project's ",[105,249,250],{},"testMatch",[252,253,254,261,271,277,283],"ul",{},[255,256,257,260],"li",{},[136,258,259],{},"chromium"," — public smoke, no session",[255,262,263,266,267,270],{},[136,264,265],{},"a11y"," — axe against every page, in light ",[152,268,269],{},"and"," dark",[255,272,273,276],{},[136,274,275],{},"app"," — authenticated feature flows on the seeded admin session",[255,278,279,282],{},[136,280,281],{},"isolation"," — logs in as a second tenant; runs with no saved session",[255,284,285,288],{},[136,286,287],{},"screenshots"," — opt-in, see below",[70,290,291],{"id":287},"Screenshots",[157,293,297],{"className":294,"code":295,"language":296,"meta":162,"style":162},"language-bash shiki shiki-themes material-theme-lighter material-theme material-theme-palenight","pnpm screenshots\n","bash",[105,298,299],{"__ignoreMap":162},[166,300,301,304],{"class":168,"line":169},[166,302,303],{"class":172},"pnpm",[166,305,306],{"class":190}," screenshots\n",[125,308,309,310,313,314,317,318,321],{},"Captures every route in both themes at 1920×1080, plus a Pixel 7 mobile pass, into\n",[105,311,312],{},"docs\u002Fscreenshots\u002F",". It's gated behind a ",[105,315,316],{},"SCREENSHOTS"," env var so a bare\n",[105,319,320],{},"pnpm test:e2e"," never regenerates PNGs and dirties your working tree.",[125,323,324],{},"Not a visual-regression baseline — there's no pixel diffing. It exists so README\nimages and design review keep up with the app instead of aging into fiction.",[326,327,328],"style",{},"html pre.shiki code .sBMFI, html code.shiki .sBMFI{--shiki-light:#E2931D;--shiki-default:#FFCB6B;--shiki-dark:#FFCB6B}html pre.shiki code .sMK4o, html code.shiki .sMK4o{--shiki-light:#39ADB5;--shiki-default:#89DDFF;--shiki-dark:#89DDFF}html pre.shiki code .sTEyZ, html code.shiki .sTEyZ{--shiki-light:#90A4AE;--shiki-default:#EEFFFF;--shiki-dark:#BABED8}html pre.shiki code .sfazB, html code.shiki .sfazB{--shiki-light:#91B859;--shiki-default:#C3E88D;--shiki-dark:#C3E88D}html pre.shiki code .sHwdD, html code.shiki .sHwdD{--shiki-light:#90A4AE;--shiki-light-font-style:italic;--shiki-default:#546E7A;--shiki-default-font-style:italic;--shiki-dark:#676E95;--shiki-dark-font-style:italic}html .light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html.light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}",{"title":162,"searchDepth":184,"depth":184,"links":330},[331,332,333,334],{"id":72,"depth":184,"text":73},{"id":130,"depth":184,"text":131},{"id":243,"depth":184,"text":244},{"id":287,"depth":184,"text":291},"Two suites with a hard line between them, and a 100% gate that means something.","md",null,{},{"icon":48},{"title":45,"description":335},"OCQGhhTgzQ2WICF4enUGjQsZP0Oexm3ZNT6cVKNXNMM",[343,345],{"title":40,"path":41,"stem":42,"description":344,"icon":43,"children":-1},"Single database, shared schema, and a policy that does the work.",{"title":55,"path":56,"stem":57,"description":346,"icon":58,"children":-1},"Every script worth knowing, grouped by what you're doing.",1785951589296]