[{"data":1,"prerenderedAt":-1},["ShallowReactive",2],{"skill:01M6CPA7E7FE5TKHABNEM840VC":3,"skill-same:01M6CPA7E7FE5TKHABNEM840VC":65},{"skillNo":4,"name":5,"nameZh":6,"description":7,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":16,"categories":17,"keywords":28,"publishedAt":35,"linkOnly":36,"markdownContent":37,"markdownBody":38,"frontMatter":39,"refName":42,"commitSha":43,"skillDirectory":44,"forksCount":45,"repositoryHtmlUrl":46,"repositoryHomepageUrl":47,"ownerHtmlUrl":48,"ownerBlogUrl":49,"ownerDescription":50,"updatedAt":51,"install":52,"resources":64},"01M6CPA7E7FE5TKHABNEM840VC","writing-tests",null,"Use when writing or reviewing tests, or deciding whether a change needs one.","bholmesdev\u002Fhubble.md","bholmesdev","https:\u002F\u002Favatars.githubusercontent.com\u002Fu\u002F51384119?v=4","01M6CP8SJ3N9W8FYVCGV41QT3G","Ben Holmes","01M6CP8SP3A84K1TFMVS8BEBDW",1524,"MIT","https:\u002F\u002Fgithub.com\u002Fbholmesdev\u002Fhubble.md\u002Fblob\u002Fmain\u002F.agents\u002Fskills\u002Fwriting-tests\u002FSKILL.md",[18,23],{"categoryNo":19,"categoryName":20,"taxonomyType":21,"taxonomyTypeDesc":22},"01JBSEEDCAT000000000000018","写作与文案","SCENARIO","使用场景",{"categoryNo":24,"categoryName":25,"taxonomyType":26,"taxonomyTypeDesc":27},"01JBSEEDCAT000000000000036","内容创作者","PROFESSION","适用职业",[29,30,31,32,33,34],"writing","writing tests","tests","change","use","true","2026-11-10 10:12:30",false,"---\nname: writing-tests\ndescription: Use when writing or reviewing tests, or deciding whether a change needs one.\nmetadata:\n  internal: true\n---\n\n# Writing tests\n\nShape coverage like Kent C. Dodds' testing trophy: static checks at the base, a thin layer of unit tests, most confidence from integration tests, and the smallest budget for end-to-end tests.\n\n## When a test is warranted\n\nA test is warranted when it catches a plausible regression that nothing else would. Prove it before keeping the test:\n\n1. Revert the source change, keeping the test. Run it and confirm it goes red.\n2. Read the failure. It should fail on the behavior in the test's name, not on a changed signature, a missing helper, or a setup error.\n3. If the change touches several behaviors, revert each one separately and confirm a test goes red for each.\n\nA test that stays green on revert, or goes red for the wrong reason, is coupled to the code's shape instead of its behavior. Rewrite its assertion or delete it.\n\nGood candidates: bug fixes, tricky UI behavior (focus handoff, keyboard flows, races between async work and navigation), and edge cases in pure logic (Markdown parsing and serialization, paths, rename rules). Glue code, prop pass-through, and variations of an already-covered behavior get no test of their own.\n\n## Pick the layer\n\n| Layer | Here | Use for |\n|---|---|---|\n| Static | Biome, tsc, `pnpm check:react-compiler` | Anything a type or lint rule can catch |\n| Unit | Vitest on pure functions | Parsers, serializers, path logic |\n| Integration (default) | Vitest + happy-dom, rendering real components against real stores | UI behavior, store actions |\n| End-to-end | Playwright driving Electron | A few core flows where a break loses users' work: opening, editing, and saving notes; creating and renaming files |\n\nhappy-dom has no layout. Verify sticky, scroll, overflow, and positioning by hand with the `test-desktop-app` skill and put a screenshot in the PR, rather than asserting DOM structure. Most changes get no end-to-end test.\n\n## Mock at the seam\n\nFake only what crosses a process or time boundary: `desktopApi`\u002FIPC, the filesystem, the network, timers, `requestAnimationFrame`. Keep stores, components, and the editor real. `apps\u002Fdesktop\u002Fsrc\u002Fstore\u002Factions.test.ts` shows the pattern: a fake desktop API with the real store behind it.\n\nIf a test needs to mock internal modules to reach the behavior, move it one layer up so those modules run for real.\n\n","# Writing tests\n\nShape coverage like Kent C. Dodds' testing trophy: static checks at the base, a thin layer of unit tests, most confidence from integration tests, and the smallest budget for end-to-end tests.\n\n## When a test is warranted\n\nA test is warranted when it catches a plausible regression that nothing else would. Prove it before keeping the test:\n\n1. Revert the source change, keeping the test. Run it and confirm it goes red.\n2. Read the failure. It should fail on the behavior in the test's name, not on a changed signature, a missing helper, or a setup error.\n3. If the change touches several behaviors, revert each one separately and confirm a test goes red for each.\n\nA test that stays green on revert, or goes red for the wrong reason, is coupled to the code's shape instead of its behavior. Rewrite its assertion or delete it.\n\nGood candidates: bug fixes, tricky UI behavior (focus handoff, keyboard flows, races between async work and navigation), and edge cases in pure logic (Markdown parsing and serialization, paths, rename rules). Glue code, prop pass-through, and variations of an already-covered behavior get no test of their own.\n\n## Pick the layer\n\n| Layer | Here | Use for |\n|---|---|---|\n| Static | Biome, tsc, `pnpm check:react-compiler` | Anything a type or lint rule can catch |\n| Unit | Vitest on pure functions | Parsers, serializers, path logic |\n| Integration (default) | Vitest + happy-dom, rendering real components against real stores | UI behavior, store actions |\n| End-to-end | Playwright driving Electron | A few core flows where a break loses users' work: opening, editing, and saving notes; creating and renaming files |\n\nhappy-dom has no layout. Verify sticky, scroll, overflow, and positioning by hand with the `test-desktop-app` skill and put a screenshot in the PR, rather than asserting DOM structure. Most changes get no end-to-end test.\n\n## Mock at the seam\n\nFake only what crosses a process or time boundary: `desktopApi`\u002FIPC, the filesystem, the network, timers, `requestAnimationFrame`. Keep stores, components, and the editor real. `apps\u002Fdesktop\u002Fsrc\u002Fstore\u002Factions.test.ts` shows the pattern: a fake desktop API with the real store behind it.\n\nIf a test needs to mock internal modules to reach the behavior, move it one layer up so those modules run for real.\n\n",{"name":5,"metadata":40,"description":7},{"internal":41},true,"main","cf633b3221b9572f22604168ac77c1ffa921debe",".agents\u002Fskills\u002Fwriting-tests",99,"https:\u002F\u002Fgithub.com\u002Fbholmesdev\u002Fhubble.md","https:\u002F\u002Fhubble.md","https:\u002F\u002Fgithub.com\u002Fbholmesdev","https:\u002F\u002Fbholmes.dev","Web developer, teacher, whiteboardist. Building the next terminal @warpdotdev ","2026-11-06 02:02:39",{"source":8,"skillName":5,"commands":53,"prompt":63,"zipAvailable":41},[54,57,60],{"tool":55,"command":56},"npx","npx skills add bholmesdev\u002Fhubble.md --skill writing-tests",{"tool":58,"command":59},"bunx","bunx skills add bholmesdev\u002Fhubble.md --skill writing-tests",{"tool":61,"command":62},"pnpm","pnpm dlx skills add bholmesdev\u002Fhubble.md --skill writing-tests","安装技能，技能地址：https:\u002F\u002Fgithub.com\u002Fbholmesdev\u002Fhubble.md\u002Fblob\u002Fmain\u002F.agents\u002Fskills\u002Fwriting-tests\u002FSKILL.md",[],[66,83,99,119,139,164],{"skillNo":67,"name":68,"nameZh":6,"description":69,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":70,"categories":71,"keywords":75,"publishedAt":35},"01M6PQAVS5DTTVEV9VA313YSYM","review-markdown-comments","Reply to, resolve, or reopen Hubble review comments stored as CriticMarkup in Markdown files. Use when asked to address a Hubble review comment.","https:\u002F\u002Fgithub.com\u002Fbholmesdev\u002Fhubble.md\u002Fblob\u002Fmain\u002F.agents\u002Fskills\u002Freview-markdown-comments\u002FSKILL.md",[72],{"categoryNo":73,"categoryName":74,"taxonomyType":21,"taxonomyTypeDesc":22},"01JBSEEDCAT000000000000002","文档处理",[76,77,78,79,80,81,82,33],"review markdown comments","comments","review","markdown","as","files","reply",{"skillNo":84,"name":85,"nameZh":6,"description":86,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":87,"categories":88,"keywords":89,"publishedAt":35},"01M6PQAH29FCQB6921Y94CA8F4","merge-risk","Use when assessing the risk of merging a PR","https:\u002F\u002Fgithub.com\u002Fbholmesdev\u002Fhubble.md\u002Fblob\u002Fmain\u002F.agents\u002Fskills\u002Fmerge-risk\u002FSKILL.md",[],[90,91,92,93,94,95,96,97,34,98,33,42],"merge","risk","merge risk","github","blob","pr","https","engineering","matt pocock",{"skillNo":100,"name":101,"nameZh":6,"description":102,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":103,"categories":104,"keywords":109,"publishedAt":35},"01M6PQA0ADFHMANPW4KRAV7EA6","embed-html-app","Embed a Hubble HTML App inline inside a Markdown File using an iframe block. Use when an app should be viewed in the context of a document.","https:\u002F\u002Fgithub.com\u002Fbholmesdev\u002Fhubble.md\u002Fblob\u002Fmain\u002F.agents\u002Fskills\u002Fembed-html-app\u002FSKILL.md",[105,106],{"categoryNo":73,"categoryName":74,"taxonomyType":21,"taxonomyTypeDesc":22},{"categoryNo":107,"categoryName":108,"taxonomyType":21,"taxonomyTypeDesc":22},"01JBSEEDCAT000000000000009","前端开发",[110,111,112,113,114,115,79,116,33,117,118],"html","app","embed","embed html app","inline","file","context","document","block",{"skillNo":120,"name":121,"nameZh":6,"description":122,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":123,"categories":124,"keywords":125,"publishedAt":35},"01M6PQ9WQM5HF4QQ1J750346NY","edit-diagram","Create or edit a Hubble diagram: a .tldr file Hubble opens in its built-in tldraw canvas. With this skill installed, any diagram, canvas, whiteboard, or .tldr file is a Hubble diagram: use this skill instead of tldraw-offline, unless the user names the tldraw offline app or the file is a .tldraw archive.","https:\u002F\u002Fgithub.com\u002Fbholmesdev\u002Fhubble.md\u002Fblob\u002Fmain\u002F.agents\u002Fskills\u002Fedit-diagram\u002FSKILL.md",[],[126,127,128,33,129,130,131,132,115,133,134,135,136,111,137,138],"edit diagram","edit","diagram","whiteboard","canvas","create","offline","tldraw offline","archive","tldr","user","tldraw","with",{"skillNo":140,"name":141,"nameZh":6,"description":142,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":143,"categories":144,"keywords":149,"publishedAt":35},"01M6PQ9JKMF9ZDQWNVSA38VCTT","create-html-app","Create or update a Hubble HTML App: a folder-local .html file Hubble runs as a self-contained interactive UI, with Alpine, Tailwind, Hubble theme tokens, and the injected hubble runtime API.","https:\u002F\u002Fgithub.com\u002Fbholmesdev\u002Fhubble.md\u002Fblob\u002Fmain\u002F.agents\u002Fskills\u002Fcreate-html-app\u002FSKILL.md",[145,146],{"categoryNo":107,"categoryName":108,"taxonomyType":21,"taxonomyTypeDesc":22},{"categoryNo":147,"categoryName":148,"taxonomyType":21,"taxonomyTypeDesc":22},"01JBSEEDCAT000000000000010","后端与接口",[150,111,110,131,151,152,153,154,155,156,157,158,159,160,115,161,80,162,138,163],"create html app","ui","theme tokens","runtime","tokens","local","self","interactive","Tailwind","api","alpine","update","theme","folder",{"skillNo":165,"name":166,"nameZh":6,"description":167,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":168,"categories":169,"keywords":170,"publishedAt":35},"01M6CPAR6ECFN9P9H1S4XWDCB1","convex","Routes general Convex requests to the right project skill. Use when the user asks which Convex skill to use or gives an underspecified Convex app task.","https:\u002F\u002Fgithub.com\u002Fbholmesdev\u002Fhubble.md\u002Fblob\u002Fmain\u002Fpackages\u002Fsync-backend\u002F.agents\u002Fskills\u002Fconvex\u002FSKILL.md",[],[166,171,172,173,33,136],"task","general","project skill"]