@hailer/mcp 1.1.12 → 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 (271) hide show
  1. package/CHANGELOG.md +0 -7
  2. package/{.claude → dist}/CLAUDE.md +2 -2
  3. package/dist/app.js +18 -5
  4. package/dist/bot/bot-config.d.ts +10 -1
  5. package/dist/bot/bot-config.js +64 -3
  6. package/dist/bot/bot-manager.d.ts +2 -0
  7. package/dist/bot/bot-manager.js +9 -2
  8. package/dist/bot/bot.d.ts +33 -0
  9. package/dist/bot/bot.js +461 -160
  10. package/dist/bot/services/message-classifier.js +17 -0
  11. package/dist/bot/services/permission-guard.d.ts +52 -0
  12. package/dist/bot/services/permission-guard.js +149 -0
  13. package/dist/bot/services/types.d.ts +5 -0
  14. package/dist/bot/services/typing-indicator.d.ts +6 -1
  15. package/dist/bot/services/typing-indicator.js +19 -3
  16. package/dist/cli.js +0 -0
  17. package/dist/config.d.ts +6 -1
  18. package/dist/config.js +43 -0
  19. package/dist/core.js +3 -6
  20. package/dist/lib/discussion-lock.d.ts +42 -0
  21. package/dist/lib/discussion-lock.js +110 -0
  22. package/dist/mcp/UserContextCache.d.ts +5 -0
  23. package/dist/mcp/UserContextCache.js +51 -19
  24. package/dist/mcp/hailer-clients.d.ts +19 -1
  25. package/dist/mcp/hailer-clients.js +158 -24
  26. package/dist/mcp/session-store.d.ts +68 -0
  27. package/dist/mcp/session-store.js +169 -0
  28. package/dist/mcp/signal-handler.js +2 -0
  29. package/dist/mcp/tool-registry.d.ts +17 -4
  30. package/dist/mcp/tool-registry.js +37 -7
  31. package/dist/mcp/tools/activity.js +99 -7
  32. package/dist/mcp/tools/app-scaffold.js +304 -336
  33. package/dist/mcp/tools/bot-config/constants.d.ts +23 -0
  34. package/dist/mcp/tools/bot-config/constants.js +94 -0
  35. package/dist/mcp/tools/bot-config/core.d.ts +253 -0
  36. package/dist/mcp/tools/bot-config/core.js +2456 -0
  37. package/dist/mcp/tools/bot-config/index.d.ts +10 -0
  38. package/dist/mcp/tools/bot-config/index.js +59 -0
  39. package/dist/mcp/tools/bot-config/tools.d.ts +7 -0
  40. package/dist/mcp/tools/bot-config/tools.js +15 -0
  41. package/dist/mcp/tools/bot-config/types.d.ts +50 -0
  42. package/dist/mcp/tools/bot-config/types.js +6 -0
  43. package/dist/mcp/tools/bug-fixer-tools.d.ts +45 -0
  44. package/dist/mcp/tools/bug-fixer-tools.js +1096 -0
  45. package/dist/mcp/tools/company.d.ts +9 -0
  46. package/dist/mcp/tools/company.js +88 -0
  47. package/dist/mcp/tools/discussion.js +68 -0
  48. package/dist/mcp/tools/document.d.ts +11 -0
  49. package/dist/mcp/tools/document.js +741 -0
  50. package/dist/mcp/tools/investigate.d.ts +9 -0
  51. package/dist/mcp/tools/investigate.js +254 -0
  52. package/dist/mcp/tools/workflow-permissions.d.ts +15 -0
  53. package/dist/mcp/tools/workflow-permissions.js +204 -0
  54. package/dist/mcp/tools/workflow.js +57 -18
  55. package/dist/mcp/utils/index.d.ts +2 -0
  56. package/dist/mcp/utils/index.js +12 -1
  57. package/dist/mcp/utils/role-utils.d.ts +74 -0
  58. package/dist/mcp/utils/role-utils.js +151 -0
  59. package/dist/mcp/utils/types.d.ts +43 -1
  60. package/dist/mcp/utils/types.js +14 -0
  61. package/dist/mcp/webhook-handler.d.ts +4 -0
  62. package/dist/mcp/webhook-handler.js +8 -0
  63. package/dist/mcp-server.d.ts +23 -2
  64. package/dist/mcp-server.js +639 -127
  65. package/dist/plugins/vipunen/client.d.ts +150 -0
  66. package/dist/plugins/vipunen/client.js +535 -0
  67. package/dist/plugins/vipunen/config/schema-config.json +19 -0
  68. package/dist/plugins/vipunen/config/schema-doc.json +22 -0
  69. package/dist/plugins/vipunen/index.d.ts +41 -0
  70. package/dist/plugins/vipunen/index.js +88 -0
  71. package/dist/plugins/vipunen/tools.d.ts +26 -0
  72. package/dist/plugins/vipunen/tools.js +501 -0
  73. package/dist/stdio-server.d.ts +14 -0
  74. package/dist/stdio-server.js +101 -0
  75. package/package.json +2 -1
  76. package/.claude/agents/agent-ada-skill-builder.md +0 -94
  77. package/.claude/agents/agent-alejandro-function-fields.md +0 -342
  78. package/.claude/agents/agent-bjorn-config-audit.md +0 -103
  79. package/.claude/agents/agent-builder-agent-creator.md +0 -130
  80. package/.claude/agents/agent-code-simplifier.md +0 -53
  81. package/.claude/agents/agent-dmitri-activity-crud.md +0 -159
  82. package/.claude/agents/agent-giuseppe-app-builder.md +0 -247
  83. package/.claude/agents/agent-gunther-mcp-tools.md +0 -39
  84. package/.claude/agents/agent-helga-workflow-config.md +0 -204
  85. package/.claude/agents/agent-igor-activity-mover-automation.md +0 -125
  86. package/.claude/agents/agent-ingrid-doc-templates.md +0 -261
  87. package/.claude/agents/agent-ivan-monolith.md +0 -154
  88. package/.claude/agents/agent-kenji-data-reader.md +0 -86
  89. package/.claude/agents/agent-lars-code-inspector.md +0 -102
  90. package/.claude/agents/agent-marco-mockup-builder.md +0 -110
  91. package/.claude/agents/agent-marcus-api-documenter.md +0 -323
  92. package/.claude/agents/agent-marketplace-publisher.md +0 -280
  93. package/.claude/agents/agent-marketplace-reviewer.md +0 -309
  94. package/.claude/agents/agent-permissions-handler.md +0 -208
  95. package/.claude/agents/agent-simple-writer.md +0 -48
  96. package/.claude/agents/agent-svetlana-code-review.md +0 -171
  97. package/.claude/agents/agent-tanya-test-runner.md +0 -333
  98. package/.claude/agents/agent-ui-designer.md +0 -100
  99. package/.claude/agents/agent-viktor-sql-insights.md +0 -212
  100. package/.claude/agents/agent-web-search.md +0 -55
  101. package/.claude/agents/agent-yevgeni-discussions.md +0 -45
  102. package/.claude/agents/agent-zara-zapier.md +0 -159
  103. package/.claude/commands/app-squad.md +0 -135
  104. package/.claude/commands/audit-squad.md +0 -158
  105. package/.claude/commands/autoplan.md +0 -563
  106. package/.claude/commands/cleanup-squad.md +0 -98
  107. package/.claude/commands/config-squad.md +0 -106
  108. package/.claude/commands/crud-squad.md +0 -87
  109. package/.claude/commands/data-squad.md +0 -97
  110. package/.claude/commands/debug-squad.md +0 -303
  111. package/.claude/commands/doc-squad.md +0 -65
  112. package/.claude/commands/handoff.md +0 -137
  113. package/.claude/commands/health.md +0 -49
  114. package/.claude/commands/help.md +0 -29
  115. package/.claude/commands/help:agents.md +0 -151
  116. package/.claude/commands/help:commands.md +0 -78
  117. package/.claude/commands/help:faq.md +0 -79
  118. package/.claude/commands/help:plugins.md +0 -50
  119. package/.claude/commands/help:skills.md +0 -93
  120. package/.claude/commands/help:tools.md +0 -75
  121. package/.claude/commands/hotfix-squad.md +0 -112
  122. package/.claude/commands/integration-squad.md +0 -82
  123. package/.claude/commands/janitor-squad.md +0 -167
  124. package/.claude/commands/learn-auto.md +0 -120
  125. package/.claude/commands/learn.md +0 -120
  126. package/.claude/commands/mcp-list.md +0 -27
  127. package/.claude/commands/onboard-squad.md +0 -140
  128. package/.claude/commands/plan-workspace.md +0 -732
  129. package/.claude/commands/prd.md +0 -130
  130. package/.claude/commands/project-status.md +0 -82
  131. package/.claude/commands/publish.md +0 -138
  132. package/.claude/commands/recap.md +0 -69
  133. package/.claude/commands/restore.md +0 -64
  134. package/.claude/commands/review-squad.md +0 -152
  135. package/.claude/commands/save.md +0 -24
  136. package/.claude/commands/stats.md +0 -19
  137. package/.claude/commands/swarm.md +0 -210
  138. package/.claude/commands/tool-builder.md +0 -39
  139. package/.claude/commands/ws-pull.md +0 -44
  140. package/.claude/hooks/_shared-memory.cjs +0 -305
  141. package/.claude/hooks/_utils.cjs +0 -108
  142. package/.claude/hooks/agent-failure-detector.cjs +0 -383
  143. package/.claude/hooks/agent-usage-logger.cjs +0 -204
  144. package/.claude/hooks/app-edit-guard.cjs +0 -494
  145. package/.claude/hooks/auto-learn.cjs +0 -304
  146. package/.claude/hooks/bash-guard.cjs +0 -272
  147. package/.claude/hooks/builder-mode-manager.cjs +0 -354
  148. package/.claude/hooks/bulk-activity-guard.cjs +0 -271
  149. package/.claude/hooks/context-watchdog.cjs +0 -230
  150. package/.claude/hooks/delegation-reminder.cjs +0 -465
  151. package/.claude/hooks/design-system-lint.cjs +0 -271
  152. package/.claude/hooks/post-scaffold-hook.cjs +0 -181
  153. package/.claude/hooks/prompt-guard.cjs +0 -354
  154. package/.claude/hooks/publish-template-guard.cjs +0 -147
  155. package/.claude/hooks/session-start.cjs +0 -35
  156. package/.claude/hooks/shared-memory-writer.cjs +0 -147
  157. package/.claude/hooks/skill-injector.cjs +0 -140
  158. package/.claude/hooks/skill-usage-logger.cjs +0 -258
  159. package/.claude/hooks/src-edit-guard.cjs +0 -240
  160. package/.claude/hooks/sync-marketplace-agents.cjs +0 -346
  161. package/.claude/settings.json +0 -257
  162. package/.claude/skills/SDK-activity-patterns/SKILL.md +0 -428
  163. package/.claude/skills/SDK-document-templates/SKILL.md +0 -1033
  164. package/.claude/skills/SDK-function-fields/SKILL.md +0 -542
  165. package/.claude/skills/SDK-generate-skill/SKILL.md +0 -92
  166. package/.claude/skills/SDK-init-skill/SKILL.md +0 -127
  167. package/.claude/skills/SDK-insight-queries/SKILL.md +0 -787
  168. package/.claude/skills/SDK-ws-config-skill/SKILL.md +0 -1139
  169. package/.claude/skills/agent-structure/SKILL.md +0 -98
  170. package/.claude/skills/api-documentation-patterns/SKILL.md +0 -474
  171. package/.claude/skills/chrome-mcp-reference/SKILL.md +0 -370
  172. package/.claude/skills/delegation-routing/SKILL.md +0 -202
  173. package/.claude/skills/frontend-design/SKILL.md +0 -254
  174. package/.claude/skills/hailer-activity-mover/SKILL.md +0 -213
  175. package/.claude/skills/hailer-api-client/SKILL.md +0 -518
  176. package/.claude/skills/hailer-app-builder/SKILL.md +0 -1434
  177. package/.claude/skills/hailer-apps-pictures/SKILL.md +0 -269
  178. package/.claude/skills/hailer-design-system/SKILL.md +0 -235
  179. package/.claude/skills/hailer-monolith-automations/SKILL.md +0 -686
  180. package/.claude/skills/hailer-permissions-system/SKILL.md +0 -121
  181. package/.claude/skills/hailer-project-protocol/SKILL.md +0 -488
  182. package/.claude/skills/hailer-rest-api/SKILL.md +0 -61
  183. package/.claude/skills/hailer-rest-api/hailer-activities.md +0 -184
  184. package/.claude/skills/hailer-rest-api/hailer-admin.md +0 -473
  185. package/.claude/skills/hailer-rest-api/hailer-calendar.md +0 -256
  186. package/.claude/skills/hailer-rest-api/hailer-feed.md +0 -249
  187. package/.claude/skills/hailer-rest-api/hailer-insights.md +0 -195
  188. package/.claude/skills/hailer-rest-api/hailer-messaging.md +0 -276
  189. package/.claude/skills/hailer-rest-api/hailer-workflows.md +0 -283
  190. package/.claude/skills/insight-join-patterns/SKILL.md +0 -174
  191. package/.claude/skills/integration-patterns/SKILL.md +0 -421
  192. package/.claude/skills/json-only-output/SKILL.md +0 -72
  193. package/.claude/skills/lsp-setup/SKILL.md +0 -160
  194. package/.claude/skills/mcp-direct-tools/SKILL.md +0 -153
  195. package/.claude/skills/optional-parameters/SKILL.md +0 -72
  196. package/.claude/skills/publish-hailer-app/SKILL.md +0 -244
  197. package/.claude/skills/testing-patterns/SKILL.md +0 -630
  198. package/.claude/skills/tool-builder/SKILL.md +0 -250
  199. package/.claude/skills/tool-parameter-usage/SKILL.md +0 -126
  200. package/.claude/skills/tool-response-verification/SKILL.md +0 -92
  201. package/.claude/skills/zapier-hailer-patterns/SKILL.md +0 -581
  202. package/.mcp.json +0 -13
  203. package/.opencode/agent/agent-ada-skill-builder.md +0 -35
  204. package/.opencode/agent/agent-alejandro-function-fields.md +0 -39
  205. package/.opencode/agent/agent-bjorn-config-audit.md +0 -36
  206. package/.opencode/agent/agent-builder-agent-creator.md +0 -39
  207. package/.opencode/agent/agent-code-simplifier.md +0 -31
  208. package/.opencode/agent/agent-dmitri-activity-crud.md +0 -40
  209. package/.opencode/agent/agent-giuseppe-app-builder.md +0 -37
  210. package/.opencode/agent/agent-gunther-mcp-tools.md +0 -39
  211. package/.opencode/agent/agent-helga-workflow-config.md +0 -203
  212. package/.opencode/agent/agent-igor-activity-mover-automation.md +0 -46
  213. package/.opencode/agent/agent-ingrid-doc-templates.md +0 -39
  214. package/.opencode/agent/agent-ivan-monolith.md +0 -46
  215. package/.opencode/agent/agent-kenji-data-reader.md +0 -53
  216. package/.opencode/agent/agent-lars-code-inspector.md +0 -28
  217. package/.opencode/agent/agent-marco-mockup-builder.md +0 -42
  218. package/.opencode/agent/agent-marcus-api-documenter.md +0 -53
  219. package/.opencode/agent/agent-marketplace-publisher.md +0 -44
  220. package/.opencode/agent/agent-marketplace-reviewer.md +0 -42
  221. package/.opencode/agent/agent-permissions-handler.md +0 -50
  222. package/.opencode/agent/agent-simple-writer.md +0 -45
  223. package/.opencode/agent/agent-svetlana-code-review.md +0 -39
  224. package/.opencode/agent/agent-tanya-test-runner.md +0 -57
  225. package/.opencode/agent/agent-ui-designer.md +0 -56
  226. package/.opencode/agent/agent-viktor-sql-insights.md +0 -34
  227. package/.opencode/agent/agent-web-search.md +0 -42
  228. package/.opencode/agent/agent-yevgeni-discussions.md +0 -37
  229. package/.opencode/agent/agent-zara-zapier.md +0 -53
  230. package/.opencode/commands/app-squad.md +0 -135
  231. package/.opencode/commands/audit-squad.md +0 -158
  232. package/.opencode/commands/autoplan.md +0 -563
  233. package/.opencode/commands/cleanup-squad.md +0 -98
  234. package/.opencode/commands/config-squad.md +0 -106
  235. package/.opencode/commands/crud-squad.md +0 -87
  236. package/.opencode/commands/data-squad.md +0 -97
  237. package/.opencode/commands/debug-squad.md +0 -303
  238. package/.opencode/commands/doc-squad.md +0 -65
  239. package/.opencode/commands/handoff.md +0 -137
  240. package/.opencode/commands/health.md +0 -49
  241. package/.opencode/commands/help-agents.md +0 -151
  242. package/.opencode/commands/help-commands.md +0 -32
  243. package/.opencode/commands/help-faq.md +0 -29
  244. package/.opencode/commands/help-plugins.md +0 -28
  245. package/.opencode/commands/help-skills.md +0 -7
  246. package/.opencode/commands/help-tools.md +0 -40
  247. package/.opencode/commands/help.md +0 -28
  248. package/.opencode/commands/hotfix-squad.md +0 -112
  249. package/.opencode/commands/integration-squad.md +0 -82
  250. package/.opencode/commands/janitor-squad.md +0 -167
  251. package/.opencode/commands/learn-auto.md +0 -120
  252. package/.opencode/commands/learn.md +0 -120
  253. package/.opencode/commands/mcp-list.md +0 -27
  254. package/.opencode/commands/onboard-squad.md +0 -140
  255. package/.opencode/commands/plan-workspace.md +0 -732
  256. package/.opencode/commands/prd.md +0 -131
  257. package/.opencode/commands/project-status.md +0 -82
  258. package/.opencode/commands/publish.md +0 -138
  259. package/.opencode/commands/recap.md +0 -69
  260. package/.opencode/commands/restore.md +0 -64
  261. package/.opencode/commands/review-squad.md +0 -152
  262. package/.opencode/commands/save.md +0 -24
  263. package/.opencode/commands/stats.md +0 -19
  264. package/.opencode/commands/swarm.md +0 -210
  265. package/.opencode/commands/tool-builder.md +0 -39
  266. package/.opencode/commands/ws-pull.md +0 -44
  267. package/.opencode/opencode.json +0 -28
  268. package/SESSION-HANDOFF.md +0 -68
  269. package/inbox/2026-03-04-bot-config-patterns.md +0 -24
  270. package/scripts/postinstall.cjs +0 -64
  271. 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.