@hailer/mcp 1.1.11 → 1.1.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 (252) hide show
  1. package/dist/app.js +18 -5
  2. package/dist/bot/bot-config.d.ts +12 -1
  3. package/dist/bot/bot-config.js +98 -14
  4. package/dist/bot/bot-manager.d.ts +13 -3
  5. package/dist/bot/bot-manager.js +80 -25
  6. package/dist/bot/bot.d.ts +46 -0
  7. package/dist/bot/bot.js +542 -166
  8. package/dist/bot/services/message-classifier.js +17 -0
  9. package/dist/bot/services/permission-guard.d.ts +52 -0
  10. package/dist/bot/services/permission-guard.js +149 -0
  11. package/dist/bot/services/types.d.ts +5 -0
  12. package/dist/bot/services/typing-indicator.d.ts +6 -1
  13. package/dist/bot/services/typing-indicator.js +19 -3
  14. package/dist/config.d.ts +6 -1
  15. package/dist/config.js +43 -0
  16. package/dist/core.js +3 -6
  17. package/dist/mcp/UserContextCache.d.ts +5 -0
  18. package/dist/mcp/UserContextCache.js +51 -19
  19. package/dist/mcp/hailer-clients.d.ts +19 -1
  20. package/dist/mcp/hailer-clients.js +157 -20
  21. package/dist/mcp/session-store.d.ts +68 -0
  22. package/dist/mcp/session-store.js +169 -0
  23. package/dist/mcp/signal-handler.js +12 -12
  24. package/dist/mcp/tool-registry.d.ts +17 -4
  25. package/dist/mcp/tool-registry.js +37 -7
  26. package/dist/mcp/tools/activity.js +99 -7
  27. package/dist/mcp/tools/app-scaffold.js +304 -336
  28. package/dist/mcp/tools/company.d.ts +9 -0
  29. package/dist/mcp/tools/company.js +88 -0
  30. package/dist/mcp/tools/discussion.js +68 -0
  31. package/dist/mcp/tools/workflow-permissions.d.ts +15 -0
  32. package/dist/mcp/tools/workflow-permissions.js +204 -0
  33. package/dist/mcp/tools/workflow.js +57 -18
  34. package/dist/mcp/utils/index.d.ts +2 -0
  35. package/dist/mcp/utils/index.js +12 -1
  36. package/dist/mcp/utils/role-utils.d.ts +74 -0
  37. package/dist/mcp/utils/role-utils.js +151 -0
  38. package/dist/mcp/utils/types.d.ts +43 -1
  39. package/dist/mcp/utils/types.js +14 -0
  40. package/dist/mcp/webhook-handler.d.ts +6 -0
  41. package/dist/mcp/webhook-handler.js +11 -0
  42. package/dist/mcp-server.d.ts +23 -2
  43. package/dist/mcp-server.js +639 -111
  44. package/dist/plugins/vipunen/client.d.ts +150 -0
  45. package/dist/plugins/vipunen/client.js +535 -0
  46. package/dist/plugins/vipunen/config/schema-config.json +19 -0
  47. package/dist/plugins/vipunen/config/schema-doc.json +22 -0
  48. package/dist/plugins/vipunen/index.d.ts +41 -0
  49. package/dist/plugins/vipunen/index.js +88 -0
  50. package/dist/plugins/vipunen/tools.d.ts +26 -0
  51. package/dist/plugins/vipunen/tools.js +501 -0
  52. package/package.json +2 -1
  53. package/.claude/.context-watchdog.json +0 -1
  54. package/.claude/.session-checked +0 -1
  55. package/.claude/CLAUDE.md +0 -370
  56. package/.claude/agents/agent-ada-skill-builder.md +0 -94
  57. package/.claude/agents/agent-alejandro-function-fields.md +0 -342
  58. package/.claude/agents/agent-bjorn-config-audit.md +0 -103
  59. package/.claude/agents/agent-builder-agent-creator.md +0 -130
  60. package/.claude/agents/agent-code-simplifier.md +0 -53
  61. package/.claude/agents/agent-dmitri-activity-crud.md +0 -159
  62. package/.claude/agents/agent-giuseppe-app-builder.md +0 -247
  63. package/.claude/agents/agent-gunther-mcp-tools.md +0 -39
  64. package/.claude/agents/agent-helga-workflow-config.md +0 -204
  65. package/.claude/agents/agent-igor-activity-mover-automation.md +0 -125
  66. package/.claude/agents/agent-ingrid-doc-templates.md +0 -261
  67. package/.claude/agents/agent-ivan-monolith.md +0 -154
  68. package/.claude/agents/agent-kenji-data-reader.md +0 -86
  69. package/.claude/agents/agent-lars-code-inspector.md +0 -102
  70. package/.claude/agents/agent-marco-mockup-builder.md +0 -110
  71. package/.claude/agents/agent-marcus-api-documenter.md +0 -323
  72. package/.claude/agents/agent-marketplace-publisher.md +0 -280
  73. package/.claude/agents/agent-marketplace-reviewer.md +0 -309
  74. package/.claude/agents/agent-permissions-handler.md +0 -208
  75. package/.claude/agents/agent-simple-writer.md +0 -48
  76. package/.claude/agents/agent-svetlana-code-review.md +0 -171
  77. package/.claude/agents/agent-tanya-test-runner.md +0 -333
  78. package/.claude/agents/agent-ui-designer.md +0 -100
  79. package/.claude/agents/agent-viktor-sql-insights.md +0 -212
  80. package/.claude/agents/agent-web-search.md +0 -55
  81. package/.claude/agents/agent-yevgeni-discussions.md +0 -45
  82. package/.claude/agents/agent-zara-zapier.md +0 -159
  83. package/.claude/commands/app-squad.md +0 -135
  84. package/.claude/commands/audit-squad.md +0 -158
  85. package/.claude/commands/autoplan.md +0 -563
  86. package/.claude/commands/cleanup-squad.md +0 -98
  87. package/.claude/commands/config-squad.md +0 -106
  88. package/.claude/commands/crud-squad.md +0 -87
  89. package/.claude/commands/data-squad.md +0 -97
  90. package/.claude/commands/debug-squad.md +0 -303
  91. package/.claude/commands/doc-squad.md +0 -65
  92. package/.claude/commands/handoff.md +0 -137
  93. package/.claude/commands/health.md +0 -49
  94. package/.claude/commands/help.md +0 -29
  95. package/.claude/commands/help:agents.md +0 -151
  96. package/.claude/commands/help:commands.md +0 -78
  97. package/.claude/commands/help:faq.md +0 -79
  98. package/.claude/commands/help:plugins.md +0 -50
  99. package/.claude/commands/help:skills.md +0 -93
  100. package/.claude/commands/help:tools.md +0 -75
  101. package/.claude/commands/hotfix-squad.md +0 -112
  102. package/.claude/commands/integration-squad.md +0 -82
  103. package/.claude/commands/janitor-squad.md +0 -167
  104. package/.claude/commands/learn-auto.md +0 -120
  105. package/.claude/commands/learn.md +0 -120
  106. package/.claude/commands/mcp-list.md +0 -27
  107. package/.claude/commands/onboard-squad.md +0 -140
  108. package/.claude/commands/plan-workspace.md +0 -732
  109. package/.claude/commands/prd.md +0 -130
  110. package/.claude/commands/project-status.md +0 -82
  111. package/.claude/commands/publish.md +0 -138
  112. package/.claude/commands/recap.md +0 -69
  113. package/.claude/commands/restore.md +0 -64
  114. package/.claude/commands/review-squad.md +0 -152
  115. package/.claude/commands/save.md +0 -24
  116. package/.claude/commands/stats.md +0 -19
  117. package/.claude/commands/swarm.md +0 -210
  118. package/.claude/commands/tool-builder.md +0 -39
  119. package/.claude/commands/ws-pull.md +0 -44
  120. package/.claude/hooks/_shared-memory.cjs +0 -305
  121. package/.claude/hooks/_utils.cjs +0 -108
  122. package/.claude/hooks/agent-failure-detector.cjs +0 -383
  123. package/.claude/hooks/agent-usage-logger.cjs +0 -204
  124. package/.claude/hooks/app-edit-guard.cjs +0 -494
  125. package/.claude/hooks/auto-learn.cjs +0 -304
  126. package/.claude/hooks/bash-guard.cjs +0 -272
  127. package/.claude/hooks/builder-mode-manager.cjs +0 -354
  128. package/.claude/hooks/bulk-activity-guard.cjs +0 -271
  129. package/.claude/hooks/context-watchdog.cjs +0 -230
  130. package/.claude/hooks/delegation-reminder.cjs +0 -465
  131. package/.claude/hooks/design-system-lint.cjs +0 -271
  132. package/.claude/hooks/post-scaffold-hook.cjs +0 -181
  133. package/.claude/hooks/prompt-guard.cjs +0 -354
  134. package/.claude/hooks/publish-template-guard.cjs +0 -147
  135. package/.claude/hooks/session-start.cjs +0 -35
  136. package/.claude/hooks/shared-memory-writer.cjs +0 -147
  137. package/.claude/hooks/skill-injector.cjs +0 -140
  138. package/.claude/hooks/skill-usage-logger.cjs +0 -258
  139. package/.claude/hooks/src-edit-guard.cjs +0 -240
  140. package/.claude/hooks/sync-marketplace-agents.cjs +0 -346
  141. package/.claude/settings.json +0 -257
  142. package/.claude/skills/SDK-activity-patterns/SKILL.md +0 -428
  143. package/.claude/skills/SDK-document-templates/SKILL.md +0 -1033
  144. package/.claude/skills/SDK-function-fields/SKILL.md +0 -542
  145. package/.claude/skills/SDK-generate-skill/SKILL.md +0 -92
  146. package/.claude/skills/SDK-init-skill/SKILL.md +0 -127
  147. package/.claude/skills/SDK-insight-queries/SKILL.md +0 -787
  148. package/.claude/skills/SDK-ws-config-skill/SKILL.md +0 -1139
  149. package/.claude/skills/agent-structure/SKILL.md +0 -98
  150. package/.claude/skills/api-documentation-patterns/SKILL.md +0 -474
  151. package/.claude/skills/chrome-mcp-reference/SKILL.md +0 -370
  152. package/.claude/skills/delegation-routing/SKILL.md +0 -202
  153. package/.claude/skills/frontend-design/SKILL.md +0 -254
  154. package/.claude/skills/hailer-activity-mover/SKILL.md +0 -213
  155. package/.claude/skills/hailer-api-client/SKILL.md +0 -518
  156. package/.claude/skills/hailer-app-builder/SKILL.md +0 -1434
  157. package/.claude/skills/hailer-apps-pictures/SKILL.md +0 -269
  158. package/.claude/skills/hailer-design-system/SKILL.md +0 -235
  159. package/.claude/skills/hailer-monolith-automations/SKILL.md +0 -686
  160. package/.claude/skills/hailer-permissions-system/SKILL.md +0 -121
  161. package/.claude/skills/hailer-project-protocol/SKILL.md +0 -488
  162. package/.claude/skills/hailer-rest-api/SKILL.md +0 -61
  163. package/.claude/skills/hailer-rest-api/hailer-activities.md +0 -184
  164. package/.claude/skills/hailer-rest-api/hailer-admin.md +0 -473
  165. package/.claude/skills/hailer-rest-api/hailer-calendar.md +0 -256
  166. package/.claude/skills/hailer-rest-api/hailer-feed.md +0 -249
  167. package/.claude/skills/hailer-rest-api/hailer-insights.md +0 -195
  168. package/.claude/skills/hailer-rest-api/hailer-messaging.md +0 -276
  169. package/.claude/skills/hailer-rest-api/hailer-workflows.md +0 -283
  170. package/.claude/skills/insight-join-patterns/SKILL.md +0 -174
  171. package/.claude/skills/integration-patterns/SKILL.md +0 -421
  172. package/.claude/skills/json-only-output/SKILL.md +0 -72
  173. package/.claude/skills/lsp-setup/SKILL.md +0 -160
  174. package/.claude/skills/mcp-direct-tools/SKILL.md +0 -153
  175. package/.claude/skills/optional-parameters/SKILL.md +0 -72
  176. package/.claude/skills/publish-hailer-app/SKILL.md +0 -244
  177. package/.claude/skills/testing-patterns/SKILL.md +0 -630
  178. package/.claude/skills/tool-builder/SKILL.md +0 -250
  179. package/.claude/skills/tool-parameter-usage/SKILL.md +0 -126
  180. package/.claude/skills/tool-response-verification/SKILL.md +0 -92
  181. package/.claude/skills/zapier-hailer-patterns/SKILL.md +0 -581
  182. package/.hailer-mcp-port +0 -1
  183. package/.mcp.json +0 -13
  184. package/.opencode/agent/agent-ada-skill-builder.md +0 -35
  185. package/.opencode/agent/agent-alejandro-function-fields.md +0 -39
  186. package/.opencode/agent/agent-bjorn-config-audit.md +0 -36
  187. package/.opencode/agent/agent-builder-agent-creator.md +0 -39
  188. package/.opencode/agent/agent-code-simplifier.md +0 -31
  189. package/.opencode/agent/agent-dmitri-activity-crud.md +0 -40
  190. package/.opencode/agent/agent-giuseppe-app-builder.md +0 -37
  191. package/.opencode/agent/agent-gunther-mcp-tools.md +0 -39
  192. package/.opencode/agent/agent-helga-workflow-config.md +0 -204
  193. package/.opencode/agent/agent-igor-activity-mover-automation.md +0 -46
  194. package/.opencode/agent/agent-ingrid-doc-templates.md +0 -39
  195. package/.opencode/agent/agent-ivan-monolith.md +0 -46
  196. package/.opencode/agent/agent-kenji-data-reader.md +0 -53
  197. package/.opencode/agent/agent-lars-code-inspector.md +0 -28
  198. package/.opencode/agent/agent-marco-mockup-builder.md +0 -42
  199. package/.opencode/agent/agent-marcus-api-documenter.md +0 -53
  200. package/.opencode/agent/agent-marketplace-publisher.md +0 -44
  201. package/.opencode/agent/agent-marketplace-reviewer.md +0 -42
  202. package/.opencode/agent/agent-permissions-handler.md +0 -50
  203. package/.opencode/agent/agent-simple-writer.md +0 -45
  204. package/.opencode/agent/agent-svetlana-code-review.md +0 -39
  205. package/.opencode/agent/agent-tanya-test-runner.md +0 -57
  206. package/.opencode/agent/agent-ui-designer.md +0 -56
  207. package/.opencode/agent/agent-viktor-sql-insights.md +0 -34
  208. package/.opencode/agent/agent-web-search.md +0 -42
  209. package/.opencode/agent/agent-yevgeni-discussions.md +0 -37
  210. package/.opencode/agent/agent-zara-zapier.md +0 -53
  211. package/.opencode/commands/app-squad.md +0 -135
  212. package/.opencode/commands/audit-squad.md +0 -158
  213. package/.opencode/commands/autoplan.md +0 -563
  214. package/.opencode/commands/cleanup-squad.md +0 -98
  215. package/.opencode/commands/config-squad.md +0 -106
  216. package/.opencode/commands/crud-squad.md +0 -87
  217. package/.opencode/commands/data-squad.md +0 -97
  218. package/.opencode/commands/debug-squad.md +0 -303
  219. package/.opencode/commands/doc-squad.md +0 -65
  220. package/.opencode/commands/handoff.md +0 -137
  221. package/.opencode/commands/health.md +0 -49
  222. package/.opencode/commands/help-agents.md +0 -151
  223. package/.opencode/commands/help-commands.md +0 -32
  224. package/.opencode/commands/help-faq.md +0 -29
  225. package/.opencode/commands/help-plugins.md +0 -28
  226. package/.opencode/commands/help-skills.md +0 -7
  227. package/.opencode/commands/help-tools.md +0 -40
  228. package/.opencode/commands/help.md +0 -28
  229. package/.opencode/commands/hotfix-squad.md +0 -112
  230. package/.opencode/commands/integration-squad.md +0 -82
  231. package/.opencode/commands/janitor-squad.md +0 -167
  232. package/.opencode/commands/learn-auto.md +0 -120
  233. package/.opencode/commands/learn.md +0 -120
  234. package/.opencode/commands/mcp-list.md +0 -27
  235. package/.opencode/commands/onboard-squad.md +0 -140
  236. package/.opencode/commands/plan-workspace.md +0 -732
  237. package/.opencode/commands/prd.md +0 -131
  238. package/.opencode/commands/project-status.md +0 -82
  239. package/.opencode/commands/publish.md +0 -138
  240. package/.opencode/commands/recap.md +0 -69
  241. package/.opencode/commands/restore.md +0 -64
  242. package/.opencode/commands/review-squad.md +0 -152
  243. package/.opencode/commands/save.md +0 -24
  244. package/.opencode/commands/stats.md +0 -19
  245. package/.opencode/commands/swarm.md +0 -210
  246. package/.opencode/commands/tool-builder.md +0 -39
  247. package/.opencode/commands/ws-pull.md +0 -44
  248. package/.opencode/opencode.json +0 -21
  249. package/inbox/failures.log +0 -1
  250. package/inbox/usage.jsonl +0 -4
  251. package/scripts/postinstall.cjs +0 -64
  252. package/scripts/test-hal-tools.ts +0 -154
@@ -1,630 +0,0 @@
1
- ---
2
- name: testing-patterns
3
- description: Testing patterns for Hailer projects - vitest for function fields, playwright for apps, build verification
4
- version: 1.0.0
5
- triggers: Run tests, vitest, playwright, test failure, build error, coverage
6
- ---
7
-
8
- # Testing Patterns
9
-
10
- ## Overview
11
-
12
- | Project Type | Test Framework | Location |
13
- |--------------|---------------|----------|
14
- | SDK (function fields) | Vitest | `workspace/[Workflow]/main.test.ts` |
15
- | Apps (E2E) | Playwright | `apps/[app]/test/` or `test/` |
16
- | Apps (unit) | Vitest | `apps/[app]/src/**/*.test.ts` |
17
-
18
- ---
19
-
20
- ## Vitest - Function Field Tests
21
-
22
- ### Running Tests
23
-
24
- ```bash
25
- # Run all tests
26
- npm test
27
-
28
- # Watch mode (re-run on changes)
29
- npm run test:watch
30
-
31
- # Interactive UI
32
- npm run test:ui
33
-
34
- # Specific file
35
- npm test -- workspace/Orders_abc/main.test.ts
36
-
37
- # Specific test by name
38
- npm test -- -t "calculates total"
39
-
40
- # With coverage
41
- npm test -- --coverage
42
-
43
- # Verbose output
44
- npm test -- --reporter=verbose
45
- ```
46
-
47
- ### Test File Structure
48
-
49
- ```typescript
50
- // workspace/Orders_abc/main.test.ts
51
- import { describe, it, expect, beforeEach } from 'vitest';
52
- import { total_cost_abc } from './functions/total_cost_abc';
53
- import { discount_def } from './functions/discount_def';
54
-
55
- describe('total_cost_abc', () => {
56
- describe('basic calculations', () => {
57
- it('multiplies quantity by unit price', () => {
58
- const result = total_cost_abc({
59
- quantity: 10,
60
- unitPrice: 25.50
61
- });
62
- expect(result).toBe(255);
63
- });
64
-
65
- it('returns 0 for zero quantity', () => {
66
- const result = total_cost_abc({
67
- quantity: 0,
68
- unitPrice: 100
69
- });
70
- expect(result).toBe(0);
71
- });
72
- });
73
-
74
- describe('null handling', () => {
75
- it('handles null quantity', () => {
76
- const result = total_cost_abc({
77
- quantity: null,
78
- unitPrice: 50
79
- });
80
- expect(result).toBe(0);
81
- });
82
-
83
- it('handles undefined unit price', () => {
84
- const result = total_cost_abc({
85
- quantity: 10,
86
- unitPrice: undefined
87
- });
88
- expect(result).toBe(0);
89
- });
90
-
91
- it('handles both null', () => {
92
- const result = total_cost_abc({
93
- quantity: null,
94
- unitPrice: null
95
- });
96
- expect(result).toBe(0);
97
- });
98
- });
99
-
100
- describe('edge cases', () => {
101
- it('handles string numbers', () => {
102
- const result = total_cost_abc({
103
- quantity: '10',
104
- unitPrice: '5'
105
- } as any);
106
- expect(result).toBe(50);
107
- });
108
-
109
- it('handles negative values', () => {
110
- const result = total_cost_abc({
111
- quantity: -5,
112
- unitPrice: 10
113
- });
114
- expect(result).toBe(-50);
115
- });
116
- });
117
- });
118
- ```
119
-
120
- ### Testing Backlink Aggregations
121
-
122
- ```typescript
123
- describe('total_from_invoices_abc', () => {
124
- it('sums array of invoice amounts', () => {
125
- const result = total_from_invoices_abc({
126
- invoiceAmounts: [100, 200, 300]
127
- });
128
- expect(result).toBe(600);
129
- });
130
-
131
- it('handles empty array', () => {
132
- const result = total_from_invoices_abc({
133
- invoiceAmounts: []
134
- });
135
- expect(result).toBe(0);
136
- });
137
-
138
- it('handles null values in array', () => {
139
- const result = total_from_invoices_abc({
140
- invoiceAmounts: [100, null, 200, undefined, 300]
141
- });
142
- expect(result).toBe(600);
143
- });
144
-
145
- it('handles undefined array', () => {
146
- const result = total_from_invoices_abc({
147
- invoiceAmounts: undefined
148
- });
149
- expect(result).toBe(0);
150
- });
151
- });
152
- ```
153
-
154
- ### Testing Parallel Arrays
155
-
156
- ```typescript
157
- describe('weighted_average_abc', () => {
158
- it('calculates weighted average from parallel arrays', () => {
159
- // Arrays from same source workflow are guaranteed same length
160
- const result = weighted_average_abc({
161
- values: [80, 90, 70],
162
- weights: [1, 2, 1]
163
- });
164
- // (80*1 + 90*2 + 70*1) / (1+2+1) = 330/4 = 82.5
165
- expect(result).toBe(82.5);
166
- });
167
-
168
- it('handles null values at same index', () => {
169
- const result = weighted_average_abc({
170
- values: [80, null, 70],
171
- weights: [1, 2, 1] // Weight at index 1 should be ignored
172
- });
173
- // Should skip index 1 entirely
174
- expect(result).toBe(75); // (80*1 + 70*1) / 2
175
- });
176
- });
177
- ```
178
-
179
- ### Testing JSON Output Functions
180
-
181
- ```typescript
182
- describe('line_items_json_abc', () => {
183
- it('returns valid JSON string', () => {
184
- const result = line_items_json_abc({
185
- names: ['Item A', 'Item B'],
186
- quantities: [2, 3],
187
- prices: [10, 20]
188
- });
189
-
190
- const parsed = JSON.parse(result);
191
- expect(parsed).toEqual([
192
- { name: 'Item A', quantity: 2, price: 10 },
193
- { name: 'Item B', quantity: 3, price: 20 }
194
- ]);
195
- });
196
-
197
- it('returns empty array JSON for no items', () => {
198
- const result = line_items_json_abc({
199
- names: [],
200
- quantities: [],
201
- prices: []
202
- });
203
- expect(result).toBe('[]');
204
- });
205
- });
206
- ```
207
-
208
- ---
209
-
210
- ## Playwright - App E2E Tests
211
-
212
- ### Running Tests
213
-
214
- ```bash
215
- # Run all tests
216
- npm run test
217
-
218
- # With UI
219
- npm run test:open
220
-
221
- # Headed (see browser)
222
- npm run test -- --headed
223
-
224
- # Specific file
225
- npm run test -- test/suites/dashboard.spec.ts
226
-
227
- # Specific test
228
- npm run test -- -g "displays activity count"
229
-
230
- # Debug mode
231
- npm run test -- --debug
232
-
233
- # Generate report
234
- npm run test -- --reporter=html
235
- npx playwright show-report
236
- ```
237
-
238
- ### Test File Structure
239
-
240
- ```typescript
241
- // test/suites/dashboard.spec.ts
242
- import { test, expect } from '@playwright/test';
243
- import { login, setupTestData } from '../helpers/setup';
244
-
245
- test.describe('Dashboard', () => {
246
- test.beforeEach(async ({ page }) => {
247
- await login(page);
248
- await page.goto('/');
249
- await page.waitForSelector('[data-testid="dashboard"]');
250
- });
251
-
252
- test('displays activity count', async ({ page }) => {
253
- const count = page.locator('[data-testid="activity-count"]');
254
- await expect(count).toBeVisible();
255
- await expect(count).toHaveText(/\d+/);
256
- });
257
-
258
- test('shows workflow list', async ({ page }) => {
259
- const workflows = page.locator('[data-testid="workflow-item"]');
260
- await expect(workflows).toHaveCount.greaterThan(0);
261
- });
262
-
263
- test('navigates to workflow on click', async ({ page }) => {
264
- await page.click('[data-testid="workflow-item"]:first-child');
265
- await expect(page).toHaveURL(/\/workflow\//);
266
- });
267
- });
268
- ```
269
-
270
- ### Page Object Pattern
271
-
272
- ```typescript
273
- // test/locators/Dashboard.ts
274
- import { Page, Locator } from '@playwright/test';
275
-
276
- export class DashboardPage {
277
- readonly page: Page;
278
- readonly activityCount: Locator;
279
- readonly workflowList: Locator;
280
- readonly searchInput: Locator;
281
-
282
- constructor(page: Page) {
283
- this.page = page;
284
- this.activityCount = page.locator('[data-testid="activity-count"]');
285
- this.workflowList = page.locator('[data-testid="workflow-list"]');
286
- this.searchInput = page.locator('[data-testid="search-input"]');
287
- }
288
-
289
- async goto() {
290
- await this.page.goto('/');
291
- await this.page.waitForSelector('[data-testid="dashboard"]');
292
- }
293
-
294
- async search(query: string) {
295
- await this.searchInput.fill(query);
296
- await this.searchInput.press('Enter');
297
- }
298
-
299
- async getWorkflowCount(): Promise<number> {
300
- return this.workflowList.locator('[data-testid="workflow-item"]').count();
301
- }
302
- }
303
-
304
- // Usage in test
305
- test('search filters workflows', async ({ page }) => {
306
- const dashboard = new DashboardPage(page);
307
- await dashboard.goto();
308
-
309
- const initialCount = await dashboard.getWorkflowCount();
310
- await dashboard.search('Orders');
311
-
312
- const filteredCount = await dashboard.getWorkflowCount();
313
- expect(filteredCount).toBeLessThanOrEqual(initialCount);
314
- });
315
- ```
316
-
317
- ### Test Helpers
318
-
319
- ```typescript
320
- // test/helpers/setup.ts
321
- import { Page } from '@playwright/test';
322
-
323
- export async function login(page: Page) {
324
- await page.goto('/login');
325
- await page.fill('[data-testid="email"]', process.env.TEST_EMAIL!);
326
- await page.fill('[data-testid="password"]', process.env.TEST_PASSWORD!);
327
- await page.click('[data-testid="login-button"]');
328
- await page.waitForURL('/');
329
- }
330
-
331
- export async function createTestActivity(page: Page, workflowId: string, data: any) {
332
- // Create via API for faster setup
333
- const response = await page.request.post('/api/activities', {
334
- data: { workflowId, ...data }
335
- });
336
- return response.json();
337
- }
338
-
339
- export async function cleanupTestData(page: Page, activityIds: string[]) {
340
- for (const id of activityIds) {
341
- await page.request.delete(`/api/activities/${id}`);
342
- }
343
- }
344
- ```
345
-
346
- ---
347
-
348
- ## Build Verification
349
-
350
- ### Commands
351
-
352
- ```bash
353
- # TypeScript compilation
354
- npm run build
355
-
356
- # Type check only (no output)
357
- npx tsc --noEmit
358
-
359
- # ESLint
360
- npm run lint
361
- npm run lint -- --fix
362
-
363
- # All checks
364
- npm run build && npm run lint && npm test
365
- ```
366
-
367
- ### Common Build Errors
368
-
369
- **Cannot find module**
370
- ```
371
- error TS2307: Cannot find module './utils' or its corresponding type declarations.
372
- ```
373
- Fix: Check import path, ensure file exists, run `npm install`
374
-
375
- **Type mismatch**
376
- ```
377
- error TS2322: Type 'string' is not assignable to type 'number'.
378
- ```
379
- Fix: Check variable types, add type assertion or fix logic
380
-
381
- **Missing property**
382
- ```
383
- error TS2339: Property 'value' does not exist on type 'Field'.
384
- ```
385
- Fix: Add optional chaining (`?.`) or check type definition
386
-
387
- ---
388
-
389
- ## Common Test Failures & Fixes
390
-
391
- ### Vitest Failures
392
-
393
- **TypeError: Cannot read property of undefined**
394
- ```typescript
395
- // Problem
396
- const total = dep.quantity * dep.price;
397
-
398
- // Fix
399
- const qty = Number(dep.quantity) || 0;
400
- const price = Number(dep.price) || 0;
401
- const total = qty * price;
402
- ```
403
-
404
- **Expected X but received Y**
405
- ```typescript
406
- // Check calculation logic
407
- // Check return type (string vs number)
408
- // Check floating point: use toBeCloseTo() for decimals
409
- expect(result).toBeCloseTo(expected, 2);
410
- ```
411
-
412
- **Test timeout**
413
- ```typescript
414
- // Increase timeout for slow tests
415
- test('slow operation', async () => {
416
- // ...
417
- }, 30000); // 30 second timeout
418
- ```
419
-
420
- ### Playwright Failures
421
-
422
- **Timeout waiting for selector**
423
- ```typescript
424
- // Problem
425
- await page.click('[data-testid="submit"]');
426
-
427
- // Fix - wait first
428
- await page.waitForSelector('[data-testid="submit"]');
429
- await page.click('[data-testid="submit"]');
430
-
431
- // Or use auto-waiting locator
432
- await page.locator('[data-testid="submit"]').click();
433
- ```
434
-
435
- **Element not visible**
436
- ```typescript
437
- // Scroll into view
438
- await page.locator('[data-testid="element"]').scrollIntoViewIfNeeded();
439
- await page.click('[data-testid="element"]');
440
- ```
441
-
442
- **Navigation timeout**
443
- ```typescript
444
- // Increase navigation timeout
445
- await page.goto('/', { timeout: 60000 });
446
-
447
- // Or wait for specific element instead
448
- await page.goto('/');
449
- await page.waitForSelector('[data-testid="loaded"]', { timeout: 60000 });
450
- ```
451
-
452
- ---
453
-
454
- ## Output Parsing
455
-
456
- ### Vitest Output
457
-
458
- ```
459
- ✓ workspace/Orders_abc/main.test.ts (8)
460
- ✓ total_cost_abc (4)
461
- ✓ multiplies quantity by price
462
- ✓ handles null quantity
463
- ✓ handles undefined price
464
- ✓ handles string numbers
465
- ✗ discount_abc (4)
466
- ✓ applies percentage discount
467
- ✓ applies fixed discount
468
- ✗ handles negative discount
469
- AssertionError: expected 100 to be 0
470
- ✓ caps at total amount
471
-
472
- Test Files 1 passed (1)
473
- Tests 7 passed | 1 failed (8)
474
- Time 1.23s
475
- ```
476
-
477
- **Extract:**
478
- - Total: 8
479
- - Passed: 7
480
- - Failed: 1
481
- - Failed test: "discount_abc > handles negative discount"
482
- - Error: "expected 100 to be 0"
483
-
484
- ### Playwright Output
485
-
486
- ```
487
- Running 12 tests using 4 workers
488
-
489
- ✓ 1 login.spec.ts:5:1 › Login › shows form (1.2s)
490
- ✓ 2 login.spec.ts:15:1 › Login › logs in (2.3s)
491
- ✗ 3 dashboard.spec.ts:8:1 › Dashboard › shows count (10.0s)
492
-
493
- 1 failed
494
- dashboard.spec.ts:8:1 › Dashboard › shows count
495
- Timeout of 10000ms exceeded.
496
- waiting for locator('[data-testid="activity-count"]')
497
- ```
498
-
499
- **Extract:**
500
- - Total: 12
501
- - Passed: 2 (shown)
502
- - Failed: 1
503
- - Failed: "Dashboard > shows count"
504
- - Error: Timeout waiting for selector
505
-
506
- ---
507
-
508
- ## CI/CD Integration
509
-
510
- ### GitHub Actions Example
511
-
512
- ```yaml
513
- name: Tests
514
- on: [push, pull_request]
515
-
516
- jobs:
517
- test:
518
- runs-on: ubuntu-latest
519
- steps:
520
- - uses: actions/checkout@v4
521
-
522
- - name: Setup Node
523
- uses: actions/setup-node@v4
524
- with:
525
- node-version: '20'
526
- cache: 'npm'
527
-
528
- - name: Install dependencies
529
- run: npm ci
530
-
531
- - name: Type check
532
- run: npx tsc --noEmit
533
-
534
- - name: Lint
535
- run: npm run lint
536
-
537
- - name: Unit tests
538
- run: npm test -- --coverage
539
-
540
- - name: Install Playwright
541
- run: npx playwright install --with-deps
542
-
543
- - name: E2E tests
544
- run: npm run test:e2e
545
- env:
546
- TEST_EMAIL: ${{ secrets.TEST_EMAIL }}
547
- TEST_PASSWORD: ${{ secrets.TEST_PASSWORD }}
548
-
549
- - name: Upload coverage
550
- uses: codecov/codecov-action@v3
551
- ```
552
-
553
- ---
554
-
555
- ## Best Practices
556
-
557
- 1. **Test null/undefined inputs** - Every function field test should cover these
558
- 2. **Use data-testid** - More stable than CSS selectors
559
- 3. **Page Objects** - Reduce duplication in E2E tests
560
- 4. **Parallel tests** - Playwright runs in parallel by default
561
- 5. **Test isolation** - Each test should be independent
562
- 6. **CI integration** - Run tests on every PR
563
- 7. **Coverage tracking** - Monitor test coverage over time
564
-
565
- ---
566
-
567
- ## Test Data Creation Order
568
-
569
- **ActivityLink targets must exist before creating linking activities.**
570
-
571
- When creating test data for workflows with relationships, follow this order:
572
-
573
- ```
574
- 1. Customers (no dependencies)
575
- 2. Contacts + Products (parallel - both link to Customers)
576
- 3. Deals (links to Customers)
577
- 4. Deal Lines (links to Deals + Products)
578
- 5. Activities with links (targets must exist first)
579
- ```
580
-
581
- ### Example Test Setup
582
-
583
- ```typescript
584
- // test/helpers/test-data.ts
585
- export async function createTestData(hailer: HailerClient) {
586
- // 1. Create independent entities first
587
- const customer = await hailer.activity.create({
588
- workflow: WorkflowIds.customers,
589
- fields: { name: 'Test Customer' }
590
- });
591
-
592
- // 2. Create entities that link to customer (can be parallel)
593
- const [contact, product] = await Promise.all([
594
- hailer.activity.create({
595
- workflow: WorkflowIds.contacts,
596
- fields: {
597
- name: 'Test Contact',
598
- customer: customer._id // Link to existing customer
599
- }
600
- }),
601
- hailer.activity.create({
602
- workflow: WorkflowIds.products,
603
- fields: { name: 'Test Product', price: 100 }
604
- })
605
- ]);
606
-
607
- // 3. Create deal (links to customer)
608
- const deal = await hailer.activity.create({
609
- workflow: WorkflowIds.deals,
610
- fields: {
611
- name: 'Test Deal',
612
- customer: customer._id
613
- }
614
- });
615
-
616
- // 4. Create deal lines (links to deal + product)
617
- const dealLine = await hailer.activity.create({
618
- workflow: WorkflowIds.deal_lines,
619
- fields: {
620
- deal: deal._id,
621
- product: product._id,
622
- quantity: 5
623
- }
624
- });
625
-
626
- return { customer, contact, product, deal, dealLine };
627
- }
628
- ```
629
-
630
- **Common mistake:** Creating a Deal Line before the Deal exists → ActivityLink validation fails.