@claude-collective/cli 0.8.0 → 0.13.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +127 -0
- package/README.md +26 -9
- package/dist/{chunk-TOPAIL5W.js → chunk-3U3R4NCG.js} +2 -2
- package/dist/chunk-3U3R4NCG.js.map +1 -0
- package/dist/{chunk-YKXBGCFD.js → chunk-57Y5RALO.js} +10 -10
- package/dist/chunk-57Y5RALO.js.map +1 -0
- package/dist/{chunk-ED73HCW2.js → chunk-6DCSSORF.js} +37 -88
- package/dist/chunk-6DCSSORF.js.map +1 -0
- package/dist/{chunk-6LS7XO3H.js → chunk-6Q3Y7KVB.js} +2 -2
- package/dist/chunk-6Q3Y7KVB.js.map +1 -0
- package/dist/{chunk-A3J6IAXK.js → chunk-76DWXGQE.js} +4 -2
- package/dist/chunk-76DWXGQE.js.map +1 -0
- package/dist/{chunk-Q6DR5QUH.js → chunk-7Q44DMSP.js} +62 -27
- package/dist/chunk-7Q44DMSP.js.map +1 -0
- package/dist/chunk-ACNBKXXJ.js +321 -0
- package/dist/chunk-ACNBKXXJ.js.map +1 -0
- package/dist/{chunk-QGGSLMO3.js → chunk-B7CCVP6Q.js} +41 -9
- package/dist/chunk-B7CCVP6Q.js.map +1 -0
- package/dist/{chunk-LVKRVFYR.js → chunk-BDLUZVKU.js} +2 -2
- package/dist/chunk-BDLUZVKU.js.map +1 -0
- package/dist/{chunk-HNDT5QRB.js → chunk-CDX4W4DM.js} +3 -3
- package/dist/chunk-CDX4W4DM.js.map +1 -0
- package/dist/{chunk-MYAVQ23U.js → chunk-CJEHB4TB.js} +23 -9
- package/dist/chunk-CJEHB4TB.js.map +1 -0
- package/dist/{chunk-DKGL77IY.js → chunk-CPZOTVCI.js} +15 -14
- package/dist/chunk-CPZOTVCI.js.map +1 -0
- package/dist/{chunk-K7PTOVX4.js → chunk-D237EVNB.js} +32 -3
- package/dist/chunk-D237EVNB.js.map +1 -0
- package/dist/{chunk-Q2LH2DAB.js → chunk-DRXPNNPB.js} +19 -18
- package/dist/chunk-DRXPNNPB.js.map +1 -0
- package/dist/{chunk-Y3V43XCU.js → chunk-E3FJH4TF.js} +12 -8
- package/dist/chunk-E3FJH4TF.js.map +1 -0
- package/dist/{chunk-3HBTELJN.js → chunk-ED4E6Q2T.js} +10 -10
- package/dist/chunk-ED4E6Q2T.js.map +1 -0
- package/dist/{chunk-SYQ7R2JO.js → chunk-EHS3TWWP.js} +3 -3
- package/dist/chunk-EHS3TWWP.js.map +1 -0
- package/dist/{chunk-LQTST4WY.js → chunk-GDH553MV.js} +6 -6
- package/dist/chunk-GDH553MV.js.map +1 -0
- package/dist/{chunk-A65SBAAJ.js → chunk-HLJX2FTL.js} +31 -5
- package/dist/chunk-HLJX2FTL.js.map +1 -0
- package/dist/chunk-I2DSLOXZ.js +75 -0
- package/dist/chunk-I2DSLOXZ.js.map +1 -0
- package/dist/{chunk-SEBPPFUW.js → chunk-I4TPKIYX.js} +33 -18
- package/dist/chunk-I4TPKIYX.js.map +1 -0
- package/dist/{chunk-NGBFJJ7Q.js → chunk-IMDW5ZUP.js} +19 -11
- package/dist/chunk-IMDW5ZUP.js.map +1 -0
- package/dist/{chunk-U4VYHKPM.js → chunk-JIPWV2FX.js} +6 -6
- package/dist/chunk-JIPWV2FX.js.map +1 -0
- package/dist/{chunk-G2FBJOZG.js → chunk-K7EVM5LY.js} +4 -4
- package/dist/chunk-K7EVM5LY.js.map +1 -0
- package/dist/{chunk-MJSFR562.js → chunk-KAAEN2PO.js} +3 -3
- package/dist/chunk-KAAEN2PO.js.map +1 -0
- package/dist/{chunk-FNOYEXUE.js → chunk-LE6IY6IT.js} +22 -17
- package/dist/chunk-LE6IY6IT.js.map +1 -0
- package/dist/{chunk-CIY5UBRB.js → chunk-NDY25DTL.js} +6 -6
- package/dist/chunk-NDY25DTL.js.map +1 -0
- package/dist/{chunk-OLBOTK3O.js → chunk-P26A2K5N.js} +7 -7
- package/dist/chunk-P26A2K5N.js.map +1 -0
- package/dist/{chunk-DHFFRMF6.js → chunk-RTE64SJA.js} +2 -2
- package/dist/chunk-RTE64SJA.js.map +1 -0
- package/dist/{chunk-3ZCB5K33.js → chunk-SGJ23HIP.js} +14 -11
- package/dist/chunk-SGJ23HIP.js.map +1 -0
- package/dist/{chunk-C4ZTIYFR.js → chunk-SVYPSDWY.js} +10 -10
- package/dist/chunk-SVYPSDWY.js.map +1 -0
- package/dist/{chunk-MMDXNZPF.js → chunk-TKFPKEV3.js} +2 -2
- package/dist/chunk-TKFPKEV3.js.map +1 -0
- package/dist/{chunk-M7YCPFIX.js → chunk-UQTEPWU7.js} +2 -2
- package/dist/chunk-UQTEPWU7.js.map +1 -0
- package/dist/{chunk-QESUUPOE.js → chunk-V46GGCCI.js} +80 -27
- package/dist/chunk-V46GGCCI.js.map +1 -0
- package/dist/{chunk-UOWHJ6BE.js → chunk-X6QONICW.js} +6 -3
- package/dist/chunk-X6QONICW.js.map +1 -0
- package/dist/chunk-Y2LW7R3Y.js +23 -0
- package/dist/chunk-Y2LW7R3Y.js.map +1 -0
- package/dist/chunk-Z2CWURZ6.js +78 -0
- package/dist/chunk-Z2CWURZ6.js.map +1 -0
- package/dist/chunk-Z7G4B5HJ.js +377 -0
- package/dist/chunk-Z7G4B5HJ.js.map +1 -0
- package/dist/{chunk-XKEG3SCV.js → chunk-ZENYS6KW.js} +13 -9
- package/dist/chunk-ZENYS6KW.js.map +1 -0
- package/dist/{cli-v2 → cli}/defaults/agent-mappings.yaml +5 -5
- package/dist/commands/build/marketplace.js +9 -9
- package/dist/commands/build/marketplace.js.map +1 -1
- package/dist/commands/build/plugins.js +12 -12
- package/dist/commands/build/plugins.js.map +1 -1
- package/dist/commands/build/stack.js +15 -15
- package/dist/commands/build/stack.js.map +1 -1
- package/dist/commands/compile.js +21 -21
- package/dist/commands/compile.js.map +1 -1
- package/dist/commands/config/get.js +9 -8
- package/dist/commands/config/get.js.map +1 -1
- package/dist/commands/config/index.js +7 -6
- package/dist/commands/config/index.js.map +1 -1
- package/dist/commands/config/path.js +8 -7
- package/dist/commands/config/path.js.map +1 -1
- package/dist/commands/config/set-project.js +9 -8
- package/dist/commands/config/set-project.js.map +1 -1
- package/dist/commands/config/set.js +9 -8
- package/dist/commands/config/set.js.map +1 -1
- package/dist/commands/config/show.js +6 -5
- package/dist/commands/config/unset-project.js +9 -8
- package/dist/commands/config/unset-project.js.map +1 -1
- package/dist/commands/config/unset.js +9 -8
- package/dist/commands/config/unset.js.map +1 -1
- package/dist/commands/diff.js +12 -12
- package/dist/commands/diff.js.map +1 -1
- package/dist/commands/doctor.js +10 -10
- package/dist/commands/doctor.js.map +1 -1
- package/dist/commands/edit.js +52 -49
- package/dist/commands/edit.js.map +1 -1
- package/dist/commands/eject.js +180 -97
- package/dist/commands/eject.js.map +1 -1
- package/dist/commands/import/skill.js +339 -0
- package/dist/commands/import/skill.js.map +1 -0
- package/dist/commands/info.js +9 -9
- package/dist/commands/info.js.map +1 -1
- package/dist/commands/init.js +205 -78
- package/dist/commands/init.js.map +1 -1
- package/dist/commands/list.js +9 -9
- package/dist/commands/list.js.map +1 -1
- package/dist/commands/new/agent.js +19 -21
- package/dist/commands/new/agent.js.map +1 -1
- package/dist/commands/new/skill.js +11 -12
- package/dist/commands/new/skill.js.map +1 -1
- package/dist/commands/outdated.js +12 -12
- package/dist/commands/outdated.js.map +1 -1
- package/dist/commands/search.js +205 -17
- package/dist/commands/search.js.map +1 -1
- package/dist/commands/test-imports.js +18 -18
- package/dist/commands/test-imports.js.map +1 -1
- package/dist/commands/uninstall.js +58 -78
- package/dist/commands/uninstall.js.map +1 -1
- package/dist/commands/update.js +21 -21
- package/dist/commands/update.js.map +1 -1
- package/dist/commands/validate.js +9 -9
- package/dist/commands/validate.js.map +1 -1
- package/dist/commands/version/bump.js +8 -8
- package/dist/commands/version/bump.js.map +1 -1
- package/dist/commands/version/index.js +8 -8
- package/dist/commands/version/index.js.map +1 -1
- package/dist/commands/version/set.js +7 -7
- package/dist/commands/version/set.js.map +1 -1
- package/dist/commands/version/show.js +8 -8
- package/dist/commands/version/show.js.map +1 -1
- package/dist/components/common/confirm.js +1 -1
- package/dist/components/common/message.js +1 -1
- package/dist/components/common/message.js.map +1 -1
- package/dist/components/common/spinner.js +1 -1
- package/dist/components/common/spinner.js.map +1 -1
- package/dist/components/skill-search/skill-search.js +10 -0
- package/dist/components/skill-search/skill-search.js.map +1 -0
- package/dist/components/wizard/category-grid.js +1 -1
- package/dist/components/wizard/category-grid.test.js +213 -80
- package/dist/components/wizard/category-grid.test.js.map +1 -1
- package/dist/components/wizard/section-progress.js +1 -1
- package/dist/components/wizard/section-progress.test.js +2 -2
- package/dist/components/wizard/section-progress.test.js.map +1 -1
- package/dist/components/wizard/step-approach.js +4 -3
- package/dist/components/wizard/step-build.js +4 -3
- package/dist/components/wizard/step-build.test.js +29 -17
- package/dist/components/wizard/step-build.test.js.map +1 -1
- package/dist/components/wizard/step-confirm.js +2 -1
- package/dist/components/wizard/step-refine.js +2 -1
- package/dist/components/wizard/step-refine.test.js +4 -3
- package/dist/components/wizard/step-refine.test.js.map +1 -1
- package/dist/components/wizard/step-stack-options.js +3 -3
- package/dist/components/wizard/step-stack.js +3 -3
- package/dist/components/wizard/wizard-footer.js +9 -0
- package/dist/components/wizard/wizard-footer.js.map +1 -0
- package/dist/components/wizard/wizard-tabs.js +1 -1
- package/dist/components/wizard/wizard.js +14 -12
- package/dist/hooks/init.js +5 -4
- package/dist/hooks/init.js.map +1 -1
- package/dist/index.js +1 -1
- package/dist/index.js.map +1 -1
- package/dist/stores/wizard-store.js +2 -2
- package/dist/stores/wizard-store.test.js +3 -3
- package/dist/stores/wizard-store.test.js.map +1 -1
- package/package.json +3 -2
- package/dist/chunk-3HBTELJN.js.map +0 -1
- package/dist/chunk-3ZCB5K33.js.map +0 -1
- package/dist/chunk-6LS7XO3H.js.map +0 -1
- package/dist/chunk-A3J6IAXK.js.map +0 -1
- package/dist/chunk-A65SBAAJ.js.map +0 -1
- package/dist/chunk-ALEPJ6YN.js +0 -80
- package/dist/chunk-ALEPJ6YN.js.map +0 -1
- package/dist/chunk-C4ZTIYFR.js.map +0 -1
- package/dist/chunk-CIY5UBRB.js.map +0 -1
- package/dist/chunk-DHFFRMF6.js.map +0 -1
- package/dist/chunk-DKGL77IY.js.map +0 -1
- package/dist/chunk-ED73HCW2.js.map +0 -1
- package/dist/chunk-FNOYEXUE.js.map +0 -1
- package/dist/chunk-G2FBJOZG.js.map +0 -1
- package/dist/chunk-HNDT5QRB.js.map +0 -1
- package/dist/chunk-K7PTOVX4.js.map +0 -1
- package/dist/chunk-LQTST4WY.js.map +0 -1
- package/dist/chunk-LVKRVFYR.js.map +0 -1
- package/dist/chunk-M7YCPFIX.js.map +0 -1
- package/dist/chunk-MJSFR562.js.map +0 -1
- package/dist/chunk-MMDXNZPF.js.map +0 -1
- package/dist/chunk-MYAVQ23U.js.map +0 -1
- package/dist/chunk-NGBFJJ7Q.js.map +0 -1
- package/dist/chunk-OLBOTK3O.js.map +0 -1
- package/dist/chunk-PPNTD5LO.js +0 -330
- package/dist/chunk-PPNTD5LO.js.map +0 -1
- package/dist/chunk-Q2LH2DAB.js.map +0 -1
- package/dist/chunk-Q6DR5QUH.js.map +0 -1
- package/dist/chunk-QESUUPOE.js.map +0 -1
- package/dist/chunk-QGGSLMO3.js.map +0 -1
- package/dist/chunk-SEBPPFUW.js.map +0 -1
- package/dist/chunk-SYQ7R2JO.js.map +0 -1
- package/dist/chunk-TOPAIL5W.js.map +0 -1
- package/dist/chunk-U4VYHKPM.js.map +0 -1
- package/dist/chunk-UOWHJ6BE.js.map +0 -1
- package/dist/chunk-XKEG3SCV.js.map +0 -1
- package/dist/chunk-Y3V43XCU.js.map +0 -1
- package/dist/chunk-YKXBGCFD.js.map +0 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../src/cli-v2/components/wizard/category-grid.test.tsx"],"sourcesContent":["/**\n * Tests for the CategoryGrid component.\n *\n * Tests 2D grid rendering and keyboard navigation for wizard Build step.\n */\nimport React from \"react\";\nimport { render } from \"ink-testing-library\";\nimport { describe, expect, it, afterEach, vi } from \"vitest\";\nimport {\n CategoryGrid,\n type CategoryGridProps,\n type CategoryRow,\n type CategoryOption,\n} from \"./category-grid\";\nimport {\n ARROW_UP,\n ARROW_DOWN,\n ARROW_LEFT,\n ARROW_RIGHT,\n TAB,\n RENDER_DELAY_MS,\n INPUT_DELAY_MS,\n delay,\n} from \"../../lib/__tests__/test-constants\";\n\n// =============================================================================\n// Test Fixtures\n// =============================================================================\n\nconst createOption = (\n id: string,\n label: string,\n overrides: Partial<CategoryOption> = {},\n): CategoryOption => ({\n id,\n label,\n state: \"normal\",\n selected: false,\n ...overrides,\n});\n\nconst createCategory = (\n id: string,\n name: string,\n options: CategoryOption[],\n overrides: Partial<CategoryRow> = {},\n): CategoryRow => ({\n id,\n name,\n required: false,\n exclusive: true,\n options,\n ...overrides,\n});\n\nconst defaultCategories: CategoryRow[] = [\n createCategory(\n \"framework\",\n \"Framework\",\n [\n createOption(\"react\", \"react\", {\n state: \"recommended\",\n stateReason: \"Popular choice\",\n }),\n createOption(\"vue\", \"vue\"),\n createOption(\"angular\", \"angular\"),\n createOption(\"svelte\", \"svelte\"),\n ],\n { required: true },\n ),\n createCategory(\n \"styling\",\n \"Styling\",\n [\n createOption(\"scss-mod\", \"scss-mod\", { selected: true }),\n createOption(\"tailwind\", \"tailwind\", { state: \"recommended\" }),\n createOption(\"styled\", \"styled\"),\n createOption(\"vanilla\", \"vanilla\"),\n ],\n { required: true },\n ),\n createCategory(\"client-state\", \"Client State\", [\n createOption(\"zustand\", \"zustand\", { state: \"recommended\" }),\n createOption(\"jotai\", \"jotai\"),\n createOption(\"redux\", \"redux\", {\n state: \"discouraged\",\n stateReason: \"Complex for most apps\",\n }),\n createOption(\"mobx\", \"mobx\"),\n ]),\n createCategory(\"server-state\", \"Server State\", [\n createOption(\"react-query\", \"react-query\", { selected: true }),\n createOption(\"swr\", \"swr\"),\n createOption(\"apollo\", \"apollo\"),\n ]),\n createCategory(\"analytics\", \"Analytics\", [\n createOption(\"posthog\", \"posthog\"),\n ]),\n];\n\nconst defaultProps: CategoryGridProps = {\n categories: defaultCategories,\n focusedRow: 0,\n focusedCol: 0,\n showDescriptions: false,\n expertMode: false,\n onToggle: vi.fn(),\n onFocusChange: vi.fn(),\n onToggleDescriptions: vi.fn(),\n onToggleExpertMode: vi.fn(),\n};\n\nconst renderGrid = (props: Partial<CategoryGridProps> = {}) => {\n return render(<CategoryGrid {...defaultProps} {...props} />);\n};\n\n// =============================================================================\n// Tests\n// =============================================================================\n\ndescribe(\"CategoryGrid component\", () => {\n let cleanup: (() => void) | undefined;\n\n afterEach(() => {\n cleanup?.();\n cleanup = undefined;\n vi.clearAllMocks();\n });\n\n // ===========================================================================\n // Basic Rendering\n // ===========================================================================\n\n describe(\"rendering\", () => {\n it(\"should render all categories\", () => {\n const { lastFrame, unmount } = renderGrid();\n cleanup = unmount;\n\n const output = lastFrame();\n expect(output).toContain(\"Framework\");\n expect(output).toContain(\"Styling\");\n expect(output).toContain(\"Client State\");\n expect(output).toContain(\"Server State\");\n expect(output).toContain(\"Analytics\");\n });\n\n it(\"should render all options in each category\", () => {\n const { lastFrame, unmount } = renderGrid();\n cleanup = unmount;\n\n const output = lastFrame();\n // Framework options\n expect(output).toContain(\"react\");\n expect(output).toContain(\"vue\");\n expect(output).toContain(\"angular\");\n expect(output).toContain(\"svelte\");\n // Styling options\n expect(output).toContain(\"scss-mod\");\n expect(output).toContain(\"tailwind\");\n });\n\n it(\"should show required indicator (*) for required categories\", () => {\n const { lastFrame, unmount } = renderGrid();\n cleanup = unmount;\n\n const output = lastFrame();\n // Framework and Styling are required\n // The * should appear after their names\n expect(output).toContain(\"*\");\n });\n\n it(\"should show (optional) for non-required categories\", () => {\n const { lastFrame, unmount } = renderGrid();\n cleanup = unmount;\n\n const output = lastFrame();\n expect(output).toContain(\"(optional)\");\n });\n\n it(\"should render legend row\", () => {\n const { lastFrame, unmount } = renderGrid();\n cleanup = unmount;\n\n const output = lastFrame();\n expect(output).toContain(\"Legend:\");\n expect(output).toContain(\"selected\");\n expect(output).toContain(\"recommended\");\n expect(output).toContain(\"discouraged\");\n expect(output).toContain(\"disabled\");\n });\n\n it(\"should render header with toggle hints\", () => {\n const { lastFrame, unmount } = renderGrid();\n cleanup = unmount;\n\n const output = lastFrame();\n expect(output).toContain(\"[Tab] Show descriptions\");\n expect(output).toContain(\"[e] Expert Mode\");\n });\n\n it(\"should handle empty categories array\", () => {\n const { lastFrame, unmount } = renderGrid({ categories: [] });\n cleanup = unmount;\n\n const output = lastFrame();\n expect(output).toContain(\"No categories to display\");\n });\n });\n\n // ===========================================================================\n // Visual States\n // ===========================================================================\n\n describe(\"visual states\", () => {\n it(\"should show selected symbol for selected options\", () => {\n const { lastFrame, unmount } = renderGrid();\n cleanup = unmount;\n\n const output = lastFrame();\n // scss-mod and react-query are selected\n // Should contain filled circles (●)\n expect(output).toContain(\"\\u25CF\"); // ●\n });\n\n it(\"should show unselected symbol for unselected options\", () => {\n const { lastFrame, unmount } = renderGrid();\n cleanup = unmount;\n\n const output = lastFrame();\n // Should contain empty circles (○)\n expect(output).toContain(\"\\u25CB\"); // ○\n });\n\n it(\"should show recommended indicator for recommended options\", () => {\n const { lastFrame, unmount } = renderGrid();\n cleanup = unmount;\n\n const output = lastFrame();\n // Should contain star (⭐) for recommended\n expect(output).toContain(\"\\u2B50\");\n });\n\n it(\"should show discouraged indicator for discouraged options\", () => {\n const { lastFrame, unmount } = renderGrid();\n cleanup = unmount;\n\n const output = lastFrame();\n // Should contain warning (⚠) for discouraged\n expect(output).toContain(\"\\u26A0\");\n });\n\n it(\"should show disabled symbol for disabled options\", () => {\n const categories: CategoryRow[] = [\n createCategory(\"test\", \"Test\", [\n createOption(\"opt1\", \"Option 1\"),\n createOption(\"opt2\", \"Option 2\", { state: \"disabled\" }),\n ]),\n ];\n\n const { lastFrame, unmount } = renderGrid({ categories });\n cleanup = unmount;\n\n const output = lastFrame();\n // Should contain X (✗) for disabled\n expect(output).toContain(\"\\u2717\");\n });\n });\n\n // ===========================================================================\n // Focus Indicator\n // ===========================================================================\n\n describe(\"focus indicator\", () => {\n it(\"should show focus indicator (>) on focused option\", () => {\n const { lastFrame, unmount } = renderGrid({\n focusedRow: 0,\n focusedCol: 0,\n });\n cleanup = unmount;\n\n const output = lastFrame();\n expect(output).toContain(\">\");\n });\n\n it(\"should update focus indicator when focusedRow changes\", () => {\n const { lastFrame: frame1, unmount: unmount1 } = renderGrid({\n focusedRow: 0,\n focusedCol: 0,\n });\n const output1 = frame1();\n unmount1();\n\n const { lastFrame: frame2, unmount: unmount2 } = renderGrid({\n focusedRow: 1,\n focusedCol: 0,\n });\n cleanup = unmount2;\n const output2 = frame2();\n\n // Both should have focus indicator, but in different rows\n expect(output1).toContain(\">\");\n expect(output2).toContain(\">\");\n });\n\n it(\"should highlight focused category name\", () => {\n const { lastFrame, unmount } = renderGrid({\n focusedRow: 1,\n focusedCol: 0,\n });\n cleanup = unmount;\n\n const output = lastFrame();\n // Styling row should be focused (index 1)\n expect(output).toContain(\"Styling\");\n });\n });\n\n // ===========================================================================\n // Keyboard Navigation - Arrow Keys\n // ===========================================================================\n\n describe(\"keyboard navigation - arrow keys\", () => {\n it(\"should call onFocusChange when pressing left arrow\", async () => {\n const onFocusChange = vi.fn();\n const { stdin, unmount } = renderGrid({\n focusedRow: 0,\n focusedCol: 1,\n onFocusChange,\n });\n cleanup = unmount;\n\n await delay(RENDER_DELAY_MS);\n await stdin.write(ARROW_LEFT);\n await delay(INPUT_DELAY_MS);\n\n expect(onFocusChange).toHaveBeenCalledWith(0, 0);\n });\n\n it(\"should call onFocusChange when pressing right arrow\", async () => {\n const onFocusChange = vi.fn();\n const { stdin, unmount } = renderGrid({\n focusedRow: 0,\n focusedCol: 0,\n onFocusChange,\n });\n cleanup = unmount;\n\n await delay(RENDER_DELAY_MS);\n await stdin.write(ARROW_RIGHT);\n await delay(INPUT_DELAY_MS);\n\n expect(onFocusChange).toHaveBeenCalledWith(0, 1);\n });\n\n it(\"should call onFocusChange when pressing up arrow\", async () => {\n const onFocusChange = vi.fn();\n const { stdin, unmount } = renderGrid({\n focusedRow: 1,\n focusedCol: 0,\n onFocusChange,\n });\n cleanup = unmount;\n\n await delay(RENDER_DELAY_MS);\n await stdin.write(ARROW_UP);\n await delay(INPUT_DELAY_MS);\n\n expect(onFocusChange).toHaveBeenCalledWith(0, 0);\n });\n\n it(\"should call onFocusChange when pressing down arrow\", async () => {\n const onFocusChange = vi.fn();\n const { stdin, unmount } = renderGrid({\n focusedRow: 0,\n focusedCol: 0,\n onFocusChange,\n });\n cleanup = unmount;\n\n await delay(RENDER_DELAY_MS);\n await stdin.write(ARROW_DOWN);\n await delay(INPUT_DELAY_MS);\n\n expect(onFocusChange).toHaveBeenCalledWith(1, 0);\n });\n\n it(\"should wrap horizontally when pressing left at first column\", async () => {\n const onFocusChange = vi.fn();\n const { stdin, unmount } = renderGrid({\n focusedRow: 0,\n focusedCol: 0,\n onFocusChange,\n });\n cleanup = unmount;\n\n await delay(RENDER_DELAY_MS);\n await stdin.write(ARROW_LEFT);\n await delay(INPUT_DELAY_MS);\n\n // Should wrap to last column (index 3 for framework with 4 options)\n expect(onFocusChange).toHaveBeenCalledWith(0, 3);\n });\n\n it(\"should wrap horizontally when pressing right at last column\", async () => {\n const onFocusChange = vi.fn();\n const { stdin, unmount } = renderGrid({\n focusedRow: 0,\n focusedCol: 3, // Last option in framework\n onFocusChange,\n });\n cleanup = unmount;\n\n await delay(RENDER_DELAY_MS);\n await stdin.write(ARROW_RIGHT);\n await delay(INPUT_DELAY_MS);\n\n // Should wrap to first column\n expect(onFocusChange).toHaveBeenCalledWith(0, 0);\n });\n\n it(\"should wrap vertically when pressing up at first row\", async () => {\n const onFocusChange = vi.fn();\n const { stdin, unmount } = renderGrid({\n focusedRow: 0,\n focusedCol: 0,\n onFocusChange,\n });\n cleanup = unmount;\n\n await delay(RENDER_DELAY_MS);\n await stdin.write(ARROW_UP);\n await delay(INPUT_DELAY_MS);\n\n // Should wrap to last row (index 4 for 5 categories)\n expect(onFocusChange).toHaveBeenCalledWith(4, 0);\n });\n\n it(\"should wrap vertically when pressing down at last row\", async () => {\n const onFocusChange = vi.fn();\n const { stdin, unmount } = renderGrid({\n focusedRow: 4, // Last category (analytics)\n focusedCol: 0,\n onFocusChange,\n });\n cleanup = unmount;\n\n await delay(RENDER_DELAY_MS);\n await stdin.write(ARROW_DOWN);\n await delay(INPUT_DELAY_MS);\n\n // Should wrap to first row\n expect(onFocusChange).toHaveBeenCalledWith(0, 0);\n });\n });\n\n // ===========================================================================\n // Keyboard Navigation - Vim Keys\n // ===========================================================================\n\n describe(\"keyboard navigation - vim keys\", () => {\n it(\"should move left with h key\", async () => {\n const onFocusChange = vi.fn();\n const { stdin, unmount } = renderGrid({\n focusedRow: 0,\n focusedCol: 1,\n onFocusChange,\n });\n cleanup = unmount;\n\n await delay(RENDER_DELAY_MS);\n await stdin.write(\"h\");\n await delay(INPUT_DELAY_MS);\n\n expect(onFocusChange).toHaveBeenCalledWith(0, 0);\n });\n\n it(\"should move right with l key\", async () => {\n const onFocusChange = vi.fn();\n const { stdin, unmount } = renderGrid({\n focusedRow: 0,\n focusedCol: 0,\n onFocusChange,\n });\n cleanup = unmount;\n\n await delay(RENDER_DELAY_MS);\n await stdin.write(\"l\");\n await delay(INPUT_DELAY_MS);\n\n expect(onFocusChange).toHaveBeenCalledWith(0, 1);\n });\n\n it(\"should move up with k key\", async () => {\n const onFocusChange = vi.fn();\n const { stdin, unmount } = renderGrid({\n focusedRow: 1,\n focusedCol: 0,\n onFocusChange,\n });\n cleanup = unmount;\n\n await delay(RENDER_DELAY_MS);\n await stdin.write(\"k\");\n await delay(INPUT_DELAY_MS);\n\n expect(onFocusChange).toHaveBeenCalledWith(0, 0);\n });\n\n it(\"should move down with j key\", async () => {\n const onFocusChange = vi.fn();\n const { stdin, unmount } = renderGrid({\n focusedRow: 0,\n focusedCol: 0,\n onFocusChange,\n });\n cleanup = unmount;\n\n await delay(RENDER_DELAY_MS);\n await stdin.write(\"j\");\n await delay(INPUT_DELAY_MS);\n\n expect(onFocusChange).toHaveBeenCalledWith(1, 0);\n });\n });\n\n // ===========================================================================\n // Selection Toggle\n // ===========================================================================\n\n describe(\"selection toggle\", () => {\n it(\"should call onToggle when pressing space on a normal option\", async () => {\n const onToggle = vi.fn();\n const { stdin, unmount } = renderGrid({\n focusedRow: 0,\n focusedCol: 1, // vue (normal state)\n onToggle,\n });\n cleanup = unmount;\n\n await delay(RENDER_DELAY_MS);\n await stdin.write(\" \"); // Space\n await delay(INPUT_DELAY_MS);\n\n expect(onToggle).toHaveBeenCalledWith(\"framework\", \"vue\");\n });\n\n it(\"should call onToggle when pressing space on a selected option\", async () => {\n const onToggle = vi.fn();\n // Use expertMode to preserve original option order\n const { stdin, unmount } = renderGrid({\n focusedRow: 1,\n focusedCol: 0, // scss-mod (selected) - first in expert mode\n expertMode: true,\n onToggle,\n });\n cleanup = unmount;\n\n await delay(RENDER_DELAY_MS);\n await stdin.write(\" \");\n await delay(INPUT_DELAY_MS);\n\n expect(onToggle).toHaveBeenCalledWith(\"styling\", \"scss-mod\");\n });\n\n it(\"should NOT call onToggle when pressing space on a disabled option\", async () => {\n const onToggle = vi.fn();\n const categories: CategoryRow[] = [\n createCategory(\"test\", \"Test\", [\n createOption(\"opt1\", \"Option 1\", { state: \"disabled\" }),\n createOption(\"opt2\", \"Option 2\"),\n ]),\n ];\n\n // Use expertMode to preserve original option order (disabled at index 0)\n const { stdin, unmount } = renderGrid({\n categories,\n focusedRow: 0,\n focusedCol: 0, // Disabled option (first in expert mode)\n expertMode: true,\n onToggle,\n });\n cleanup = unmount;\n\n await delay(RENDER_DELAY_MS);\n await stdin.write(\" \");\n await delay(INPUT_DELAY_MS);\n\n expect(onToggle).not.toHaveBeenCalled();\n });\n });\n\n // ===========================================================================\n // Disabled Options Navigation\n // ===========================================================================\n\n describe(\"disabled options navigation\", () => {\n it(\"should skip disabled options when navigating right\", async () => {\n const onFocusChange = vi.fn();\n const categories: CategoryRow[] = [\n createCategory(\"test\", \"Test\", [\n createOption(\"opt1\", \"Option 1\"),\n createOption(\"opt2\", \"Option 2\", { state: \"disabled\" }),\n createOption(\"opt3\", \"Option 3\"),\n ]),\n ];\n\n // Use expertMode to preserve original option order\n const { stdin, unmount } = renderGrid({\n categories,\n focusedRow: 0,\n focusedCol: 0,\n expertMode: true,\n onFocusChange,\n });\n cleanup = unmount;\n\n await delay(RENDER_DELAY_MS);\n await stdin.write(ARROW_RIGHT);\n await delay(INPUT_DELAY_MS);\n\n // Should skip opt2 (disabled) and go to opt3 (index 2)\n expect(onFocusChange).toHaveBeenCalledWith(0, 2);\n });\n\n it(\"should skip disabled options when navigating left\", async () => {\n const onFocusChange = vi.fn();\n const categories: CategoryRow[] = [\n createCategory(\"test\", \"Test\", [\n createOption(\"opt1\", \"Option 1\"),\n createOption(\"opt2\", \"Option 2\", { state: \"disabled\" }),\n createOption(\"opt3\", \"Option 3\"),\n ]),\n ];\n\n // Use expertMode to preserve original option order\n const { stdin, unmount } = renderGrid({\n categories,\n focusedRow: 0,\n focusedCol: 2, // Start at opt3\n expertMode: true,\n onFocusChange,\n });\n cleanup = unmount;\n\n await delay(RENDER_DELAY_MS);\n await stdin.write(ARROW_LEFT);\n await delay(INPUT_DELAY_MS);\n\n // Should skip opt2 (disabled) and go to opt1 (index 0)\n expect(onFocusChange).toHaveBeenCalledWith(0, 0);\n });\n\n it(\"should handle all options disabled in a row\", async () => {\n const onFocusChange = vi.fn();\n const categories: CategoryRow[] = [\n createCategory(\"test\", \"Test\", [\n createOption(\"opt1\", \"Option 1\", { state: \"disabled\" }),\n createOption(\"opt2\", \"Option 2\", { state: \"disabled\" }),\n ]),\n ];\n\n // Use expertMode to preserve original option order\n const { stdin, unmount } = renderGrid({\n categories,\n focusedRow: 0,\n focusedCol: 0,\n expertMode: true,\n onFocusChange,\n });\n cleanup = unmount;\n\n await delay(RENDER_DELAY_MS);\n await stdin.write(ARROW_RIGHT);\n await delay(INPUT_DELAY_MS);\n\n // Should stay at current position when all disabled\n expect(onFocusChange).toHaveBeenCalledWith(0, 0);\n });\n });\n\n // ===========================================================================\n // Exclusive Categories\n // ===========================================================================\n\n describe(\"exclusive categories\", () => {\n it(\"should render exclusive category correctly\", () => {\n const { lastFrame, unmount } = renderGrid();\n cleanup = unmount;\n\n const output = lastFrame();\n // All default categories are exclusive, should render normally\n expect(output).toContain(\"Framework\");\n });\n\n // Note: The actual exclusivity logic (only one selection) is handled\n // by the parent component via onToggle. CategoryGrid just displays\n // the current state.\n });\n\n // ===========================================================================\n // Show Descriptions Toggle\n // ===========================================================================\n\n describe(\"show descriptions toggle\", () => {\n it(\"should call onToggleDescriptions when pressing Tab\", async () => {\n const onToggleDescriptions = vi.fn();\n const { stdin, unmount } = renderGrid({\n onToggleDescriptions,\n });\n cleanup = unmount;\n\n await delay(RENDER_DELAY_MS);\n await stdin.write(TAB);\n await delay(INPUT_DELAY_MS);\n\n expect(onToggleDescriptions).toHaveBeenCalled();\n });\n\n it(\"should show descriptions when showDescriptions is true\", () => {\n const { lastFrame, unmount } = renderGrid({ showDescriptions: true });\n cleanup = unmount;\n\n const output = lastFrame();\n // Should show state reasons\n expect(output).toContain(\"Popular choice\"); // react's stateReason\n });\n\n it(\"should hide descriptions when showDescriptions is false\", () => {\n const { lastFrame, unmount } = renderGrid({ showDescriptions: false });\n cleanup = unmount;\n\n const output = lastFrame();\n // stateReasons should not be visible (though they might still be in DOM)\n // At minimum, the display should be different\n expect(output).toBeDefined();\n });\n\n it(\"should show toggle state in header\", () => {\n const { lastFrame: frame1, unmount: unmount1 } = renderGrid({\n showDescriptions: false,\n });\n const output1 = frame1();\n unmount1();\n\n const { lastFrame: frame2, unmount: unmount2 } = renderGrid({\n showDescriptions: true,\n });\n cleanup = unmount2;\n const output2 = frame2();\n\n expect(output1).toContain(\"Show descriptions: OFF\");\n expect(output2).toContain(\"Show descriptions: ON\");\n });\n });\n\n // ===========================================================================\n // Expert Mode Toggle\n // ===========================================================================\n\n describe(\"expert mode toggle\", () => {\n it(\"should call onToggleExpertMode when pressing e\", async () => {\n const onToggleExpertMode = vi.fn();\n const { stdin, unmount } = renderGrid({\n onToggleExpertMode,\n });\n cleanup = unmount;\n\n await delay(RENDER_DELAY_MS);\n await stdin.write(\"e\");\n await delay(INPUT_DELAY_MS);\n\n expect(onToggleExpertMode).toHaveBeenCalled();\n });\n\n it(\"should call onToggleExpertMode when pressing E (uppercase)\", async () => {\n const onToggleExpertMode = vi.fn();\n const { stdin, unmount } = renderGrid({\n onToggleExpertMode,\n });\n cleanup = unmount;\n\n await delay(RENDER_DELAY_MS);\n await stdin.write(\"E\");\n await delay(INPUT_DELAY_MS);\n\n expect(onToggleExpertMode).toHaveBeenCalled();\n });\n\n it(\"should show toggle state in header\", () => {\n const { lastFrame: frame1, unmount: unmount1 } = renderGrid({\n expertMode: false,\n });\n const output1 = frame1();\n unmount1();\n\n const { lastFrame: frame2, unmount: unmount2 } = renderGrid({\n expertMode: true,\n });\n cleanup = unmount2;\n const output2 = frame2();\n\n expect(output1).toContain(\"Expert Mode: OFF\");\n expect(output2).toContain(\"Expert Mode: ON\");\n });\n });\n\n // ===========================================================================\n // Option Ordering\n // ===========================================================================\n\n describe(\"option ordering\", () => {\n it(\"should sort options by state when expertMode is false\", () => {\n // In non-expert mode, recommended should come first\n // The default categories have recommended options at various positions\n const { lastFrame, unmount } = renderGrid({ expertMode: false });\n cleanup = unmount;\n\n const output = lastFrame();\n // This test verifies the component renders - sorting happens internally\n expect(output).toBeDefined();\n });\n\n it(\"should preserve original order when expertMode is true\", () => {\n const { lastFrame, unmount } = renderGrid({ expertMode: true });\n cleanup = unmount;\n\n const output = lastFrame();\n // This test verifies the component renders with expert mode\n expect(output).toBeDefined();\n });\n });\n\n // ===========================================================================\n // Edge Cases\n // ===========================================================================\n\n describe(\"edge cases\", () => {\n it(\"should handle single category\", () => {\n const categories: CategoryRow[] = [\n createCategory(\"single\", \"Single Category\", [\n createOption(\"opt1\", \"Option 1\"),\n ]),\n ];\n\n const { lastFrame, unmount } = renderGrid({ categories });\n cleanup = unmount;\n\n const output = lastFrame();\n expect(output).toContain(\"Single Category\");\n expect(output).toContain(\"Option 1\");\n });\n\n it(\"should handle single option in category\", () => {\n const categories: CategoryRow[] = [\n createCategory(\"single\", \"Single\", [\n createOption(\"only\", \"Only Option\"),\n ]),\n ];\n\n const { lastFrame, unmount } = renderGrid({ categories });\n cleanup = unmount;\n\n const output = lastFrame();\n expect(output).toContain(\"Only Option\");\n });\n\n it(\"should handle category with many options\", () => {\n const options = Array.from({ length: 10 }, (_, i) =>\n createOption(`opt${i}`, `Option ${i}`),\n );\n const categories: CategoryRow[] = [\n createCategory(\"many\", \"Many Options\", options),\n ];\n\n const { lastFrame, unmount } = renderGrid({ categories });\n cleanup = unmount;\n\n const output = lastFrame();\n expect(output).toContain(\"Many Options\");\n expect(output).toContain(\"Option 0\");\n expect(output).toContain(\"Option 9\");\n });\n\n it(\"should handle long option labels\", () => {\n const categories: CategoryRow[] = [\n createCategory(\"long\", \"Long Labels\", [\n createOption(\"long1\", \"very-long-option-name-here\"),\n createOption(\"long2\", \"another-very-long-option\"),\n ]),\n ];\n\n const { lastFrame, unmount } = renderGrid({ categories });\n cleanup = unmount;\n\n const output = lastFrame();\n expect(output).toContain(\"very-long-option-name-here\");\n });\n\n it(\"should handle categories with different option counts\", () => {\n const categories: CategoryRow[] = [\n createCategory(\"cat1\", \"Category 1\", [\n createOption(\"opt1\", \"Option 1\"),\n createOption(\"opt2\", \"Option 2\"),\n ]),\n createCategory(\"cat2\", \"Category 2\", [\n createOption(\"opt3\", \"Option 3\"),\n ]),\n createCategory(\"cat3\", \"Category 3\", [\n createOption(\"opt4\", \"Option 4\"),\n createOption(\"opt5\", \"Option 5\"),\n createOption(\"opt6\", \"Option 6\"),\n ]),\n ];\n\n const { lastFrame, unmount } = renderGrid({ categories });\n cleanup = unmount;\n\n const output = lastFrame();\n expect(output).toContain(\"Category 1\");\n expect(output).toContain(\"Category 2\");\n expect(output).toContain(\"Category 3\");\n });\n });\n\n // ===========================================================================\n // Column Adjustment on Row Change\n // ===========================================================================\n\n describe(\"column adjustment\", () => {\n it(\"should adjust focusedCol when changing to row with fewer options\", async () => {\n const onFocusChange = vi.fn();\n const categories: CategoryRow[] = [\n createCategory(\"cat1\", \"Category 1\", [\n createOption(\"opt1\", \"Option 1\"),\n createOption(\"opt2\", \"Option 2\"),\n createOption(\"opt3\", \"Option 3\"),\n ]),\n createCategory(\"cat2\", \"Category 2\", [\n createOption(\"opt4\", \"Option 4\"),\n ]),\n ];\n\n const { stdin, unmount } = renderGrid({\n categories,\n focusedRow: 0,\n focusedCol: 2, // Last option in first row\n onFocusChange,\n });\n cleanup = unmount;\n\n await delay(RENDER_DELAY_MS);\n await stdin.write(ARROW_DOWN);\n await delay(INPUT_DELAY_MS);\n\n // Should move to row 1, and col should be clamped to 0 (only option)\n expect(onFocusChange).toHaveBeenCalledWith(1, 0);\n });\n });\n});\n"],"mappings":"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","names":[]}
|
|
1
|
+
{"version":3,"sources":["../../../src/cli/components/wizard/category-grid.test.tsx"],"sourcesContent":["/**\n * Tests for the CategoryGrid component.\n *\n * Tests section-based rendering and keyboard navigation for wizard Build step.\n */\nimport React from \"react\";\nimport { render } from \"ink-testing-library\";\nimport { describe, expect, it, afterEach, vi } from \"vitest\";\nimport {\n CategoryGrid,\n type CategoryGridProps,\n type CategoryRow,\n type CategoryOption,\n} from \"./category-grid\";\nimport {\n ARROW_UP,\n ARROW_DOWN,\n ARROW_LEFT,\n ARROW_RIGHT,\n TAB,\n RENDER_DELAY_MS,\n INPUT_DELAY_MS,\n delay,\n} from \"../../lib/__tests__/test-constants\";\n\n// =============================================================================\n// Test Fixtures\n// =============================================================================\n\nconst createOption = (\n id: string,\n label: string,\n overrides: Partial<CategoryOption> = {},\n): CategoryOption => ({\n id,\n label,\n state: \"normal\",\n selected: false,\n ...overrides,\n});\n\nconst createCategory = (\n id: string,\n name: string,\n options: CategoryOption[],\n overrides: Partial<CategoryRow> = {},\n): CategoryRow => ({\n id,\n name,\n required: false,\n exclusive: true,\n options,\n ...overrides,\n});\n\n// Default categories with framework-first scenario\nconst defaultCategories: CategoryRow[] = [\n createCategory(\n \"framework\",\n \"Framework\",\n [\n createOption(\"react\", \"React\", {\n state: \"recommended\",\n stateReason: \"Popular choice\",\n }),\n createOption(\"vue\", \"Vue\"),\n createOption(\"angular\", \"Angular\"),\n createOption(\"svelte\", \"Svelte\"),\n ],\n { required: true },\n ),\n createCategory(\n \"styling\",\n \"Styling\",\n [\n createOption(\"scss-mod\", \"SCSS Modules\", { selected: true }),\n createOption(\"tailwind\", \"Tailwind\", { state: \"recommended\" }),\n createOption(\"styled\", \"Styled Components\"),\n createOption(\"vanilla\", \"Vanilla CSS\"),\n ],\n { required: true },\n ),\n createCategory(\"client-state\", \"Client State\", [\n createOption(\"zustand\", \"Zustand\", { state: \"recommended\" }),\n createOption(\"jotai\", \"Jotai\"),\n createOption(\"redux\", \"Redux\", {\n state: \"discouraged\",\n stateReason: \"Complex for most apps\",\n }),\n createOption(\"mobx\", \"MobX\"),\n ]),\n createCategory(\"server-state\", \"Server State\", [\n createOption(\"react-query\", \"React Query\", { selected: true }),\n createOption(\"swr\", \"SWR\"),\n createOption(\"apollo\", \"Apollo\"),\n ]),\n createCategory(\"analytics\", \"Analytics\", [\n createOption(\"posthog\", \"PostHog\"),\n ]),\n];\n\n// Categories with framework selected (unlocks other sections)\nconst categoriesWithFramework: CategoryRow[] = [\n createCategory(\n \"framework\",\n \"Framework\",\n [\n createOption(\"react\", \"React\", {\n state: \"recommended\",\n stateReason: \"Popular choice\",\n selected: true, // Framework selected\n }),\n createOption(\"vue\", \"Vue\"),\n createOption(\"angular\", \"Angular\"),\n createOption(\"svelte\", \"Svelte\"),\n ],\n { required: true },\n ),\n createCategory(\n \"styling\",\n \"Styling\",\n [\n createOption(\"scss-mod\", \"SCSS Modules\"),\n createOption(\"tailwind\", \"Tailwind\", { state: \"recommended\" }),\n createOption(\"styled\", \"Styled Components\"),\n createOption(\"vanilla\", \"Vanilla CSS\"),\n ],\n { required: true },\n ),\n createCategory(\"client-state\", \"Client State\", [\n createOption(\"zustand\", \"Zustand\", { state: \"recommended\" }),\n createOption(\"jotai\", \"Jotai\"),\n createOption(\"redux\", \"Redux\", { state: \"discouraged\" }),\n createOption(\"mobx\", \"MobX\"),\n ]),\n];\n\nconst defaultProps: CategoryGridProps = {\n categories: defaultCategories,\n focusedRow: 0,\n focusedCol: 0,\n showDescriptions: false,\n expertMode: false,\n onToggle: vi.fn(),\n onFocusChange: vi.fn(),\n onToggleDescriptions: vi.fn(),\n onToggleExpertMode: vi.fn(),\n};\n\nconst renderGrid = (props: Partial<CategoryGridProps> = {}) => {\n return render(<CategoryGrid {...defaultProps} {...props} />);\n};\n\n// =============================================================================\n// Tests\n// =============================================================================\n\ndescribe(\"CategoryGrid component\", () => {\n let cleanup: (() => void) | undefined;\n\n afterEach(() => {\n cleanup?.();\n cleanup = undefined;\n vi.clearAllMocks();\n });\n\n // ===========================================================================\n // Basic Rendering\n // ===========================================================================\n\n describe(\"rendering\", () => {\n it(\"should render all categories as sections\", () => {\n const { lastFrame, unmount } = renderGrid();\n cleanup = unmount;\n\n const output = lastFrame();\n expect(output).toContain(\"Framework\");\n expect(output).toContain(\"Styling\");\n expect(output).toContain(\"Client State\");\n expect(output).toContain(\"Server State\");\n expect(output).toContain(\"Analytics\");\n });\n\n it(\"should render all options in each category\", () => {\n const { lastFrame, unmount } = renderGrid();\n cleanup = unmount;\n\n const output = lastFrame();\n // Framework options\n expect(output).toContain(\"React\");\n expect(output).toContain(\"Vue\");\n expect(output).toContain(\"Angular\");\n expect(output).toContain(\"Svelte\");\n // Styling options\n expect(output).toContain(\"SCSS Modules\");\n expect(output).toContain(\"Tailwind\");\n });\n\n it(\"should show required indicator (*) for required categories\", () => {\n const { lastFrame, unmount } = renderGrid();\n cleanup = unmount;\n\n const output = lastFrame();\n // Framework and Styling are required\n // The * should appear after their names\n expect(output).toContain(\"*\");\n });\n\n it(\"should NOT show (optional) for non-required categories\", () => {\n const { lastFrame, unmount } = renderGrid();\n cleanup = unmount;\n\n const output = lastFrame();\n // Optional is assumed by default, so we don't show the label\n expect(output).not.toContain(\"(optional)\");\n });\n\n it(\"should render legend row with visual states\", () => {\n const { lastFrame, unmount } = renderGrid();\n cleanup = unmount;\n\n const output = lastFrame();\n expect(output).toContain(\"Legend:\");\n expect(output).toContain(\"selected\");\n expect(output).toContain(\"recommended\");\n expect(output).toContain(\"discouraged\");\n expect(output).toContain(\"disabled\");\n });\n\n it(\"should render header with toggle hints\", () => {\n const { lastFrame, unmount } = renderGrid();\n cleanup = unmount;\n\n const output = lastFrame();\n expect(output).toContain(\"[d] Descriptions\");\n expect(output).toContain(\"[e] Expert Mode\");\n });\n\n it(\"should handle empty categories array\", () => {\n const { lastFrame, unmount } = renderGrid({ categories: [] });\n cleanup = unmount;\n\n const output = lastFrame();\n expect(output).toContain(\"No categories to display\");\n });\n\n it(\"should render section underlines\", () => {\n const { lastFrame, unmount } = renderGrid();\n cleanup = unmount;\n\n const output = lastFrame();\n // Unicode horizontal line character used for underlines\n expect(output).toContain(\"\\u2500\");\n });\n });\n\n // ===========================================================================\n // Visual States (No Stars)\n // ===========================================================================\n\n describe(\"visual states\", () => {\n it(\"should show selected options with label text\", () => {\n const { lastFrame, unmount } = renderGrid();\n cleanup = unmount;\n\n const output = lastFrame();\n // scss-mod and react-query are selected in defaultCategories\n expect(output).toContain(\"SCSS Modules\");\n expect(output).toContain(\"React Query\");\n });\n\n it(\"should show unselected options with label text\", () => {\n const { lastFrame, unmount } = renderGrid();\n cleanup = unmount;\n\n const output = lastFrame();\n // Unselected options should display their labels\n expect(output).toContain(\"Vue\");\n expect(output).toContain(\"Angular\");\n });\n\n it(\"should NOT show star indicator for recommended options (uses background instead)\", () => {\n const { lastFrame, unmount } = renderGrid();\n cleanup = unmount;\n\n const output = lastFrame();\n // Should NOT contain star (⭐) for recommended\n expect(output).not.toContain(\"\\u2B50\");\n });\n\n it(\"should NOT show warning indicator for discouraged options (uses color instead)\", () => {\n const { lastFrame, unmount } = renderGrid();\n cleanup = unmount;\n\n const output = lastFrame();\n // Should NOT contain warning (⚠) for discouraged\n expect(output).not.toContain(\"\\u26A0\");\n });\n\n it(\"should show disabled options with dimmed styling\", () => {\n const categories: CategoryRow[] = [\n createCategory(\"test\", \"Test\", [\n createOption(\"opt1\", \"Option 1\"),\n createOption(\"opt2\", \"Option 2\", { state: \"disabled\" }),\n ]),\n ];\n\n const { lastFrame, unmount } = renderGrid({ categories });\n cleanup = unmount;\n\n const output = lastFrame();\n // Disabled options should show label (dimmed text)\n expect(output).toContain(\"Option 2\");\n });\n });\n\n // ===========================================================================\n // Locked Sections (Framework-First Flow)\n // ===========================================================================\n\n describe(\"locked sections\", () => {\n it(\"should show all categories including locked ones\", () => {\n // No framework selected, so non-framework sections should be locked but visible\n const { lastFrame, unmount } = renderGrid();\n cleanup = unmount;\n\n const output = lastFrame();\n expect(output).toContain(\"Framework\");\n expect(output).toContain(\"Styling\");\n expect(output).toContain(\"Client State\");\n });\n\n it(\"should unlock sections when framework is selected\", () => {\n const { lastFrame, unmount } = renderGrid({\n categories: categoriesWithFramework,\n });\n cleanup = unmount;\n\n const output = lastFrame();\n // All sections should be visible and navigable\n expect(output).toContain(\"Framework\");\n expect(output).toContain(\"Styling\");\n expect(output).toContain(\"Client State\");\n });\n\n it(\"should not lock any sections when no framework category exists\", () => {\n const categoriesNoFramework: CategoryRow[] = [\n createCategory(\"styling\", \"Styling\", [\n createOption(\"scss\", \"SCSS\"),\n createOption(\"tailwind\", \"Tailwind\"),\n ]),\n createCategory(\"state\", \"State\", [createOption(\"zustand\", \"Zustand\")]),\n ];\n\n const { lastFrame, unmount } = renderGrid({\n categories: categoriesNoFramework,\n });\n cleanup = unmount;\n\n const output = lastFrame();\n expect(output).toContain(\"Styling\");\n expect(output).toContain(\"State\");\n });\n });\n\n // ===========================================================================\n // Focus Indicator (Background-based, no > symbol)\n // ===========================================================================\n\n describe(\"focus indicator\", () => {\n it(\"should render focused option with label text\", () => {\n const { lastFrame, unmount } = renderGrid({\n focusedRow: 0,\n focusedCol: 0,\n });\n cleanup = unmount;\n\n const output = lastFrame();\n // Focused option should be visible (background highlighting is visual-only)\n expect(output).toContain(\"React\");\n });\n\n it(\"should render correctly when focusedRow changes\", () => {\n const { lastFrame: frame1, unmount: unmount1 } = renderGrid({\n categories: categoriesWithFramework,\n focusedRow: 0,\n focusedCol: 0,\n });\n const output1 = frame1();\n unmount1();\n\n const { lastFrame: frame2, unmount: unmount2 } = renderGrid({\n categories: categoriesWithFramework,\n focusedRow: 1,\n focusedCol: 0,\n });\n cleanup = unmount2;\n const output2 = frame2();\n\n // Both should render the focused category options\n expect(output1).toContain(\"Framework\");\n expect(output2).toContain(\"Styling\");\n });\n\n it(\"should highlight focused category name\", () => {\n const { lastFrame, unmount } = renderGrid({\n categories: categoriesWithFramework,\n focusedRow: 1,\n focusedCol: 0,\n });\n cleanup = unmount;\n\n const output = lastFrame();\n // Styling row should be focused (index 1)\n expect(output).toContain(\"Styling\");\n });\n });\n\n // ===========================================================================\n // Keyboard Navigation - Arrow Keys\n // ===========================================================================\n\n describe(\"keyboard navigation - arrow keys\", () => {\n it(\"should call onFocusChange when pressing left arrow\", async () => {\n const onFocusChange = vi.fn();\n const { stdin, unmount } = renderGrid({\n focusedRow: 0,\n focusedCol: 1,\n onFocusChange,\n });\n cleanup = unmount;\n\n await delay(RENDER_DELAY_MS);\n await stdin.write(ARROW_LEFT);\n await delay(INPUT_DELAY_MS);\n\n expect(onFocusChange).toHaveBeenCalledWith(0, 0);\n });\n\n it(\"should call onFocusChange when pressing right arrow\", async () => {\n const onFocusChange = vi.fn();\n const { stdin, unmount } = renderGrid({\n focusedRow: 0,\n focusedCol: 0,\n onFocusChange,\n });\n cleanup = unmount;\n\n await delay(RENDER_DELAY_MS);\n await stdin.write(ARROW_RIGHT);\n await delay(INPUT_DELAY_MS);\n\n expect(onFocusChange).toHaveBeenCalledWith(0, 1);\n });\n\n it(\"should call onFocusChange when pressing up arrow (wraps to framework when locked)\", async () => {\n const onFocusChange = vi.fn();\n // No framework selected, so navigation stays on framework row\n const { stdin, unmount } = renderGrid({\n focusedRow: 0,\n focusedCol: 0,\n onFocusChange,\n });\n cleanup = unmount;\n\n await delay(RENDER_DELAY_MS);\n await stdin.write(ARROW_UP);\n await delay(INPUT_DELAY_MS);\n\n // Should stay on row 0 (framework) since other rows are locked\n expect(onFocusChange).toHaveBeenCalledWith(0, 0);\n });\n\n it(\"should navigate between sections when unlocked\", async () => {\n const onFocusChange = vi.fn();\n const { stdin, unmount } = renderGrid({\n categories: categoriesWithFramework,\n focusedRow: 0,\n focusedCol: 0,\n onFocusChange,\n });\n cleanup = unmount;\n\n await delay(RENDER_DELAY_MS);\n await stdin.write(ARROW_DOWN);\n await delay(INPUT_DELAY_MS);\n\n // Should move to row 1 (Styling) since framework is selected\n expect(onFocusChange).toHaveBeenCalledWith(1, 0);\n });\n\n it(\"should wrap horizontally when pressing left at first column\", async () => {\n const onFocusChange = vi.fn();\n const { stdin, unmount } = renderGrid({\n focusedRow: 0,\n focusedCol: 0,\n onFocusChange,\n });\n cleanup = unmount;\n\n await delay(RENDER_DELAY_MS);\n await stdin.write(ARROW_LEFT);\n await delay(INPUT_DELAY_MS);\n\n // Should wrap to last column (index 3 for framework with 4 options)\n expect(onFocusChange).toHaveBeenCalledWith(0, 3);\n });\n\n it(\"should wrap horizontally when pressing right at last column\", async () => {\n const onFocusChange = vi.fn();\n const { stdin, unmount } = renderGrid({\n focusedRow: 0,\n focusedCol: 3, // Last option in framework\n onFocusChange,\n });\n cleanup = unmount;\n\n await delay(RENDER_DELAY_MS);\n await stdin.write(ARROW_RIGHT);\n await delay(INPUT_DELAY_MS);\n\n // Should wrap to first column\n expect(onFocusChange).toHaveBeenCalledWith(0, 0);\n });\n\n it(\"should wrap vertically when all sections are unlocked\", async () => {\n const onFocusChange = vi.fn();\n const { stdin, unmount } = renderGrid({\n categories: categoriesWithFramework,\n focusedRow: 0,\n focusedCol: 0,\n onFocusChange,\n });\n cleanup = unmount;\n\n await delay(RENDER_DELAY_MS);\n await stdin.write(ARROW_UP);\n await delay(INPUT_DELAY_MS);\n\n // Should wrap to last row (index 2 for 3 categories)\n expect(onFocusChange).toHaveBeenCalledWith(2, 0);\n });\n });\n\n // ===========================================================================\n // Keyboard Navigation - Vim Keys\n // ===========================================================================\n\n describe(\"keyboard navigation - vim keys\", () => {\n it(\"should move left with h key\", async () => {\n const onFocusChange = vi.fn();\n const { stdin, unmount } = renderGrid({\n focusedRow: 0,\n focusedCol: 1,\n onFocusChange,\n });\n cleanup = unmount;\n\n await delay(RENDER_DELAY_MS);\n await stdin.write(\"h\");\n await delay(INPUT_DELAY_MS);\n\n expect(onFocusChange).toHaveBeenCalledWith(0, 0);\n });\n\n it(\"should move right with l key\", async () => {\n const onFocusChange = vi.fn();\n const { stdin, unmount } = renderGrid({\n focusedRow: 0,\n focusedCol: 0,\n onFocusChange,\n });\n cleanup = unmount;\n\n await delay(RENDER_DELAY_MS);\n await stdin.write(\"l\");\n await delay(INPUT_DELAY_MS);\n\n expect(onFocusChange).toHaveBeenCalledWith(0, 1);\n });\n\n it(\"should move up with k key\", async () => {\n const onFocusChange = vi.fn();\n const { stdin, unmount } = renderGrid({\n categories: categoriesWithFramework,\n focusedRow: 1,\n focusedCol: 0,\n onFocusChange,\n });\n cleanup = unmount;\n\n await delay(RENDER_DELAY_MS);\n await stdin.write(\"k\");\n await delay(INPUT_DELAY_MS);\n\n expect(onFocusChange).toHaveBeenCalledWith(0, 0);\n });\n\n it(\"should move down with j key\", async () => {\n const onFocusChange = vi.fn();\n const { stdin, unmount } = renderGrid({\n categories: categoriesWithFramework,\n focusedRow: 0,\n focusedCol: 0,\n onFocusChange,\n });\n cleanup = unmount;\n\n await delay(RENDER_DELAY_MS);\n await stdin.write(\"j\");\n await delay(INPUT_DELAY_MS);\n\n expect(onFocusChange).toHaveBeenCalledWith(1, 0);\n });\n });\n\n // ===========================================================================\n // Selection Toggle\n // ===========================================================================\n\n describe(\"selection toggle\", () => {\n it(\"should call onToggle when pressing space on a normal option\", async () => {\n const onToggle = vi.fn();\n const { stdin, unmount } = renderGrid({\n focusedRow: 0,\n focusedCol: 1, // vue (normal state)\n onToggle,\n });\n cleanup = unmount;\n\n await delay(RENDER_DELAY_MS);\n await stdin.write(\" \"); // Space\n await delay(INPUT_DELAY_MS);\n\n expect(onToggle).toHaveBeenCalledWith(\"framework\", \"vue\");\n });\n\n it(\"should call onToggle when pressing space on a selected option\", async () => {\n const onToggle = vi.fn();\n // Use expertMode to preserve original option order\n const categories: CategoryRow[] = [\n createCategory(\n \"framework\",\n \"Framework\",\n [\n createOption(\"react\", \"React\", { selected: true }),\n createOption(\"vue\", \"Vue\"),\n ],\n { required: true },\n ),\n ];\n const { stdin, unmount } = renderGrid({\n categories,\n focusedRow: 0,\n focusedCol: 0, // react (selected)\n expertMode: true,\n onToggle,\n });\n cleanup = unmount;\n\n await delay(RENDER_DELAY_MS);\n await stdin.write(\" \");\n await delay(INPUT_DELAY_MS);\n\n expect(onToggle).toHaveBeenCalledWith(\"framework\", \"react\");\n });\n\n it(\"should NOT call onToggle when pressing space on a disabled option\", async () => {\n const onToggle = vi.fn();\n const categories: CategoryRow[] = [\n createCategory(\"test\", \"Test\", [\n createOption(\"opt1\", \"Option 1\", { state: \"disabled\" }),\n createOption(\"opt2\", \"Option 2\"),\n ]),\n ];\n\n // Use expertMode to preserve original option order (disabled at index 0)\n const { stdin, unmount } = renderGrid({\n categories,\n focusedRow: 0,\n focusedCol: 0, // Disabled option (first in expert mode)\n expertMode: true,\n onToggle,\n });\n cleanup = unmount;\n\n await delay(RENDER_DELAY_MS);\n await stdin.write(\" \");\n await delay(INPUT_DELAY_MS);\n\n expect(onToggle).not.toHaveBeenCalled();\n });\n\n it(\"should NOT call onToggle when section is locked\", async () => {\n const onToggle = vi.fn();\n // No framework selected, styling section is locked\n const { stdin, unmount } = renderGrid({\n focusedRow: 1, // Styling (locked)\n focusedCol: 0,\n onToggle,\n });\n cleanup = unmount;\n\n await delay(RENDER_DELAY_MS);\n await stdin.write(\" \");\n await delay(INPUT_DELAY_MS);\n\n expect(onToggle).not.toHaveBeenCalled();\n });\n });\n\n // ===========================================================================\n // Disabled Options Navigation\n // ===========================================================================\n\n describe(\"disabled options navigation\", () => {\n it(\"should skip disabled options when navigating right\", async () => {\n const onFocusChange = vi.fn();\n const categories: CategoryRow[] = [\n createCategory(\"test\", \"Test\", [\n createOption(\"opt1\", \"Option 1\"),\n createOption(\"opt2\", \"Option 2\", { state: \"disabled\" }),\n createOption(\"opt3\", \"Option 3\"),\n ]),\n ];\n\n // Use expertMode to preserve original option order\n const { stdin, unmount } = renderGrid({\n categories,\n focusedRow: 0,\n focusedCol: 0,\n expertMode: true,\n onFocusChange,\n });\n cleanup = unmount;\n\n await delay(RENDER_DELAY_MS);\n await stdin.write(ARROW_RIGHT);\n await delay(INPUT_DELAY_MS);\n\n // Should skip opt2 (disabled) and go to opt3 (index 2)\n expect(onFocusChange).toHaveBeenCalledWith(0, 2);\n });\n\n it(\"should skip disabled options when navigating left\", async () => {\n const onFocusChange = vi.fn();\n const categories: CategoryRow[] = [\n createCategory(\"test\", \"Test\", [\n createOption(\"opt1\", \"Option 1\"),\n createOption(\"opt2\", \"Option 2\", { state: \"disabled\" }),\n createOption(\"opt3\", \"Option 3\"),\n ]),\n ];\n\n // Use expertMode to preserve original option order\n const { stdin, unmount } = renderGrid({\n categories,\n focusedRow: 0,\n focusedCol: 2, // Start at opt3\n expertMode: true,\n onFocusChange,\n });\n cleanup = unmount;\n\n await delay(RENDER_DELAY_MS);\n await stdin.write(ARROW_LEFT);\n await delay(INPUT_DELAY_MS);\n\n // Should skip opt2 (disabled) and go to opt1 (index 0)\n expect(onFocusChange).toHaveBeenCalledWith(0, 0);\n });\n\n it(\"should handle all options disabled in a row\", async () => {\n const onFocusChange = vi.fn();\n const categories: CategoryRow[] = [\n createCategory(\"test\", \"Test\", [\n createOption(\"opt1\", \"Option 1\", { state: \"disabled\" }),\n createOption(\"opt2\", \"Option 2\", { state: \"disabled\" }),\n ]),\n ];\n\n // Use expertMode to preserve original option order\n const { stdin, unmount } = renderGrid({\n categories,\n focusedRow: 0,\n focusedCol: 0,\n expertMode: true,\n onFocusChange,\n });\n cleanup = unmount;\n\n await delay(RENDER_DELAY_MS);\n await stdin.write(ARROW_RIGHT);\n await delay(INPUT_DELAY_MS);\n\n // Should stay at current position when all disabled\n expect(onFocusChange).toHaveBeenCalledWith(0, 0);\n });\n });\n\n // ===========================================================================\n // Exclusive Categories\n // ===========================================================================\n\n describe(\"exclusive categories\", () => {\n it(\"should render exclusive category correctly\", () => {\n const { lastFrame, unmount } = renderGrid();\n cleanup = unmount;\n\n const output = lastFrame();\n // All default categories are exclusive, should render normally\n expect(output).toContain(\"Framework\");\n });\n\n // Note: The actual exclusivity logic (only one selection) is handled\n // by the parent component via onToggle. CategoryGrid just displays\n // the current state.\n });\n\n // ===========================================================================\n // Tab Navigation (Section Jumping)\n // ===========================================================================\n\n describe(\"tab navigation\", () => {\n it(\"should jump to next section when pressing Tab\", async () => {\n const onFocusChange = vi.fn();\n const { stdin, unmount } = renderGrid({\n categories: categoriesWithFramework,\n focusedRow: 0,\n focusedCol: 0,\n onFocusChange,\n });\n cleanup = unmount;\n\n await delay(RENDER_DELAY_MS);\n await stdin.write(TAB);\n await delay(INPUT_DELAY_MS);\n\n // Should jump to next section (row 1)\n expect(onFocusChange).toHaveBeenCalledWith(1, 0);\n });\n\n it(\"should only jump to unlocked sections\", async () => {\n const onFocusChange = vi.fn();\n // No framework selected, so Tab should stay on framework\n const { stdin, unmount } = renderGrid({\n focusedRow: 0,\n focusedCol: 0,\n onFocusChange,\n });\n cleanup = unmount;\n\n await delay(RENDER_DELAY_MS);\n await stdin.write(TAB);\n await delay(INPUT_DELAY_MS);\n\n // Should NOT call onFocusChange since there's nowhere to go (all others locked)\n expect(onFocusChange).not.toHaveBeenCalled();\n });\n });\n\n // ===========================================================================\n // Show Descriptions Toggle\n // ===========================================================================\n\n describe(\"show descriptions toggle\", () => {\n it(\"should call onToggleDescriptions when pressing d key\", async () => {\n const onToggleDescriptions = vi.fn();\n const { stdin, unmount } = renderGrid({\n onToggleDescriptions,\n });\n cleanup = unmount;\n\n await delay(RENDER_DELAY_MS);\n await stdin.write(\"d\");\n await delay(INPUT_DELAY_MS);\n\n expect(onToggleDescriptions).toHaveBeenCalled();\n });\n\n it(\"should show descriptions when showDescriptions is true\", () => {\n const { lastFrame, unmount } = renderGrid({ showDescriptions: true });\n cleanup = unmount;\n\n const output = lastFrame();\n // Should show state reasons\n expect(output).toContain(\"Popular choice\"); // react's stateReason\n });\n\n it(\"should hide descriptions when showDescriptions is false\", () => {\n const { lastFrame, unmount } = renderGrid({ showDescriptions: false });\n cleanup = unmount;\n\n const output = lastFrame();\n // stateReasons should not be visible (though they might still be in DOM)\n // At minimum, the display should be different\n expect(output).toBeDefined();\n });\n\n it(\"should show toggle state in header\", () => {\n const { lastFrame: frame1, unmount: unmount1 } = renderGrid({\n showDescriptions: false,\n });\n const output1 = frame1();\n unmount1();\n\n const { lastFrame: frame2, unmount: unmount2 } = renderGrid({\n showDescriptions: true,\n });\n cleanup = unmount2;\n const output2 = frame2();\n\n expect(output1).toContain(\"Descriptions: OFF\");\n expect(output2).toContain(\"Descriptions: ON\");\n });\n });\n\n // ===========================================================================\n // Expert Mode Toggle\n // ===========================================================================\n\n describe(\"expert mode toggle\", () => {\n it(\"should call onToggleExpertMode when pressing e\", async () => {\n const onToggleExpertMode = vi.fn();\n const { stdin, unmount } = renderGrid({\n onToggleExpertMode,\n });\n cleanup = unmount;\n\n await delay(RENDER_DELAY_MS);\n await stdin.write(\"e\");\n await delay(INPUT_DELAY_MS);\n\n expect(onToggleExpertMode).toHaveBeenCalled();\n });\n\n it(\"should call onToggleExpertMode when pressing E (uppercase)\", async () => {\n const onToggleExpertMode = vi.fn();\n const { stdin, unmount } = renderGrid({\n onToggleExpertMode,\n });\n cleanup = unmount;\n\n await delay(RENDER_DELAY_MS);\n await stdin.write(\"E\");\n await delay(INPUT_DELAY_MS);\n\n expect(onToggleExpertMode).toHaveBeenCalled();\n });\n\n it(\"should show toggle state in header\", () => {\n const { lastFrame: frame1, unmount: unmount1 } = renderGrid({\n expertMode: false,\n });\n const output1 = frame1();\n unmount1();\n\n const { lastFrame: frame2, unmount: unmount2 } = renderGrid({\n expertMode: true,\n });\n cleanup = unmount2;\n const output2 = frame2();\n\n expect(output1).toContain(\"Expert Mode: OFF\");\n expect(output2).toContain(\"Expert Mode: ON\");\n });\n });\n\n // ===========================================================================\n // Option Ordering\n // ===========================================================================\n\n describe(\"option ordering\", () => {\n it(\"should sort options by state when expertMode is false\", () => {\n // In non-expert mode, recommended should come first\n // The default categories have recommended options at various positions\n const { lastFrame, unmount } = renderGrid({ expertMode: false });\n cleanup = unmount;\n\n const output = lastFrame();\n // This test verifies the component renders - sorting happens internally\n expect(output).toBeDefined();\n });\n\n it(\"should preserve original order when expertMode is true\", () => {\n const { lastFrame, unmount } = renderGrid({ expertMode: true });\n cleanup = unmount;\n\n const output = lastFrame();\n // This test verifies the component renders with expert mode\n expect(output).toBeDefined();\n });\n });\n\n // ===========================================================================\n // Edge Cases\n // ===========================================================================\n\n describe(\"edge cases\", () => {\n it(\"should handle single category\", () => {\n const categories: CategoryRow[] = [\n createCategory(\"single\", \"Single Category\", [\n createOption(\"opt1\", \"Option 1\"),\n ]),\n ];\n\n const { lastFrame, unmount } = renderGrid({ categories });\n cleanup = unmount;\n\n const output = lastFrame();\n expect(output).toContain(\"Single Category\");\n expect(output).toContain(\"Option 1\");\n });\n\n it(\"should handle single option in category\", () => {\n const categories: CategoryRow[] = [\n createCategory(\"single\", \"Single\", [\n createOption(\"only\", \"Only Option\"),\n ]),\n ];\n\n const { lastFrame, unmount } = renderGrid({ categories });\n cleanup = unmount;\n\n const output = lastFrame();\n expect(output).toContain(\"Only Option\");\n });\n\n it(\"should handle category with many options (flows naturally)\", () => {\n const options = Array.from({ length: 10 }, (_, i) =>\n createOption(`opt${i}`, `Option ${i}`),\n );\n const categories: CategoryRow[] = [\n createCategory(\"many\", \"Many Options\", options),\n ];\n\n const { lastFrame, unmount } = renderGrid({ categories });\n cleanup = unmount;\n\n const output = lastFrame();\n expect(output).toContain(\"Many Options\");\n // All options should be present (no fixed row limit)\n expect(output).toContain(\"Option 0\");\n expect(output).toContain(\"Option 9\");\n });\n\n it(\"should handle long option labels\", () => {\n const categories: CategoryRow[] = [\n createCategory(\"long\", \"Long Labels\", [\n createOption(\"long1\", \"Very Long Option Name\"),\n createOption(\"long2\", \"Another Long Name\"),\n ]),\n ];\n\n const { lastFrame, unmount } = renderGrid({ categories });\n cleanup = unmount;\n\n const output = lastFrame();\n // Labels should be rendered (flowing layout handles long labels)\n expect(output).toContain(\"Very Long Option Name\");\n });\n\n it(\"should handle categories with different option counts\", () => {\n const categories: CategoryRow[] = [\n createCategory(\"cat1\", \"Category 1\", [\n createOption(\"opt1\", \"Option 1\"),\n createOption(\"opt2\", \"Option 2\"),\n ]),\n createCategory(\"cat2\", \"Category 2\", [\n createOption(\"opt3\", \"Option 3\"),\n ]),\n createCategory(\"cat3\", \"Category 3\", [\n createOption(\"opt4\", \"Option 4\"),\n createOption(\"opt5\", \"Option 5\"),\n createOption(\"opt6\", \"Option 6\"),\n ]),\n ];\n\n const { lastFrame, unmount } = renderGrid({ categories });\n cleanup = unmount;\n\n const output = lastFrame();\n expect(output).toContain(\"Category 1\");\n expect(output).toContain(\"Category 2\");\n expect(output).toContain(\"Category 3\");\n });\n });\n\n // ===========================================================================\n // Column Adjustment on Row Change\n // ===========================================================================\n\n describe(\"column adjustment\", () => {\n it(\"should adjust focusedCol when changing to row with fewer options\", async () => {\n const onFocusChange = vi.fn();\n const categories: CategoryRow[] = [\n createCategory(\n \"framework\",\n \"Framework\",\n [\n createOption(\"opt1\", \"Option 1\", { selected: true }), // Framework selected\n createOption(\"opt2\", \"Option 2\"),\n createOption(\"opt3\", \"Option 3\"),\n ],\n { required: true },\n ),\n createCategory(\"cat2\", \"Category 2\", [\n createOption(\"opt4\", \"Option 4\"),\n ]),\n ];\n\n const { stdin, unmount } = renderGrid({\n categories,\n focusedRow: 0,\n focusedCol: 2, // Last option in first row\n onFocusChange,\n });\n cleanup = unmount;\n\n await delay(RENDER_DELAY_MS);\n await stdin.write(ARROW_DOWN);\n await delay(INPUT_DELAY_MS);\n\n // Should move to row 1, and col should be clamped to 0 (only option)\n expect(onFocusChange).toHaveBeenCalledWith(1, 0);\n });\n });\n});\n"],"mappings":"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","names":[]}
|
|
@@ -10,12 +10,12 @@ import {
|
|
|
10
10
|
} from "../../chunk-XY3XDVMI.js";
|
|
11
11
|
import {
|
|
12
12
|
SectionProgress
|
|
13
|
-
} from "../../chunk-
|
|
13
|
+
} from "../../chunk-BDLUZVKU.js";
|
|
14
14
|
import {
|
|
15
15
|
init_esm_shims
|
|
16
16
|
} from "../../chunk-DHET7RCE.js";
|
|
17
17
|
|
|
18
|
-
// src/cli
|
|
18
|
+
// src/cli/components/wizard/section-progress.test.tsx
|
|
19
19
|
init_esm_shims();
|
|
20
20
|
import { jsx } from "react/jsx-runtime";
|
|
21
21
|
describe("SectionProgress component", () => {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../src/cli-v2/components/wizard/section-progress.test.tsx"],"sourcesContent":["/**\n * Tests for the SectionProgress component.\n *\n * Tests sub-step progress display within wizard steps (e.g., multi-domain Build).\n */\nimport React from \"react\";\nimport { render } from \"ink-testing-library\";\nimport { describe, expect, it, afterEach } from \"vitest\";\nimport { SectionProgress } from \"./section-progress\";\n\ndescribe(\"SectionProgress component\", () => {\n let cleanup: (() => void) | undefined;\n\n afterEach(() => {\n cleanup?.();\n cleanup = undefined;\n });\n\n // ===========================================================================\n // Basic Rendering\n // ===========================================================================\n\n describe(\"rendering\", () => {\n it(\"should render label and current value\", () => {\n const { lastFrame, unmount } = render(\n <SectionProgress\n label=\"Domain\"\n current=\"Web\"\n index={1}\n total={2}\n next=\"API\"\n />,\n );\n cleanup = unmount;\n\n const output = lastFrame();\n expect(output).toContain(\"Domain:\");\n expect(output).toContain(\"Web\");\n });\n\n it(\"should show correct index/total format\", () => {\n const { lastFrame, unmount } = render(\n <SectionProgress\n label=\"Skill\"\n current=\"react\"\n index={1}\n total={8}\n next=\"zustand\"\n />,\n );\n cleanup = unmount;\n\n const output = lastFrame();\n expect(output).toContain(\"[1/8]\");\n });\n\n it(\"should show different index/total values\", () => {\n const { lastFrame, unmount } = render(\n <SectionProgress\n label=\"Domain\"\n current=\"API\"\n index={3}\n total={4}\n next=\"Mobile\"\n />,\n );\n cleanup = unmount;\n\n const output = lastFrame();\n expect(output).toContain(\"[3/4]\");\n });\n });\n\n // ===========================================================================\n // Next Item Display\n // ===========================================================================\n\n describe(\"next item display\", () => {\n it(\"should show 'Next: X' when not last item\", () => {\n const { lastFrame, unmount } = render(\n <SectionProgress\n label=\"Domain\"\n current=\"Web\"\n index={1}\n total={2}\n next=\"API\"\n />,\n );\n cleanup = unmount;\n\n const output = lastFrame();\n expect(output).toContain(\"Next: API\");\n });\n\n it(\"should show 'Last step' when on final item\", () => {\n const { lastFrame, unmount } = render(\n <SectionProgress label=\"Domain\" current=\"API\" index={2} total={2} />,\n );\n cleanup = unmount;\n\n const output = lastFrame();\n expect(output).toContain(\"Last step\");\n expect(output).not.toContain(\"Next:\");\n });\n\n it(\"should show 'Last step' for single item (1/1)\", () => {\n const { lastFrame, unmount } = render(\n <SectionProgress label=\"Domain\" current=\"Web\" index={1} total={1} />,\n );\n cleanup = unmount;\n\n const output = lastFrame();\n expect(output).toContain(\"[1/1]\");\n expect(output).toContain(\"Last step\");\n });\n });\n\n // ===========================================================================\n // Text Styling\n // ===========================================================================\n\n describe(\"text styling\", () => {\n it(\"should render label in bold\", () => {\n // Note: ink-testing-library doesn't expose ANSI codes directly in lastFrame()\n // but we verify the text content is present and structure is correct\n const { lastFrame, unmount } = render(\n <SectionProgress\n label=\"Domain\"\n current=\"Web\"\n index={1}\n total={2}\n next=\"API\"\n />,\n );\n cleanup = unmount;\n\n const output = lastFrame();\n // The label should be present - bold styling is applied via Ink Text component\n expect(output).toContain(\"Domain:\");\n });\n\n it(\"should render current value with cyan color\", () => {\n const { lastFrame, unmount } = render(\n <SectionProgress\n label=\"Skill\"\n current=\"react\"\n index={1}\n total={3}\n next=\"vue\"\n />,\n );\n cleanup = unmount;\n\n const output = lastFrame();\n // Current value should be present - cyan styling is applied via Ink Text component\n expect(output).toContain(\"react\");\n });\n\n it(\"should render index/total as dim text\", () => {\n const { lastFrame, unmount } = render(\n <SectionProgress\n label=\"Domain\"\n current=\"CLI\"\n index={2}\n total={3}\n next=\"Mobile\"\n />,\n );\n cleanup = unmount;\n\n const output = lastFrame();\n // Progress indicator should be present - dim styling is applied via Ink Text component\n expect(output).toContain(\"[2/3]\");\n });\n });\n\n // ===========================================================================\n // Edge Cases\n // ===========================================================================\n\n describe(\"edge cases\", () => {\n it(\"should handle long label and current value\", () => {\n const { lastFrame, unmount } = render(\n <SectionProgress\n label=\"Subcategory\"\n current=\"styled-components\"\n index={1}\n total={5}\n next=\"emotion\"\n />,\n );\n cleanup = unmount;\n\n const output = lastFrame();\n expect(output).toContain(\"Subcategory:\");\n expect(output).toContain(\"styled-components\");\n });\n\n it(\"should handle large index/total numbers\", () => {\n const { lastFrame, unmount } = render(\n <SectionProgress\n label=\"Skill\"\n current=\"some-skill\"\n index={99}\n total={100}\n next=\"last-skill\"\n />,\n );\n cleanup = unmount;\n\n const output = lastFrame();\n expect(output).toContain(\"[99/100]\");\n expect(output).toContain(\"Next: last-skill\");\n });\n\n it(\"should handle empty next value gracefully\", () => {\n // When next is undefined (last item case)\n const { lastFrame, unmount } = render(\n <SectionProgress\n label=\"Domain\"\n current=\"Mobile\"\n index={4}\n total={4}\n next={undefined}\n />,\n );\n cleanup = unmount;\n\n const output = lastFrame();\n expect(output).toContain(\"Last step\");\n expect(output).not.toContain(\"undefined\");\n });\n });\n\n // ===========================================================================\n // Display Variants (from spec examples)\n // ===========================================================================\n\n describe(\"display variants from spec\", () => {\n it(\"should match multi-domain Build step format\", () => {\n // Domain: Web [1/2] Next: API\n const { lastFrame, unmount } = render(\n <SectionProgress\n label=\"Domain\"\n current=\"Web\"\n index={1}\n total={2}\n next=\"API\"\n />,\n );\n cleanup = unmount;\n\n const output = lastFrame();\n expect(output).toContain(\"Domain:\");\n expect(output).toContain(\"Web\");\n expect(output).toContain(\"[1/2]\");\n expect(output).toContain(\"Next: API\");\n });\n\n it(\"should match Refine step format\", () => {\n // Skill: react [1/8] Next: zustand\n const { lastFrame, unmount } = render(\n <SectionProgress\n label=\"Skill\"\n current=\"react\"\n index={1}\n total={8}\n next=\"zustand\"\n />,\n );\n cleanup = unmount;\n\n const output = lastFrame();\n expect(output).toContain(\"Skill:\");\n expect(output).toContain(\"react\");\n expect(output).toContain(\"[1/8]\");\n expect(output).toContain(\"Next: zustand\");\n });\n\n it(\"should match last item format\", () => {\n // Domain: API [2/2] Last step\n const { lastFrame, unmount } = render(\n <SectionProgress label=\"Domain\" current=\"API\" index={2} total={2} />,\n );\n cleanup = unmount;\n\n const output = lastFrame();\n expect(output).toContain(\"Domain:\");\n expect(output).toContain(\"API\");\n expect(output).toContain(\"[2/2]\");\n expect(output).toContain(\"Last step\");\n });\n });\n});\n"],"mappings":"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","names":[]}
|
|
1
|
+
{"version":3,"sources":["../../../src/cli/components/wizard/section-progress.test.tsx"],"sourcesContent":["/**\n * Tests for the SectionProgress component.\n *\n * Tests sub-step progress display within wizard steps (e.g., multi-domain Build).\n */\nimport React from \"react\";\nimport { render } from \"ink-testing-library\";\nimport { describe, expect, it, afterEach } from \"vitest\";\nimport { SectionProgress } from \"./section-progress\";\n\ndescribe(\"SectionProgress component\", () => {\n let cleanup: (() => void) | undefined;\n\n afterEach(() => {\n cleanup?.();\n cleanup = undefined;\n });\n\n // ===========================================================================\n // Basic Rendering\n // ===========================================================================\n\n describe(\"rendering\", () => {\n it(\"should render label and current value\", () => {\n const { lastFrame, unmount } = render(\n <SectionProgress\n label=\"Domain\"\n current=\"Web\"\n index={1}\n total={2}\n next=\"API\"\n />,\n );\n cleanup = unmount;\n\n const output = lastFrame();\n expect(output).toContain(\"Domain:\");\n expect(output).toContain(\"Web\");\n });\n\n it(\"should show correct index/total format\", () => {\n const { lastFrame, unmount } = render(\n <SectionProgress\n label=\"Skill\"\n current=\"react\"\n index={1}\n total={8}\n next=\"zustand\"\n />,\n );\n cleanup = unmount;\n\n const output = lastFrame();\n expect(output).toContain(\"[1/8]\");\n });\n\n it(\"should show different index/total values\", () => {\n const { lastFrame, unmount } = render(\n <SectionProgress\n label=\"Domain\"\n current=\"API\"\n index={3}\n total={4}\n next=\"Mobile\"\n />,\n );\n cleanup = unmount;\n\n const output = lastFrame();\n expect(output).toContain(\"[3/4]\");\n });\n });\n\n // ===========================================================================\n // Next Item Display\n // ===========================================================================\n\n describe(\"next item display\", () => {\n it(\"should show 'Next: X' when not last item\", () => {\n const { lastFrame, unmount } = render(\n <SectionProgress\n label=\"Domain\"\n current=\"Web\"\n index={1}\n total={2}\n next=\"API\"\n />,\n );\n cleanup = unmount;\n\n const output = lastFrame();\n expect(output).toContain(\"Next: API\");\n });\n\n it(\"should show 'Last step' when on final item\", () => {\n const { lastFrame, unmount } = render(\n <SectionProgress label=\"Domain\" current=\"API\" index={2} total={2} />,\n );\n cleanup = unmount;\n\n const output = lastFrame();\n expect(output).toContain(\"Last step\");\n expect(output).not.toContain(\"Next:\");\n });\n\n it(\"should show 'Last step' for single item (1/1)\", () => {\n const { lastFrame, unmount } = render(\n <SectionProgress label=\"Domain\" current=\"Web\" index={1} total={1} />,\n );\n cleanup = unmount;\n\n const output = lastFrame();\n expect(output).toContain(\"[1/1]\");\n expect(output).toContain(\"Last step\");\n });\n });\n\n // ===========================================================================\n // Text Styling\n // ===========================================================================\n\n describe(\"text styling\", () => {\n it(\"should render label in bold\", () => {\n // Note: ink-testing-library doesn't expose ANSI codes directly in lastFrame()\n // but we verify the text content is present and structure is correct\n const { lastFrame, unmount } = render(\n <SectionProgress\n label=\"Domain\"\n current=\"Web\"\n index={1}\n total={2}\n next=\"API\"\n />,\n );\n cleanup = unmount;\n\n const output = lastFrame();\n // The label should be present - bold styling is applied via Ink Text component\n expect(output).toContain(\"Domain:\");\n });\n\n it(\"should render current value with cyan color\", () => {\n const { lastFrame, unmount } = render(\n <SectionProgress\n label=\"Skill\"\n current=\"react\"\n index={1}\n total={3}\n next=\"vue\"\n />,\n );\n cleanup = unmount;\n\n const output = lastFrame();\n // Current value should be present - cyan styling is applied via Ink Text component\n expect(output).toContain(\"react\");\n });\n\n it(\"should render index/total as dim text\", () => {\n const { lastFrame, unmount } = render(\n <SectionProgress\n label=\"Domain\"\n current=\"CLI\"\n index={2}\n total={3}\n next=\"Mobile\"\n />,\n );\n cleanup = unmount;\n\n const output = lastFrame();\n // Progress indicator should be present - dim styling is applied via Ink Text component\n expect(output).toContain(\"[2/3]\");\n });\n });\n\n // ===========================================================================\n // Edge Cases\n // ===========================================================================\n\n describe(\"edge cases\", () => {\n it(\"should handle long label and current value\", () => {\n const { lastFrame, unmount } = render(\n <SectionProgress\n label=\"Subcategory\"\n current=\"styled-components\"\n index={1}\n total={5}\n next=\"emotion\"\n />,\n );\n cleanup = unmount;\n\n const output = lastFrame();\n expect(output).toContain(\"Subcategory:\");\n expect(output).toContain(\"styled-components\");\n });\n\n it(\"should handle large index/total numbers\", () => {\n const { lastFrame, unmount } = render(\n <SectionProgress\n label=\"Skill\"\n current=\"some-skill\"\n index={99}\n total={100}\n next=\"last-skill\"\n />,\n );\n cleanup = unmount;\n\n const output = lastFrame();\n expect(output).toContain(\"[99/100]\");\n expect(output).toContain(\"Next: last-skill\");\n });\n\n it(\"should handle empty next value gracefully\", () => {\n // When next is undefined (last item case)\n const { lastFrame, unmount } = render(\n <SectionProgress\n label=\"Domain\"\n current=\"Mobile\"\n index={4}\n total={4}\n next={undefined}\n />,\n );\n cleanup = unmount;\n\n const output = lastFrame();\n expect(output).toContain(\"Last step\");\n expect(output).not.toContain(\"undefined\");\n });\n });\n\n // ===========================================================================\n // Display Variants (from spec examples)\n // ===========================================================================\n\n describe(\"display variants from spec\", () => {\n it(\"should match multi-domain Build step format\", () => {\n // Domain: Web [1/2] Next: API\n const { lastFrame, unmount } = render(\n <SectionProgress\n label=\"Domain\"\n current=\"Web\"\n index={1}\n total={2}\n next=\"API\"\n />,\n );\n cleanup = unmount;\n\n const output = lastFrame();\n expect(output).toContain(\"Domain:\");\n expect(output).toContain(\"Web\");\n expect(output).toContain(\"[1/2]\");\n expect(output).toContain(\"Next: API\");\n });\n\n it(\"should match Refine step format\", () => {\n // Skill: react [1/8] Next: zustand\n const { lastFrame, unmount } = render(\n <SectionProgress\n label=\"Skill\"\n current=\"react\"\n index={1}\n total={8}\n next=\"zustand\"\n />,\n );\n cleanup = unmount;\n\n const output = lastFrame();\n expect(output).toContain(\"Skill:\");\n expect(output).toContain(\"react\");\n expect(output).toContain(\"[1/8]\");\n expect(output).toContain(\"Next: zustand\");\n });\n\n it(\"should match last item format\", () => {\n // Domain: API [2/2] Last step\n const { lastFrame, unmount } = render(\n <SectionProgress label=\"Domain\" current=\"API\" index={2} total={2} />,\n );\n cleanup = unmount;\n\n const output = lastFrame();\n expect(output).toContain(\"Domain:\");\n expect(output).toContain(\"API\");\n expect(output).toContain(\"[2/2]\");\n expect(output).toContain(\"Last step\");\n });\n });\n});\n"],"mappings":"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","names":[]}
|
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import {
|
|
3
3
|
StepApproach
|
|
4
|
-
} from "../../chunk-
|
|
5
|
-
import "../../chunk-
|
|
6
|
-
import "../../chunk-
|
|
4
|
+
} from "../../chunk-ZENYS6KW.js";
|
|
5
|
+
import "../../chunk-Y2LW7R3Y.js";
|
|
6
|
+
import "../../chunk-D237EVNB.js";
|
|
7
|
+
import "../../chunk-76DWXGQE.js";
|
|
7
8
|
import "../../chunk-DHET7RCE.js";
|
|
8
9
|
export {
|
|
9
10
|
StepApproach
|
|
@@ -3,9 +3,10 @@ import {
|
|
|
3
3
|
StepBuild,
|
|
4
4
|
getDisplayLabel,
|
|
5
5
|
validateBuildStep
|
|
6
|
-
} from "../../chunk-
|
|
7
|
-
import "../../chunk-
|
|
8
|
-
import "../../chunk-
|
|
6
|
+
} from "../../chunk-7Q44DMSP.js";
|
|
7
|
+
import "../../chunk-Z7G4B5HJ.js";
|
|
8
|
+
import "../../chunk-BDLUZVKU.js";
|
|
9
|
+
import "../../chunk-Y2LW7R3Y.js";
|
|
9
10
|
import "../../chunk-DHET7RCE.js";
|
|
10
11
|
export {
|
|
11
12
|
StepBuild,
|
|
@@ -5,7 +5,7 @@ import {
|
|
|
5
5
|
INPUT_DELAY_MS,
|
|
6
6
|
RENDER_DELAY_MS,
|
|
7
7
|
delay
|
|
8
|
-
} from "../../chunk-
|
|
8
|
+
} from "../../chunk-6Q3Y7KVB.js";
|
|
9
9
|
import {
|
|
10
10
|
render
|
|
11
11
|
} from "../../chunk-66UDJBF6.js";
|
|
@@ -20,14 +20,15 @@ import {
|
|
|
20
20
|
StepBuild,
|
|
21
21
|
getDisplayLabel,
|
|
22
22
|
validateBuildStep
|
|
23
|
-
} from "../../chunk-
|
|
24
|
-
import "../../chunk-
|
|
25
|
-
import "../../chunk-
|
|
23
|
+
} from "../../chunk-7Q44DMSP.js";
|
|
24
|
+
import "../../chunk-Z7G4B5HJ.js";
|
|
25
|
+
import "../../chunk-BDLUZVKU.js";
|
|
26
|
+
import "../../chunk-Y2LW7R3Y.js";
|
|
26
27
|
import {
|
|
27
28
|
init_esm_shims
|
|
28
29
|
} from "../../chunk-DHET7RCE.js";
|
|
29
30
|
|
|
30
|
-
// src/cli
|
|
31
|
+
// src/cli/components/wizard/step-build.test.tsx
|
|
31
32
|
init_esm_shims();
|
|
32
33
|
import { jsx } from "react/jsx-runtime";
|
|
33
34
|
var createCategory = (id, name, overrides = {}) => ({
|
|
@@ -54,6 +55,7 @@ var createSkill = (id, name, category, overrides = {}) => ({
|
|
|
54
55
|
requiredBy: [],
|
|
55
56
|
alternatives: [],
|
|
56
57
|
discourages: [],
|
|
58
|
+
compatibleWith: [],
|
|
57
59
|
requiresSetup: [],
|
|
58
60
|
providesSetupFor: [],
|
|
59
61
|
path: `test/${id}`,
|
|
@@ -200,7 +202,10 @@ describe("StepBuild component", () => {
|
|
|
200
202
|
});
|
|
201
203
|
describe("rendering", () => {
|
|
202
204
|
it("should render CategoryGrid with correct categories for domain", () => {
|
|
203
|
-
const { lastFrame, unmount } = renderStepBuild(
|
|
205
|
+
const { lastFrame, unmount } = renderStepBuild({
|
|
206
|
+
selections: { framework: ["react"] }
|
|
207
|
+
// Framework selected to show other categories
|
|
208
|
+
});
|
|
204
209
|
cleanup = unmount;
|
|
205
210
|
const output = lastFrame();
|
|
206
211
|
globalExpect(output).toContain("Framework");
|
|
@@ -221,7 +226,9 @@ describe("StepBuild component", () => {
|
|
|
221
226
|
globalExpect(output).not.toContain("Client State");
|
|
222
227
|
});
|
|
223
228
|
it("should render skills as options", () => {
|
|
224
|
-
const { lastFrame, unmount } = renderStepBuild(
|
|
229
|
+
const { lastFrame, unmount } = renderStepBuild({
|
|
230
|
+
selections: { framework: ["react"] }
|
|
231
|
+
});
|
|
225
232
|
cleanup = unmount;
|
|
226
233
|
const output = lastFrame();
|
|
227
234
|
globalExpect(output).toContain("React");
|
|
@@ -293,7 +300,8 @@ describe("StepBuild component", () => {
|
|
|
293
300
|
describe("category filtering", () => {
|
|
294
301
|
it("should filter categories correctly by domain", () => {
|
|
295
302
|
const { lastFrame: webFrame, unmount: webUnmount } = renderStepBuild({
|
|
296
|
-
domain: "web"
|
|
303
|
+
domain: "web",
|
|
304
|
+
selections: { framework: ["react"] }
|
|
297
305
|
});
|
|
298
306
|
const webOutput = webFrame();
|
|
299
307
|
webUnmount();
|
|
@@ -311,7 +319,9 @@ describe("StepBuild component", () => {
|
|
|
311
319
|
globalExpect(apiOutput).not.toContain("Styling");
|
|
312
320
|
});
|
|
313
321
|
it("should sort categories by order", () => {
|
|
314
|
-
const { lastFrame, unmount } = renderStepBuild(
|
|
322
|
+
const { lastFrame, unmount } = renderStepBuild({
|
|
323
|
+
selections: { framework: ["react"] }
|
|
324
|
+
});
|
|
315
325
|
cleanup = unmount;
|
|
316
326
|
const output = lastFrame();
|
|
317
327
|
const frameworkIndex = output?.indexOf("Framework") ?? -1;
|
|
@@ -324,11 +334,12 @@ describe("StepBuild component", () => {
|
|
|
324
334
|
describe("option states", () => {
|
|
325
335
|
it("should show selected options correctly", () => {
|
|
326
336
|
const { lastFrame, unmount } = renderStepBuild({
|
|
327
|
-
allSelections: ["react (@vince)"]
|
|
337
|
+
allSelections: ["react (@vince)"],
|
|
338
|
+
selections: { framework: ["react"] }
|
|
328
339
|
});
|
|
329
340
|
cleanup = unmount;
|
|
330
341
|
const output = lastFrame();
|
|
331
|
-
globalExpect(output).toContain("
|
|
342
|
+
globalExpect(output).toContain("React");
|
|
332
343
|
});
|
|
333
344
|
it("should pass expertMode to CategoryGrid", () => {
|
|
334
345
|
const { lastFrame, unmount } = renderStepBuild({
|
|
@@ -344,7 +355,7 @@ describe("StepBuild component", () => {
|
|
|
344
355
|
});
|
|
345
356
|
cleanup = unmount;
|
|
346
357
|
const output = lastFrame();
|
|
347
|
-
globalExpect(output).toContain("
|
|
358
|
+
globalExpect(output).toContain("Descriptions: ON");
|
|
348
359
|
});
|
|
349
360
|
});
|
|
350
361
|
describe("keyboard navigation", () => {
|
|
@@ -405,7 +416,7 @@ describe("StepBuild component", () => {
|
|
|
405
416
|
const { stdin, unmount } = renderStepBuild({ onToggleDescriptions });
|
|
406
417
|
cleanup = unmount;
|
|
407
418
|
await delay(RENDER_DELAY_MS);
|
|
408
|
-
await stdin.write("
|
|
419
|
+
await stdin.write("d");
|
|
409
420
|
await delay(INPUT_DELAY_MS);
|
|
410
421
|
globalExpect(onToggleDescriptions).toHaveBeenCalled();
|
|
411
422
|
});
|
|
@@ -448,8 +459,10 @@ describe("StepBuild component", () => {
|
|
|
448
459
|
it("should handle allSelections with skills from other domains", () => {
|
|
449
460
|
const { lastFrame, unmount } = renderStepBuild({
|
|
450
461
|
domain: "web",
|
|
451
|
-
allSelections: ["hono (@vince)", "postgres (@vince)"]
|
|
462
|
+
allSelections: ["hono (@vince)", "postgres (@vince)"],
|
|
452
463
|
// API skills
|
|
464
|
+
selections: { framework: ["react"] }
|
|
465
|
+
// Need framework to see other categories
|
|
453
466
|
});
|
|
454
467
|
cleanup = unmount;
|
|
455
468
|
const output = lastFrame();
|
|
@@ -573,10 +586,9 @@ describe("StepBuild component", () => {
|
|
|
573
586
|
cleanup = unmount;
|
|
574
587
|
const output = lastFrame();
|
|
575
588
|
globalExpect(output).toContain("SPACE select");
|
|
576
|
-
globalExpect(output).toContain("
|
|
589
|
+
globalExpect(output).toContain("desc");
|
|
577
590
|
globalExpect(output).toContain("ENTER continue");
|
|
578
|
-
globalExpect(output).toContain("ESC");
|
|
579
|
-
globalExpect(output).toContain("back");
|
|
591
|
+
globalExpect(output).toContain("ESC back");
|
|
580
592
|
});
|
|
581
593
|
it("should show arrow key navigation hints", () => {
|
|
582
594
|
const { lastFrame, unmount } = renderStepBuild();
|