create-lego-one 2.0.10 → 2.0.13

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.
Files changed (242) hide show
  1. package/dist/index.cjs +179 -0
  2. package/dist/index.cjs.map +1 -1
  3. package/package.json +5 -3
  4. package/template/.cursor/rules/rules.mdc +639 -0
  5. package/template/.dockerignore +58 -0
  6. package/template/.env.example +18 -0
  7. package/template/.eslintignore +5 -0
  8. package/template/.eslintrc.js +28 -0
  9. package/template/.prettierignore +6 -0
  10. package/template/.prettierrc +11 -0
  11. package/template/CLAUDE.md +634 -0
  12. package/template/Dockerfile +67 -0
  13. package/template/PROMPT.md +457 -0
  14. package/template/README.md +325 -0
  15. package/template/docker-compose.yml +48 -0
  16. package/template/docker-entrypoint.sh +23 -0
  17. package/template/docs/checkpoints/.template.md +64 -0
  18. package/template/docs/checkpoints/framework/01-infrastructure-setup.md +132 -0
  19. package/template/docs/checkpoints/framework/02-pocketbase-setup.md +155 -0
  20. package/template/docs/checkpoints/framework/03-host-kernel.md +170 -0
  21. package/template/docs/checkpoints/framework/04-auth-system.md +163 -0
  22. package/template/docs/checkpoints/framework/phase-05-multitenancy-rbac.md +223 -0
  23. package/template/docs/checkpoints/framework/phase-06-ui-components.md +260 -0
  24. package/template/docs/checkpoints/framework/phase-07-communication-system.md +276 -0
  25. package/template/docs/checkpoints/framework/phase-08-plugin-system.md +91 -0
  26. package/template/docs/checkpoints/framework/phase-09-dashboard-plugin.md +111 -0
  27. package/template/docs/checkpoints/framework/phase-10-todo-plugin.md +169 -0
  28. package/template/docs/checkpoints/framework/phase-11-testing.md +264 -0
  29. package/template/docs/checkpoints/framework/phase-12-deployment.md +294 -0
  30. package/template/docs/checkpoints/framework/phase-13-documentation.md +312 -0
  31. package/template/docs/framework/plans/00-index.md +164 -0
  32. package/template/docs/framework/plans/01-infrastructure-setup.md +855 -0
  33. package/template/docs/framework/plans/02-pocketbase-setup.md +1374 -0
  34. package/template/docs/framework/plans/03-host-kernel.md +1518 -0
  35. package/template/docs/framework/plans/04-auth-system.md +1466 -0
  36. package/template/docs/framework/plans/05-multitenancy-rbac.md +1527 -0
  37. package/template/docs/framework/plans/06-ui-components.md +1478 -0
  38. package/template/docs/framework/plans/07-communication-system.md +1106 -0
  39. package/template/docs/framework/plans/08-plugin-system.md +1179 -0
  40. package/template/docs/framework/plans/09-dashboard-plugin.md +1137 -0
  41. package/template/docs/framework/plans/10-todo-plugin.md +1343 -0
  42. package/template/docs/framework/plans/11-testing.md +935 -0
  43. package/template/docs/framework/plans/12-deployment.md +896 -0
  44. package/template/docs/framework/prompts/0-boilerplate-modernjs.md +151 -0
  45. package/template/docs/framework/research/00-modernjs-audit.md +488 -0
  46. package/template/docs/framework/research/01-system-blueprint.md +721 -0
  47. package/template/docs/framework/research/02-data-migration-protocol.md +699 -0
  48. package/template/docs/framework/research/03-host-setup.md +714 -0
  49. package/template/docs/framework/research/04-plugin-architecture.md +645 -0
  50. package/template/docs/framework/research/05-slot-injection-pattern.md +671 -0
  51. package/template/docs/framework/research/06-cli-strategy.md +615 -0
  52. package/template/docs/framework/research/07-deployment.md +629 -0
  53. package/template/docs/framework/research/README.md +282 -0
  54. package/template/docs/framework/setup/00-index.md +210 -0
  55. package/template/docs/framework/setup/01-framework-structure.md +308 -0
  56. package/template/docs/framework/setup/02-development-workflow.md +405 -0
  57. package/template/docs/framework/setup/03-environment-setup.md +215 -0
  58. package/template/docs/framework/setup/04-kernel-architecture.md +499 -0
  59. package/template/docs/framework/setup/05-plugin-system.md +620 -0
  60. package/template/docs/framework/setup/06-communication-patterns.md +451 -0
  61. package/template/docs/framework/setup/07-plugin-development.md +582 -0
  62. package/template/docs/framework/setup/08-component-library.md +658 -0
  63. package/template/docs/framework/setup/09-data-integration.md +609 -0
  64. package/template/docs/framework/setup/10-auth-rbac.md +497 -0
  65. package/template/docs/framework/setup/11-hooks-api.md +393 -0
  66. package/template/docs/framework/setup/12-components-api.md +665 -0
  67. package/template/docs/framework/setup/13-deployment-guide.md +566 -0
  68. package/template/docs/framework/setup/README.md +548 -0
  69. package/template/host/e2e/auth.spec.ts +38 -0
  70. package/template/host/e2e/layout.spec.ts +38 -0
  71. package/template/host/modern.config.ts +19 -0
  72. package/template/host/package.json +71 -0
  73. package/template/host/playwright.config.ts +34 -0
  74. package/template/host/postcss.config.mjs +6 -0
  75. package/template/host/src/App.tsx +6 -0
  76. package/template/host/src/bootstrap.tsx +74 -0
  77. package/template/host/src/global.css +59 -0
  78. package/template/host/src/index.ts +2 -0
  79. package/template/host/src/kernel/__tests__/lib-utils.test.ts +32 -0
  80. package/template/host/src/kernel/__tests__/rbac-hooks.test.tsx +114 -0
  81. package/template/host/src/kernel/__tests__/rbac-utils.test.ts +108 -0
  82. package/template/host/src/kernel/auth/ProtectedRoute.tsx +41 -0
  83. package/template/host/src/kernel/auth/components/LoginForm.tsx +97 -0
  84. package/template/host/src/kernel/auth/components/LogoutButton.tsx +79 -0
  85. package/template/host/src/kernel/auth/hooks.ts +174 -0
  86. package/template/host/src/kernel/auth/index.ts +5 -0
  87. package/template/host/src/kernel/auth/schemas.ts +27 -0
  88. package/template/host/src/kernel/auth/service.ts +197 -0
  89. package/template/host/src/kernel/auth/types.ts +36 -0
  90. package/template/host/src/kernel/channels/ChannelBus.ts +181 -0
  91. package/template/host/src/kernel/channels/ChannelProvider.tsx +57 -0
  92. package/template/host/src/kernel/channels/events.ts +27 -0
  93. package/template/host/src/kernel/channels/hooks.ts +168 -0
  94. package/template/host/src/kernel/channels/index.ts +6 -0
  95. package/template/host/src/kernel/channels/integrations/ToastIntegration.tsx +60 -0
  96. package/template/host/src/kernel/channels/plugin-hooks.ts +72 -0
  97. package/template/host/src/kernel/channels/types.ts +112 -0
  98. package/template/host/src/kernel/components/__tests__/Badge.test.tsx +35 -0
  99. package/template/host/src/kernel/components/__tests__/Button.test.tsx +63 -0
  100. package/template/host/src/kernel/components/__tests__/Input.test.tsx +64 -0
  101. package/template/host/src/kernel/components/index.ts +32 -0
  102. package/template/host/src/kernel/components/ui/alert.tsx +58 -0
  103. package/template/host/src/kernel/components/ui/avatar.tsx +47 -0
  104. package/template/host/src/kernel/components/ui/badge.tsx +35 -0
  105. package/template/host/src/kernel/components/ui/button.tsx +50 -0
  106. package/template/host/src/kernel/components/ui/card.tsx +78 -0
  107. package/template/host/src/kernel/components/ui/dialog.tsx +116 -0
  108. package/template/host/src/kernel/components/ui/dropdown-menu.tsx +192 -0
  109. package/template/host/src/kernel/components/ui/index.ts +7 -0
  110. package/template/host/src/kernel/components/ui/input.tsx +24 -0
  111. package/template/host/src/kernel/components/ui/label.tsx +21 -0
  112. package/template/host/src/kernel/components/ui/popover.tsx +28 -0
  113. package/template/host/src/kernel/components/ui/progress.tsx +25 -0
  114. package/template/host/src/kernel/components/ui/scroll-area.tsx +45 -0
  115. package/template/host/src/kernel/components/ui/select.tsx +155 -0
  116. package/template/host/src/kernel/components/ui/separator.tsx +28 -0
  117. package/template/host/src/kernel/components/ui/skeleton.tsx +15 -0
  118. package/template/host/src/kernel/components/ui/switch.tsx +26 -0
  119. package/template/host/src/kernel/components/ui/table.tsx +116 -0
  120. package/template/host/src/kernel/components/ui/tabs.tsx +52 -0
  121. package/template/host/src/kernel/components/ui/toast.tsx +126 -0
  122. package/template/host/src/kernel/components/ui/toaster.tsx +34 -0
  123. package/template/host/src/kernel/components/ui/tooltip.tsx +27 -0
  124. package/template/host/src/kernel/components/ui/use-toast.ts +183 -0
  125. package/template/host/src/kernel/index.ts +48 -0
  126. package/template/host/src/kernel/lib/cn.ts +1 -0
  127. package/template/host/src/kernel/lib/utils.ts +36 -0
  128. package/template/host/src/kernel/plugins/Slot.tsx +41 -0
  129. package/template/host/src/kernel/plugins/SlotProvider.tsx +88 -0
  130. package/template/host/src/kernel/plugins/index.ts +23 -0
  131. package/template/host/src/kernel/plugins/loader.ts +122 -0
  132. package/template/host/src/kernel/plugins/schemas.ts +54 -0
  133. package/template/host/src/kernel/plugins/store.ts +185 -0
  134. package/template/host/src/kernel/plugins/types.ts +103 -0
  135. package/template/host/src/kernel/providers/PocketBaseProvider.tsx +70 -0
  136. package/template/host/src/kernel/providers/QueryProvider.tsx +28 -0
  137. package/template/host/src/kernel/providers/ThemeProvider.tsx +25 -0
  138. package/template/host/src/kernel/providers/index.ts +3 -0
  139. package/template/host/src/kernel/rbac/components/OrganizationSelector.tsx +69 -0
  140. package/template/host/src/kernel/rbac/components/PermissionGate.tsx +43 -0
  141. package/template/host/src/kernel/rbac/hooks.ts +379 -0
  142. package/template/host/src/kernel/rbac/index.ts +6 -0
  143. package/template/host/src/kernel/rbac/service.ts +504 -0
  144. package/template/host/src/kernel/rbac/types.ts +164 -0
  145. package/template/host/src/kernel/rbac/utils.ts +34 -0
  146. package/template/host/src/kernel/shared-state/bridge.ts +31 -0
  147. package/template/host/src/kernel/shared-state/index.ts +3 -0
  148. package/template/host/src/kernel/shared-state/store.ts +62 -0
  149. package/template/host/src/kernel/shared-state/types.ts +60 -0
  150. package/template/host/src/kernel/use-migrations.ts +72 -0
  151. package/template/host/src/layout/MobileMenu.tsx +61 -0
  152. package/template/host/src/layout/Shell.tsx +42 -0
  153. package/template/host/src/layout/Sidebar.tsx +178 -0
  154. package/template/host/src/layout/Topbar.tsx +50 -0
  155. package/template/host/src/layout/index.ts +4 -0
  156. package/template/host/src/lib/pocketbase/client.ts +38 -0
  157. package/template/host/src/lib/pocketbase/collections/audit_logs.ts +87 -0
  158. package/template/host/src/lib/pocketbase/collections/index.ts +19 -0
  159. package/template/host/src/lib/pocketbase/collections/organizations.ts +63 -0
  160. package/template/host/src/lib/pocketbase/collections/permissions.ts +57 -0
  161. package/template/host/src/lib/pocketbase/collections/roles.ts +55 -0
  162. package/template/host/src/lib/pocketbase/collections/todos.ts +74 -0
  163. package/template/host/src/lib/pocketbase/collections/user_roles.ts +57 -0
  164. package/template/host/src/lib/pocketbase/collections/users.ts +43 -0
  165. package/template/host/src/lib/pocketbase/index.ts +5 -0
  166. package/template/host/src/lib/pocketbase/migrations.ts +44 -0
  167. package/template/host/src/lib/pocketbase/seed/permissions.ts +8 -0
  168. package/template/host/src/lib/pocketbase/seed/roles.ts +22 -0
  169. package/template/host/src/lib/pocketbase/seed.ts +113 -0
  170. package/template/host/src/lib/pocketbase/types.ts +102 -0
  171. package/template/host/src/modern.runtime.ts +26 -0
  172. package/template/host/src/plugins.d.ts +9 -0
  173. package/template/host/src/providers/PocketBaseProvider.tsx +30 -0
  174. package/template/host/src/routes/_.tsx +6 -0
  175. package/template/host/src/routes/dashboard._.tsx +41 -0
  176. package/template/host/src/routes/index.tsx +93 -0
  177. package/template/host/src/routes/login.tsx +36 -0
  178. package/template/host/src/saas.config.ts +52 -0
  179. package/template/host/src/test/setup.ts +65 -0
  180. package/template/host/src/test/utils.tsx +69 -0
  181. package/template/host/src/test/vitest-globals.d.ts +19 -0
  182. package/template/host/src/vite-env.d.ts +16 -0
  183. package/template/host/tailwind.config.ts +77 -0
  184. package/template/host/tsconfig.json +19 -0
  185. package/template/host/vitest.config.ts +30 -0
  186. package/template/nginx.conf +72 -0
  187. package/template/package.json +44 -0
  188. package/template/packages/plugins/@lego/plugin-dashboard/modern.config.ts +19 -0
  189. package/template/packages/plugins/@lego/plugin-dashboard/package.json +35 -0
  190. package/template/packages/plugins/@lego/plugin-dashboard/postcss.config.mjs +6 -0
  191. package/template/packages/plugins/@lego/plugin-dashboard/src/App.tsx +27 -0
  192. package/template/packages/plugins/@lego/plugin-dashboard/src/components/ActivityFeed.tsx +63 -0
  193. package/template/packages/plugins/@lego/plugin-dashboard/src/components/QuickActionSlot.tsx +11 -0
  194. package/template/packages/plugins/@lego/plugin-dashboard/src/components/QuickActions.tsx +68 -0
  195. package/template/packages/plugins/@lego/plugin-dashboard/src/components/SidebarWidget.tsx +35 -0
  196. package/template/packages/plugins/@lego/plugin-dashboard/src/components/StatCard.tsx +47 -0
  197. package/template/packages/plugins/@lego/plugin-dashboard/src/global.css +24 -0
  198. package/template/packages/plugins/@lego/plugin-dashboard/src/hooks/useChannelIntegration.ts +43 -0
  199. package/template/packages/plugins/@lego/plugin-dashboard/src/hooks/useDashboardStats.ts +65 -0
  200. package/template/packages/plugins/@lego/plugin-dashboard/src/hooks/usePocketBase.ts +47 -0
  201. package/template/packages/plugins/@lego/plugin-dashboard/src/hooks/useRecentActivity.ts +55 -0
  202. package/template/packages/plugins/@lego/plugin-dashboard/src/lib/utils.ts +6 -0
  203. package/template/packages/plugins/@lego/plugin-dashboard/src/pages/DashboardPage.tsx +105 -0
  204. package/template/packages/plugins/@lego/plugin-dashboard/src/plugin.config.ts +121 -0
  205. package/template/packages/plugins/@lego/plugin-dashboard/src/plugin.ts +18 -0
  206. package/template/packages/plugins/@lego/plugin-dashboard/src/vite-env.d.ts +32 -0
  207. package/template/packages/plugins/@lego/plugin-dashboard/tailwind.config.ts +35 -0
  208. package/template/packages/plugins/@lego/plugin-dashboard/tsconfig.json +18 -0
  209. package/template/packages/plugins/@lego/plugin-todo/modern.config.ts +18 -0
  210. package/template/packages/plugins/@lego/plugin-todo/package.json +41 -0
  211. package/template/packages/plugins/@lego/plugin-todo/postcss.config.mjs +6 -0
  212. package/template/packages/plugins/@lego/plugin-todo/src/App.tsx +12 -0
  213. package/template/packages/plugins/@lego/plugin-todo/src/components/SidebarWidget.tsx +16 -0
  214. package/template/packages/plugins/@lego/plugin-todo/src/components/TodoDialog.tsx +55 -0
  215. package/template/packages/plugins/@lego/plugin-todo/src/components/TodoFilters.tsx +79 -0
  216. package/template/packages/plugins/@lego/plugin-todo/src/components/TodoForm.tsx +94 -0
  217. package/template/packages/plugins/@lego/plugin-todo/src/components/TodoItem.tsx +121 -0
  218. package/template/packages/plugins/@lego/plugin-todo/src/components/TodoList.tsx +41 -0
  219. package/template/packages/plugins/@lego/plugin-todo/src/components/index.ts +6 -0
  220. package/template/packages/plugins/@lego/plugin-todo/src/global.css +59 -0
  221. package/template/packages/plugins/@lego/plugin-todo/src/hooks/useCreateTodo.ts +62 -0
  222. package/template/packages/plugins/@lego/plugin-todo/src/hooks/useDeleteTodo.ts +46 -0
  223. package/template/packages/plugins/@lego/plugin-todo/src/hooks/usePocketBase.ts +38 -0
  224. package/template/packages/plugins/@lego/plugin-todo/src/hooks/useTodos.ts +64 -0
  225. package/template/packages/plugins/@lego/plugin-todo/src/hooks/useUpdateTodo.ts +35 -0
  226. package/template/packages/plugins/@lego/plugin-todo/src/index.tsx +5 -0
  227. package/template/packages/plugins/@lego/plugin-todo/src/lib/utils.ts +20 -0
  228. package/template/packages/plugins/@lego/plugin-todo/src/pages/TodoPage.tsx +89 -0
  229. package/template/packages/plugins/@lego/plugin-todo/src/plugin.config.ts +104 -0
  230. package/template/packages/plugins/@lego/plugin-todo/src/plugin.ts +13 -0
  231. package/template/packages/plugins/@lego/plugin-todo/src/schemas.ts +37 -0
  232. package/template/packages/plugins/@lego/plugin-todo/src/types.ts +42 -0
  233. package/template/packages/plugins/@lego/plugin-todo/src/vite-env.d.ts +31 -0
  234. package/template/packages/plugins/@lego/plugin-todo/tailwind.config.ts +51 -0
  235. package/template/packages/plugins/@lego/plugin-todo/tsconfig.json +18 -0
  236. package/template/pnpm-workspace.yaml +4 -0
  237. package/template/pocketbase/CHANGELOG.md +911 -0
  238. package/template/pocketbase/LICENSE.md +17 -0
  239. package/template/scripts/create-plugin.js +221 -0
  240. package/template/scripts/deploy.sh +56 -0
  241. package/template/tsconfig.base.json +26 -0
  242. package/template/tsconfig.json +8 -0
@@ -0,0 +1,935 @@
1
+ # Testing Implementation Plan
2
+
3
+ > **For AI Implementing This Plan:** This is document 11 of 13. Complete documents 01-10 first.
4
+
5
+ **Goal:** Implement comprehensive testing strategy with unit tests (Vitest), component tests (React Testing Library), and E2E tests (Playwright).
6
+
7
+ **Architecture:** Unit tests for utilities and hooks, component tests for UI components, E2E tests for critical user flows. Tests run in CI/CD pipeline.
8
+
9
+ **Tech Stack:** Vitest, React Testing Library, Playwright, MSW (for API mocking), TypeScript
10
+
11
+ ---
12
+
13
+ ## Prerequisites
14
+
15
+ - ✅ Completed all documents 01-10
16
+ - ✅ Application is functional with all features implemented
17
+
18
+ ---
19
+
20
+ ## Task 1: Setup Testing Infrastructure
21
+
22
+ **Files:**
23
+ - Create: `vitest.workspace.ts`
24
+ - Create: `vitest.config.ts`
25
+ - Modify: `package.json` (root)
26
+
27
+ ### Step 1: Create root testing dependencies
28
+
29
+ **File:** `package.json` (root)
30
+
31
+ Add to devDependencies:
32
+
33
+ ```json
34
+ {
35
+ "devDependencies": {
36
+ "@playwright/test": "^1.48.0",
37
+ "@testing-library/jest-dom": "^6.6.2",
38
+ "@testing-library/react": "^16.0.1",
39
+ "@testing-library/user-event": "^14.5.2",
40
+ "@vitejs/plugin-react": "^4.3.2",
41
+ "@vitest/ui": "^2.1.2",
42
+ "jsdom": "^25.0.1",
43
+ "msw": "^2.4.0",
44
+ "vitest": "^2.1.2",
45
+ "happy-dom": "^15.7.3"
46
+ }
47
+ }
48
+ ```
49
+
50
+ Add test scripts:
51
+
52
+ ```json
53
+ {
54
+ "scripts": {
55
+ "test": "vitest",
56
+ "test:ui": "vitest --ui",
57
+ "test:run": "vitest run",
58
+ "test:coverage": "vitest run --coverage",
59
+ "test:e2e": "playwright test",
60
+ "test:e2e:ui": "playwright test --ui",
61
+ "test:e2e:debug": "playwright test --debug"
62
+ }
63
+ }
64
+ ```
65
+
66
+ ### Step 2: Create Vitest workspace config
67
+
68
+ **File:** `vitest.workspace.ts`
69
+
70
+ ```typescript
71
+ import { defineWorkspace } from 'vitest/config';
72
+
73
+ export default defineWorkspace([
74
+ // Host app tests
75
+ {
76
+ test: {
77
+ name: 'host',
78
+ root: './host',
79
+ environment: 'jsdom',
80
+ setupFiles: ['./src/test/setup.ts'],
81
+ include: ['**/__tests__/**/*.test.{ts,tsx}'],
82
+ globals: true,
83
+ coverage: {
84
+ provider: 'v8',
85
+ reporter: ['text', 'json', 'html'],
86
+ exclude: ['node_modules/', 'dist/', '**/*.test.{ts,tsx}'],
87
+ },
88
+ },
89
+ },
90
+ // Dashboard plugin tests
91
+ {
92
+ test: {
93
+ name: 'plugin-dashboard',
94
+ root: './packages/plugins/@lego/plugin-dashboard',
95
+ environment: 'jsdom',
96
+ setupFiles: ['./src/test/setup.ts'],
97
+ include: ['**/__tests__/**/*.test.{ts,tsx}'],
98
+ globals: true,
99
+ },
100
+ },
101
+ // Todo plugin tests
102
+ {
103
+ test: {
104
+ name: 'plugin-todo',
105
+ root: './packages/plugins/@lego/plugin-todo',
106
+ environment: 'jsdom',
107
+ setupFiles: ['./src/test/setup.ts'],
108
+ include: ['**/__tests__/**/*.test.{ts,tsx}'],
109
+ globals: true,
110
+ },
111
+ },
112
+ ]);
113
+ ```
114
+
115
+ ### Step 3: Create Vitest config for host
116
+
117
+ **File:** `host/vitest.config.ts`
118
+
119
+ ```typescript
120
+ import { defineConfig } from 'vitest/config';
121
+ import react from '@vitejs/plugin-react';
122
+ import { resolve } from 'path';
123
+
124
+ export default defineConfig({
125
+ plugins: [react()],
126
+ test: {
127
+ globals: true,
128
+ environment: 'jsdom',
129
+ setupFiles: ['./src/test/setup.ts'],
130
+ include: ['**/__tests__/**/*.test.{ts,tsx}'],
131
+ alias: {
132
+ '@': resolve(__dirname, './src'),
133
+ '@lego/kernel': resolve(__dirname, './src/kernel'),
134
+ },
135
+ },
136
+ resolve: {
137
+ alias: {
138
+ '@': resolve(__dirname, './src'),
139
+ },
140
+ },
141
+ });
142
+ ```
143
+
144
+ ### Step 4: Create test setup files
145
+
146
+ **File:** `host/src/test/setup.ts`
147
+
148
+ ```typescript
149
+ import { expect, afterEach, vi } from 'vitest';
150
+ import { cleanup } from '@testing-library/react';
151
+ import '@testing-library/jest-dom';
152
+
153
+ // Cleanup after each test
154
+ afterEach(() => {
155
+ cleanup();
156
+ });
157
+
158
+ // Mock PocketBase
159
+ vi.mock('pocketbase', () => ({
160
+ default: vi.fn().mockImplementation(() => ({
161
+ collection: vi.fn(),
162
+ authStore: {
163
+ isValid: false,
164
+ token: '',
165
+ model: null,
166
+ save: vi.fn(),
167
+ clear: vi.fn(),
168
+ onChange: vi.fn(),
169
+ },
170
+ })),
171
+ }));
172
+
173
+ // Mock window bridge
174
+ global.__LEGO_KERNEL_STATE__ = {
175
+ useGlobalKernelState: vi.fn(),
176
+ };
177
+ ```
178
+
179
+ ---
180
+
181
+ ## Task 2: Setup Playwright for E2E Tests
182
+
183
+ **Files:**
184
+ - Create: `playwright.config.ts`
185
+ - Create: `e2e/playwright.config.ts`
186
+
187
+ ### Step 1: Create Playwright config
188
+
189
+ **File:** `playwright.config.ts`
190
+
191
+ ```typescript
192
+ import { defineConfig, devices } from '@playwright/test';
193
+
194
+ export default defineConfig({
195
+ testDir: './e2e',
196
+ fullyParallel: true,
197
+ forbidOnly: !!process.env.CI,
198
+ retries: process.env.CI ? 2 : 0,
199
+ workers: process.env.CI ? 1 : undefined,
200
+ reporter: 'html',
201
+ use: {
202
+ baseURL: 'http://localhost:8080',
203
+ trace: 'on-first-retry',
204
+ screenshot: 'only-on-failure',
205
+ },
206
+
207
+ projects: [
208
+ {
209
+ name: 'chromium',
210
+ use: { ...devices['Desktop Chrome'] },
211
+ },
212
+ {
213
+ name: 'firefox',
214
+ use: { ...devices['Desktop Firefox'] },
215
+ },
216
+ {
217
+ name: 'webkit',
218
+ use: { ...devices['Desktop Safari'] },
219
+ },
220
+ {
221
+ name: 'Mobile Chrome',
222
+ use: { ...devices['Pixel 5'] },
223
+ },
224
+ {
225
+ name: 'Mobile Safari',
226
+ use: { ...devices['iPhone 12'] },
227
+ },
228
+ ],
229
+
230
+ webServer: {
231
+ command: 'cd host && pnpm run dev',
232
+ url: 'http://localhost:8080',
233
+ reuseExistingServer: !process.env.CI,
234
+ timeout: 120 * 1000,
235
+ },
236
+ });
237
+ ```
238
+
239
+ ### Step 2: Create E2E test helpers
240
+
241
+ **File:** `e2e/helpers.ts`
242
+
243
+ ```typescript
244
+ import { Page, expect } from '@playwright/test';
245
+
246
+ export class DashboardPage {
247
+ constructor(private page: Page) {}
248
+
249
+ async goto() {
250
+ await this.page.goto('/dashboard');
251
+ }
252
+
253
+ async expectStatsVisible() {
254
+ await expect(this.page.getByText('Total Users')).toBeVisible();
255
+ await expect(this.page.getByText('Active Todos')).toBeVisible();
256
+ await expect(this.page.getByText('Completed Todos')).toBeVisible();
257
+ }
258
+ }
259
+
260
+ export class TodoPage {
261
+ constructor(private page: Page) {}
262
+
263
+ async goto() {
264
+ await this.page.goto('/todos');
265
+ }
266
+
267
+ async createTodo(title: string) {
268
+ await this.page.getByRole('button', { name: 'New Todo' }).click();
269
+ await this.page.getByLabel('Title').fill(title);
270
+ await this.page.getByRole('button', { name: 'Create Todo' }).click();
271
+ }
272
+
273
+ async expectTodoVisible(title: string) {
274
+ await expect(this.page.getByText(title)).toBeVisible();
275
+ }
276
+
277
+ async toggleTodo(title: string) {
278
+ const todo = this.page.getByText(title).locator('..').getByRole('button').first();
279
+ await todo.click();
280
+ }
281
+
282
+ async deleteTodo(title: string) {
283
+ const todo = this.page.getByText(title).locator('..');
284
+ await todo.getByRole('button', { name: /delete/i }).click();
285
+ await this.page.getByRole('button', { name: /confirm/i }).click();
286
+ }
287
+ }
288
+
289
+ export class AuthHelper {
290
+ constructor(private page: Page) {}
291
+
292
+ async login(email = 'admin@example.com', password = 'admin123') {
293
+ await this.page.goto('/login');
294
+ await this.page.getByLabel('Email').fill(email);
295
+ await this.page.getByLabel('Password').fill(password);
296
+ await this.page.getByRole('button', { name: 'Sign In' }).click();
297
+ await this.page.waitForURL('/dashboard');
298
+ }
299
+
300
+ async logout() {
301
+ await this.page.getByRole('button').filter({ hasText: /admin/i }).click();
302
+ await this.page.getByRole('menuitem', { name: /log out/i }).click();
303
+ await this.page.waitForURL('/login');
304
+ }
305
+ }
306
+ ```
307
+
308
+ ---
309
+
310
+ ## Task 3: Create Unit Tests
311
+
312
+ **Files:**
313
+ - Create: `host/src/kernel/lib/__tests__/utils.test.ts`
314
+ - Create: `host/src/kernel/shared-state/__tests__/store.test.ts`
315
+
316
+ ### Step 1: Test utility functions
317
+
318
+ **File:** `host/src/kernel/lib/__tests__/utils.test.ts`
319
+
320
+ ```typescript
321
+ import { describe, it, expect } from 'vitest';
322
+ import { cn, getInitials, formatDate, formatRelativeTime } from '../utils';
323
+
324
+ describe('cn (classnames utility)', () => {
325
+ it('should merge classnames correctly', () => {
326
+ expect(cn('foo', 'bar')).toBe('foo bar');
327
+ });
328
+
329
+ it('should handle conditional classes', () => {
330
+ expect(cn('foo', false && 'bar', 'baz')).toBe('foo baz');
331
+ });
332
+
333
+ it('should handle tailwind merge conflicts', () => {
334
+ expect(cn('p-4', 'p-2')).toBe('p-2');
335
+ });
336
+ });
337
+
338
+ describe('getInitials', () => {
339
+ it('should extract initials from name', () => {
340
+ expect(getInitials('John Doe')).toBe('JD');
341
+ });
342
+
343
+ it('should handle single word names', () => {
344
+ expect(getInitials('John')).toBe('J');
345
+ });
346
+
347
+ it('should handle multiple words', () => {
348
+ expect(getInitials('John Middle Doe')).toBe('JM');
349
+ });
350
+
351
+ it('should limit to 2 characters', () => {
352
+ expect(getInitials('John Middle Last Doe')).toBe('JM');
353
+ });
354
+ });
355
+
356
+ describe('formatDate', () => {
357
+ it('should format date correctly', () => {
358
+ const date = new Date('2024-01-15');
359
+ const formatted = formatDate(date);
360
+ expect(formatted).toMatch(/Jan/);
361
+ expect(formatted).toMatch(/15/);
362
+ expect(formatted).toMatch(/2024/);
363
+ });
364
+ });
365
+
366
+ describe('formatRelativeTime', () => {
367
+ it('should return "just now" for very recent times', () => {
368
+ const now = new Date();
369
+ expect(formatRelativeTime(now)).toBe('just now');
370
+ });
371
+
372
+ it('should return minutes ago', () => {
373
+ const twoMinutesAgo = new Date(Date.now() - 2 * 60 * 1000);
374
+ expect(formatRelativeTime(twoMinutesAgo)).toBe('2m ago');
375
+ });
376
+
377
+ it('should return hours ago', () => {
378
+ const twoHoursAgo = new Date(Date.now() - 2 * 60 * 60 * 1000);
379
+ expect(formatRelativeTime(twoHoursAgo)).toBe('2h ago');
380
+ });
381
+
382
+ it('should return days ago', () => {
383
+ const twoDaysAgo = new Date(Date.now() - 2 * 24 * 60 * 60 * 1000);
384
+ expect(formatRelativeTime(twoDaysAgo)).toBe('2d ago');
385
+ });
386
+ });
387
+ ```
388
+
389
+ ### Step 2: Test shared state store
390
+
391
+ **File:** `host/src/kernel/shared-state/__tests__/store.test.ts`
392
+
393
+ ```typescript
394
+ import { describe, it, expect, beforeEach } from 'vitest';
395
+ import { act, renderHook } from '@testing-library/react';
396
+ import { useGlobalKernelState } from '../store';
397
+
398
+ describe('useGlobalKernelState', () => {
399
+ beforeEach(() => {
400
+ // Reset state before each test
401
+ const { result } = renderHook(() => useGlobalKernelState());
402
+ act(() => {
403
+ result.current.clearAuth();
404
+ });
405
+ });
406
+
407
+ it('should initialize with default state', () => {
408
+ const { result } = renderHook(() => useGlobalKernelState());
409
+
410
+ expect(result.current.user).toBeNull();
411
+ expect(result.current.isAuthenticated).toBe(false);
412
+ expect(result.current.theme).toBe('system');
413
+ expect(result.current.sidebarOpen).toBe(true);
414
+ });
415
+
416
+ it('should set user', () => {
417
+ const { result } = renderHook(() => useGlobalKernelState());
418
+
419
+ act(() => {
420
+ result.current.setUser({
421
+ id: '123',
422
+ email: 'test@example.com',
423
+ name: 'Test User',
424
+ });
425
+ });
426
+
427
+ expect(result.current.user).toEqual({
428
+ id: '123',
429
+ email: 'test@example.com',
430
+ name: 'Test User',
431
+ });
432
+ expect(result.current.isAuthenticated).toBe(true);
433
+ });
434
+
435
+ it('should toggle sidebar', () => {
436
+ const { result } = renderHook(() => useGlobalKernelState());
437
+
438
+ expect(result.current.sidebarOpen).toBe(true);
439
+
440
+ act(() => {
441
+ result.current.toggleSidebar();
442
+ });
443
+
444
+ expect(result.current.sidebarOpen).toBe(false);
445
+
446
+ act(() => {
447
+ result.current.toggleSidebar();
448
+ });
449
+
450
+ expect(result.current.sidebarOpen).toBe(true);
451
+ });
452
+
453
+ it('should add and remove toasts', () => {
454
+ const { result } = renderHook(() => useGlobalKernelState());
455
+
456
+ act(() => {
457
+ result.current.addToast({
458
+ title: 'Test toast',
459
+ description: 'Test description',
460
+ });
461
+ });
462
+
463
+ expect(result.current.toasts).toHaveLength(1);
464
+ expect(result.current.toasts[0].title).toBe('Test toast');
465
+
466
+ act(() => {
467
+ result.current.removeToast(result.current.toasts[0].id);
468
+ });
469
+
470
+ expect(result.current.toasts).toHaveLength(0);
471
+ });
472
+ });
473
+ ```
474
+
475
+ ---
476
+
477
+ ## Task 4: Create Component Tests
478
+
479
+ **Files:**
480
+ - Create: `host/src/kernel/components/ui/__tests__/button.test.tsx`
481
+ - Create: `host/src/kernel/auth/__tests__/LoginForm.test.tsx`
482
+
483
+ ### Step 1: Test button component
484
+
485
+ **File:** `host/src/kernel/components/ui/__tests__/button.test.tsx`
486
+
487
+ ```typescript
488
+ import { describe, it, expect } from 'vitest';
489
+ import { render, screen } from '@testing-library/react';
490
+ import userEvent from '@testing-library/user-event';
491
+ import { Button } from '../button';
492
+
493
+ describe('Button', () => {
494
+ it('should render children', () => {
495
+ render(<Button>Click me</Button>);
496
+ expect(screen.getByRole('button')).toHaveTextContent('Click me');
497
+ });
498
+
499
+ it('should call onClick when clicked', async () => {
500
+ const handleClick = vi.fn();
501
+ const user = userEvent.setup();
502
+
503
+ render(<Button onClick={handleClick}>Click me</Button>);
504
+
505
+ await user.click(screen.getByRole('button'));
506
+
507
+ expect(handleClick).toHaveBeenCalledTimes(1);
508
+ });
509
+
510
+ it('should be disabled when disabled prop is true', async () => {
511
+ const handleClick = vi.fn();
512
+ const user = userEvent.setup();
513
+
514
+ render(
515
+ <Button onClick={handleClick} disabled>
516
+ Click me
517
+ </Button>
518
+ );
519
+
520
+ await user.click(screen.getByRole('button'));
521
+
522
+ expect(handleClick).not.toHaveBeenCalled();
523
+ });
524
+
525
+ it('should apply variant classes', () => {
526
+ const { rerender } = render(<Button variant="destructive">Delete</Button>);
527
+ expect(screen.getByRole('button')).toHaveClass('bg-destructive');
528
+
529
+ rerender(<Button variant="outline">Cancel</Button>);
530
+ expect(screen.getByRole('button')).toHaveClass('border');
531
+ });
532
+ });
533
+ ```
534
+
535
+ ### Step 2: Test login form component
536
+
537
+ **File:** `host/src/kernel/auth/__tests__/LoginForm.test.tsx`
538
+
539
+ ```typescript
540
+ import { describe, it, expect, vi, beforeEach } from 'vitest';
541
+ import { render, screen, waitFor } from '@testing-library/react';
542
+ import userEvent from '@testing-library/user-event';
543
+ import { LoginForm } from '../components/LoginForm';
544
+
545
+ // Mock useAuth hook
546
+ vi.mock('../hooks', () => ({
547
+ useAuth: () => ({
548
+ login: mockLogin,
549
+ isLoading: false,
550
+ error: null,
551
+ }),
552
+ }));
553
+
554
+ const mockLogin = vi.fn();
555
+
556
+ // Mock navigate
557
+ vi.mock('@modern-js/runtime/router', () => ({
558
+ useNavigate: () => mockNavigate,
559
+ }));
560
+
561
+ const mockNavigate = vi.fn();
562
+
563
+ describe('LoginForm', () => {
564
+ beforeEach(() => {
565
+ vi.clearAllMocks();
566
+ });
567
+
568
+ it('should render form fields', () => {
569
+ render(<LoginForm />);
570
+
571
+ expect(screen.getByLabelText('Email')).toBeInTheDocument();
572
+ expect(screen.getByLabelText('Password')).toBeInTheDocument();
573
+ expect(screen.getByRole('button', { name: /sign in/i })).toBeInTheDocument();
574
+ });
575
+
576
+ it('should show validation errors for empty fields', async () => {
577
+ const user = userEvent.setup();
578
+ render(<LoginForm />);
579
+
580
+ const submitButton = screen.getByRole('button', { name: /sign in/i });
581
+ await user.click(submitButton);
582
+
583
+ expect(await screen.findByText('Invalid email address')).toBeInTheDocument();
584
+ expect(screen.getByText(/password must be at least/i)).toBeInTheDocument();
585
+ });
586
+
587
+ it('should call login with form data', async () => {
588
+ const user = userEvent.setup();
589
+ render(<LoginForm />);
590
+
591
+ await user.type(screen.getByLabelText('Email'), 'admin@example.com');
592
+ await user.type(screen.getByLabelText('Password'), 'admin123');
593
+ await user.click(screen.getByRole('button', { name: /sign in/i }));
594
+
595
+ await waitFor(() => {
596
+ expect(mockLogin).toHaveBeenCalledWith({
597
+ email: 'admin@example.com',
598
+ password: 'admin123',
599
+ });
600
+ });
601
+ });
602
+
603
+ it('should navigate to dashboard on successful login', async () => {
604
+ const user = userEvent.setup();
605
+ mockLogin.mockResolvedValueOnce({});
606
+
607
+ render(<LoginForm />);
608
+
609
+ await user.type(screen.getByLabelText('Email'), 'admin@example.com');
610
+ await user.type(screen.getByLabelText('Password'), 'admin123');
611
+ await user.click(screen.getByRole('button', { name: /sign in/i }));
612
+
613
+ await waitFor(() => {
614
+ expect(mockNavigate).toHaveBeenCalledWith('/dashboard');
615
+ });
616
+ });
617
+ });
618
+ ```
619
+
620
+ ---
621
+
622
+ ## Task 5: Create E2E Tests
623
+
624
+ **Files:**
625
+ - Create: `e2e/auth.spec.ts`
626
+ - Create: `e2e/dashboard.spec.ts`
627
+ - Create: `e2e/todo.spec.ts`
628
+
629
+ ### Step 1: Test authentication flow
630
+
631
+ **File:** `e2e/auth.spec.ts`
632
+
633
+ ```typescript
634
+ import { test, expect } from '@playwright/test';
635
+ import { AuthHelper } from './helpers';
636
+
637
+ test.describe('Authentication', () => {
638
+ let auth: AuthHelper;
639
+
640
+ test.beforeEach(async ({ page }) => {
641
+ auth = new AuthHelper(page);
642
+ });
643
+
644
+ test('should login with valid credentials', async ({ page }) => {
645
+ await auth.login();
646
+
647
+ await expect(page).toHaveURL('/dashboard');
648
+ await expect(page.getByText('Welcome to your organization dashboard')).toBeVisible();
649
+ });
650
+
651
+ test('should show error with invalid credentials', async ({ page }) => {
652
+ await page.goto('/login');
653
+ await page.getByLabel('Email').fill('invalid@example.com');
654
+ await page.getByLabel('Password').fill('wrongpassword');
655
+ await page.getByRole('button', { name: 'Sign In' }).click();
656
+
657
+ await expect(page.getByText(/login failed/i)).toBeVisible();
658
+ });
659
+
660
+ test('should logout and redirect to login', async ({ page }) => {
661
+ await auth.login();
662
+ await auth.logout();
663
+
664
+ await expect(page).toHaveURL('/login');
665
+ await expect(page.getByText('Sign In')).toBeVisible();
666
+ });
667
+
668
+ test('should protect authenticated routes', async ({ page }) => {
669
+ await page.goto('/dashboard');
670
+
671
+ await expect(page).toHaveURL('/login');
672
+ });
673
+ });
674
+ ```
675
+
676
+ ### Step 2: Test dashboard functionality
677
+
678
+ **File:** `e2e/dashboard.spec.ts`
679
+
680
+ ```typescript
681
+ import { test, expect } from '@playwright/test';
682
+ import { AuthHelper } from './helpers';
683
+
684
+ test.describe('Dashboard', () => {
685
+ test.beforeEach(async ({ page }) => {
686
+ const auth = new AuthHelper(page);
687
+ await auth.login();
688
+ });
689
+
690
+ test('should display dashboard stats', async ({ page }) => {
691
+ await page.goto('/dashboard');
692
+
693
+ await expect(page.getByText('Total Users')).toBeVisible();
694
+ await expect(page.getByText('Active Todos')).toBeVisible();
695
+ await expect(page.getByText('Completed Todos')).toBeVisible();
696
+ });
697
+
698
+ test('should display recent activity', async ({ page }) => {
699
+ await page.goto('/dashboard');
700
+
701
+ await expect(page.getByText('Recent Activity')).toBeVisible();
702
+ });
703
+
704
+ test('should display quick actions', async ({ page }) => {
705
+ await page.goto('/dashboard');
706
+
707
+ await expect(page.getByText('Quick Actions')).toBeVisible();
708
+ await expect(page.getByText('New Todo')).toBeVisible();
709
+ await expect(page.getByText('Invite User')).toBeVisible();
710
+ });
711
+ });
712
+ ```
713
+
714
+ ### Step 3: Test todo CRUD functionality
715
+
716
+ **File:** `e2e/todo.spec.ts`
717
+
718
+ ```typescript
719
+ import { test, expect } from '@playwright/test';
720
+ import { AuthHelper, TodoPage } from './helpers';
721
+
722
+ test.describe('Todos', () => {
723
+ let auth: AuthHelper;
724
+ let todoPage: TodoPage;
725
+
726
+ test.beforeEach(async ({ page }) => {
727
+ auth = new AuthHelper(page);
728
+ todoPage = new TodoPage(page);
729
+ await auth.login();
730
+ });
731
+
732
+ test('should display todos page', async ({ page }) => {
733
+ await todoPage.goto();
734
+
735
+ await expect(page.getByText('Manage your tasks and stay organized')).toBeVisible();
736
+ await expect(page.getByRole('button', { name: 'New Todo' })).toBeVisible();
737
+ });
738
+
739
+ test('should create a new todo', async ({ page }) => {
740
+ await todoPage.goto();
741
+ await todoPage.createTodo('Test E2E Todo');
742
+
743
+ await todoPage.expectTodoVisible('Test E2E Todo');
744
+ });
745
+
746
+ test('should toggle todo completion', async ({ page }) => {
747
+ await todoPage.goto();
748
+ await todoPage.createTodo('Toggle Test');
749
+
750
+ const todoItem = page.getByText('Toggle Test').locator('..');
751
+
752
+ // Get initial state
753
+ const checkbox = todoItem.getByRole('button').first();
754
+ await expect(checkbox).toHaveAttribute('aria-checked', 'false');
755
+
756
+ // Toggle to complete
757
+ await todoPage.toggleTodo('Toggle Test');
758
+ await expect(todoItem).toHaveClass(/opacity-60/);
759
+
760
+ // Toggle back
761
+ await todoPage.toggleTodo('Toggle Test');
762
+ await expect(todoItem).not.toHaveClass(/opacity-60/);
763
+ });
764
+
765
+ test('should delete a todo', async ({ page }) => {
766
+ await todoPage.goto();
767
+ await todoPage.createTodo('Delete Test');
768
+
769
+ await todoPage.expectTodoVisible('Delete Test');
770
+
771
+ await todoPage.deleteTodo('Delete Test');
772
+
773
+ await expect(page.getByText('Delete Test')).not.toBeVisible();
774
+ });
775
+
776
+ test('should filter todos by status', async ({ page }) => {
777
+ await todoPage.goto();
778
+ await todoPage.createTodo('Active Todo');
779
+ await todoPage.createTodo('Completed Todo');
780
+
781
+ // Mark one as completed
782
+ await todoPage.toggleTodo('Completed Todo');
783
+
784
+ // Filter to active only
785
+ await page.getByRole('combobox', { name: 'Status' }).click();
786
+ await page.getByRole('option', { name: 'Active' }).click();
787
+
788
+ await expect(page.getByText('Active Todo')).toBeVisible();
789
+ await expect(page.getByText('Completed Todo')).not.toBeVisible();
790
+ });
791
+ });
792
+ ```
793
+
794
+ ---
795
+
796
+ ## Task 6: Create Test Scripts
797
+
798
+ **Files:**
799
+ - Modify: `package.json` (root)
800
+
801
+ ### Step 1: Add test scripts to root package.json
802
+
803
+ **File:** `package.json`
804
+
805
+ ```json
806
+ {
807
+ "scripts": {
808
+ "test": "vitest",
809
+ "test:ui": "vitest --ui",
810
+ "test:run": "vitest run",
811
+ "test:coverage": "vitest run --coverage",
812
+ "test:watch": "vitest watch",
813
+ "test:e2e": "playwright test",
814
+ "test:e2e:ui": "playwright test --ui",
815
+ "test:e2e:debug": "playwright test --debug",
816
+ "test:all": "pnpm run test:run && pnpm run test:e2e"
817
+ }
818
+ }
819
+ ```
820
+
821
+ ### Step 2: Update host package.json
822
+
823
+ **File:** `host/package.json`
824
+
825
+ ```json
826
+ {
827
+ "scripts": {
828
+ "test": "vitest",
829
+ "test:ui": "vitest --ui",
830
+ "test:coverage": "vitest run --coverage"
831
+ }
832
+ }
833
+ ```
834
+
835
+ ---
836
+
837
+ ## Verification
838
+
839
+ ### Step 1: Install test dependencies
840
+
841
+ **Run:**
842
+
843
+ ```bash
844
+ pnpm install
845
+ ```
846
+
847
+ Expected: All test dependencies installed.
848
+
849
+ ### Step 2: Run unit tests
850
+
851
+ **Run:**
852
+
853
+ ```bash
854
+ pnpm test:run
855
+ ```
856
+
857
+ Expected: Unit tests pass.
858
+
859
+ ### Step 3: Run component tests with UI
860
+
861
+ **Run:**
862
+
863
+ ```bash
864
+ pnpm test:ui
865
+ ```
866
+
867
+ Expected: Vitest UI opens in browser.
868
+
869
+ ### Step 4: Install Playwright browsers
870
+
871
+ **Run:**
872
+
873
+ ```bash
874
+ npx playwright install
875
+ ```
876
+
877
+ Expected: Playwright browsers downloaded.
878
+
879
+ ### Step 5: Run E2E tests
880
+
881
+ **Run:**
882
+
883
+ ```bash
884
+ pnpm test:e2e
885
+ ```
886
+
887
+ Expected: E2E tests run and pass (requires dev server running).
888
+
889
+ ---
890
+
891
+ ## Summary
892
+
893
+ After completing this document, you will have:
894
+
895
+ 1. ✅ Vitest configured for unit and component tests
896
+ 2. ✅ Playwright configured for E2E tests
897
+ 3. ✅ Unit tests for utilities and hooks
898
+ 4. ✅ Component tests for UI components
899
+ 5. ✅ E2E tests for critical user flows (auth, dashboard, todos)
900
+ 6. ✅ Test helpers and fixtures
901
+ 7. ✅ Coverage reporting configured
902
+ 8. ✅ Test scripts in package.json
903
+
904
+ **Next:** `12-deployment.md` - Implement build configuration, deployment scripts, and CI/CD pipeline.
905
+
906
+ ---
907
+
908
+ ## Files Created
909
+
910
+ ```
911
+ root/
912
+ ├── vitest.workspace.ts
913
+ ├── vitest.config.ts
914
+ ├── playwright.config.ts
915
+ └── e2e/
916
+ ├── auth.spec.ts
917
+ ├── dashboard.spec.ts
918
+ ├── todo.spec.ts
919
+ └── helpers.ts
920
+
921
+ host/
922
+ ├── vitest.config.ts
923
+ └── src/
924
+ ├── test/
925
+ │ └── setup.ts
926
+ └── kernel/
927
+ ├── lib/__tests__/
928
+ │ └── utils.test.ts
929
+ ├── shared-state/__tests__/
930
+ │ └── store.test.ts
931
+ ├── components/ui/__tests__/
932
+ │ └── button.test.tsx
933
+ └── auth/__tests__/
934
+ └── LoginForm.test.tsx
935
+ ```