qa360 1.4.5 → 2.0.1

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 (209) hide show
  1. package/README.md +1 -1
  2. package/dist/commands/ai.d.ts +41 -0
  3. package/dist/commands/ai.js +499 -0
  4. package/dist/commands/ask.js +12 -12
  5. package/dist/commands/coverage.d.ts +8 -0
  6. package/dist/commands/coverage.js +252 -0
  7. package/dist/commands/explain.d.ts +27 -0
  8. package/dist/commands/explain.js +630 -0
  9. package/dist/commands/flakiness.d.ts +73 -0
  10. package/dist/commands/flakiness.js +435 -0
  11. package/dist/commands/generate.d.ts +66 -0
  12. package/dist/commands/generate.js +438 -0
  13. package/dist/commands/init.d.ts +56 -9
  14. package/dist/commands/init.js +217 -10
  15. package/dist/commands/monitor.d.ts +27 -0
  16. package/dist/commands/monitor.js +225 -0
  17. package/dist/commands/ollama.d.ts +40 -0
  18. package/dist/commands/ollama.js +301 -0
  19. package/dist/commands/pack.d.ts +37 -9
  20. package/dist/commands/pack.js +240 -141
  21. package/dist/commands/regression.d.ts +8 -0
  22. package/dist/commands/regression.js +340 -0
  23. package/dist/commands/repair.d.ts +26 -0
  24. package/dist/commands/repair.js +307 -0
  25. package/dist/commands/retry.d.ts +43 -0
  26. package/dist/commands/retry.js +275 -0
  27. package/dist/commands/run.d.ts +8 -3
  28. package/dist/commands/run.js +45 -31
  29. package/dist/commands/slo.d.ts +8 -0
  30. package/dist/commands/slo.js +327 -0
  31. package/dist/core/adapters/playwright-native-api.d.ts +183 -0
  32. package/dist/core/adapters/playwright-native-api.js +461 -0
  33. package/dist/core/adapters/playwright-ui.d.ts +7 -0
  34. package/dist/core/adapters/playwright-ui.js +29 -1
  35. package/dist/core/ai/anthropic-provider.d.ts +50 -0
  36. package/dist/core/ai/anthropic-provider.js +211 -0
  37. package/dist/core/ai/deepseek-provider.d.ts +81 -0
  38. package/dist/core/ai/deepseek-provider.js +254 -0
  39. package/dist/core/ai/index.d.ts +60 -0
  40. package/dist/core/ai/index.js +18 -0
  41. package/dist/core/ai/llm-client.d.ts +45 -0
  42. package/dist/core/ai/llm-client.js +7 -0
  43. package/dist/core/ai/mock-provider.d.ts +49 -0
  44. package/dist/core/ai/mock-provider.js +121 -0
  45. package/dist/core/ai/ollama-provider.d.ts +78 -0
  46. package/dist/core/ai/ollama-provider.js +192 -0
  47. package/dist/core/ai/openai-provider.d.ts +48 -0
  48. package/dist/core/ai/openai-provider.js +188 -0
  49. package/dist/core/ai/provider-factory.d.ts +160 -0
  50. package/dist/core/ai/provider-factory.js +269 -0
  51. package/dist/core/auth/api-key-provider.d.ts +16 -0
  52. package/dist/core/auth/api-key-provider.js +63 -0
  53. package/dist/core/auth/aws-iam-provider.d.ts +35 -0
  54. package/dist/core/auth/aws-iam-provider.js +177 -0
  55. package/dist/core/auth/azure-ad-provider.d.ts +15 -0
  56. package/dist/core/auth/azure-ad-provider.js +99 -0
  57. package/dist/core/auth/basic-auth-provider.d.ts +26 -0
  58. package/dist/core/auth/basic-auth-provider.js +111 -0
  59. package/dist/core/auth/gcp-adc-provider.d.ts +27 -0
  60. package/dist/core/auth/gcp-adc-provider.js +126 -0
  61. package/dist/core/auth/index.d.ts +238 -0
  62. package/dist/core/auth/index.js +82 -0
  63. package/dist/core/auth/jwt-provider.d.ts +19 -0
  64. package/dist/core/auth/jwt-provider.js +160 -0
  65. package/dist/core/auth/manager.d.ts +84 -0
  66. package/dist/core/auth/manager.js +230 -0
  67. package/dist/core/auth/oauth2-provider.d.ts +17 -0
  68. package/dist/core/auth/oauth2-provider.js +114 -0
  69. package/dist/core/auth/totp-provider.d.ts +31 -0
  70. package/dist/core/auth/totp-provider.js +134 -0
  71. package/dist/core/auth/ui-login-provider.d.ts +26 -0
  72. package/dist/core/auth/ui-login-provider.js +198 -0
  73. package/dist/core/cache/index.d.ts +7 -0
  74. package/dist/core/cache/index.js +6 -0
  75. package/dist/core/cache/lru-cache.d.ts +203 -0
  76. package/dist/core/cache/lru-cache.js +397 -0
  77. package/dist/core/coverage/analyzer.d.ts +101 -0
  78. package/dist/core/coverage/analyzer.js +415 -0
  79. package/dist/core/coverage/collector.d.ts +74 -0
  80. package/dist/core/coverage/collector.js +459 -0
  81. package/dist/core/coverage/config.d.ts +37 -0
  82. package/dist/core/coverage/config.js +156 -0
  83. package/dist/core/coverage/index.d.ts +11 -0
  84. package/dist/core/coverage/index.js +15 -0
  85. package/dist/core/coverage/types.d.ts +267 -0
  86. package/dist/core/coverage/types.js +6 -0
  87. package/dist/core/coverage/vault.d.ts +95 -0
  88. package/dist/core/coverage/vault.js +405 -0
  89. package/dist/core/dashboard/assets.d.ts +6 -0
  90. package/dist/core/dashboard/assets.js +690 -0
  91. package/dist/core/dashboard/index.d.ts +6 -0
  92. package/dist/core/dashboard/index.js +5 -0
  93. package/dist/core/dashboard/server.d.ts +72 -0
  94. package/dist/core/dashboard/server.js +354 -0
  95. package/dist/core/dashboard/types.d.ts +70 -0
  96. package/dist/core/dashboard/types.js +5 -0
  97. package/dist/core/discoverer/index.d.ts +115 -0
  98. package/dist/core/discoverer/index.js +250 -0
  99. package/dist/core/flakiness/index.d.ts +228 -0
  100. package/dist/core/flakiness/index.js +384 -0
  101. package/dist/core/generation/code-formatter.d.ts +111 -0
  102. package/dist/core/generation/code-formatter.js +307 -0
  103. package/dist/core/generation/code-generator.d.ts +144 -0
  104. package/dist/core/generation/code-generator.js +293 -0
  105. package/dist/core/generation/generator.d.ts +40 -0
  106. package/dist/core/generation/generator.js +76 -0
  107. package/dist/core/generation/index.d.ts +30 -0
  108. package/dist/core/generation/index.js +28 -0
  109. package/dist/core/generation/pack-generator.d.ts +107 -0
  110. package/dist/core/generation/pack-generator.js +416 -0
  111. package/dist/core/generation/prompt-builder.d.ts +132 -0
  112. package/dist/core/generation/prompt-builder.js +672 -0
  113. package/dist/core/generation/source-analyzer.d.ts +213 -0
  114. package/dist/core/generation/source-analyzer.js +657 -0
  115. package/dist/core/generation/test-optimizer.d.ts +117 -0
  116. package/dist/core/generation/test-optimizer.js +328 -0
  117. package/dist/core/generation/types.d.ts +214 -0
  118. package/dist/core/generation/types.js +4 -0
  119. package/dist/core/index.d.ts +23 -1
  120. package/dist/core/index.js +39 -0
  121. package/dist/core/pack/validator.js +31 -1
  122. package/dist/core/pack-v2/index.d.ts +9 -0
  123. package/dist/core/pack-v2/index.js +8 -0
  124. package/dist/core/pack-v2/loader.d.ts +62 -0
  125. package/dist/core/pack-v2/loader.js +231 -0
  126. package/dist/core/pack-v2/migrator.d.ts +56 -0
  127. package/dist/core/pack-v2/migrator.js +455 -0
  128. package/dist/core/pack-v2/validator.d.ts +61 -0
  129. package/dist/core/pack-v2/validator.js +577 -0
  130. package/dist/core/regression/detector.d.ts +107 -0
  131. package/dist/core/regression/detector.js +497 -0
  132. package/dist/core/regression/index.d.ts +9 -0
  133. package/dist/core/regression/index.js +11 -0
  134. package/dist/core/regression/trend-analyzer.d.ts +102 -0
  135. package/dist/core/regression/trend-analyzer.js +345 -0
  136. package/dist/core/regression/types.d.ts +222 -0
  137. package/dist/core/regression/types.js +7 -0
  138. package/dist/core/regression/vault.d.ts +87 -0
  139. package/dist/core/regression/vault.js +289 -0
  140. package/dist/core/repair/engine/fixer.d.ts +24 -0
  141. package/dist/core/repair/engine/fixer.js +226 -0
  142. package/dist/core/repair/engine/suggestion-engine.d.ts +18 -0
  143. package/dist/core/repair/engine/suggestion-engine.js +187 -0
  144. package/dist/core/repair/index.d.ts +10 -0
  145. package/dist/core/repair/index.js +13 -0
  146. package/dist/core/repair/repairer.d.ts +90 -0
  147. package/dist/core/repair/repairer.js +284 -0
  148. package/dist/core/repair/types.d.ts +91 -0
  149. package/dist/core/repair/types.js +6 -0
  150. package/dist/core/repair/utils/error-analyzer.d.ts +28 -0
  151. package/dist/core/repair/utils/error-analyzer.js +264 -0
  152. package/dist/core/retry/flakiness-integration.d.ts +60 -0
  153. package/dist/core/retry/flakiness-integration.js +228 -0
  154. package/dist/core/retry/index.d.ts +14 -0
  155. package/dist/core/retry/index.js +16 -0
  156. package/dist/core/retry/retry-engine.d.ts +80 -0
  157. package/dist/core/retry/retry-engine.js +296 -0
  158. package/dist/core/retry/types.d.ts +178 -0
  159. package/dist/core/retry/types.js +52 -0
  160. package/dist/core/retry/vault.d.ts +77 -0
  161. package/dist/core/retry/vault.js +304 -0
  162. package/dist/core/runner/e2e-helpers.d.ts +102 -0
  163. package/dist/core/runner/e2e-helpers.js +153 -0
  164. package/dist/core/runner/phase3-runner.d.ts +101 -2
  165. package/dist/core/runner/phase3-runner.js +559 -24
  166. package/dist/core/self-healing/assertion-healer.d.ts +97 -0
  167. package/dist/core/self-healing/assertion-healer.js +371 -0
  168. package/dist/core/self-healing/engine.d.ts +122 -0
  169. package/dist/core/self-healing/engine.js +538 -0
  170. package/dist/core/self-healing/index.d.ts +10 -0
  171. package/dist/core/self-healing/index.js +11 -0
  172. package/dist/core/self-healing/selector-healer.d.ts +103 -0
  173. package/dist/core/self-healing/selector-healer.js +372 -0
  174. package/dist/core/self-healing/types.d.ts +152 -0
  175. package/dist/core/self-healing/types.js +6 -0
  176. package/dist/core/slo/config.d.ts +107 -0
  177. package/dist/core/slo/config.js +360 -0
  178. package/dist/core/slo/index.d.ts +11 -0
  179. package/dist/core/slo/index.js +15 -0
  180. package/dist/core/slo/sli-calculator.d.ts +92 -0
  181. package/dist/core/slo/sli-calculator.js +364 -0
  182. package/dist/core/slo/slo-tracker.d.ts +148 -0
  183. package/dist/core/slo/slo-tracker.js +379 -0
  184. package/dist/core/slo/types.d.ts +281 -0
  185. package/dist/core/slo/types.js +7 -0
  186. package/dist/core/slo/vault.d.ts +102 -0
  187. package/dist/core/slo/vault.js +427 -0
  188. package/dist/core/tui/index.d.ts +7 -0
  189. package/dist/core/tui/index.js +6 -0
  190. package/dist/core/tui/monitor.d.ts +92 -0
  191. package/dist/core/tui/monitor.js +271 -0
  192. package/dist/core/tui/renderer.d.ts +33 -0
  193. package/dist/core/tui/renderer.js +218 -0
  194. package/dist/core/tui/types.d.ts +63 -0
  195. package/dist/core/tui/types.js +5 -0
  196. package/dist/core/types/pack-v2.d.ts +425 -0
  197. package/dist/core/types/pack-v2.js +8 -0
  198. package/dist/core/vault/index.d.ts +116 -0
  199. package/dist/core/vault/index.js +400 -5
  200. package/dist/core/watch/index.d.ts +7 -0
  201. package/dist/core/watch/index.js +6 -0
  202. package/dist/core/watch/watch-mode.d.ts +213 -0
  203. package/dist/core/watch/watch-mode.js +389 -0
  204. package/dist/index.js +68 -68
  205. package/dist/utils/config.d.ts +5 -0
  206. package/dist/utils/config.js +136 -0
  207. package/package.json +5 -1
  208. package/dist/core/adapters/playwright-api.d.ts +0 -82
  209. package/dist/core/adapters/playwright-api.js +0 -264
@@ -0,0 +1,672 @@
1
+ /**
2
+ * QA360 Prompt Builder
3
+ *
4
+ * Constructs effective prompts for LLM test generation.
5
+ * Uses system prompts, few-shot examples, and structured templates.
6
+ */
7
+ /**
8
+ * Prompt Builder class
9
+ */
10
+ export class PromptBuilder {
11
+ options;
12
+ defaultOptions = {
13
+ includeComments: true,
14
+ includeDetailedAssertions: true,
15
+ includeRetryLogic: true,
16
+ codeStyle: 'clean',
17
+ examples: [],
18
+ systemPrompt: '',
19
+ };
20
+ constructor(options = {}) {
21
+ this.options = { ...this.defaultOptions, ...options };
22
+ }
23
+ /**
24
+ * Build prompt from generation source
25
+ */
26
+ async buildFromSource(source) {
27
+ switch (source.type) {
28
+ case 'openapi':
29
+ return this.buildFromOpenAPI(source.urlOrPath);
30
+ case 'har':
31
+ return this.buildFromHAR(source.filePath);
32
+ case 'url':
33
+ return this.buildFromUrl(source.url);
34
+ case 'spec':
35
+ return this.buildFromSpec(source.spec);
36
+ case 'description':
37
+ return this.buildFromDescription(source.description);
38
+ case 'code':
39
+ return this.buildFromCode(source.code, source.language);
40
+ default:
41
+ throw new Error(`Unsupported source type: ${source.type}`);
42
+ }
43
+ }
44
+ /**
45
+ * Build prompt from OpenAPI/Swagger spec
46
+ */
47
+ buildFromOpenAPI(specPath) {
48
+ const system = this.options.systemPrompt || this.getDefaultSystemPrompt();
49
+ const user = `You are a test generation expert. Generate comprehensive tests from the following OpenAPI/Swagger specification.
50
+
51
+ **OpenAPI Spec Location**: ${specPath}
52
+
53
+ **Requirements**:
54
+ 1. Generate tests for all major endpoints
55
+ 2. Include happy path tests for each operation
56
+ 3. Include error cases (400, 401, 404, 500)
57
+ 4. Use TypeScript with Playwright
58
+ 5. Include proper assertions for status codes and response schemas
59
+ 6. Add authentication if the spec requires it
60
+
61
+ **Output Format**:
62
+ Return valid TypeScript code that can be used with Playwright testing framework.
63
+ Include clear comments describing each test.
64
+
65
+ ${this.getCodeStyleInstructions()}
66
+
67
+ ${this.getFewShotExample('api')}`;
68
+ return {
69
+ system,
70
+ user,
71
+ estimatedTokens: this.countTokens(system + user),
72
+ };
73
+ }
74
+ /**
75
+ * Build prompt from HAR file
76
+ */
77
+ buildFromHAR(filePath) {
78
+ const system = this.options.systemPrompt || this.getDefaultSystemPrompt();
79
+ const user = `You are a test generation expert. Convert the following HAR (HTTP Archive) file into Playwright API tests.
80
+
81
+ **HAR File**: ${filePath}
82
+
83
+ **Requirements**:
84
+ 1. Create a test for each unique request in the HAR
85
+ 2. Extract request parameters, headers, and body
86
+ 3. Add assertions based on the response status and structure
87
+ 4. Group related tests logically
88
+ 5. Use TypeScript with Playwright
89
+
90
+ **Output Format**:
91
+ Return valid TypeScript code with Playwright API tests.
92
+ Include setup/teardown for any required state.
93
+
94
+ ${this.getCodeStyleInstructions()}
95
+
96
+ ${this.getFewShotExample('api')}`;
97
+ return {
98
+ system,
99
+ user,
100
+ estimatedTokens: this.countTokens(system + user),
101
+ };
102
+ }
103
+ /**
104
+ * Build prompt from URL
105
+ */
106
+ buildFromUrl(url) {
107
+ const system = this.options.systemPrompt || this.getDefaultSystemPrompt();
108
+ const user = `You are a test generation expert. Generate comprehensive API smoke tests for the following URL.
109
+
110
+ **Target URL**: ${url}
111
+
112
+ **Requirements**:
113
+ 1. Analyze the URL structure to infer API endpoints
114
+ 2. Generate tests for common REST patterns (list, get, create, update, delete)
115
+ 3. Include health check if applicable
116
+ 4. Add authentication placeholders
117
+ 5. Use TypeScript with Playwright
118
+ 6. Include proper error handling
119
+
120
+ **Assumptions**:
121
+ - Assume RESTful API conventions
122
+ - Use reasonable defaults for any missing information
123
+ - Add TODO comments where assumptions are made
124
+
125
+ ${this.getCodeStyleInstructions()}
126
+
127
+ ${this.getFewShotExample('api')}`;
128
+ return {
129
+ system,
130
+ user,
131
+ estimatedTokens: this.countTokens(system + user),
132
+ };
133
+ }
134
+ /**
135
+ * Build prompt from test spec
136
+ */
137
+ buildFromSpec(spec) {
138
+ const system = this.options.systemPrompt || this.getDefaultSystemPrompt();
139
+ let specPrompt = '';
140
+ let exampleType = 'api';
141
+ switch (spec.type) {
142
+ case 'api':
143
+ specPrompt = this.buildApiSpecPrompt(spec);
144
+ exampleType = 'api';
145
+ break;
146
+ case 'ui':
147
+ specPrompt = this.buildUiSpecPrompt(spec);
148
+ exampleType = 'ui';
149
+ break;
150
+ case 'perf':
151
+ specPrompt = this.buildPerfSpecPrompt(spec);
152
+ exampleType = 'perf';
153
+ break;
154
+ case 'unit':
155
+ specPrompt = this.buildUnitSpecPrompt(spec);
156
+ exampleType = 'unit';
157
+ break;
158
+ }
159
+ const user = `You are a test generation expert. Generate tests based on the following specification.
160
+
161
+ **Test Specification**:
162
+ ${specPrompt}
163
+
164
+ **Requirements**:
165
+ 1. Follow the specification exactly
166
+ 2. Include all specified endpoints/actions/scenarios
167
+ 3. Add appropriate assertions
168
+ 4. Handle edge cases
169
+ 5. Use clean, maintainable code
170
+
171
+ ${this.getCodeStyleInstructions()}
172
+
173
+ ${this.getFewShotExample(exampleType)}`;
174
+ return {
175
+ system,
176
+ user,
177
+ estimatedTokens: this.countTokens(system + user),
178
+ };
179
+ }
180
+ /**
181
+ * Build prompt from description
182
+ */
183
+ buildFromDescription(description) {
184
+ const system = this.options.systemPrompt || this.getDefaultSystemPrompt();
185
+ const user = `You are a test generation expert. Generate tests based on the following description.
186
+
187
+ **Description**:
188
+ ${description}
189
+
190
+ **Requirements**:
191
+ 1. Infer the test requirements from the description
192
+ 2. Generate comprehensive, production-ready tests
193
+ 3. Make reasonable assumptions and document them
194
+ 4. Include proper assertions and error handling
195
+
196
+ ${this.getCodeStyleInstructions()}
197
+
198
+ ${this.getFewShotExample('api')}`;
199
+ return {
200
+ system,
201
+ user,
202
+ estimatedTokens: this.countTokens(system + user),
203
+ };
204
+ }
205
+ /**
206
+ * Build prompt from existing code
207
+ */
208
+ buildFromCode(code, language) {
209
+ const system = this.options.systemPrompt || this.getDefaultSystemPrompt();
210
+ const user = `You are a test generation expert. Generate comprehensive tests for the following code.
211
+
212
+ **Code Language**: ${language}
213
+ **Code to Test**:
214
+ \`\`\`
215
+ ${code}
216
+ \`\`\`
217
+
218
+ **Requirements**:
219
+ 1. Test all exported functions/classes
220
+ 2. Include edge cases and error conditions
221
+ 3. Test valid and invalid inputs
222
+ 4. Mock external dependencies
223
+ 5. Aim for high coverage
224
+ 6. Use ${language === 'typescript' ? 'Vitest' : language === 'python' ? 'pytest' : 'appropriate testing framework'}
225
+
226
+ ${this.getCodeStyleInstructions()}
227
+
228
+ ${this.getFewShotExample('unit')}`;
229
+ return {
230
+ system,
231
+ user,
232
+ estimatedTokens: this.countTokens(system + user),
233
+ };
234
+ }
235
+ /**
236
+ * Build API spec prompt
237
+ */
238
+ buildApiSpecPrompt(spec) {
239
+ const parts = [
240
+ `**Type**: API Tests`,
241
+ `**Base URL**: ${spec.baseUrl}`,
242
+ `**Test Name**: ${spec.name}`,
243
+ ];
244
+ if (spec.description) {
245
+ parts.push(`**Description**: ${spec.description}`);
246
+ }
247
+ parts.push('\n**Endpoints**:');
248
+ for (const endpoint of spec.endpoints) {
249
+ parts.push(`- ${endpoint.method} ${endpoint.path}`);
250
+ if (endpoint.description) {
251
+ parts.push(` Description: ${endpoint.description}`);
252
+ }
253
+ if (endpoint.expectations && endpoint.expectations.length > 0) {
254
+ parts.push(` Expectations:`);
255
+ for (const exp of endpoint.expectations) {
256
+ parts.push(` - Status: ${exp.status}, Response Time: ${exp.responseTime || 'N/A'}`);
257
+ }
258
+ }
259
+ }
260
+ if (spec.auth) {
261
+ parts.push(`\n**Authentication**: ${spec.auth.type}`);
262
+ }
263
+ return parts.join('\n');
264
+ }
265
+ /**
266
+ * Build UI spec prompt
267
+ */
268
+ buildUiSpecPrompt(spec) {
269
+ const parts = [
270
+ `**Type**: UI/E2E Tests`,
271
+ `**Base URL**: ${spec.baseUrl}`,
272
+ `**Test Name**: ${spec.name}`,
273
+ ];
274
+ if (spec.description) {
275
+ parts.push(`**Description**: ${spec.description}`);
276
+ }
277
+ if (spec.viewport) {
278
+ parts.push(`**Viewport**: ${spec.viewport.width}x${spec.viewport.height}`);
279
+ }
280
+ parts.push('\n**Pages**:');
281
+ for (const page of spec.pages) {
282
+ parts.push(`- ${page.name}: ${page.url}`);
283
+ parts.push(' Actions:');
284
+ for (const action of page.actions) {
285
+ parts.push(` - ${action.type} ${action.selector || ''} ${action.value || ''}`);
286
+ }
287
+ if (page.assertions && page.assertions.length > 0) {
288
+ parts.push(' Assertions:');
289
+ for (const assertion of page.assertions) {
290
+ parts.push(` - ${assertion.type} ${assertion.expected}`);
291
+ }
292
+ }
293
+ }
294
+ return parts.join('\n');
295
+ }
296
+ /**
297
+ * Build performance spec prompt
298
+ */
299
+ buildPerfSpecPrompt(spec) {
300
+ const parts = [
301
+ `**Type**: Performance Tests`,
302
+ `**Base URL**: ${spec.baseUrl}`,
303
+ `**Test Name**: ${spec.name}`,
304
+ ];
305
+ if (spec.options) {
306
+ parts.push(`**Duration**: ${spec.options.duration || '30s'}`);
307
+ parts.push(`**Rate**: ${spec.options.rate || 'auto'} req/s`);
308
+ parts.push(`**VUs**: ${spec.options.vus || 10}`);
309
+ }
310
+ parts.push('\n**Scenarios**:');
311
+ for (const scenario of spec.scenarios) {
312
+ parts.push(`- ${scenario.name} (weight: ${scenario.weight || 1})`);
313
+ for (const req of scenario.requests) {
314
+ parts.push(` - ${req.method} ${req.path}`);
315
+ }
316
+ }
317
+ if (spec.options?.thresholds) {
318
+ parts.push('\n**Thresholds**:');
319
+ for (const threshold of spec.options.thresholds) {
320
+ parts.push(` - ${threshold.metric}: ${threshold.condition}`);
321
+ }
322
+ }
323
+ return parts.join('\n');
324
+ }
325
+ /**
326
+ * Build unit test spec prompt
327
+ */
328
+ buildUnitSpecPrompt(spec) {
329
+ const parts = [
330
+ `**Type**: Unit Tests`,
331
+ `**Test Name**: ${spec.name}`,
332
+ `**Language**: ${spec.source.language}`,
333
+ ];
334
+ if (spec.source.exports) {
335
+ parts.push(`**Functions/Classes to Test**: ${spec.source.exports.join(', ')}`);
336
+ }
337
+ parts.push(`**Framework**: ${spec.framework || 'vitest'}`);
338
+ return parts.join('\n');
339
+ }
340
+ /**
341
+ * Get default system prompt
342
+ */
343
+ getDefaultSystemPrompt() {
344
+ return `You are an expert test generation engineer. Your role is to create high-quality, maintainable test code.
345
+
346
+ **Principles**:
347
+ 1. **Clarity**: Tests should be self-documenting with clear names and structure
348
+ 2. **Reliability**: Use proper waits, assertions, and error handling
349
+ 3. **Maintainability**: Follow DRY principles, use helpers when appropriate
350
+ 4. **Coverage**: Test happy paths AND edge cases
351
+ 5. **Best Practices**: Follow testing framework conventions and idioms
352
+
353
+ **Output Requirements**:
354
+ - Return ONLY the test code, no explanations outside the code
355
+ - Use descriptive test names that explain what is being tested
356
+ - Include assertions for all critical conditions
357
+ - Handle errors appropriately
358
+ - Add comments for complex logic`;
359
+ }
360
+ /**
361
+ * Get code style instructions
362
+ */
363
+ getCodeStyleInstructions() {
364
+ const style = this.options.codeStyle;
365
+ const instructions = [];
366
+ instructions.push('**Code Style**:\n');
367
+ switch (style) {
368
+ case 'clean':
369
+ instructions.push('- Use clear, descriptive names');
370
+ instructions.push('- Group related tests with describe blocks');
371
+ instructions.push('- Extract common setup into beforeEach/beforeAll');
372
+ break;
373
+ case 'verbose':
374
+ instructions.push('- Include detailed comments for each step');
375
+ instructions.push('- Add explanatory comments for assertions');
376
+ instructions.push('- Document setup and teardown');
377
+ break;
378
+ case 'concise':
379
+ instructions.push('- Keep tests brief and to the point');
380
+ instructions.push('- Use minimal comments (only for complex logic)');
381
+ instructions.push('- Leverage framework features for conciseness');
382
+ break;
383
+ }
384
+ if (this.options.includeComments) {
385
+ instructions.push('- Include helpful comments explaining test intent');
386
+ }
387
+ else {
388
+ instructions.push('- Minimize comments (let code be self-documenting)');
389
+ }
390
+ if (this.options.includeDetailedAssertions) {
391
+ instructions.push('- Include detailed assertions for all response properties');
392
+ }
393
+ if (this.options.includeRetryLogic) {
394
+ instructions.push('- Add retry logic for potentially flaky network operations');
395
+ }
396
+ return instructions.join('\n');
397
+ }
398
+ /**
399
+ * Get few-shot example
400
+ */
401
+ getFewShotExample(type) {
402
+ if (this.options.examples.length > 0) {
403
+ return '**Example**:\n' + this.options.examples.join('\n\n');
404
+ }
405
+ // Built-in examples
406
+ const examples = {
407
+ api: this.getApiExample(),
408
+ ui: this.getUiExample(),
409
+ perf: this.getPerfExample(),
410
+ unit: this.getUnitExample(),
411
+ };
412
+ return examples[type] || '';
413
+ }
414
+ /**
415
+ * Get API test example
416
+ */
417
+ getApiExample() {
418
+ return `**Example: API Test with Playwright**
419
+
420
+ \`\`\`typescript
421
+ import { test, expect } from '@playwright/test';
422
+
423
+ test.describe('Users API', () => {
424
+ const baseUrl = 'https://api.example.com';
425
+ let authToken: string;
426
+
427
+ test.beforeAll(async () => {
428
+ // Setup: Get auth token
429
+ const response = await fetch(\`\${baseUrl}/auth/login\`, {
430
+ method: 'POST',
431
+ headers: { 'Content-Type': 'application/json' },
432
+ body: JSON.stringify({ username: 'test', password: 'test' }),
433
+ });
434
+ const data = await response.json();
435
+ authToken = data.token;
436
+ });
437
+
438
+ test('GET /users - should return list of users', async () => {
439
+ const response = await fetch(\`\${baseUrl}/users\`, {
440
+ headers: { 'Authorization': \`Bearer \${authToken}\` },
441
+ });
442
+
443
+ expect(response.status).toBe(200);
444
+ const data = await response.json();
445
+ expect(Array.isArray(data.users)).toBe(true);
446
+ expect(data.users.length).toBeGreaterThan(0);
447
+ });
448
+
449
+ test('GET /users/:id - should return specific user', async () => {
450
+ const response = await fetch(\`\${baseUrl}/users/1\`, {
451
+ headers: { 'Authorization': \`Bearer \${authToken}\` },
452
+ });
453
+
454
+ expect(response.status).toBe(200);
455
+ const user = await response.json();
456
+ expect(user.id).toBe(1);
457
+ expect(user).toHaveProperty('name');
458
+ expect(user).toHaveProperty('email');
459
+ });
460
+
461
+ test('POST /users - should create new user', async () => {
462
+ const newUser = {
463
+ name: 'Test User',
464
+ email: 'test@example.com',
465
+ };
466
+
467
+ const response = await fetch(\`\${baseUrl}/users\`, {
468
+ method: 'POST',
469
+ headers: {
470
+ 'Authorization': \`Bearer \${authToken}\`,
471
+ 'Content-Type': 'application/json',
472
+ },
473
+ body: JSON.stringify(newUser),
474
+ });
475
+
476
+ expect(response.status).toBe(201);
477
+ const created = await response.json();
478
+ expect(created.id).toBeDefined();
479
+ expect(created.name).toBe(newUser.name);
480
+ });
481
+
482
+ test('GET /users/:id - should return 404 for non-existent user', async () => {
483
+ const response = await fetch(\`\${baseUrl}/users/999999\`, {
484
+ headers: { 'Authorization': \`Bearer \${authToken}\` },
485
+ });
486
+
487
+ expect(response.status).toBe(404);
488
+ });
489
+ });
490
+ \`\`\``;
491
+ }
492
+ /**
493
+ * Get UI test example
494
+ */
495
+ getUiExample() {
496
+ return `**Example: UI Test with Playwright**
497
+
498
+ \`\`\`typescript
499
+ import { test, expect } from '@playwright/test';
500
+
501
+ test.describe('Login Page', () => {
502
+ test.beforeEach(async ({ page }) => {
503
+ await page.goto('https://example.com/login');
504
+ });
505
+
506
+ test('should show login form', async ({ page }) => {
507
+ await expect(page.locator('input[name="email"]')).toBeVisible();
508
+ await expect(page.locator('input[name="password"]')).toBeVisible();
509
+ await expect(page.locator('button[type="submit"]')).toBeVisible();
510
+ });
511
+
512
+ test('should login with valid credentials', async ({ page }) => {
513
+ await page.fill('input[name="email"]', 'test@example.com');
514
+ await page.fill('input[name="password"]', 'password123');
515
+ await page.click('button[type="submit"]');
516
+
517
+ // Wait for navigation
518
+ await page.waitForURL('**/dashboard');
519
+ await expect(page).toHaveURL(/.*dashboard/);
520
+ });
521
+
522
+ test('should show error with invalid credentials', async ({ page }) => {
523
+ await page.fill('input[name="email"]', 'invalid@example.com');
524
+ await page.fill('input[name="password"]', 'wrongpassword');
525
+ await page.click('button[type="submit"]');
526
+
527
+ await expect(page.locator('.error-message')).toBeVisible();
528
+ await expect(page.locator('.error-message')).toContainText('Invalid credentials');
529
+ });
530
+
531
+ test('should require email field', async ({ page }) => {
532
+ await page.fill('input[name="password"]', 'password123');
533
+ await page.click('button[type="submit"]');
534
+
535
+ await expect(page.locator('input[name="email"]')).toHaveAttribute('required', '');
536
+ });
537
+ });
538
+ \`\`\``;
539
+ }
540
+ /**
541
+ * Get performance test example
542
+ */
543
+ getPerfExample() {
544
+ return `**Example: Performance Test with K6**
545
+
546
+ \`\`\`javascript
547
+ import http from 'k6/http';
548
+ import { check, sleep } from 'k6';
549
+ import { Rate } from 'k6/metrics';
550
+
551
+ const errorRate = new Rate('errors');
552
+
553
+ export const options = {
554
+ stages: [
555
+ { duration: '30s', target: 20 }, // Ramp up
556
+ { duration: '1m', target: 20 }, // Stay at 20
557
+ { duration: '20s', target: 0 }, // Ramp down
558
+ ],
559
+ thresholds: {
560
+ http_req_duration: ['p(95)<500', 'p(99)<1000'],
561
+ http_req_failed: ['rate<0.01'],
562
+ errors: ['rate<0.05'],
563
+ },
564
+ };
565
+
566
+ const BASE_URL = 'https://api.example.com';
567
+
568
+ export default function () {
569
+ // List users
570
+ const listRes = http.get(\`\${BASE_URL}/users\`);
571
+ check(listRes, {
572
+ 'list status is 200': (r) => r.status === 200,
573
+ 'list has users': (r) => JSON.parse(r.body).users.length > 0,
574
+ }) || errorRate.add(1);
575
+
576
+ sleep(1);
577
+
578
+ // Get specific user
579
+ const getRes = http.get(\`\${BASE_URL}/users/1\`);
580
+ check(getRes, {
581
+ 'get status is 200': (r) => r.status === 200,
582
+ 'user has name': (r) => JSON.parse(r.body).name !== undefined,
583
+ }) || errorRate.add(1);
584
+
585
+ sleep(Math.random() * 2 + 1); // Random think time 1-3s
586
+ }
587
+ \`\`\``;
588
+ }
589
+ /**
590
+ * Get unit test example
591
+ */
592
+ getUnitExample() {
593
+ return `**Example: Unit Test with Vitest**
594
+
595
+ \`\`\`typescript
596
+ import { describe, it, expect, vi, beforeEach } from 'vitest';
597
+
598
+ describe('UserService', () => {
599
+ let userService: UserService;
600
+ let mockRepository: any;
601
+
602
+ beforeEach(() => {
603
+ mockRepository = {
604
+ findById: vi.fn(),
605
+ create: vi.fn(),
606
+ update: vi.fn(),
607
+ delete: vi.fn(),
608
+ };
609
+ userService = new UserService(mockRepository);
610
+ });
611
+
612
+ describe('findById', () => {
613
+ it('should return user when found', async () => {
614
+ const expectedUser = { id: 1, name: 'Test User' };
615
+ mockRepository.findById.mockResolvedValue(expectedUser);
616
+
617
+ const result = await userService.findById(1);
618
+
619
+ expect(result).toEqual(expectedUser);
620
+ expect(mockRepository.findById).toHaveBeenCalledWith(1);
621
+ });
622
+
623
+ it('should return null when not found', async () => {
624
+ mockRepository.findById.mockResolvedValue(null);
625
+
626
+ const result = await userService.findById(999);
627
+
628
+ expect(result).toBeNull();
629
+ });
630
+
631
+ it('should throw on repository error', async () => {
632
+ mockRepository.findById.mockRejectedValue(new Error('Database error'));
633
+
634
+ await expect(userService.findById(1)).rejects.toThrow('Database error');
635
+ });
636
+ });
637
+
638
+ describe('create', () => {
639
+ it('should create user with valid data', async () => {
640
+ const userData = { name: 'New User', email: 'new@example.com' };
641
+ const createdUser = { id: 1, ...userData };
642
+ mockRepository.create.mockResolvedValue(createdUser);
643
+
644
+ const result = await userService.create(userData);
645
+
646
+ expect(result).toEqual(createdUser);
647
+ expect(mockRepository.create).toHaveBeenCalledWith(userData);
648
+ });
649
+
650
+ it('should throw validation error for invalid email', async () => {
651
+ const userData = { name: 'User', email: 'invalid-email' };
652
+
653
+ await expect(userService.create(userData)).rejects.toThrow('Invalid email');
654
+ });
655
+ });
656
+ });
657
+ \`\`\``;
658
+ }
659
+ /**
660
+ * Count approximate tokens
661
+ */
662
+ countTokens(text) {
663
+ // Rough approximation: ~4 chars per token
664
+ return Math.ceil(text.length / 4);
665
+ }
666
+ }
667
+ /**
668
+ * Create a prompt builder with default options
669
+ */
670
+ export function createPromptBuilder(options) {
671
+ return new PromptBuilder(options);
672
+ }