@yasserkhanorg/e2e-agents 1.8.5 → 1.10.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (274) hide show
  1. package/README.md +95 -8
  2. package/dist/adapters/cypress.d.ts +10 -0
  3. package/dist/adapters/cypress.d.ts.map +1 -0
  4. package/dist/adapters/cypress.js +86 -0
  5. package/dist/adapters/framework_adapter.d.ts +41 -0
  6. package/dist/adapters/framework_adapter.d.ts.map +1 -0
  7. package/dist/adapters/framework_adapter.js +152 -0
  8. package/dist/adapters/playwright.d.ts +10 -0
  9. package/dist/adapters/playwright.d.ts.map +1 -0
  10. package/dist/adapters/playwright.js +86 -0
  11. package/dist/adapters/pytest.d.ts +10 -0
  12. package/dist/adapters/pytest.d.ts.map +1 -0
  13. package/dist/adapters/pytest.js +96 -0
  14. package/dist/adapters/supertest.d.ts +12 -0
  15. package/dist/adapters/supertest.d.ts.map +1 -0
  16. package/dist/adapters/supertest.js +85 -0
  17. package/dist/agent/config.d.ts +1 -1
  18. package/dist/agent/config.d.ts.map +1 -1
  19. package/dist/agent/git.d.ts +1 -0
  20. package/dist/agent/git.d.ts.map +1 -1
  21. package/dist/agent/git.js +3 -0
  22. package/dist/agentic/fix_loop.d.ts.map +1 -1
  23. package/dist/agentic/fix_loop.js +5 -4
  24. package/dist/agentic/runner.d.ts +2 -0
  25. package/dist/agentic/runner.d.ts.map +1 -1
  26. package/dist/agentic/runner.js +15 -12
  27. package/dist/agents/cross-impact.d.ts.map +1 -1
  28. package/dist/agents/cross-impact.js +6 -1
  29. package/dist/agents/executor.d.ts.map +1 -1
  30. package/dist/agents/executor.js +6 -1
  31. package/dist/agents/strategist.d.ts.map +1 -1
  32. package/dist/agents/strategist.js +6 -1
  33. package/dist/agents/test-designer.d.ts.map +1 -1
  34. package/dist/agents/test-designer.js +6 -1
  35. package/dist/anthropic_provider.d.ts.map +1 -1
  36. package/dist/anthropic_provider.js +1 -0
  37. package/dist/base_provider.d.ts +56 -0
  38. package/dist/base_provider.d.ts.map +1 -1
  39. package/dist/base_provider.js +123 -1
  40. package/dist/budget_ledger.d.ts +28 -0
  41. package/dist/budget_ledger.d.ts.map +1 -0
  42. package/dist/budget_ledger.js +62 -0
  43. package/dist/cache/cached_provider.d.ts +45 -0
  44. package/dist/cache/cached_provider.d.ts.map +1 -0
  45. package/dist/cache/cached_provider.js +88 -0
  46. package/dist/cache/response_cache.d.ts +79 -0
  47. package/dist/cache/response_cache.d.ts.map +1 -0
  48. package/dist/cache/response_cache.js +177 -0
  49. package/dist/cli/commands/bootstrap.d.ts +3 -0
  50. package/dist/cli/commands/bootstrap.d.ts.map +1 -0
  51. package/dist/cli/commands/bootstrap.js +109 -0
  52. package/dist/cli/commands/cost_report.d.ts +3 -0
  53. package/dist/cli/commands/cost_report.d.ts.map +1 -0
  54. package/dist/cli/commands/cost_report.js +115 -0
  55. package/dist/cli/commands/crew.d.ts.map +1 -1
  56. package/dist/cli/commands/crew.js +118 -1
  57. package/dist/cli/commands/gate.d.ts +3 -0
  58. package/dist/cli/commands/gate.d.ts.map +1 -0
  59. package/dist/cli/commands/gate.js +86 -0
  60. package/dist/cli/commands/init.d.ts.map +1 -1
  61. package/dist/cli/commands/init.js +7 -62
  62. package/dist/cli/commands/train.d.ts.map +1 -1
  63. package/dist/cli/commands/train.js +16 -21
  64. package/dist/cli/defaults.d.ts +35 -0
  65. package/dist/cli/defaults.d.ts.map +1 -0
  66. package/dist/cli/defaults.js +125 -0
  67. package/dist/cli/errors.d.ts +27 -0
  68. package/dist/cli/errors.d.ts.map +1 -0
  69. package/dist/cli/errors.js +57 -0
  70. package/dist/cli/parse_args.d.ts.map +1 -1
  71. package/dist/cli/parse_args.js +24 -2
  72. package/dist/cli/types.d.ts +7 -1
  73. package/dist/cli/types.d.ts.map +1 -1
  74. package/dist/cli.js +47 -2
  75. package/dist/crew/context.d.ts +15 -0
  76. package/dist/crew/context.d.ts.map +1 -1
  77. package/dist/crew/orchestrator.d.ts +14 -0
  78. package/dist/crew/orchestrator.d.ts.map +1 -1
  79. package/dist/crew/orchestrator.js +162 -4
  80. package/dist/crew/protocol.d.ts +13 -0
  81. package/dist/crew/protocol.d.ts.map +1 -1
  82. package/dist/crew/provider.d.ts +15 -1
  83. package/dist/crew/provider.d.ts.map +1 -1
  84. package/dist/crew/provider.js +24 -4
  85. package/dist/custom_provider.d.ts.map +1 -1
  86. package/dist/custom_provider.js +1 -0
  87. package/dist/engine/diff_loader.d.ts.map +1 -1
  88. package/dist/engine/diff_loader.js +3 -14
  89. package/dist/engine/impact_engine.d.ts.map +1 -1
  90. package/dist/engine/impact_engine.js +9 -23
  91. package/dist/esm/adapters/cypress.js +49 -0
  92. package/dist/esm/adapters/framework_adapter.js +114 -0
  93. package/dist/esm/adapters/playwright.js +49 -0
  94. package/dist/esm/adapters/pytest.js +59 -0
  95. package/dist/esm/adapters/supertest.js +48 -0
  96. package/dist/esm/agent/git.js +3 -1
  97. package/dist/esm/agentic/fix_loop.js +5 -4
  98. package/dist/esm/agentic/runner.js +15 -12
  99. package/dist/esm/agents/cross-impact.js +6 -1
  100. package/dist/esm/agents/executor.js +6 -1
  101. package/dist/esm/agents/strategist.js +6 -1
  102. package/dist/esm/agents/test-designer.js +6 -1
  103. package/dist/esm/anthropic_provider.js +1 -0
  104. package/dist/esm/base_provider.js +121 -0
  105. package/dist/esm/budget_ledger.js +58 -0
  106. package/dist/esm/cache/cached_provider.js +82 -0
  107. package/dist/esm/cache/response_cache.js +140 -0
  108. package/dist/esm/cli/commands/bootstrap.js +106 -0
  109. package/dist/esm/cli/commands/cost_report.js +112 -0
  110. package/dist/esm/cli/commands/crew.js +118 -1
  111. package/dist/esm/cli/commands/gate.js +83 -0
  112. package/dist/esm/cli/commands/init.js +3 -58
  113. package/dist/esm/cli/commands/train.js +16 -21
  114. package/dist/esm/cli/defaults.js +118 -0
  115. package/dist/esm/cli/errors.js +52 -0
  116. package/dist/esm/cli/parse_args.js +24 -2
  117. package/dist/esm/cli.js +47 -2
  118. package/dist/esm/crew/orchestrator.js +162 -4
  119. package/dist/esm/crew/provider.js +24 -4
  120. package/dist/esm/custom_provider.js +1 -0
  121. package/dist/esm/engine/diff_loader.js +1 -12
  122. package/dist/esm/engine/impact_engine.js +9 -23
  123. package/dist/esm/index.js +21 -0
  124. package/dist/esm/knowledge/api_surface.js +265 -34
  125. package/dist/esm/knowledge/cluster_utils.js +60 -0
  126. package/dist/esm/knowledge/failure_history.js +121 -0
  127. package/dist/esm/knowledge/kg_bridge.js +381 -0
  128. package/dist/esm/knowledge/kg_types.js +3 -0
  129. package/dist/esm/knowledge/route_families.js +119 -0
  130. package/dist/esm/mcp-server.js +2 -4
  131. package/dist/esm/metrics/prometheus.js +149 -0
  132. package/dist/esm/model_router.js +59 -0
  133. package/dist/esm/ollama_provider.js +1 -0
  134. package/dist/esm/openai_provider.js +1 -0
  135. package/dist/esm/pipeline/orchestrator.js +6 -12
  136. package/dist/esm/pipeline/stage0_preprocess.js +12 -19
  137. package/dist/esm/pipeline/stage1_impact.js +19 -3
  138. package/dist/esm/pipeline/stage2_coverage.js +29 -7
  139. package/dist/esm/pipeline/stage3_generation.js +21 -1
  140. package/dist/esm/progress.js +112 -0
  141. package/dist/esm/prompts/coverage.js +17 -24
  142. package/dist/esm/prompts/cross-impact.js +3 -21
  143. package/dist/esm/prompts/generation.js +201 -45
  144. package/dist/esm/prompts/generation_profile.js +147 -0
  145. package/dist/esm/prompts/heal.js +33 -15
  146. package/dist/esm/prompts/impact.js +3 -22
  147. package/dist/esm/prompts/json_extract.js +36 -0
  148. package/dist/esm/prompts/strategist.js +2 -20
  149. package/dist/esm/prompts/test-designer.js +6 -21
  150. package/dist/esm/provider_factory.js +6 -4
  151. package/dist/esm/reporters/junit.js +86 -0
  152. package/dist/esm/reporters/reporter.js +3 -0
  153. package/dist/esm/reporters/sarif.js +131 -0
  154. package/dist/esm/resilience/circuit_breaker.js +78 -0
  155. package/dist/esm/resilience/retry.js +56 -0
  156. package/dist/esm/sanitize.js +66 -0
  157. package/dist/esm/training/kg_scanner.js +115 -0
  158. package/dist/esm/training/scanner.js +27 -34
  159. package/dist/esm/validation/guardrails.js +5 -0
  160. package/dist/esm/version.js +33 -0
  161. package/dist/index.d.ts +21 -1
  162. package/dist/index.d.ts.map +1 -1
  163. package/dist/index.js +45 -1
  164. package/dist/knowledge/api_surface.d.ts +12 -0
  165. package/dist/knowledge/api_surface.d.ts.map +1 -1
  166. package/dist/knowledge/api_surface.js +268 -34
  167. package/dist/knowledge/cluster_utils.d.ts +28 -0
  168. package/dist/knowledge/cluster_utils.d.ts.map +1 -0
  169. package/dist/knowledge/cluster_utils.js +67 -0
  170. package/dist/knowledge/failure_history.d.ts +39 -0
  171. package/dist/knowledge/failure_history.d.ts.map +1 -0
  172. package/dist/knowledge/failure_history.js +128 -0
  173. package/dist/knowledge/kg_bridge.d.ts +31 -0
  174. package/dist/knowledge/kg_bridge.d.ts.map +1 -0
  175. package/dist/knowledge/kg_bridge.js +388 -0
  176. package/dist/knowledge/kg_types.d.ts +75 -0
  177. package/dist/knowledge/kg_types.d.ts.map +1 -0
  178. package/dist/knowledge/kg_types.js +4 -0
  179. package/dist/knowledge/route_families.d.ts +29 -0
  180. package/dist/knowledge/route_families.d.ts.map +1 -1
  181. package/dist/knowledge/route_families.js +122 -0
  182. package/dist/mcp-server.d.ts.map +1 -1
  183. package/dist/mcp-server.js +2 -4
  184. package/dist/metrics/prometheus.d.ts +37 -0
  185. package/dist/metrics/prometheus.d.ts.map +1 -0
  186. package/dist/metrics/prometheus.js +153 -0
  187. package/dist/model_router.d.ts +28 -0
  188. package/dist/model_router.d.ts.map +1 -0
  189. package/dist/model_router.js +63 -0
  190. package/dist/ollama_provider.d.ts.map +1 -1
  191. package/dist/ollama_provider.js +1 -0
  192. package/dist/openai_provider.d.ts.map +1 -1
  193. package/dist/openai_provider.js +1 -0
  194. package/dist/pipeline/orchestrator.d.ts +2 -0
  195. package/dist/pipeline/orchestrator.d.ts.map +1 -1
  196. package/dist/pipeline/orchestrator.js +6 -12
  197. package/dist/pipeline/stage0_preprocess.d.ts.map +1 -1
  198. package/dist/pipeline/stage0_preprocess.js +11 -18
  199. package/dist/pipeline/stage1_impact.d.ts +1 -1
  200. package/dist/pipeline/stage1_impact.d.ts.map +1 -1
  201. package/dist/pipeline/stage1_impact.js +18 -2
  202. package/dist/pipeline/stage2_coverage.d.ts +2 -0
  203. package/dist/pipeline/stage2_coverage.d.ts.map +1 -1
  204. package/dist/pipeline/stage2_coverage.js +29 -7
  205. package/dist/pipeline/stage3_generation.d.ts +2 -0
  206. package/dist/pipeline/stage3_generation.d.ts.map +1 -1
  207. package/dist/pipeline/stage3_generation.js +21 -1
  208. package/dist/pipeline/stage4_heal.d.ts +2 -0
  209. package/dist/pipeline/stage4_heal.d.ts.map +1 -1
  210. package/dist/progress.d.ts +22 -0
  211. package/dist/progress.d.ts.map +1 -0
  212. package/dist/progress.js +116 -0
  213. package/dist/prompts/coverage.d.ts +2 -0
  214. package/dist/prompts/coverage.d.ts.map +1 -1
  215. package/dist/prompts/coverage.js +17 -24
  216. package/dist/prompts/cross-impact.d.ts +1 -0
  217. package/dist/prompts/cross-impact.d.ts.map +1 -1
  218. package/dist/prompts/cross-impact.js +3 -21
  219. package/dist/prompts/generation.d.ts +4 -2
  220. package/dist/prompts/generation.d.ts.map +1 -1
  221. package/dist/prompts/generation.js +201 -45
  222. package/dist/prompts/generation_profile.d.ts +29 -0
  223. package/dist/prompts/generation_profile.d.ts.map +1 -0
  224. package/dist/prompts/generation_profile.js +151 -0
  225. package/dist/prompts/heal.d.ts +3 -1
  226. package/dist/prompts/heal.d.ts.map +1 -1
  227. package/dist/prompts/heal.js +33 -15
  228. package/dist/prompts/impact.d.ts +1 -0
  229. package/dist/prompts/impact.d.ts.map +1 -1
  230. package/dist/prompts/impact.js +3 -22
  231. package/dist/prompts/json_extract.d.ts +14 -0
  232. package/dist/prompts/json_extract.d.ts.map +1 -0
  233. package/dist/prompts/json_extract.js +39 -0
  234. package/dist/prompts/strategist.d.ts.map +1 -1
  235. package/dist/prompts/strategist.js +2 -20
  236. package/dist/prompts/test-designer.d.ts +2 -0
  237. package/dist/prompts/test-designer.d.ts.map +1 -1
  238. package/dist/prompts/test-designer.js +6 -21
  239. package/dist/provider_factory.d.ts.map +1 -1
  240. package/dist/provider_factory.js +6 -4
  241. package/dist/reporters/junit.d.ts +6 -0
  242. package/dist/reporters/junit.d.ts.map +1 -0
  243. package/dist/reporters/junit.js +89 -0
  244. package/dist/reporters/reporter.d.ts +42 -0
  245. package/dist/reporters/reporter.d.ts.map +1 -0
  246. package/dist/reporters/reporter.js +4 -0
  247. package/dist/reporters/sarif.d.ts +7 -0
  248. package/dist/reporters/sarif.d.ts.map +1 -0
  249. package/dist/reporters/sarif.js +134 -0
  250. package/dist/resilience/circuit_breaker.d.ts +36 -0
  251. package/dist/resilience/circuit_breaker.d.ts.map +1 -0
  252. package/dist/resilience/circuit_breaker.js +82 -0
  253. package/dist/resilience/retry.d.ts +11 -0
  254. package/dist/resilience/retry.d.ts.map +1 -0
  255. package/dist/resilience/retry.js +59 -0
  256. package/dist/sanitize.d.ts +15 -0
  257. package/dist/sanitize.d.ts.map +1 -0
  258. package/dist/sanitize.js +71 -0
  259. package/dist/training/kg_scanner.d.ts +13 -0
  260. package/dist/training/kg_scanner.d.ts.map +1 -0
  261. package/dist/training/kg_scanner.js +118 -0
  262. package/dist/training/scanner.d.ts +7 -2
  263. package/dist/training/scanner.d.ts.map +1 -1
  264. package/dist/training/scanner.js +27 -34
  265. package/dist/validation/guardrails.d.ts +2 -0
  266. package/dist/validation/guardrails.d.ts.map +1 -1
  267. package/dist/validation/guardrails.js +5 -0
  268. package/dist/validation/output_schema.d.ts +3 -0
  269. package/dist/validation/output_schema.d.ts.map +1 -1
  270. package/dist/version.d.ts +6 -0
  271. package/dist/version.d.ts.map +1 -0
  272. package/dist/version.js +36 -0
  273. package/package.json +7 -2
  274. package/schemas/route-families.schema.json +31 -1
@@ -6,6 +6,8 @@ exports.buildGenerationPrompt = buildGenerationPrompt;
6
6
  exports.parseGenerationResponse = parseGenerationResponse;
7
7
  exports.detectHallucinatedMethods = detectHallucinatedMethods;
8
8
  const api_surface_js_1 = require("../knowledge/api_surface.js");
9
+ const sanitize_js_1 = require("../crew/sanitize.js");
10
+ const generation_profile_js_1 = require("./generation_profile.js");
9
11
  function resolveRelevantPageObjects(apiSurface, decision) {
10
12
  const relevant = [];
11
13
  const familyHints = [
@@ -25,7 +27,21 @@ function resolveRelevantPageObjects(apiSurface, decision) {
25
27
  }
26
28
  return [...new Set(relevant)].slice(0, 10);
27
29
  }
30
+ function buildAssertionPatternsBlock(patterns) {
31
+ if (!patterns || patterns.length === 0) {
32
+ return [];
33
+ }
34
+ return [
35
+ 'REQUIRED ASSERTION PATTERNS:',
36
+ 'Your tests MUST include assertions that verify each of these behaviors.',
37
+ 'Do NOT just check element visibility — verify the actual business outcome.',
38
+ ...patterns.map((p) => ` - [${p.type}] ${p.pattern}`),
39
+ '',
40
+ ];
41
+ }
28
42
  function buildGenerationPrompt(ctx) {
43
+ const profile = ctx.profile;
44
+ const isMM = profile ? (0, generation_profile_js_1.isMattermostProfile)(profile) : true;
29
45
  const relevantClasses = resolveRelevantPageObjects(ctx.apiSurface, ctx.decision);
30
46
  const apiBlock = relevantClasses.length > 0
31
47
  ? (0, api_surface_js_1.formatApiSurfaceForPrompt)(ctx.apiSurface, relevantClasses)
@@ -40,73 +56,190 @@ function buildGenerationPrompt(ctx) {
40
56
  ? `Create a NEW spec file at: ${ctx.specPath}`
41
57
  : `ADD scenarios to the EXISTING spec at: ${ctx.specPath}`;
42
58
  const routeFamilyTag = ctx.decision.routeFamily;
59
+ // Build prompt based on profile
60
+ const projectName = profile?.projectName || 'Mattermost';
61
+ const testFramework = profile?.testFramework || 'Playwright';
62
+ const importStatement = profile?.importStatement || '@mattermost/playwright-lib';
63
+ // API test mode prompt
64
+ if (profile?.testMode === 'api') {
65
+ return buildApiTestPrompt(ctx, profile, scenariosBlock, routeFamilyTag);
66
+ }
67
+ // Build rules from profile conventions or use Mattermost defaults
68
+ const rules = isMM
69
+ ? [
70
+ `1. Import ONLY from "${importStatement}" — no other test framework imports.`,
71
+ '2. Every test must call `await pw.initSetup()` first.',
72
+ '3. Use `await pw.testBrowser.login(user)` to log in — never hardcode credentials.',
73
+ '4. Use ONLY page object methods listed above. Do NOT invent methods that are not listed.',
74
+ '5. If a method is not available, use `page.getByRole()` or `page.getByTestId()`.',
75
+ `6. Tag every test: {tag: '@${routeFamilyTag}'}`,
76
+ '7. Write one test per scenario with a descriptive name of what the user does and what is verified.',
77
+ `8. Use \`expect\` from "${importStatement}" — do NOT import from "@playwright/test".`,
78
+ '9. Include the copyright header for new files.',
79
+ '10. NEVER fabricate test IDs (MM-TXXXX). Use descriptive names only.',
80
+ ]
81
+ : [
82
+ ...(profile?.conventions || []).map((c, i) => `${i + 1}. ${c}`),
83
+ `${(profile?.conventions?.length || 0) + 1}. Use ONLY page object methods listed above. Do NOT invent methods that are not listed.`,
84
+ `${(profile?.conventions?.length || 0) + 2}. If a method is not available, use \`page.getByRole()\` or \`page.getByTestId()\`.`,
85
+ `${(profile?.conventions?.length || 0) + 3}. Tag every test: {tag: '@${routeFamilyTag}'}`,
86
+ ];
87
+ // Build example block
88
+ const exampleBlock = isMM
89
+ ? [
90
+ 'EXAMPLE SPEC STRUCTURE:',
91
+ '```typescript',
92
+ '// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.',
93
+ '// See LICENSE.txt for license information.',
94
+ '',
95
+ `import {expect, test} from '${importStatement}';`,
96
+ '',
97
+ 'test(',
98
+ " 'descriptive name of what is tested',",
99
+ ` {tag: '@${routeFamilyTag}'},`,
100
+ ' async ({pw}) => {',
101
+ ' const {user} = await pw.initSetup();',
102
+ ' const {channelsPage} = await pw.testBrowser.login(user);',
103
+ ' await channelsPage.goto();',
104
+ ' await channelsPage.toBeVisible();',
105
+ ' // test steps...',
106
+ ' },',
107
+ ');',
108
+ '```',
109
+ ]
110
+ : [
111
+ 'EXAMPLE SPEC STRUCTURE:',
112
+ '```typescript',
113
+ ...(profile?.copyrightHeader ? [profile.copyrightHeader, ''] : []),
114
+ `import {test, expect} from '${importStatement}';`,
115
+ '',
116
+ 'test(',
117
+ " 'descriptive name of what is tested',",
118
+ ` {tag: '@${routeFamilyTag}'},`,
119
+ ' async ({page}) => {',
120
+ ' // test steps...',
121
+ ' },',
122
+ ');',
123
+ '```',
124
+ ];
43
125
  return [
44
- 'You are generating Mattermost Playwright E2E test code.',
126
+ `You are generating ${projectName} ${testFramework} E2E test code.`,
45
127
  '',
46
128
  `TASK: ${modeInstruction}`,
47
129
  '',
48
- `FLOW: ${ctx.decision.flowName}`,
130
+ `FLOW: ${(0, sanitize_js_1.sanitizeForPrompt)(ctx.decision.flowName)}`,
49
131
  `Route Family: ${ctx.decision.routeFamily}${ctx.decision.featureId ? ` / ${ctx.decision.featureId}` : ''}`,
50
132
  `Route: ${ctx.decision.specificRoute || '(not specified)'}`,
51
133
  `Priority: ${ctx.decision.priority}`,
52
- `Evidence: ${ctx.decision.evidence}`,
134
+ `Evidence: ${(0, sanitize_js_1.sanitizeForPrompt)(ctx.decision.evidence)}`,
53
135
  '',
54
136
  'SCENARIOS TO IMPLEMENT:',
55
137
  scenariosBlock || ' (implement core user actions for this flow)',
56
138
  '',
57
139
  'USER ACTIONS:',
58
- ctx.decision.userActions.map((a) => ` - ${a}`).join('\n') || ' (none specified)',
140
+ ctx.decision.userActions.map((a) => ` - ${(0, sanitize_js_1.sanitizeForPrompt)(a)}`).join('\n') || ' (none specified)',
59
141
  '',
142
+ ...buildAssertionPatternsBlock(ctx.decision.assertionPatterns),
60
143
  'AVAILABLE PAGE OBJECTS AND METHODS:',
61
144
  apiBlock,
62
145
  existingBlock,
63
146
  '',
64
147
  'MANDATORY RULES:',
65
- '1. Import ONLY from "@mattermost/playwright-lib" — no other test framework imports.',
66
- '2. Every test must call `await pw.initSetup()` first.',
67
- '3. Use `await pw.testBrowser.login(user)` to log in — never hardcode credentials.',
68
- '4. Use ONLY page object methods listed above. Do NOT invent methods that are not listed.',
69
- '5. If a method is not available, use `page.getByRole()` or `page.getByTestId()`.',
70
- `6. Tag every test: {tag: '@${routeFamilyTag}'}`,
71
- '7. Write one test per scenario with a descriptive name of what the user does and what is verified.',
72
- '8. Use `expect` from "@mattermost/playwright-lib" — do NOT import from "@playwright/test".',
73
- '9. Include the copyright header for new files.',
74
- '10. NEVER fabricate test IDs (MM-TXXXX). Use descriptive names only.',
75
- '',
76
- 'EXAMPLE SPEC STRUCTURE:',
77
- '```typescript',
78
- '// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.',
79
- '// See LICENSE.txt for license information.',
80
- '',
81
- "import {expect, test} from '@mattermost/playwright-lib';",
82
- '',
83
- 'test(',
84
- " 'descriptive name of what is tested',",
85
- ` {tag: '@${routeFamilyTag}'},`,
86
- ' async ({pw}) => {',
87
- ' const {user} = await pw.initSetup();',
88
- ' const {channelsPage} = await pw.testBrowser.login(user);',
89
- ' await channelsPage.goto();',
90
- ' await channelsPage.toBeVisible();',
91
- ' // test steps...',
92
- ' },',
93
- ');',
94
- '```',
148
+ ...rules,
149
+ '',
150
+ ...exampleBlock,
95
151
  '',
96
152
  'Return ONLY the TypeScript code. No explanations, no markdown fences.',
97
153
  ].join('\n');
98
154
  }
99
- function parseGenerationResponse(text, expectedPath, mode, flowId) {
155
+ function buildApiTestPrompt(ctx, profile, scenariosBlock, routeFamilyTag) {
156
+ const modeInstruction = ctx.mode === 'create_spec'
157
+ ? `Create a NEW test file at: ${ctx.specPath}`
158
+ : `ADD test cases to the EXISTING file at: ${ctx.specPath}`;
159
+ const existingBlock = ctx.existingSpecContent
160
+ ? `\nEXISTING FILE (extend this):\n\`\`\`typescript\n${ctx.existingSpecContent}\n\`\`\``
161
+ : '';
162
+ return [
163
+ `You are generating ${profile.projectName} API test code using ${profile.testFramework}.`,
164
+ '',
165
+ `TASK: ${modeInstruction}`,
166
+ '',
167
+ `FLOW: ${(0, sanitize_js_1.sanitizeForPrompt)(ctx.decision.flowName)}`,
168
+ `Route Family: ${ctx.decision.routeFamily}${ctx.decision.featureId ? ` / ${ctx.decision.featureId}` : ''}`,
169
+ `Endpoint: ${ctx.decision.specificRoute || '(not specified)'}`,
170
+ `Priority: ${ctx.decision.priority}`,
171
+ `Evidence: ${(0, sanitize_js_1.sanitizeForPrompt)(ctx.decision.evidence)}`,
172
+ '',
173
+ 'SCENARIOS TO IMPLEMENT:',
174
+ scenariosBlock || ' (implement core API endpoint tests)',
175
+ '',
176
+ 'USER ACTIONS:',
177
+ ctx.decision.userActions.map((a) => ` - ${(0, sanitize_js_1.sanitizeForPrompt)(a)}`).join('\n') || ' (none specified)',
178
+ '',
179
+ ...buildAssertionPatternsBlock(ctx.decision.assertionPatterns),
180
+ existingBlock,
181
+ '',
182
+ 'MANDATORY RULES:',
183
+ ...profile.conventions.map((c, i) => `${i + 1}. ${c}`),
184
+ `${profile.conventions.length + 1}. Tag every test: {tag: '@${routeFamilyTag}'}`,
185
+ '',
186
+ 'EXAMPLE TEST STRUCTURE:',
187
+ ...(profile.testFramework.toLowerCase().includes('pytest')
188
+ ? [
189
+ '```python',
190
+ ...(profile.copyrightHeader ? [profile.copyrightHeader, ''] : []),
191
+ 'import pytest',
192
+ 'import requests',
193
+ '',
194
+ `BASE_URL = 'http://localhost:3000'`,
195
+ '',
196
+ '',
197
+ `class Test${ctx.decision.routeFamily.replace(/[^a-zA-Z0-9]/g, '')}:`,
198
+ " def test_should_return_200_for_valid_request(self):",
199
+ ` res = requests.get(f'{BASE_URL}${ctx.decision.specificRoute || '/api/endpoint'}')`,
200
+ ' assert res.status_code == 200',
201
+ '```',
202
+ ]
203
+ : [
204
+ '```typescript',
205
+ ...(profile.copyrightHeader ? [profile.copyrightHeader, ''] : []),
206
+ `import {describe, it, expect} from '${profile.importStatement}';`,
207
+ "import supertest from 'supertest';",
208
+ '',
209
+ "const request = supertest('http://localhost:3000');",
210
+ '',
211
+ `describe('${ctx.decision.routeFamily}', () => {`,
212
+ " it('should return 200 for valid request', async () => {",
213
+ ` const res = await request.get('${ctx.decision.specificRoute || '/api/endpoint'}');`,
214
+ ' expect(res.status).toBe(200);',
215
+ ' });',
216
+ '});',
217
+ '```',
218
+ ]),
219
+ '',
220
+ ...(profile.testFramework.toLowerCase().includes('pytest')
221
+ ? ['Return ONLY the Python code. No explanations, no markdown fences.']
222
+ : ['Return ONLY the TypeScript code. No explanations, no markdown fences.']),
223
+ ].join('\n');
224
+ }
225
+ function parseGenerationResponse(text, expectedPath, mode, flowId, profile) {
100
226
  let code = text.trim();
101
227
  const fenced = code.match(/^```(?:typescript|ts)?\s*([\s\S]*?)```\s*$/i);
102
228
  if (fenced) {
103
229
  code = fenced[1].trim();
104
230
  }
105
- if (!code.includes('test(')) {
231
+ if (!code.includes('test(') && !code.includes('it(') && !code.includes('describe(')) {
106
232
  return null;
107
233
  }
108
- if (!code.includes('@mattermost/playwright-lib')) {
109
- code = `import {expect, test} from '@mattermost/playwright-lib';\n\n${code}`;
234
+ const importStatement = profile?.importStatement || '@mattermost/playwright-lib';
235
+ // Auto-add import if missing
236
+ if (!code.includes(importStatement)) {
237
+ if (profile?.testMode === 'api') {
238
+ code = `import {describe, it, expect} from '${importStatement}';\n\n${code}`;
239
+ }
240
+ else {
241
+ code = `import {expect, test} from '${importStatement}';\n\n${code}`;
242
+ }
110
243
  }
111
244
  return { specPath: expectedPath, code, mode, flowId };
112
245
  }
@@ -126,21 +259,44 @@ const BUILT_IN_METHODS = new Set([
126
259
  'initSetup', 'login', 'waitUntil', 'skipIfNoLicense', 'ensureLicense',
127
260
  'random', 'duration', 'isOutsideRemoteUserHour', 'setTimeout',
128
261
  'skip', 'fixme', 'slow', 'fail',
262
+ // API testing built-ins
263
+ 'get', 'post', 'put', 'patch', 'delete', 'send', 'set', 'query',
264
+ 'toBe', 'toEqual', 'toBeDefined', 'toContain', 'toHaveProperty',
265
+ 'toMatchObject', 'toHaveLength', 'toBeTruthy', 'toBeFalsy',
129
266
  ]);
130
267
  /**
131
268
  * Returns method names that appear in generated code but do not exist in the API surface.
132
- * Used for logging; does not block generation.
269
+ * Detects all call patterns: await X.Y(), X.Y(), const z = X.Y(), chained calls.
133
270
  */
134
271
  function detectHallucinatedMethods(code, apiSurface) {
135
272
  const allMethods = new Set(apiSurface.pageObjects.flatMap((po) => po.methods.map((m) => m.name)));
136
- const suspected = [];
137
- const callRe = /\bawait\s+\w+\.([a-zA-Z_]\w*)\s*\(/g;
273
+ const suspected = new Set();
274
+ const callPatterns = [
275
+ /\bawait\s+\w+\.([a-zA-Z_]\w*)\s*\(/g,
276
+ /\b(?:const|let|var)\s+\w+\s*=\s*\w+\.([a-zA-Z_]\w*)\s*\(/g,
277
+ /\b\w+Page\.([a-zA-Z_]\w*)\s*\(/g, // any *Page object (channelsPage, settingsPage, etc.)
278
+ /\b(?:pw|page|this)\.\w*\.?([a-zA-Z_]\w*)\s*\(/g,
279
+ ];
280
+ const generalCallRe = /\b[a-zA-Z_]\w*\.([a-zA-Z_]\w*)\s*\(/g;
281
+ for (const re of callPatterns) {
282
+ let match;
283
+ while ((match = re.exec(code)) !== null) {
284
+ const methodName = match[1];
285
+ if (!BUILT_IN_METHODS.has(methodName) && !allMethods.has(methodName) && methodName.length > 3) {
286
+ suspected.add(methodName);
287
+ }
288
+ }
289
+ }
138
290
  let match;
139
- while ((match = callRe.exec(code)) !== null) {
291
+ while ((match = generalCallRe.exec(code)) !== null) {
140
292
  const methodName = match[1];
141
- if (!BUILT_IN_METHODS.has(methodName) && !allMethods.has(methodName) && methodName.length > 3) {
142
- suspected.push(methodName);
293
+ if (!BUILT_IN_METHODS.has(methodName) &&
294
+ !allMethods.has(methodName) &&
295
+ methodName.length > 3 &&
296
+ !methodName.startsWith('to') &&
297
+ !methodName.startsWith('get')) {
298
+ suspected.add(methodName);
143
299
  }
144
300
  }
145
- return [...new Set(suspected)];
301
+ return [...suspected];
146
302
  }
@@ -0,0 +1,29 @@
1
+ /**
2
+ * Dynamic generation profile — replaces hardcoded Mattermost references with
3
+ * project-specific configuration. Enables e2e-agents to generate tests for any project.
4
+ */
5
+ import type { KnowledgeGraph } from '../knowledge/kg_types.js';
6
+ import type { TestType } from '../knowledge/route_families.js';
7
+ export interface GenerationProfile {
8
+ projectName: string;
9
+ testFramework: string;
10
+ importStatement: string;
11
+ conventions: string[];
12
+ copyrightHeader?: string;
13
+ testMode: TestType;
14
+ }
15
+ /**
16
+ * Resolves the generation profile from config and optional KG metadata.
17
+ * - If profile='mattermost' or Mattermost is detected, returns Mattermost profile.
18
+ * - If KG is present, derives project-specific profile from it.
19
+ * - Otherwise, returns generic Playwright profile.
20
+ */
21
+ export declare function resolveGenerationProfile(config?: {
22
+ profile?: string;
23
+ testMode?: TestType;
24
+ }, kg?: KnowledgeGraph | null): GenerationProfile;
25
+ /**
26
+ * Checks if a profile is the Mattermost profile (for backward compatibility checks).
27
+ */
28
+ export declare function isMattermostProfile(profile: GenerationProfile): boolean;
29
+ //# sourceMappingURL=generation_profile.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"generation_profile.d.ts","sourceRoot":"","sources":["../../src/prompts/generation_profile.ts"],"names":[],"mappings":"AAGA;;;GAGG;AAEH,OAAO,KAAK,EAAC,cAAc,EAAC,MAAM,0BAA0B,CAAC;AAC7D,OAAO,KAAK,EAAC,QAAQ,EAAC,MAAM,gCAAgC,CAAC;AAG7D,MAAM,WAAW,iBAAiB;IAC9B,WAAW,EAAE,MAAM,CAAC;IACpB,aAAa,EAAE,MAAM,CAAC;IACtB,eAAe,EAAE,MAAM,CAAC;IACxB,WAAW,EAAE,MAAM,EAAE,CAAC;IACtB,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,QAAQ,EAAE,QAAQ,CAAC;CACtB;AA8CD;;;;;GAKG;AACH,wBAAgB,wBAAwB,CACpC,MAAM,CAAC,EAAE;IAAC,OAAO,CAAC,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,EAAE,QAAQ,CAAA;CAAC,EAChD,EAAE,CAAC,EAAE,cAAc,GAAG,IAAI,GAC3B,iBAAiB,CAmCnB;AAED;;GAEG;AACH,wBAAgB,mBAAmB,CAAC,OAAO,EAAE,iBAAiB,GAAG,OAAO,CAEvE"}
@@ -0,0 +1,151 @@
1
+ "use strict";
2
+ // Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
3
+ // See LICENSE.txt for license information.
4
+ Object.defineProperty(exports, "__esModule", { value: true });
5
+ exports.resolveGenerationProfile = resolveGenerationProfile;
6
+ exports.isMattermostProfile = isMattermostProfile;
7
+ const framework_adapter_js_1 = require("../adapters/framework_adapter.js");
8
+ const MATTERMOST_PROFILE = {
9
+ projectName: 'Mattermost',
10
+ testFramework: 'Playwright',
11
+ importStatement: '@mattermost/playwright-lib',
12
+ conventions: [
13
+ 'Import ONLY from "@mattermost/playwright-lib" — no other test framework imports.',
14
+ 'Every test must call `await pw.initSetup()` first.',
15
+ 'Use `await pw.testBrowser.login(user)` to log in — never hardcode credentials.',
16
+ 'Use `expect` from "@mattermost/playwright-lib" — do NOT import from "@playwright/test".',
17
+ 'Include the copyright header for new files.',
18
+ ],
19
+ copyrightHeader: [
20
+ '// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.',
21
+ '// See LICENSE.txt for license information.',
22
+ ].join('\n'),
23
+ testMode: 'ui',
24
+ };
25
+ const DEFAULT_PLAYWRIGHT_PROFILE = {
26
+ projectName: 'Project',
27
+ testFramework: 'Playwright',
28
+ importStatement: '@playwright/test',
29
+ conventions: [
30
+ 'Import from "@playwright/test" for test and expect.',
31
+ 'Use page fixtures provided by Playwright.',
32
+ 'Prefer ARIA roles and data-testid attributes for selectors.',
33
+ 'Write one test per scenario with a descriptive name.',
34
+ ],
35
+ testMode: 'ui',
36
+ };
37
+ const DEFAULT_API_PROFILE = {
38
+ projectName: 'Project',
39
+ testFramework: 'vitest + supertest',
40
+ importStatement: 'vitest',
41
+ conventions: [
42
+ 'Import from "vitest" for test and expect.',
43
+ 'Use supertest for HTTP request assertions.',
44
+ 'Validate response status codes, headers, and body structure.',
45
+ 'Test both success and error paths for each endpoint.',
46
+ ],
47
+ testMode: 'api',
48
+ };
49
+ /**
50
+ * Resolves the generation profile from config and optional KG metadata.
51
+ * - If profile='mattermost' or Mattermost is detected, returns Mattermost profile.
52
+ * - If KG is present, derives project-specific profile from it.
53
+ * - Otherwise, returns generic Playwright profile.
54
+ */
55
+ function resolveGenerationProfile(config, kg) {
56
+ // Explicit Mattermost profile
57
+ if (config?.profile === 'mattermost') {
58
+ return { ...MATTERMOST_PROFILE };
59
+ }
60
+ // KG-based profile derivation
61
+ if (kg) {
62
+ const frameworks = kg.project.frameworks.map((f) => f.toLowerCase());
63
+ const isMattermost = kg.project.name.toLowerCase().includes('mattermost') ||
64
+ frameworks.includes('@mattermost/playwright-lib');
65
+ if (isMattermost) {
66
+ return { ...MATTERMOST_PROFILE };
67
+ }
68
+ const testMode = config?.testMode || deriveTestMode(frameworks);
69
+ const testFramework = deriveTestFramework(frameworks, testMode);
70
+ const importStatement = deriveImportStatement(frameworks, testMode);
71
+ return {
72
+ projectName: kg.project.name || 'Project',
73
+ testFramework,
74
+ importStatement,
75
+ conventions: buildConventions(testFramework, importStatement, testMode),
76
+ testMode,
77
+ };
78
+ }
79
+ // Default profiles based on test mode
80
+ if (config?.testMode === 'api') {
81
+ return { ...DEFAULT_API_PROFILE };
82
+ }
83
+ return { ...DEFAULT_PLAYWRIGHT_PROFILE };
84
+ }
85
+ /**
86
+ * Checks if a profile is the Mattermost profile (for backward compatibility checks).
87
+ */
88
+ function isMattermostProfile(profile) {
89
+ return profile.importStatement === '@mattermost/playwright-lib';
90
+ }
91
+ // ---------------------------------------------------------------------------
92
+ // Internal helpers
93
+ // ---------------------------------------------------------------------------
94
+ function deriveTestMode(frameworks) {
95
+ const uiSet = new Set(framework_adapter_js_1.UI_FRAMEWORKS);
96
+ const apiSet = new Set(framework_adapter_js_1.API_FRAMEWORKS);
97
+ const hasUiFramework = frameworks.some((f) => uiSet.has(f));
98
+ const hasApiFramework = frameworks.some((f) => apiSet.has(f));
99
+ if (hasUiFramework && hasApiFramework)
100
+ return 'both';
101
+ if (hasApiFramework && !hasUiFramework)
102
+ return 'api';
103
+ return 'ui';
104
+ }
105
+ function deriveTestFramework(frameworks, testMode) {
106
+ if (testMode === 'api') {
107
+ if (frameworks.includes('pytest'))
108
+ return 'pytest';
109
+ if (frameworks.includes('jest'))
110
+ return 'jest + supertest';
111
+ return 'vitest + supertest';
112
+ }
113
+ if (frameworks.includes('cypress'))
114
+ return 'Cypress';
115
+ if (frameworks.includes('selenium'))
116
+ return 'Selenium';
117
+ return 'Playwright';
118
+ }
119
+ function deriveImportStatement(frameworks, testMode) {
120
+ if (testMode === 'api') {
121
+ if (frameworks.includes('pytest'))
122
+ return 'pytest';
123
+ if (frameworks.includes('jest'))
124
+ return 'jest';
125
+ return 'vitest';
126
+ }
127
+ if (frameworks.includes('cypress'))
128
+ return 'cypress';
129
+ return '@playwright/test';
130
+ }
131
+ function buildConventions(testFramework, importStatement, testMode) {
132
+ const conventions = [];
133
+ if (testMode === 'api' || testMode === 'both') {
134
+ conventions.push(`Import from "${importStatement}" for test and expect.`);
135
+ conventions.push('Validate response status codes, headers, and body structure.');
136
+ conventions.push('Test both success and error paths for each endpoint.');
137
+ }
138
+ if (testMode === 'ui' || testMode === 'both') {
139
+ if (testFramework.includes('Playwright')) {
140
+ conventions.push('Import from "@playwright/test" for test and expect.');
141
+ conventions.push('Use page fixtures provided by Playwright.');
142
+ }
143
+ else if (testFramework.includes('Cypress')) {
144
+ conventions.push('Use cy.* commands for browser interaction.');
145
+ }
146
+ conventions.push('Prefer ARIA roles and data-testid attributes for selectors.');
147
+ }
148
+ conventions.push('Write one test per scenario with a descriptive name of what the user does and what is verified.');
149
+ conventions.push('NEVER fabricate test IDs. Use descriptive names only.');
150
+ return conventions;
151
+ }
@@ -1,4 +1,5 @@
1
1
  import type { FlowDecision } from '../validation/output_schema.js';
2
+ import type { GenerationProfile } from './generation_profile.js';
2
3
  export interface HealPromptContext {
3
4
  specPath: string;
4
5
  status: 'failed' | 'flaky';
@@ -6,6 +7,7 @@ export interface HealPromptContext {
6
7
  failureDetail?: string;
7
8
  /** Last 3 console errors from the test run */
8
9
  consoleErrors?: string[];
10
+ profile?: GenerationProfile;
9
11
  }
10
12
  /**
11
13
  * Builds a route-family-aware heal prompt for the playwright-test-healer agent.
@@ -17,5 +19,5 @@ export declare function buildHealPrompt(ctx: HealPromptContext): string;
17
19
  * Builds a minimal quality-fix prompt for spec files that fail content validation
18
20
  * (e.g. contain test.describe, test.only, wrong imports).
19
21
  */
20
- export declare function buildQualityFixPrompt(specPath: string, qualityIssues: string[]): string;
22
+ export declare function buildQualityFixPrompt(specPath: string, qualityIssues: string[], profile?: GenerationProfile): string;
21
23
  //# sourceMappingURL=heal.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"heal.d.ts","sourceRoot":"","sources":["../../src/prompts/heal.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAC,YAAY,EAAC,MAAM,gCAAgC,CAAC;AAEjE,MAAM,WAAW,iBAAiB;IAC9B,QAAQ,EAAE,MAAM,CAAC;IACjB,MAAM,EAAE,QAAQ,GAAG,OAAO,CAAC;IAC3B,QAAQ,CAAC,EAAE,YAAY,CAAC;IACxB,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,8CAA8C;IAC9C,aAAa,CAAC,EAAE,MAAM,EAAE,CAAC;CAC5B;AAED;;;;GAIG;AACH,wBAAgB,eAAe,CAAC,GAAG,EAAE,iBAAiB,GAAG,MAAM,CAgD9D;AAED;;;GAGG;AACH,wBAAgB,qBAAqB,CAAC,QAAQ,EAAE,MAAM,EAAE,aAAa,EAAE,MAAM,EAAE,GAAG,MAAM,CAgBvF"}
1
+ {"version":3,"file":"heal.d.ts","sourceRoot":"","sources":["../../src/prompts/heal.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAC,YAAY,EAAC,MAAM,gCAAgC,CAAC;AAEjE,OAAO,KAAK,EAAC,iBAAiB,EAAC,MAAM,yBAAyB,CAAC;AAG/D,MAAM,WAAW,iBAAiB;IAC9B,QAAQ,EAAE,MAAM,CAAC;IACjB,MAAM,EAAE,QAAQ,GAAG,OAAO,CAAC;IAC3B,QAAQ,CAAC,EAAE,YAAY,CAAC;IACxB,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,8CAA8C;IAC9C,aAAa,CAAC,EAAE,MAAM,EAAE,CAAC;IACzB,OAAO,CAAC,EAAE,iBAAiB,CAAC;CAC/B;AAED;;;;GAIG;AACH,wBAAgB,eAAe,CAAC,GAAG,EAAE,iBAAiB,GAAG,MAAM,CAgE9D;AAED;;;GAGG;AACH,wBAAgB,qBAAqB,CAAC,QAAQ,EAAE,MAAM,EAAE,aAAa,EAAE,MAAM,EAAE,EAAE,OAAO,CAAC,EAAE,iBAAiB,GAAG,MAAM,CAmBpH"}
@@ -4,6 +4,8 @@
4
4
  Object.defineProperty(exports, "__esModule", { value: true });
5
5
  exports.buildHealPrompt = buildHealPrompt;
6
6
  exports.buildQualityFixPrompt = buildQualityFixPrompt;
7
+ const sanitize_js_1 = require("../crew/sanitize.js");
8
+ const generation_profile_js_1 = require("./generation_profile.js");
7
9
  /**
8
10
  * Builds a route-family-aware heal prompt for the playwright-test-healer agent.
9
11
  * Enriches the base healer constraints with flow context so the agent understands
@@ -14,24 +16,43 @@ function buildHealPrompt(ctx) {
14
16
  ? [
15
17
  '',
16
18
  'FLOW CONTEXT (use to understand test intent — do not change test objectives):',
17
- ` Flow: ${ctx.decision.flowName}`,
19
+ ` Flow: ${(0, sanitize_js_1.sanitizeForPrompt)(ctx.decision.flowName)}`,
18
20
  ` Route Family: ${ctx.decision.routeFamily}${ctx.decision.featureId ? ` / ${ctx.decision.featureId}` : ''}`,
19
21
  ` Route: ${ctx.decision.specificRoute || '(family-level)'}`,
20
- ` User Actions: ${ctx.decision.userActions.join('; ') || 'not specified'}`,
21
- ` Evidence: ${ctx.decision.evidence}`,
22
+ ` User Actions: ${(0, sanitize_js_1.sanitizeForPrompt)(ctx.decision.userActions.join('; ')) || 'not specified'}`,
23
+ ` Evidence: ${(0, sanitize_js_1.sanitizeForPrompt)(ctx.decision.evidence)}`,
22
24
  ].join('\n')
23
25
  : '';
24
26
  const statusNote = ctx.status === 'flaky'
25
27
  ? 'This test is FLAKY (passes sometimes, fails other times). Look for race conditions, missing waits, or order-dependent state.'
26
28
  : 'This test is FAILING consistently. The selector, URL, or API call is likely broken.';
27
29
  const failureBlock = ctx.failureDetail
28
- ? `\nFailure detail:\n${ctx.failureDetail}`
30
+ ? `\nFailure detail:\n${(0, sanitize_js_1.sanitizeForPrompt)(ctx.failureDetail)}`
29
31
  : '';
30
32
  const consoleBlock = ctx.consoleErrors && ctx.consoleErrors.length > 0
31
- ? `\nRecent console errors from test run:\n${ctx.consoleErrors.slice(-3).map((e) => ` - ${e}`).join('\n')}`
33
+ ? `\nRecent console errors from test run:\n${ctx.consoleErrors.slice(-3).map((e) => ` - ${(0, sanitize_js_1.sanitizeForPrompt)(e)}`).join('\n')}`
32
34
  : '';
35
+ const importLib = ctx.profile?.importStatement || '@mattermost/playwright-lib';
36
+ const isMM = ctx.profile ? (0, generation_profile_js_1.isMattermostProfile)(ctx.profile) : true;
37
+ const projectLabel = ctx.profile?.projectName || 'Mattermost';
38
+ const frameworkLabel = ctx.profile?.testFramework || 'Playwright';
39
+ const constraints = isMM
40
+ ? [
41
+ `- Import ONLY from "${importLib}". Do not use "@playwright/test" directly.`,
42
+ '- Do not use test.describe or test.only.',
43
+ '- Keep a single tag string matching the route family (e.g. "@channels", "@scheduled_posts").',
44
+ `- Use only existing ${projectLabel} ${frameworkLabel} fixture and page-object APIs.`,
45
+ '- Do NOT invent new pw.* clients or page object methods that do not exist.',
46
+ '- Avoid brittle class selectors (.backstage-navbar, .admin-console__wrapper, .left-panel, .panel-card).',
47
+ ]
48
+ : [
49
+ `- Import from "${importLib}".`,
50
+ '- Keep a single tag string matching the route family.',
51
+ `- Use only existing ${projectLabel} ${frameworkLabel} page-object APIs.`,
52
+ '- Do NOT invent page object methods that do not exist.',
53
+ ];
33
54
  return [
34
- 'Heal this specific Playwright test file and keep edits minimal.',
55
+ `Heal this specific ${frameworkLabel} test file and keep edits minimal.`,
35
56
  '',
36
57
  `Target test file: ${ctx.specPath}`,
37
58
  `Status: ${ctx.status.toUpperCase()} — ${statusNote}`,
@@ -40,12 +61,7 @@ function buildHealPrompt(ctx) {
40
61
  flowBlock,
41
62
  '',
42
63
  'Healing constraints (must follow):',
43
- '- Import ONLY from "@mattermost/playwright-lib". Do not use "@playwright/test" directly.',
44
- '- Do not use test.describe or test.only.',
45
- '- Keep a single tag string matching the route family (e.g. "@channels", "@scheduled_posts").',
46
- '- Use only existing Mattermost Playwright fixture and page-object APIs.',
47
- '- Do NOT invent new pw.* clients or page object methods that do not exist.',
48
- '- Avoid brittle class selectors (.backstage-navbar, .admin-console__wrapper, .left-panel, .panel-card).',
64
+ ...constraints,
49
65
  '- Prefer stable assertions using URL patterns, data-testid attributes, ARIA roles, and page-object methods.',
50
66
  '- For flaky tests: add explicit waits (waitFor, expect().toBeVisible()) before interactions.',
51
67
  '- Keep the test intent and scenario unchanged — only fix what is broken.',
@@ -58,9 +74,11 @@ function buildHealPrompt(ctx) {
58
74
  * Builds a minimal quality-fix prompt for spec files that fail content validation
59
75
  * (e.g. contain test.describe, test.only, wrong imports).
60
76
  */
61
- function buildQualityFixPrompt(specPath, qualityIssues) {
77
+ function buildQualityFixPrompt(specPath, qualityIssues, profile) {
78
+ const importLib = profile?.importStatement || '@mattermost/playwright-lib';
79
+ const frameworkLabel = profile?.testFramework || 'Playwright';
62
80
  return [
63
- 'Fix quality issues in this Playwright spec file. Make minimal edits only.',
81
+ `Fix quality issues in this ${frameworkLabel} spec file. Make minimal edits only.`,
64
82
  '',
65
83
  `Target file: ${specPath}`,
66
84
  '',
@@ -68,7 +86,7 @@ function buildQualityFixPrompt(specPath, qualityIssues) {
68
86
  ...qualityIssues.map((issue) => ` - ${issue}`),
69
87
  '',
70
88
  'Rules:',
71
- '- Import only from "@mattermost/playwright-lib".',
89
+ `- Import only from "${importLib}".`,
72
90
  '- Remove test.describe wrappers (flatten to top-level test() calls).',
73
91
  '- Remove test.only calls.',
74
92
  '- Ensure each test has exactly one tag string.',
@@ -11,6 +11,7 @@ export interface ImpactPromptContext {
11
11
  existingSpecs: SpecEntry[];
12
12
  apiSurface: ApiSurfaceCatalog;
13
13
  contextBlock: string;
14
+ projectName?: string;
14
15
  }
15
16
  export declare function buildImpactPrompt(ctx: ImpactPromptContext): string;
16
17
  export interface ImpactAgentResponse {
@@ -1 +1 @@
1
- {"version":3,"file":"impact.d.ts","sourceRoot":"","sources":["../../src/prompts/impact.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAC,WAAW,EAAC,MAAM,gCAAgC,CAAC;AAChE,OAAO,KAAK,EAAC,SAAS,EAAC,MAAM,4BAA4B,CAAC;AAE1D,OAAO,EAA4B,KAAK,iBAAiB,EAAC,MAAM,6BAA6B,CAAC;AAE9F,MAAM,WAAW,mBAAmB;IAChC,MAAM,EAAE,WAAW,CAAC;IACpB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,YAAY,EAAE,KAAK,CAAC;QAAC,IAAI,EAAE,MAAM,CAAC;QAAC,OAAO,CAAC,EAAE,MAAM,CAAA;KAAC,CAAC,CAAC;IACtD,aAAa,EAAE,SAAS,EAAE,CAAC;IAC3B,UAAU,EAAE,iBAAiB,CAAC;IAC9B,YAAY,EAAE,MAAM,CAAC;CACxB;AAED,wBAAgB,iBAAiB,CAAC,GAAG,EAAE,mBAAmB,GAAG,MAAM,CA0DlE;AAED,MAAM,WAAW,mBAAmB;IAChC,KAAK,EAAE,KAAK,CAAC;QACT,EAAE,EAAE,MAAM,CAAC;QACX,IAAI,EAAE,MAAM,CAAC;QACb,KAAK,CAAC,EAAE,MAAM,CAAC;QACf,WAAW,CAAC,EAAE,MAAM,EAAE,CAAC;QACvB,QAAQ,CAAC,EAAE,MAAM,CAAC;QAClB,UAAU,CAAC,EAAE,MAAM,CAAC;QACpB,QAAQ,CAAC,EAAE,MAAM,CAAC;QAClB,WAAW,CAAC,EAAE,MAAM,EAAE,CAAC;QACvB,YAAY,CAAC,EAAE,MAAM,EAAE,CAAC;KAC3B,CAAC,CAAC;CACN;AAED,wBAAgB,mBAAmB,CAAC,IAAI,EAAE,MAAM,GAAG,mBAAmB,GAAG,IAAI,CAsB5E"}
1
+ {"version":3,"file":"impact.d.ts","sourceRoot":"","sources":["../../src/prompts/impact.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAC,WAAW,EAAC,MAAM,gCAAgC,CAAC;AAChE,OAAO,KAAK,EAAC,SAAS,EAAC,MAAM,4BAA4B,CAAC;AAG1D,OAAO,EAA4B,KAAK,iBAAiB,EAAC,MAAM,6BAA6B,CAAC;AAE9F,MAAM,WAAW,mBAAmB;IAChC,MAAM,EAAE,WAAW,CAAC;IACpB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,YAAY,EAAE,KAAK,CAAC;QAAC,IAAI,EAAE,MAAM,CAAC;QAAC,OAAO,CAAC,EAAE,MAAM,CAAA;KAAC,CAAC,CAAC;IACtD,aAAa,EAAE,SAAS,EAAE,CAAC;IAC3B,UAAU,EAAE,iBAAiB,CAAC;IAC9B,YAAY,EAAE,MAAM,CAAC;IACrB,WAAW,CAAC,EAAE,MAAM,CAAC;CACxB;AAED,wBAAgB,iBAAiB,CAAC,GAAG,EAAE,mBAAmB,GAAG,MAAM,CA0DlE;AAED,MAAM,WAAW,mBAAmB;IAChC,KAAK,EAAE,KAAK,CAAC;QACT,EAAE,EAAE,MAAM,CAAC;QACX,IAAI,EAAE,MAAM,CAAC;QACb,KAAK,CAAC,EAAE,MAAM,CAAC;QACf,WAAW,CAAC,EAAE,MAAM,EAAE,CAAC;QACvB,QAAQ,CAAC,EAAE,MAAM,CAAC;QAClB,UAAU,CAAC,EAAE,MAAM,CAAC;QACpB,QAAQ,CAAC,EAAE,MAAM,CAAC;QAClB,WAAW,CAAC,EAAE,MAAM,EAAE,CAAC;QACvB,YAAY,CAAC,EAAE,MAAM,EAAE,CAAC;KAC3B,CAAC,CAAC;CACN;AAED,wBAAgB,mBAAmB,CAAC,IAAI,EAAE,MAAM,GAAG,mBAAmB,GAAG,IAAI,CAM5E"}