[{"data":1,"prerenderedAt":782},["ShallowReactive",2],{"footer-links":3,"content-\u002Fdevelop\u002Ftests":12},[4,9],{"path":5,"title":6,"type":7,"isExternal":8},"https:\u002F\u002Fgithub.com\u002Flife-and-dev\u002Fmdsite","GitHub Repo","link",true,{"path":10,"title":11,"type":7,"isExternal":8},"https:\u002F\u002Fwww.npmjs.com\u002Fpackage\u002F@life-and-dev\u002Fmdsite","NPM Registry",{"id":13,"title":14,"body":15,"description":775,"extension":776,"meta":777,"navigation":8,"path":778,"seo":779,"stem":780,"__hash__":781},"content\u002Fdevelop\u002Ftests.md","Testing",{"type":16,"value":17,"toc":762},"minimark",[18,22,32,37,40,115,126,130,133,155,173,176,189,192,197,222,226,237,325,352,356,359,367,371,378,447,450,454,459,542,557,561,567,597,616,620,626,737,740,743,758],[19,20,14],"h1",{"id":21},"testing",[23,24,25,26,31],"p",{},"This page covers how to run the MDsite test suites, how they are configured, and what to add when you change code. For the broader contributor picture, start at ",[27,28,30],"a",{"href":29},"..\u002Fdevelop","Developing mdsite",".",[33,34,36],"h2",{"id":35},"_1-two-test-suites","1. Two test suites",[23,38,39],{},"MDsite has two independent test suites:",[41,42,43,62],"table",{},[44,45,46],"thead",{},[47,48,49,53,56,59],"tr",{},[50,51,52],"th",{},"Suite",[50,54,55],{},"Location",[50,57,58],{},"Runner",[50,60,61],{},"Covers",[63,64,65,90],"tbody",{},[47,66,67,74,80,87],{},[68,69,70],"td",{},[71,72,73],"strong",{},"CLI tests",[68,75,76],{},[77,78,79],"code",{},"src\u002F**\u002F*.test.ts",[68,81,82,83,86],{},"Vitest (root ",[77,84,85],{},"vitest.config.ts",")",[68,88,89],{},"The TypeScript CLI: command dispatch, config parsing, renderer prep, process management.",[47,91,92,97,102,112],{},[68,93,94],{},[71,95,96],{},"Renderer tests",[68,98,99],{},[77,100,101],{},"mdsite-nuxt\u002F**",[68,103,104,105,108,109,86],{},"Vitest + Playwright (",[77,106,107],{},"mdsite-nuxt\u002Fvitest.config.ts",", ",[77,110,111],{},"mdsite-nuxt\u002Fplaywright.config.js",[68,113,114],{},"The Nuxt renderer: utility scripts, hooks, end-to-end flows.",[23,116,117,118,121,122,125],{},"The CLI test suite is what ",[77,119,120],{},"npm test"," runs at the repo root. The renderer suite is run separately, inside ",[77,123,124],{},"mdsite-nuxt\u002F",". They do not share configuration or dependencies.",[33,127,129],{"id":128},"_2-running-the-cli-tests","2. Running the CLI tests",[23,131,132],{},"From the repo root:",[134,135,140],"pre",{"className":136,"code":137,"language":138,"meta":139,"style":139},"language-bash shiki shiki-themes github-light github-dark","npm test\n","bash","",[77,141,142],{"__ignoreMap":139},[143,144,147,151],"span",{"class":145,"line":146},"line",1,[143,148,150],{"class":149},"sScJk","npm",[143,152,154],{"class":153},"sZZnC"," test\n",[23,156,157,158,161,162,165,166,169,170,172],{},"This invokes ",[77,159,160],{},"vitest run"," (see the ",[77,163,164],{},"test"," script in ",[77,167,168],{},"package.json","). It runs every file matching ",[77,171,79],{}," exactly once and exits.",[23,174,175],{},"For iterative work, run Vitest in watch mode:",[134,177,179],{"className":136,"code":178,"language":138,"meta":139,"style":139},"npx vitest\n",[77,180,181],{"__ignoreMap":139},[143,182,183,186],{"class":145,"line":146},[143,184,185],{"class":149},"npx",[143,187,188],{"class":153}," vitest\n",[23,190,191],{},"This re-runs affected tests on every file change.",[193,194,196],"h3",{"id":195},"useful-flags","Useful flags",[198,199,200,207,216],"ul",{},[201,202,203,206],"li",{},[77,204,205],{},"npx vitest run --reporter=verbose"," — full per-test output.",[201,208,209,212,213,31],{},[77,210,211],{},"npx vitest run src\u002Fconfig"," — restrict to tests under ",[77,214,215],{},"src\u002Fconfig\u002F",[201,217,218,221],{},[77,219,220],{},"npx vitest run -t \"dispatches mdsite live\""," — run only tests matching a name pattern.",[33,223,225],{"id":224},"_3-cli-test-configuration","3. CLI test configuration",[23,227,228,229,236],{},"The root ",[27,230,234],{"href":231,"rel":232},"https:\u002F\u002Fgithub.com\u002Flife-and-dev\u002Fmdsite\u002Fblob\u002Fmain\u002Fvitest.config.ts",[233],"nofollow",[77,235,85],{}," is intentionally minimal:",[134,238,242],{"className":239,"code":240,"language":241,"meta":139,"style":139},"language-typescript shiki shiki-themes github-light github-dark","import { defineConfig } from 'vitest\u002Fconfig';\n\nexport default defineConfig({\n  test: {\n    include: ['src\u002F**\u002F*.test.ts'],\n    exclude: ['dist\u002F**'],\n  },\n});\n","typescript",[77,243,244,263,269,284,290,302,313,319],{"__ignoreMap":139},[143,245,246,250,254,257,260],{"class":145,"line":146},[143,247,249],{"class":248},"szBVR","import",[143,251,253],{"class":252},"sVt8B"," { defineConfig } ",[143,255,256],{"class":248},"from",[143,258,259],{"class":153}," 'vitest\u002Fconfig'",[143,261,262],{"class":252},";\n",[143,264,266],{"class":145,"line":265},2,[143,267,268],{"emptyLinePlaceholder":8},"\n",[143,270,272,275,278,281],{"class":145,"line":271},3,[143,273,274],{"class":248},"export",[143,276,277],{"class":248}," default",[143,279,280],{"class":149}," defineConfig",[143,282,283],{"class":252},"({\n",[143,285,287],{"class":145,"line":286},4,[143,288,289],{"class":252},"  test: {\n",[143,291,293,296,299],{"class":145,"line":292},5,[143,294,295],{"class":252},"    include: [",[143,297,298],{"class":153},"'src\u002F**\u002F*.test.ts'",[143,300,301],{"class":252},"],\n",[143,303,305,308,311],{"class":145,"line":304},6,[143,306,307],{"class":252},"    exclude: [",[143,309,310],{"class":153},"'dist\u002F**'",[143,312,301],{"class":252},[143,314,316],{"class":145,"line":315},7,[143,317,318],{"class":252},"  },\n",[143,320,322],{"class":145,"line":321},8,[143,323,324],{"class":252},"});\n",[198,326,327,340,349],{},[201,328,329,332,333,336,337,31],{},[71,330,331],{},"Include",": every ",[77,334,335],{},"*.test.ts"," next to its source under ",[77,338,339],{},"src\u002F",[201,341,342,345,346,31],{},[71,343,344],{},"Exclude",": anything compiled into ",[77,347,348],{},"dist\u002F",[201,350,351],{},"No environment is set (tests run in Node by default), because the CLI is pure Node.js code with no DOM dependency.",[193,353,355],{"id":354},"test-file-layout","Test file layout",[23,357,358],{},"Tests sit next to the module they cover:",[134,360,365],{"className":361,"code":363,"language":364,"meta":139},[362],"language-text","src\u002F\n├── index.ts                    # CLI entrypoint\n├── index.test.ts               # tests for the entrypoint\n├── commands\u002F\n│   ├── init.ts\n│   ├── start.ts\n│   ├── generate.ts\n│   ├── preview.ts\n│   ├── stop.ts\n│   ├── prepare.ts\n│   ├── commands.test.ts        # cross-command tests\n│   ├── prepare.test.ts\n│   └── workflows.test.ts       # GitHub workflow generation\n├── config\u002F\n│   ├── mdsite-config.ts\n│   ├── mdsite-config.test.ts\n│   ├── default-mdsite-config.ts\n│   ├── default-mdsite-config.test.ts\n│   ├── menu.ts\n│   └── menu.test.ts\n├── process\u002F\n│   ├── child-process.ts\n│   ├── child-process.test.ts\n│   ├── runtime-state.ts\n│   └── runtime-state.test.ts\n└── renderer\u002F\n    ├── mdsite-nuxt.ts\n    └── mdsite-nuxt.test.ts\n","text",[77,366,363],{"__ignoreMap":139},[193,368,370],{"id":369},"conventions-used-in-the-tests","Conventions used in the tests",[23,372,373,374,377],{},"Looking at ",[77,375,376],{},"src\u002Findex.test.ts"," as a reference:",[198,379,380,390,400,413,431],{},[201,381,382,385,386,389],{},[71,383,384],{},"Mocks for sibling modules",": ",[77,387,388],{},"vi.mock('.\u002Fcommands\u002Finit.js', () => ({ runInitCommand: vi.fn() }))"," — every command handler is mocked so the entrypoint test only checks dispatch logic, not real command behaviour.",[201,391,392,395,396,399],{},[71,393,394],{},"Argv manipulation",": tests set ",[77,397,398],{},"process.argv"," directly to simulate CLI invocations.",[201,401,402,385,405,408,409,412],{},[71,403,404],{},"Console spies",[77,406,407],{},"vi.spyOn(console, 'log')"," and ",[77,410,411],{},"vi.spyOn(console, 'error')"," assert output without polluting the terminal.",[201,414,415,424,425,108,427,430],{},[71,416,417,420,421],{},[77,418,419],{},"beforeEach"," \u002F ",[77,422,423],{},"afterEach",": reset ",[77,426,398],{},[77,428,429],{},"process.exitCode",", and restore mocks between cases.",[201,432,433,385,436,439,440,443,444,446],{},[71,434,435],{},"Dynamic import",[77,437,438],{},"await import('.\u002Findex.js')"," re-imports the entrypoint after ",[77,441,442],{},"vi.resetModules()"," so the new ",[77,445,398],{}," takes effect.",[23,448,449],{},"Follow the same pattern when adding CLI tests.",[33,451,453],{"id":452},"_4-other-verification-scripts","4. Other verification scripts",[23,455,228,456,458],{},[77,457,168],{}," exposes three related scripts:",[41,460,461,471],{},[44,462,463],{},[47,464,465,468],{},[50,466,467],{},"Script",[50,469,470],{},"What it does",[63,472,473,485,506,527],{},[47,474,475,479],{},[68,476,477],{},[77,478,120],{},[68,480,481,482,484],{},"Runs the CLI Vitest suite (",[77,483,160],{},").",[47,486,487,492],{},[68,488,489],{},[77,490,491],{},"npm run typecheck",[68,493,494,497,498,501,502,505],{},[77,495,496],{},"tsc --noEmit"," against both ",[77,499,500],{},"tsconfig.json"," (CLI) and ",[77,503,504],{},"tsconfig.scripts.json"," (repo scripts). Emits nothing.",[47,507,508,513],{},[68,509,510],{},[77,511,512],{},"npm run verify:package",[68,514,515,516,519,520,522,523,526],{},"Runs ",[77,517,518],{},"scripts\u002Fverify-package-artifacts.ts"," to confirm the published package layout matches ",[77,521,168],{},"'s ",[77,524,525],{},"files"," field.",[47,528,529,534],{},[68,530,531],{},[77,532,533],{},"npm run build",[68,535,536,539,540,31],{},[77,537,538],{},"tsc -p tsconfig.json"," — emits ",[77,541,348],{},[23,543,544,547,548,551,552,556],{},[77,545,546],{},"prepublishOnly"," runs all four (test, typecheck, build, verify) before any ",[77,549,550],{},"npm publish",". The release workflow (see ",[27,553,555],{"href":554},"release","Release",") relies on this hook to block bad publishes.",[33,558,560],{"id":559},"_5-running-the-renderer-tests","5. Running the renderer tests",[23,562,563,564,566],{},"The renderer has its own test setup inside ",[77,565,124],{},". Run them from that directory:",[134,568,570],{"className":136,"code":569,"language":138,"meta":139,"style":139},"cd mdsite-nuxt\nnpm test                # Vitest (unit tests for scripts\u002F)\n# npx playwright test   # Playwright (browser-based end-to-end tests)\n",[77,571,572,581,592],{"__ignoreMap":139},[143,573,574,578],{"class":145,"line":146},[143,575,577],{"class":576},"sj4cs","cd",[143,579,580],{"class":153}," mdsite-nuxt\n",[143,582,583,585,588],{"class":145,"line":265},[143,584,150],{"class":149},[143,586,587],{"class":153}," test",[143,589,591],{"class":590},"sJ8bj","                # Vitest (unit tests for scripts\u002F)\n",[143,593,594],{"class":145,"line":271},[143,595,596],{"class":590},"# npx playwright test   # Playwright (browser-based end-to-end tests)\n",[23,598,599,600,108,603,108,606,108,609,612,613,615],{},"Renderer test files live next to their source — for example ",[77,601,602],{},"scripts\u002Fgenerate-indices.test.ts",[77,604,605],{},"scripts\u002Frenderer-hooks.test.ts",[77,607,608],{},"scripts\u002Fstart.test.ts",[77,610,611],{},"utils\u002Fbase-url.test.ts",". These tests are completely separate from the CLI suite and are not invoked by ",[77,614,120],{}," at the repo root.",[33,617,619],{"id":618},"_6-what-to-test","6. What to test",[23,621,622,623,625],{},"When adding or changing CLI code, add or update the matching ",[77,624,335],{}," file:",[41,627,628,638],{},[44,629,630],{},[47,631,632,635],{},[50,633,634],{},"Change",[50,636,637],{},"Where to test",[63,639,640,657,675,685,707,717,727],{},[47,641,642,648],{},[68,643,644,645],{},"New command in ",[77,646,647],{},"src\u002Fcommands\u002F",[68,649,650,651,654,655,31],{},"New ",[77,652,653],{},"src\u002Fcommands\u002F\u003Cname>.test.ts"," plus a dispatch case in ",[77,656,376],{},[47,658,659,665],{},[68,660,650,661,664],{},[77,662,663],{},"mdsite.yml"," field",[68,666,667,670,671,674],{},[77,668,669],{},"src\u002Fconfig\u002Fmdsite-config.test.ts"," (parsing) and ",[77,672,673],{},"src\u002Fconfig\u002Fdefault-mdsite-config.test.ts"," (defaults).",[47,676,677,680],{},[68,678,679],{},"New menu shape",[68,681,682,31],{},[77,683,684],{},"src\u002Fconfig\u002Fmenu.test.ts",[47,686,687,693],{},[68,688,650,689,692],{},[77,690,691],{},"footer:"," config",[68,694,695,670,697,699,700,703,704,31],{},[77,696,669],{},[77,698,673],{}," (defaults). The renderer's ",[77,701,702],{},"_footer.json"," generation and menu dedup are covered by ",[77,705,706],{},"mdsite-nuxt\u002Fscripts\u002Fgenerate-indices.test.ts",[47,708,709,712],{},[68,710,711],{},"New runtime-state behaviour",[68,713,714,31],{},[77,715,716],{},"src\u002Fprocess\u002Fruntime-state.test.ts",[47,718,719,722],{},[68,720,721],{},"New renderer preparation step",[68,723,724,31],{},[77,725,726],{},"src\u002Frenderer\u002Fmdsite-nuxt.test.ts",[47,728,729,732],{},[68,730,731],{},"New GitHub workflow template",[68,733,734,31],{},[77,735,736],{},"src\u002Fcommands\u002Fworkflows.test.ts",[23,738,739],{},"Aim for the same style as existing tests: mock sibling modules, drive behaviour via inputs (argv, config objects, temp dirs), and assert on outputs and side effects rather than internal state.",[741,742],"hr",{},[744,745,747],"markdown-alert",{"type":746},"tip",[23,748,749,751,752,754,755,757],{},[77,750,120],{}," at the repo root only covers the CLI. If you change code under ",[77,753,124],{},", remember to also run ",[77,756,120],{}," inside that directory before pushing.",[759,760,761],"style",{},"html pre.shiki code .sScJk, html code.shiki .sScJk{--shiki-default:#6F42C1;--shiki-dark:#B392F0}html pre.shiki code .sZZnC, html code.shiki .sZZnC{--shiki-default:#032F62;--shiki-dark:#9ECBFF}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);}html pre.shiki code .szBVR, html code.shiki .szBVR{--shiki-default:#D73A49;--shiki-dark:#F97583}html pre.shiki code .sVt8B, html code.shiki .sVt8B{--shiki-default:#24292E;--shiki-dark:#E1E4E8}html pre.shiki code .sj4cs, html code.shiki .sj4cs{--shiki-default:#005CC5;--shiki-dark:#79B8FF}html pre.shiki code .sJ8bj, html code.shiki .sJ8bj{--shiki-default:#6A737D;--shiki-dark:#6A737D}",{"title":139,"searchDepth":265,"depth":265,"links":763},[764,765,768,772,773,774],{"id":35,"depth":265,"text":36},{"id":128,"depth":265,"text":129,"children":766},[767],{"id":195,"depth":271,"text":196},{"id":224,"depth":265,"text":225,"children":769},[770,771],{"id":354,"depth":271,"text":355},{"id":369,"depth":271,"text":370},{"id":452,"depth":265,"text":453},{"id":559,"depth":265,"text":560},{"id":618,"depth":265,"text":619},"This page covers how to run the MDsite test suites, how they are configured, and what to add when you change code. For the broader contributor picture, start at Developing mdsite.","md",{},"\u002Fdevelop\u002Ftests",{"title":14,"description":775},"develop\u002Ftests","cqA_lD6bbEXSXm9eSF3MUJwzNQKpIgBF3vLZzVo_L6I",1784017753070]