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