@skyramp/mcp 0.1.0-rc.5 → 0.1.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.
- package/build/index.js +4 -1
- package/build/playwright/registerPlaywrightTools.js +2 -0
- package/build/playwright/traceRecordingPrompt.js +0 -5
- package/build/prompts/code-reuse.js +7 -1
- package/build/prompts/enhance-assertions/contractProviderAssertionsPrompt.js +110 -0
- package/build/prompts/enhance-assertions/integrationAssertionsPrompt.js +128 -0
- package/build/prompts/enhance-assertions/uiAssertionsPrompt.js +90 -0
- package/build/prompts/initialize-workspace/initializeWorkspacePrompt.js +53 -14
- package/build/prompts/personas.js +9 -5
- package/build/prompts/pom-aware-code-reuse.js +440 -0
- package/build/prompts/test-maintenance/driftAnalysisSections.js +5 -3
- package/build/prompts/test-recommendation/analysisOutputPrompt.js +12 -2
- package/build/prompts/test-recommendation/recommendationSections.js +41 -20
- package/build/prompts/test-recommendation/registerRecommendTestsPrompt.js +2 -2
- package/build/prompts/test-recommendation/scopeAssessment.js +21 -8
- package/build/prompts/test-recommendation/scopeAssessment.test.js +28 -0
- package/build/prompts/test-recommendation/test-recommendation-prompt.js +65 -28
- package/build/prompts/test-recommendation/test-recommendation-prompt.test.js +71 -12
- package/build/prompts/testbot/testbot-prompts.js +91 -73
- package/build/prompts/testbot/testbot-prompts.test.js +60 -0
- package/build/resources/testbotResource.js +5 -1
- package/build/services/ScenarioGenerationService.js +3 -2
- package/build/services/TestDiscoveryService.js +5 -5
- package/build/services/TestExecutionService.js +23 -0
- package/build/services/TestExecutionService.test.js +47 -6
- package/build/services/TestGenerationService.js +39 -28
- package/build/services/containerEnv.js +18 -2
- package/build/tool-phases.js +1 -0
- package/build/tools/code-refactor/codeReuseTool.js +11 -8
- package/build/tools/code-refactor/enhanceAssertionsTool.js +67 -0
- package/build/tools/code-refactor/modularizationTool.js +2 -0
- package/build/tools/executeSkyrampTestTool.js +11 -4
- package/build/tools/fixErrorTool.js +2 -0
- package/build/tools/generate-tests/generateBatchScenarioRestTool.js +17 -6
- package/build/tools/generate-tests/generateContractRestTool.js +127 -54
- package/build/tools/generate-tests/generateE2ERestTool.js +1 -1
- package/build/tools/generate-tests/generateIntegrationRestTool.js +2 -11
- package/build/tools/generate-tests/generateLoadRestTool.js +1 -1
- package/build/tools/generate-tests/generateUIRestTool.js +3 -36
- package/build/tools/test-management/analyzeChangesTool.js +37 -26
- package/build/tools/workspace/initializeWorkspaceTool.js +55 -3
- package/build/utils/docker.test.js +1 -1
- package/build/utils/featureFlags.js +34 -0
- package/build/utils/normalizeSkyrampImports.js +75 -0
- package/build/utils/routeParsers.js +7 -2
- package/build/utils/routeParsers.test.js +8 -0
- package/build/utils/trace-parser.js +11 -2
- package/build/utils/versions.js +1 -1
- package/build/utils/workspaceAuth.js +212 -28
- package/node_modules/playwright/lib/common/expectBundleImpl.js +221 -221
- package/node_modules/playwright/lib/mcp/browser/tools/extensionFrames.js +180 -0
- package/node_modules/playwright/lib/mcp/browser/tools/keyboard.js +2 -2
- package/node_modules/playwright/lib/mcp/skyramp/exportTool.js +11 -0
- package/node_modules/playwright/lib/mcp/skyramp/traceRecordingBackend.js +20 -2
- package/node_modules/playwright/lib/mcp/terminal/help.json +32 -0
- package/node_modules/playwright/lib/mcp/test/skyRampExport.js +63 -3
- package/node_modules/playwright/lib/utilsBundleImpl.js +49 -49
- package/package.json +6 -3
- package/build/prompts/test-maintenance/enhanceAssertionSection.js +0 -99
|
@@ -0,0 +1,440 @@
|
|
|
1
|
+
import { generateSkyrampHeader, SKYRAMP_UTILS_HEADER } from "../utils/utils.js";
|
|
2
|
+
const TS_UTILS_FILE = "skyrampUtils.ts";
|
|
3
|
+
export function getPomAwareCodeReusePrompt(testFile) {
|
|
4
|
+
return `# POM-AWARE CODE REUSE — TYPESCRIPT/PLAYWRIGHT
|
|
5
|
+
|
|
6
|
+
## COMPLIANCE RULES — READ THIS FIRST, APPLY TO EVERY STEP
|
|
7
|
+
|
|
8
|
+
You are about to execute a multi-step process. These rules govern your behavior for the entire session:
|
|
9
|
+
|
|
10
|
+
1. **Read the ENTIRE guidance before taking any action**
|
|
11
|
+
2. **Execute steps in order** — never skip, combine, or reorder steps
|
|
12
|
+
3. **Output all intermediate artifacts** (POM catalog, mapping table) to chat before proceeding to the next step
|
|
13
|
+
4. **BLOCKING and MANDATORY mean hard stops** — do not proceed until that step is complete; confirm completion in a single output line then continue automatically — do NOT ask the user for confirmation
|
|
14
|
+
5. **Never shortcut** — even if the pattern seems obvious from existing tests, follow every step. "Being efficient" by skipping steps is non-compliance, not optimization.
|
|
15
|
+
6. **Never pause for user input** between steps — execute the full sequence autonomously from start to finish.
|
|
16
|
+
|
|
17
|
+
If you catch yourself about to skip a step, stop and execute it.
|
|
18
|
+
|
|
19
|
+
---
|
|
20
|
+
|
|
21
|
+
## DEFINITIONS
|
|
22
|
+
|
|
23
|
+
**POM-Aware Code Reuse** means refactoring a generated test to use the project's existing Page Object Model (POM) classes and methods, replacing raw inline locators and action sequences with the proper abstractions the customer already has.
|
|
24
|
+
|
|
25
|
+
This is NOT the same as SkyrampUtils consolidation. Do NOT create a SkyrampUtils file on the POM path.
|
|
26
|
+
|
|
27
|
+
---
|
|
28
|
+
|
|
29
|
+
## STEP 1: DETECT POM CLASSES IN THE PROJECT
|
|
30
|
+
|
|
31
|
+
Use the Glob tool to search for POM files starting from the directory containing \`${testFile}\`:
|
|
32
|
+
- Pattern 1: \`**/pageobjects/**/*.ts\`
|
|
33
|
+
- Pattern 2: \`**/page-objects/**/*.ts\`
|
|
34
|
+
- Pattern 3: \`**/pages/**/*.ts\`
|
|
35
|
+
- Pattern 4: \`**/*.page.ts\`
|
|
36
|
+
- Pattern 5: \`**/*Page.ts\`
|
|
37
|
+
- Pattern 6: \`**/pom/**/*.ts\`
|
|
38
|
+
|
|
39
|
+
Exclude any file with \`test\`, \`spec\`, \`.test.\`, or \`.spec.\` in its name.
|
|
40
|
+
Also exclude \`node_modules\`.
|
|
41
|
+
|
|
42
|
+
**Decision:**
|
|
43
|
+
- Zero matches found → **SKIP TO STEP 6 (FALLBACK)**
|
|
44
|
+
- One or more matches found → continue to STEP 2
|
|
45
|
+
|
|
46
|
+
---
|
|
47
|
+
|
|
48
|
+
## STEP 2: READ AND CATALOG ALL REUSABLE MODULES
|
|
49
|
+
|
|
50
|
+
**Step 2a: Check for existing catalog (MANDATORY FIRST STEP)**
|
|
51
|
+
|
|
52
|
+
Check if \`skyramp-pom-catalog.md\` exists in the same directory as \`tsconfig.json\`:
|
|
53
|
+
- **If it exists:** Read it and verify it starts with \`<!-- skyramp-pom-catalog: auto-generated -->\`
|
|
54
|
+
- Valid → Output: *"Using existing POM catalog from /path/to/skyramp-pom-catalog.md"* and **skip to STEP 2b**
|
|
55
|
+
- Invalid → Continue to full catalog generation below
|
|
56
|
+
- **If it doesn't exist:** Continue to full catalog generation below
|
|
57
|
+
|
|
58
|
+
---
|
|
59
|
+
|
|
60
|
+
**Step 2 (full catalog generation — only if catalog doesn't exist or is invalid):**
|
|
61
|
+
|
|
62
|
+
Before writing a single line of refactored code, do the following:
|
|
63
|
+
|
|
64
|
+
can 1. **Read existing refactored tests** — Find any already-refactored test files in the repository and note their established conventions: how imports are grouped, what comment style sections use, and how \`waitForTimeout\`, \`waitForResponse\`, and dynamic locators are handled. Where existing tests provide a pattern, follow it exactly.
|
|
65
|
+
|
|
66
|
+
2. **Read tsconfig.json / jsconfig.json** — Capture path alias conventions (e.g., \`@pageobjects/*\`, \`@common-function/*\`). All imports in the refactored test must use these aliases exactly.
|
|
67
|
+
|
|
68
|
+
3. **Read every POM file found in STEP 1** — For each file, document:
|
|
69
|
+
- Class name, import path (using tsconfig aliases), and what page/component it represents
|
|
70
|
+
- Iframe selector used in the constructor (e.g. \`page.frameLocator('iframe#asset-list')\`), or "top-level page" if no iframe
|
|
71
|
+
- Every property (locator): name and its exact selector string from the constructor assignment
|
|
72
|
+
- Every method: name, parameters, what action it performs
|
|
73
|
+
- **For each method, explicitly check and record:**
|
|
74
|
+
- Any \`waitForTimeout\` or \`sleep\` calls inside the method body and their durations
|
|
75
|
+
- Any \`waitForResponse\` calls inside the method body and their URL patterns
|
|
76
|
+
- Whether the method wraps a multi-step sequence using sub-properties or components (e.g. \`searchAssetsByCondition\` internally calls \`searchConditionArea.expandWhenClosed()\`, \`clearBtn.click()\`, \`selectItemName()\`, \`valueKeyword.fill()\`, \`searchBtn.click()\`) — flag these as **wrapping methods**
|
|
77
|
+
- Flag methods as **timing-aware**, **network-aware**, or **wrapping** as appropriate — all three are high-value substitution targets
|
|
78
|
+
|
|
79
|
+
4. **Write the POM catalog — MANDATORY** — Write the complete catalog to \`skyramp-pom-catalog.md\` in the same directory as \`tsconfig.json\`. This gives you a clean structured reference for STEP 2b and leaves a record for debugging.
|
|
80
|
+
|
|
81
|
+
**BLOCKING: Do not proceed to STEP 2b until you have written this file and confirmed the path in a single output line:**
|
|
82
|
+
*"POM catalog written to /path/to/skyramp-pom-catalog.md"*
|
|
83
|
+
|
|
84
|
+
**Verification gate:** Before continuing to STEP 2b, confirm you either (a) found and used an existing catalog in Step 2a, OR (b) just wrote a new catalog in step 4 above. If neither is true, STOP and complete step 4 now.
|
|
85
|
+
|
|
86
|
+
Use this exact format for the file:
|
|
87
|
+
|
|
88
|
+
\`\`\`markdown
|
|
89
|
+
<!-- skyramp-pom-catalog: auto-generated by skyramp_reuse_code -->
|
|
90
|
+
|
|
91
|
+
## ClassName
|
|
92
|
+
- **Import:** \`@pageobjects/path/to/file\`
|
|
93
|
+
- **Iframe:** \`iframe#selector\` | top-level page
|
|
94
|
+
- **Properties:**
|
|
95
|
+
- \`propertyName\` — selector: \`exact-selector-string\`
|
|
96
|
+
- **Methods:**
|
|
97
|
+
- \`methodName(param1, param2)\` — description; ⏱ timing-aware: waitForTimeout(Xms) + sleep(Ys) | 🌐 network-aware: waitForResponse('**/pattern**') | (omit tags if neither)
|
|
98
|
+
\`\`\`
|
|
99
|
+
|
|
100
|
+
---
|
|
101
|
+
|
|
102
|
+
## STEP 2b: BUILD AND OUTPUT A MAPPING TABLE
|
|
103
|
+
|
|
104
|
+
**This step is MANDATORY. Do not write any code until it is complete.**
|
|
105
|
+
|
|
106
|
+
For every raw locator and action sequence in \`${testFile}\`, find the best POM match and record it in a table. Output the complete table to chat before proceeding to STEP 3.
|
|
107
|
+
|
|
108
|
+
### Matching criteria
|
|
109
|
+
|
|
110
|
+
Use **selector-string evidence** as the primary signal — not naming similarity or context guessing:
|
|
111
|
+
|
|
112
|
+
**PREFERRED** (highest priority — always substitute, even if raw test has explicit waits or correlations for the same sequence):
|
|
113
|
+
- The POM method is **timing-aware** (has internal \`waitForTimeout\`/\`sleep\`) AND its action sequence covers the raw test's steps — the method's internal timing replaces the raw test's explicit waits for that sequence
|
|
114
|
+
- The POM method is **network-aware** (has internal \`waitForResponse\`) AND its URL pattern matches or is equivalent to the raw test's correlation — the method's internal correlation replaces the raw test's explicit \`responsePromise\` block
|
|
115
|
+
- When a PREFERRED method is used: drop the raw test's \`waitForTimeout\` and/or \`responsePromise\` block that fall *inside* the method's covered sequence; add a comment noting the method handles it internally
|
|
116
|
+
|
|
117
|
+
**HIGH confidence** (substitute in refactored test):
|
|
118
|
+
- The selector string in the raw locator appears verbatim or near-verbatim in the POM property assignment in the constructor. Example: raw test has \`locator('button:has-text("資産登録")')\` and the POM constructor has \`this.iframe.locator('button:has-text("資産登録")')\`
|
|
119
|
+
- A POM method's internal steps clearly and completely cover the entire raw action sequence with no gaps
|
|
120
|
+
- **HIGH via convention:** The existing refactored tests in this repository already use this POM method for the same action pattern — even if the selector strings differ between the raw test and the POM. Convention evidence from the repo's own tests overrides selector mismatch. Example: every refactored test in the repo calls \`mainMenu.assetInformation.select()\` to navigate to the asset list, so a raw test doing the same navigation with different locators should also use it.
|
|
121
|
+
|
|
122
|
+
**LOW / UNCERTAIN confidence** (leave inline):
|
|
123
|
+
- You can only infer the match from naming or context alone — no selector string overlap AND no convention evidence from existing refactored tests
|
|
124
|
+
- The POM property's selector is different, no existing test uses this method for this action, and you are not sure the elements match
|
|
125
|
+
|
|
126
|
+
**UNMAPPED** (leave inline):
|
|
127
|
+
- No POM property or method found that covers this locator or action
|
|
128
|
+
|
|
129
|
+
### Iframe context gate — apply BEFORE assigning any confidence
|
|
130
|
+
|
|
131
|
+
Before considering any POM match, verify the iframe context matches:
|
|
132
|
+
- Identify the iframe the raw locator uses (e.g., \`frameLocator('iframe#asset-list')\`)
|
|
133
|
+
- Read the POM class constructor to find which iframe it scopes to
|
|
134
|
+
- If the iframe IDs differ → the match is UNMAPPED regardless of selector similarity
|
|
135
|
+
- **Never match a method from a different-iframe class just because it has a similar name.** Example: \`AssetListPage.searchAssetsByCondition()\` uses \`iframe#asset-list\` — it CANNOT be used for actions in \`#application-new-applied\`. For asset search inside a workflow, use \`ApplicationNewAppliedAssetSelectPage.searchAssetsSection.searchByAssetName()\` which uses \`iframe#application-new-applied\`.
|
|
136
|
+
- When filtering POM candidates for a raw action, first narrow to classes whose iframe matches, then look for methods within those classes only.
|
|
137
|
+
|
|
138
|
+
### Mapping table format
|
|
139
|
+
|
|
140
|
+
Output the table in this format:
|
|
141
|
+
|
|
142
|
+
\`\`\`
|
|
143
|
+
| Raw locator / action | POM class | Property / Method | Selector evidence | Confidence |
|
|
144
|
+
|---|---|---|---|---|
|
|
145
|
+
| goto + fill email/password + click login | LoginPage | login(email, password) | method is timing-aware (waitForTimeout+sleep) covering login sequence | PREFERRED |
|
|
146
|
+
| frameLocator('iframe#asset-list').locator('button:has-text("資産登録")') | AssetListPage | assetEntryBtn | iframe.locator('button:has-text("資産登録")') | HIGH |
|
|
147
|
+
| page.locator('input[name="email"]').fill(...) | LoginPage | emailInput | getByPlaceholder('メールアドレス(半角)') | LOW |
|
|
148
|
+
| frameLocator('iframe#wf-input').locator('input[name="changeQty"]') | — | — | No match | UNMAPPED |
|
|
149
|
+
\`\`\`
|
|
150
|
+
|
|
151
|
+
### Rules after completing the table
|
|
152
|
+
|
|
153
|
+
- **PREFERRED entries MUST be substituted** — use the POM method and drop the raw test's explicit waits/correlations that fall inside the method's scope
|
|
154
|
+
- **HIGH entries MUST be substituted** — use the POM method or property; do NOT write an inline locator
|
|
155
|
+
- **Always prefer the highest level of abstraction available** — if a wrapping method exists that covers a multi-step sequence, use it instead of calling the individual sub-properties directly. Example: use \`searchAssetsByCondition("資産名", assetName)\` not \`searchConditionArea.expandWhenClosed()\` + \`getConditionRow(1)\` + \`selectItemName()\` etc. Use \`bulkDelete()\` not \`bulkDeleteBtn.click()\` + \`confirmDeleteDialog\` + \`noticeDialog\` inline.
|
|
156
|
+
- **Do NOT upgrade LOW or UNCERTAIN** because the substitution seems reasonable
|
|
157
|
+
- **Conservative default**: an incorrect substitution that breaks the test is worse than leaving something inline — when in doubt, leave it inline
|
|
158
|
+
- If the table has ZERO PREFERRED or HIGH entries → **SKIP TO STEP 6 (FALLBACK)**
|
|
159
|
+
- If AT LEAST ONE PREFERRED or HIGH entry exists → continue to STEP 3
|
|
160
|
+
|
|
161
|
+
---
|
|
162
|
+
|
|
163
|
+
## STEP 3: ANALYZE AND APPLY TRANSFORMATION RULES
|
|
164
|
+
|
|
165
|
+
Read through \`${testFile}\` and apply the following rules:
|
|
166
|
+
|
|
167
|
+
### 3a. Skyramp Infrastructure — PRESERVE EXACTLY, NO EXCEPTIONS
|
|
168
|
+
|
|
169
|
+
The following must appear in the refactored test exactly as they are in the original. Never remove, alter, or move them:
|
|
170
|
+
|
|
171
|
+
\`\`\`typescript
|
|
172
|
+
// Generated by Skyramp ...
|
|
173
|
+
// Command: skyramp generate ui rest \\
|
|
174
|
+
// ...
|
|
175
|
+
\`\`\`
|
|
176
|
+
|
|
177
|
+
\`\`\`typescript
|
|
178
|
+
import { test } from '@playwright/test';
|
|
179
|
+
import { newSkyrampPlaywrightPage, expect } from '@skyramp/skyramp';
|
|
180
|
+
|
|
181
|
+
test.use({
|
|
182
|
+
viewport: { width: 1450, height: 900 },
|
|
183
|
+
});
|
|
184
|
+
|
|
185
|
+
// Inside the test body:
|
|
186
|
+
test.setTimeout(1000000);
|
|
187
|
+
page.setDefaultTimeout(pageTimeout);
|
|
188
|
+
const skyrampPage = newSkyrampPlaywrightPage(page, testInfo);
|
|
189
|
+
\`\`\`
|
|
190
|
+
|
|
191
|
+
> **Note on the page variable:** Assign \`newSkyrampPlaywrightPage\` to \`skyrampPage\`. Pass \`skyrampPage\` to all module constructors and use it for all \`waitForTimeout\`, \`waitForResponse\`, \`locator\`, and \`expect\` calls. If the original test reassigns \`page\` directly, update it to use a separate \`skyrampPage\` variable instead.
|
|
192
|
+
|
|
193
|
+
### 3b. Test Data — Extract to Named Constants
|
|
194
|
+
|
|
195
|
+
Extract all hardcoded strings (credentials, tenant names, workflow names, asset names, URLs, etc.) to named \`const\` declarations at the top of the file, before \`test.use\`. Group them under a \`// Test data\` comment.
|
|
196
|
+
|
|
197
|
+
**Uniqueness:** Any string used as the name of an entity being created, registered, or saved during the test must be made unique per run. First check whether existing refactored tests in the repository already use a convention for this. If a convention exists, follow it exactly. If not, use:
|
|
198
|
+
\`\`\`typescript
|
|
199
|
+
const uniqueSuffix = Date.now();
|
|
200
|
+
const workflowName = "MyWorkflow_" + uniqueSuffix;
|
|
201
|
+
\`\`\`
|
|
202
|
+
|
|
203
|
+
After making a name dynamic, scan the entire test for any raw locator that hardcodes that same string value and replace only the hardcoded value with the variable.
|
|
204
|
+
|
|
205
|
+
### 3c. Module Instantiation — Declare All Together at the Top of the Test Body
|
|
206
|
+
|
|
207
|
+
All module instances must be declared together in a clearly labeled block immediately after \`skyrampPage\` is initialized, before any test actions begin. Match whatever label and comment style the repository's existing tests use. Pass \`skyrampPage\` to every constructor unless the class explicitly requires a different argument (such as a frame handle).
|
|
208
|
+
|
|
209
|
+
The only exception: a module that genuinely cannot be instantiated until a dynamic value is known at that point in the flow. When this happens, instantiate it inline and add a comment explaining why.
|
|
210
|
+
|
|
211
|
+
### 3d. Import Organization — Match the Repository's Conventions
|
|
212
|
+
|
|
213
|
+
Match the import grouping and labeling style of the existing test files exactly:
|
|
214
|
+
- Keep Skyramp imports (\`@playwright/test\` and \`@skyramp/skyramp\`) together and unchanged
|
|
215
|
+
- Group POM imports separately, consistent with the repo's pattern
|
|
216
|
+
- Use whatever path alias convention the repository defines — never introduce a new path style
|
|
217
|
+
|
|
218
|
+
### 3e. \`waitForTimeout\` Calls
|
|
219
|
+
|
|
220
|
+
**Always prefer the POM method over preserving a raw wait:**
|
|
221
|
+
If an action sequence maps to a POM method at HIGH or PREFERRED confidence, **always call the method** — even if the raw test has a \`waitForTimeout\` in the middle of that sequence. Drop the raw wait that falls inside the method's covered scope. The customer's POM is the right abstraction; duplicating their code inline just to preserve a trace-recorded wait defeats the purpose of code reuse.
|
|
222
|
+
|
|
223
|
+
- Call the POM method
|
|
224
|
+
- Drop any \`waitForTimeout\` that falls *inside* the method's covered sequence
|
|
225
|
+
- Add a comment: \`// raw waitForTimeout(Xms) dropped — POM method used instead\`
|
|
226
|
+
|
|
227
|
+
**How to determine inside vs outside a method's scope:**
|
|
228
|
+
- **Inside scope** = the wait appears between two actions that the POM method covers. Example: a raw test has \`fill(password)\` → \`waitForTimeout(1500)\` → \`click(loginBtn)\`. Since \`loginPage.login()\` covers both the fill and the click, the \`waitForTimeout(1500)\` is inside scope → drop it.
|
|
229
|
+
- **Outside scope** = the wait appears AFTER all actions the POM method covers, as a standalone pause before the next operation. Example: \`await loginPage.login(...)\` → \`waitForTimeout(27000)\`. The login is complete; the 27000ms is waiting for the app to settle after login → this is outside scope → preserve it.
|
|
230
|
+
|
|
231
|
+
**Preserve waits that fall between POM calls:**
|
|
232
|
+
Any \`waitForTimeout\` that falls *outside* all POM method scopes — i.e. as a standalone wait between two POM calls — must be preserved in exact position, updated to \`await skyrampPage.waitForTimeout(...)\`.
|
|
233
|
+
|
|
234
|
+
### 3f. \`waitForResponse\` Network Intercepts
|
|
235
|
+
|
|
236
|
+
**If the action maps to a PREFERRED method (network-aware):**
|
|
237
|
+
- Call the POM method — its internal \`waitForResponse\` handles the correlation
|
|
238
|
+
- Drop the raw test's entire \`const responsePromiseN / await action / const responseN\` block
|
|
239
|
+
- Add a comment: \`// method handles network correlation internally — raw responsePromise dropped\`
|
|
240
|
+
|
|
241
|
+
**If no POM method is network-aware for this action:**
|
|
242
|
+
- Leave the \`waitForResponse\` block inline as-is. Do NOT create a new POM method.
|
|
243
|
+
|
|
244
|
+
**Side-effect endpoints — always remove regardless:**
|
|
245
|
+
Remove the \`waitForResponse\` wrapper entirely (keep only the action click) for endpoints that do not correlate to the primary action. These cause flaky timeouts. Patterns to remove:
|
|
246
|
+
- \`**/live/**\` — live chat / messaging (e.g. \`getAFTalkRelations\`)
|
|
247
|
+
- \`**/notification/**\`, \`**/notifications/**\`
|
|
248
|
+
- \`**/analytics/**\`, \`**/tracking/**\`, \`**/telemetry/**\`
|
|
249
|
+
|
|
250
|
+
\`\`\`typescript
|
|
251
|
+
// Before — remove this
|
|
252
|
+
const responsePromise1 = skyrampPage.waitForResponse("**/live/getAFTalkRelations**");
|
|
253
|
+
await someAction.click();
|
|
254
|
+
const response1 = await responsePromise1;
|
|
255
|
+
|
|
256
|
+
// After — just the action
|
|
257
|
+
await someAction.click();
|
|
258
|
+
\`\`\`
|
|
259
|
+
|
|
260
|
+
### 3g. Iframe and Frame Context — Match Constructor Expectations
|
|
261
|
+
|
|
262
|
+
When a module operates inside an iframe, determine the correct frame handle by reading its constructor. Pass the matching \`contentFrame()\` handle — never pass \`skyrampPage\` when the class expects a frame.
|
|
263
|
+
|
|
264
|
+
**Never create standalone \`frameLocator()\` references in the test body.** If an action is UNMAPPED and must stay inline, access the frame through the POM's own \`iframe\` property (e.g. \`assetSelectPage.iframe.locator(...)\`, \`inputPage.iframe.getByRole(...)\`). Do not create a separate \`const inputPageFrame = skyrampPage.frameLocator('#application-new-applied')\` — this duplicates the frame reference that already exists in the POM.
|
|
265
|
+
|
|
266
|
+
### 3h. Dynamic Locators — Prefer Positional Module Methods
|
|
267
|
+
|
|
268
|
+
Before keeping a dynamic locator inline, check whether a module method exists that targets the same element by position rather than by value. If one exists, use it with the appropriate position (typically \`0\` for the first result) and add a comment noting it replaces the original dynamic locator. If no positional equivalent exists, keep the raw dynamic locator inline as-is.
|
|
269
|
+
|
|
270
|
+
**Never hardcode dynamic values** (IDs, generated names, timestamps) in locators. Example: \`getByRole("link", { name: "28457681" })\` hardcodes a workflow ID that changes every run — use \`appliedListPage.wfidLinkByIndex(0)\` instead.
|
|
271
|
+
|
|
272
|
+
### 3i. Checkbox Interactions — Follow Repo Pattern
|
|
273
|
+
|
|
274
|
+
If the existing POM files have an established pattern for checkbox interactions, follow it. If no such pattern exists, keep the checkbox interaction inline exactly as it appears — do not invent a new abstraction.
|
|
275
|
+
|
|
276
|
+
### 3j. Screenshot Assertions — Preserve Exact Position
|
|
277
|
+
|
|
278
|
+
Screenshot assertions (\`toHaveScreenshot\`) must stay in their **exact original position** relative to surrounding actions from the raw test. Note what actions appear immediately before and after the screenshot in the raw test, then place the screenshot at that same relative position in the refactored test. If a POM method would move the screenshot, inline that method's steps around it with a comment: \`// POM method not used — screenshot position must be preserved\`
|
|
279
|
+
|
|
280
|
+
---
|
|
281
|
+
|
|
282
|
+
## STEP 4: WRITE THE REFACTORED TEST
|
|
283
|
+
|
|
284
|
+
Using your catalog from STEP 2 and analysis from STEP 3, produce the refactored test.
|
|
285
|
+
|
|
286
|
+
**CRITICAL — Consult the mapping table for every single action before writing it:**
|
|
287
|
+
Before writing each line of refactored code, look up that action in your STEP 2b mapping table:
|
|
288
|
+
- If it is marked **PREFERRED** → use the POM method and drop any raw waits/correlations inside its scope. This is the highest-value substitution.
|
|
289
|
+
- If it is marked **HIGH** → you MUST use the POM method or property. Do NOT write an inline locator.
|
|
290
|
+
- If it is marked **LOW / UNCERTAIN / UNMAPPED** → write it inline exactly as in the original test.
|
|
291
|
+
|
|
292
|
+
Do not skip this lookup for any action. The mapping table is your source of truth — not the raw test's locators.
|
|
293
|
+
|
|
294
|
+
## STEP 4b: VERIFY AGAINST MAPPING TABLE
|
|
295
|
+
|
|
296
|
+
Before proceeding to STEP 5, scan your refactored test output and for each HIGH entry in the mapping table confirm the corresponding POM call is present in the output. If any HIGH entry is missing its POM call and was written as inline code instead, go back and fix it before writing the file.
|
|
297
|
+
|
|
298
|
+
**Section Comments:** Divide the test body into clearly labeled sections corresponding to the logical phases of the user journey. Use the comment style from the repository's existing tests. If none exists, use \`/*----- Section Name -----*/\`.
|
|
299
|
+
|
|
300
|
+
**Modularization Priority:**
|
|
301
|
+
1. **Existing module method** — always preferred; replaces entire action sequences
|
|
302
|
+
2. **Existing module property** — use directly in \`expect(...)\` or chained calls when no method wraps it
|
|
303
|
+
3. **Inline raw locator** — only when no modular equivalent exists; keep as-is
|
|
304
|
+
|
|
305
|
+
**Assertions:**
|
|
306
|
+
- Module locator assertions: \`expect(module.someLocator).toBeVisible()\`
|
|
307
|
+
- Screenshot assertions (\`toHaveScreenshot\`): always keep inline; if mid-method sequence, inline that method's steps around it
|
|
308
|
+
- Page readiness checks: use module methods such as \`pageToBeVisible()\` if they exist
|
|
309
|
+
|
|
310
|
+
**IMPORTANT:** Do NOT create a \`skyrampUtils.ts\` or any SkyrampUtils file on this path. Do NOT call \`skyramp_modularization\` after completing POM-aware code reuse — POM refactoring replaces that step.
|
|
311
|
+
|
|
312
|
+
---
|
|
313
|
+
|
|
314
|
+
## STEP 5: WRITE THE OUTPUT
|
|
315
|
+
|
|
316
|
+
Write the complete, runnable refactored test directly to \`${testFile}\`, overwriting it in place. The file must contain every line — never use placeholder comments like \`// ...continue\` or \`// ...existing code\`. If the test is long, write it in multiple passes until the file is complete. Confirm the file path written to in a single line once done.
|
|
317
|
+
|
|
318
|
+
Do not output a section breakdown, modularization summary, or any analysis to the chat. Perform all analysis internally.
|
|
319
|
+
|
|
320
|
+
**Do NOT run the test. Do NOT call any other tools. Your work is complete once the file is written.**
|
|
321
|
+
|
|
322
|
+
---
|
|
323
|
+
|
|
324
|
+
## STEP 6: FALLBACK — STANDARD SKYRAMPUTILS PATH
|
|
325
|
+
|
|
326
|
+
**You should only reach this step if:**
|
|
327
|
+
- No POM files were detected in STEP 1, OR
|
|
328
|
+
- Zero locators/actions in the test mapped to any existing module in STEP 2
|
|
329
|
+
|
|
330
|
+
Inform the user: *"No POM layer found in this project — falling back to standard SkyrampUtils code reuse."*
|
|
331
|
+
|
|
332
|
+
Then follow the standard SkyrampUtils process below exactly:
|
|
333
|
+
|
|
334
|
+
---
|
|
335
|
+
|
|
336
|
+
# CODE REUSE - 6 CLEAR STEPS
|
|
337
|
+
**CRITICAL WARNING: VIOLATION OF THESE RULES WILL RESULT IN ERROR**
|
|
338
|
+
|
|
339
|
+
## DEFINITIONS (READ FIRST):
|
|
340
|
+
**What is a "Helper Function"?**
|
|
341
|
+
- A helper function is a STANDALONE, DISTINCT function that is ALREADY DEFINED in the code
|
|
342
|
+
- It has a clear function signature (e.g., \`async function createProduct(page, data) { ... }\`)
|
|
343
|
+
- It is NOT just repetitive inline test code or patterns
|
|
344
|
+
- Example of helper function: \`async function loginUser(page, email, password) { ... }\`
|
|
345
|
+
- Example of NOT a helper function: Repetitive \`await page.click()\` statements scattered in test
|
|
346
|
+
|
|
347
|
+
**What is "Repetitive Code Pattern"?**
|
|
348
|
+
- Sequential test steps that look similar but are NOT extracted into standalone functions
|
|
349
|
+
- Example: Multiple sequences of \`await page.fill()\`, \`await page.click()\` directly in test body
|
|
350
|
+
- These patterns should NOT be extracted during code reuse - that's what modularization is for
|
|
351
|
+
|
|
352
|
+
** CODE REUSE ≠ REFACTORING**
|
|
353
|
+
- Code reuse = Finding EXISTING helper functions and reusing them
|
|
354
|
+
- Refactoring = Creating NEW helper functions from patterns (handled by modularization tool)
|
|
355
|
+
|
|
356
|
+
## STEP 1: READ TEST FILE
|
|
357
|
+
Read ${testFile} and look for ALREADY DEFINED helper functions (not just repetitive patterns).
|
|
358
|
+
|
|
359
|
+
## STEP 2: FIND EXISTING UTILS
|
|
360
|
+
Use the Grep tool to search for files containing "${SKYRAMP_UTILS_HEADER}":
|
|
361
|
+
- Pattern: "${SKYRAMP_UTILS_HEADER}"
|
|
362
|
+
- Type: "ts"
|
|
363
|
+
- Output mode: "files_with_matches"
|
|
364
|
+
**CRITICAL: Only look for util files with header ${SKYRAMP_UTILS_HEADER}** - exclude test files (files with "test", "spec", ".test.", ".spec." in the name).
|
|
365
|
+
Read the matching non-test files and check if any helpers can be reused in ${testFile}.
|
|
366
|
+
|
|
367
|
+
## STEP 3: USE EXISTING HELPERS FROM UTILS SOURCE FILES FOUND IN STEP 2
|
|
368
|
+
If helpers exist in util file that can be reused in ${testFile} without modifying them:
|
|
369
|
+
- Import them into ${testFile}
|
|
370
|
+
- Remove any duplicate code in ${testFile}
|
|
371
|
+
- Test that ${testFile} still works without any errors and logical is same as original test file.
|
|
372
|
+
|
|
373
|
+
## STEP 4: FIND LOCAL HELPERS IN OTHER TEST SOURCE FILES THAT HAS HEADER ${SKYRAMP_UTILS_HEADER}
|
|
374
|
+
Use the Grep tool to search for other test files containing "${SKYRAMP_UTILS_HEADER}":
|
|
375
|
+
- Pattern: "${SKYRAMP_UTILS_HEADER}"
|
|
376
|
+
- Type: "ts"
|
|
377
|
+
- Output mode: "files_with_matches"
|
|
378
|
+
**CRITICAL: Exclude ${testFile} from the results** - only look at OTHER test files, not the current file.
|
|
379
|
+
|
|
380
|
+
**STOP HERE IF NO OTHER TEST FILES FOUND**
|
|
381
|
+
**IF NO OTHER TEST FILES ARE FOUND, SKIP TO STEP 6 - DO NOT CREATE ANY UTILS FILES.**
|
|
382
|
+
|
|
383
|
+
If other test files are found, read those files and look for ALREADY DEFINED helper functions with clear function signatures.
|
|
384
|
+
|
|
385
|
+
**How to identify helper functions in other test files:**
|
|
386
|
+
HELPER FUNCTION (move to utils):
|
|
387
|
+
- Has function signature: \`async function doSomething(params) { ... }\`
|
|
388
|
+
- Can be called multiple times with different parameters
|
|
389
|
+
- Example: \`async function fillProductForm(page, product) { ... }\`
|
|
390
|
+
|
|
391
|
+
NOT A HELPER FUNCTION (do not extract):
|
|
392
|
+
- Just repetitive inline test code in test body
|
|
393
|
+
- No function definition/signature
|
|
394
|
+
- Example: Multiple \`await page.getByTestId("xyz").click()\` directly in test
|
|
395
|
+
|
|
396
|
+
**IF OTHER TEST FILES ONLY CONTAIN REPETITIVE PATTERNS (NO ACTUAL HELPER FUNCTIONS), SKIP TO STEP 6**
|
|
397
|
+
**IF OTHER TEST FILES ARE ESSENTIALLY IDENTICAL TO CURRENT FILE, SKIP TO STEP 6**
|
|
398
|
+
|
|
399
|
+
## STEP 5: IF LOCAL HELPERS ARE FOUND IN STEP 4 THAT CAN BE REUSED in ${testFile}, MOVE THOSE LOCAL HELPERS TO UTILS SOURCE FILES AND USE THEM
|
|
400
|
+
|
|
401
|
+
**ONLY PROCEED WITH STEP 5 IF ALL CONDITIONS ARE MET:**
|
|
402
|
+
- You found OTHER test files in STEP 4 (not just ${testFile})
|
|
403
|
+
- Those test files contain ACTUAL HELPER FUNCTIONS with function signatures (not just repetitive patterns)
|
|
404
|
+
- The helper functions are ALREADY IMPLEMENTED and working in those OTHER test files
|
|
405
|
+
- The helper functions are DIFFERENT from the current file (not just identical patterns)
|
|
406
|
+
|
|
407
|
+
**IF ANY CONDITION IS NOT MET, SKIP TO STEP 6 - DO NOT CREATE ANY UTILS FILES.**
|
|
408
|
+
|
|
409
|
+
**CRITICAL:** For helpers found in STEP 4:
|
|
410
|
+
1. **CREATE** \`${TS_UTILS_FILE}\` file if it doesn't exist with the following header:
|
|
411
|
+
\`\`\`ts
|
|
412
|
+
${generateSkyrampHeader("typescript")}
|
|
413
|
+
\`\`\`
|
|
414
|
+
**CRITICAL: IF \`${TS_UTILS_FILE}\` is already big, create a new file with a different name and add the header to the new file.**
|
|
415
|
+
2. **COPY** those local helper functions from original test source files to \`${TS_UTILS_FILE}\` without modifying them
|
|
416
|
+
3. **DELETE** those local helper functions from test source files (REMOVE THEM COMPLETELY) WITHOUT ANY OTHER CHANGES.
|
|
417
|
+
4. **IMPORT** those local helper functions from \`${TS_UTILS_FILE}\` back into test source files WITHOUT MAKING ANY OTHER CHANGE.
|
|
418
|
+
5. **IMPORT** those local helper functions from \`${TS_UTILS_FILE}\` into ${testFile}
|
|
419
|
+
6. **REPLACE** duplicate code in ${testFile} with calls to imported helper functions WITHOUT MAKING ANY OTHER CHANGE.
|
|
420
|
+
|
|
421
|
+
**CRITICAL: DO NOT CREATE UNNECESSARY HELPER FUNCTIONS**
|
|
422
|
+
**DO NOT CREATE HELPERS BASED ON PATTERNS IN THE CURRENT FILE**
|
|
423
|
+
|
|
424
|
+
## STEP 6: VERIFY AND VALIDATE
|
|
425
|
+
1. **BEFORE** making any changes, read the original test file and identify ALL helper functions
|
|
426
|
+
2. **AFTER** copying helpers to utils, read the original file again to confirm helpers are GONE
|
|
427
|
+
3. **VERIFY** that helper functions are NO LONGER in original test files
|
|
428
|
+
4. **VERIFY** that the original test files only have import statements and no duplicate code
|
|
429
|
+
5. **VERIFY** that both original and new test files import from utils and use the helper functions
|
|
430
|
+
6. **VERIFY** that no unnecessary helper functions were created
|
|
431
|
+
7. **VERIFY** that all helper functions in utils are actually imported and used in the test files
|
|
432
|
+
8. **REMOVE** any helper functions that are not being used after refactoring
|
|
433
|
+
9. **NEVER** refactor, reorganize, or restructure existing source test files beyond moving helpers
|
|
434
|
+
10. **Do NOT run the test** — your work is complete once the file is written
|
|
435
|
+
|
|
436
|
+
**Do NOT call skyramp_modularization.** Code reuse is complete. The test file has been refactored in place — no further tools should be called.
|
|
437
|
+
|
|
438
|
+
SUMMARIZE THE CODE REUSE PROCESS AND THE RESULTS.
|
|
439
|
+
`;
|
|
440
|
+
}
|
|
@@ -2,7 +2,6 @@
|
|
|
2
2
|
* Modular section builders for the Drift Analysis prompt,
|
|
3
3
|
* mirroring the recommendationSections.ts pattern.
|
|
4
4
|
*/
|
|
5
|
-
import { ENHANCE_ASSERTIONS_FOR_INTEGRATION_AND_CONTRACTPROVIDER } from "./enhanceAssertionSection.js";
|
|
6
5
|
export function buildDriftScoringGuide() {
|
|
7
6
|
return `## Drift Score Guide (0–100)
|
|
8
7
|
|
|
@@ -159,9 +158,12 @@ Remove the test file when ALL endpoints it covers were removed from the codebase
|
|
|
159
158
|
Never use hardcoded resource IDs (e.g. \`order_id=1\`) in any test step, including GET or DELETE steps. Always create required resources via prior POST steps and chain IDs dynamically. Use timestamp-based unique names for created resources (e.g. \`"Product-\${int(time.time())}"\`) to prevent collisions across test runs.
|
|
160
159
|
|
|
161
160
|
### Enhance assertions after UPDATE (MANDATORY)
|
|
162
|
-
|
|
161
|
+
Call \`skyramp_enhance_assertions\` with \`testFile\` set to the absolute path of the test file you just updated, \`enhanceType: "maintenance"\`, and the matching \`testType\` based on the file you are editing:
|
|
162
|
+
- **Integration test file** (multi-step chained requests): call with \`testType: "integration"\`
|
|
163
|
+
- **Contract-provider test file** (single endpoint with \`beforeAll\`/\`afterAll\` setup, provider mode): call with \`testType: "contract"\`. Skip for consumer-mode contract tests.
|
|
164
|
+
- **UI test file** (imports \`@playwright/test\`, uses \`page.\` calls): call with \`testType: "ui"\`
|
|
163
165
|
|
|
164
|
-
|
|
166
|
+
Then apply every instruction returned by the tool to the test file.`;
|
|
165
167
|
}
|
|
166
168
|
export function buildDriftOutputChecklist(existingTestCount, newEndpointCount, inlineMode = false, stateFile) {
|
|
167
169
|
const finalStep = inlineMode
|
|
@@ -93,8 +93,18 @@ ${nextStep}`;
|
|
|
93
93
|
? `\n<diff>\n${p.diffContent}\n</diff>`
|
|
94
94
|
: "";
|
|
95
95
|
const step2 = isUIOnly
|
|
96
|
-
? `### Step 2: Identify consumed API endpoints
|
|
97
|
-
UI-only PR —
|
|
96
|
+
? `### Step 2: Identify consumed API endpoints and integration status
|
|
97
|
+
UI-only PR — perform two checks:
|
|
98
|
+
1. Read changed frontend files to find API calls (fetch, axios, hooks).
|
|
99
|
+
2. For each changed component file (skip CSS/HTML/style-only files — they have no exported component name to search for): check whether any production source file imports, re-exports, or renders it.
|
|
100
|
+
- Search for both the component's exported name AND its module path/filename to catch aliased and default imports (e.g. \`import Foo from './CartLine'\`).
|
|
101
|
+
- Derive the exported name from the file itself: use the default export name, a named exported PascalCase component, or the PascalCase file basename when no clearer name exists.
|
|
102
|
+
- Exclude test/story files from the search: ignore matches in \`*.test.*\`, \`*.spec.*\`, \`*.stories.*\`, and \`__tests__/\` directories — only production code imports count as integration.
|
|
103
|
+
|
|
104
|
+
If no production file imports, re-exports, or renders a changed component, mark it as **unintegrated** in the Execution Plan output.
|
|
105
|
+
Exception: if the same PR also adds a route/page file (e.g. under Next.js \`pages/\` or \`app/\`) that imports the component, the route IS the integration point — do NOT mark it as unintegrated.
|
|
106
|
+
Do NOT apply the unintegrated heuristic to route/entrypoint files themselves — those are always reachable by convention.
|
|
107
|
+
An unintegrated non-route component has no DOM node in the running app and cannot be browser-tested — it qualifies as a dead-code / unintegrated-component no-surface PR regardless of how complex the component logic is.`
|
|
98
108
|
: p.diffContent
|
|
99
109
|
? `### Step 2: Extract new and modified API endpoints from the diff
|
|
100
110
|
Read the \`<diff>\` above and identify every new or modified API endpoint — route registrations, handler methods, controller annotations. Then use the **Router Mounting / Nesting** section above to reconstruct the full URL path for each endpoint by chaining all parent router prefixes down to the handler (e.g. a handler in a file with \`prefix="/reviews"\` that is mounted at \`/{product_id}\` under a router mounted at \`/api/v1/products\` → full path \`/api/v1/products/{product_id}/reviews\`).
|
|
@@ -1,3 +1,7 @@
|
|
|
1
|
+
import { isContractConsumerModeEnabled } from "../../utils/featureFlags.js";
|
|
2
|
+
import { WorkspaceAuthType, getAuthScheme, isAuthorizationHeaderName, AUTH_MIDDLEWARE_PATTERNS_STR } from "../../utils/workspaceAuth.js";
|
|
3
|
+
// Cached at module-load — the flag is process-wide and cannot change per call.
|
|
4
|
+
const CONSUMER_MODE_ENABLED = isContractConsumerModeEnabled();
|
|
1
5
|
export const MAX_TESTS_TO_GENERATE = 3;
|
|
2
6
|
export const MAX_RECOMMENDATIONS = 20;
|
|
3
7
|
export const MAX_CRITICAL_TESTS = 3;
|
|
@@ -65,19 +69,30 @@ function serializeAuthCallParams(params) {
|
|
|
65
69
|
}
|
|
66
70
|
return parts.join(", ");
|
|
67
71
|
}
|
|
68
|
-
|
|
72
|
+
/**
|
|
73
|
+
* Returns the authScheme snippet for a given authHeader + authType combination.
|
|
74
|
+
*
|
|
75
|
+
* Delegates scheme resolution to getAuthScheme() in workspaceAuth.ts — the single
|
|
76
|
+
* source of truth — so this file no longer maintains its own slug→scheme table.
|
|
77
|
+
*/
|
|
78
|
+
export function getAuthSnippets(authHeaderValue, authType, explicitScheme) {
|
|
69
79
|
if (!authHeaderValue) {
|
|
70
80
|
return { authSchemeSnippet: "", authTokenSnippet: "" };
|
|
71
81
|
}
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
82
|
+
// Only Authorization headers carry an authScheme prefix.
|
|
83
|
+
if (!isAuthorizationHeaderName(authHeaderValue)) {
|
|
84
|
+
return { authSchemeSnippet: "", authTokenSnippet: "" };
|
|
85
|
+
}
|
|
86
|
+
if (authType && authType !== WorkspaceAuthType.None) {
|
|
87
|
+
// Resolve via the canonical workspaceAuth mapping, passing explicit scheme if set.
|
|
88
|
+
const wsType = authType;
|
|
89
|
+
const scheme = getAuthScheme(wsType, explicitScheme);
|
|
90
|
+
// Use !== undefined so intentionally empty scheme ("" = raw token, no prefix) is preserved.
|
|
91
|
+
if (scheme !== undefined) {
|
|
76
92
|
return { authSchemeSnippet: `, authScheme: "${scheme}"`, authTokenSnippet: "" };
|
|
77
93
|
}
|
|
78
|
-
return { authSchemeSnippet: ', authScheme: "<scheme e.g. Bearer, Basic, Token or empty>"', authTokenSnippet: "" };
|
|
79
94
|
}
|
|
80
|
-
return { authSchemeSnippet: "", authTokenSnippet: "" };
|
|
95
|
+
return { authSchemeSnippet: ', authScheme: "<scheme e.g. Bearer, Basic, Token or empty>"', authTokenSnippet: "" };
|
|
81
96
|
}
|
|
82
97
|
export const PATH_PARAM_UUID_GUIDANCE = `**Path parameters:** keep the placeholder in \`endpointURL\` (e.g. \`/coupons/{coupon_id}\`). ` +
|
|
83
98
|
`Pass the value via \`pathParams\` (e.g. \`coupon_id=<random-uuid-v4>\`). ` +
|
|
@@ -137,10 +152,11 @@ export function buildGenerationRules(isUIOnlyPR) {
|
|
|
137
152
|
**Available test types:** integration, contract, E2E, UI. **No smoke or fuzz tests.**
|
|
138
153
|
Choose based on what adds the most value for this PR's changes.
|
|
139
154
|
|
|
140
|
-
|
|
155
|
+
${CONSUMER_MODE_ENABLED ? `**Contract test mode — signal-based selection:**
|
|
141
156
|
- **Consumer contract** (\`consumerMode: true\`): Look for outbound HTTP client code (fetch, axios, httpx, requests, http.Client), service client classes, or calls to external base URLs. If an endpoint's implementation makes downstream calls, that downstream boundary is a consumer contract test candidate.
|
|
142
157
|
- **Provider contract** (\`providerMode: true\`): Look for new or modified endpoint handlers, route changes, or response shape modifications. If the diff adds/changes an endpoint this service owns, that is a provider contract test candidate.
|
|
143
|
-
- **Both modes** (\`providerMode: true, consumerMode: true\`) — produces the same output as omitting both flags (generates provider and consumer contract tests). Use when the diff contains BOTH provider signals (new/modified endpoint handlers) AND consumer signals (outbound HTTP client calls to another service)
|
|
158
|
+
- **Both modes** (\`providerMode: true, consumerMode: true\`) — produces the same output as omitting both flags (generates provider and consumer contract tests). Use when the diff contains BOTH provider signals (new/modified endpoint handlers) AND consumer signals (outbound HTTP client calls to another service).` : `**Contract tests — provider-only:**
|
|
159
|
+
Only provider-side contract tests are supported. Recommend a contract test (\`providerMode: true\`) when the diff adds or modifies an endpoint handler, route, or response shape that this service owns.`}
|
|
144
160
|
|
|
145
161
|
**Scenario fidelity:** Every workflow scenario should reflect the actual resource
|
|
146
162
|
relationships in the code. If the pre-drafted scenarios don't match the real data model,
|
|
@@ -152,8 +168,7 @@ Use Playwright browser tools to auto-record traces and generate UI tests.
|
|
|
152
168
|
When no Playwright trace exists, use the Playwright browser tools (\`browser_navigate\`,
|
|
153
169
|
\`browser_click\`, etc.) to record a trace, then export via \`skyramp_export_zip\` for UI tests.
|
|
154
170
|
`}
|
|
155
|
-
|
|
156
|
-
recommend a different test that adds new coverage.`;
|
|
171
|
+
`;
|
|
157
172
|
}
|
|
158
173
|
export function buildVerificationChecklist(topN, maxGen) {
|
|
159
174
|
const minTotal = Math.min(maxGen + 1, topN);
|
|
@@ -168,6 +183,7 @@ Before finalizing your output, verify:
|
|
|
168
183
|
7. **scenarioFile**: \`skyramp_integration_test_generation\` uses the exact \`filePath\` returned by \`skyramp_batch_scenario_test_generation\` — not a guessed or hardcoded filename.
|
|
169
184
|
8. **bugCatchingTarget**: Every GENERATE integration test that targets a business rule, formula, or constraint has a non-empty \`bugCatchingTarget\`.
|
|
170
185
|
9. **FK chaining**: In multi-step integration tests, path params sourced from a prior step's response (e.g. \`order_id\` from step 1) use \`chainsFrom\` — not hardcoded IDs.
|
|
186
|
+
10. **Concrete scenario names**: No GENERATE item uses a placeholder name ending in a numeric suffix (e.g. \`ui-test-for-changed-component-1\`, \`ui-test-from-trace-2\`). Derive the name from the actual changed component or flow: if the diff touches \`LinkCard.tsx\`, the scenario name should be \`link-card-pin-toggle\` or \`link-card-edit-description\`, not \`ui-test-for-changed-component-1\`. The changed file list is available above — use it.
|
|
171
187
|
</verification>`;
|
|
172
188
|
}
|
|
173
189
|
export function buildFewShotExamples() {
|
|
@@ -222,29 +238,33 @@ Reasoning: Catches a missing 404 guard on DELETE — verifies the handler return
|
|
|
222
238
|
</example>
|
|
223
239
|
</examples>`;
|
|
224
240
|
}
|
|
225
|
-
export function buildToolWorkflows(authHeaderValue, authTypeValue = "") {
|
|
226
|
-
const isAuthorizationHeader =
|
|
241
|
+
export function buildToolWorkflows(authHeaderValue, authTypeValue = "", explicitScheme) {
|
|
242
|
+
const isAuthorizationHeader = isAuthorizationHeaderName(authHeaderValue);
|
|
227
243
|
const noAuth = !authHeaderValue;
|
|
228
244
|
let authGuidance;
|
|
229
245
|
let authParams;
|
|
230
246
|
if (noAuth) {
|
|
231
247
|
authGuidance = `**Auth Verification Required:** The workspace config indicates no authentication, but you MUST verify this independently before omitting auth:
|
|
232
248
|
1. **OpenAPI spec** \u2192 check \`securitySchemes\` / \`securityDefinitions\` for \`type: http\`, \`type: apiKey\`, or \`type: oauth2\`
|
|
233
|
-
2. **Source code** \u2192 look for auth
|
|
249
|
+
2. **Source code** \u2192 look for known auth signals (${AUTH_MIDDLEWARE_PATTERNS_STR}).
|
|
234
250
|
3. **Route definitions** \u2192 check if routes have auth decorators or middleware applied
|
|
235
|
-
|
|
251
|
+
4. **Still unknown** \u2192 proceed with \`authHeader: ""\` and note "auth pattern unrecognized" in your recommendation description.
|
|
252
|
+
If you find auth requirements, pass the appropriate \`authHeader\` and \`authScheme\` to EVERY tool call. Only pass \`authHeader: ""\` if you confirm the API is truly unauthenticated.`;
|
|
236
253
|
authParams = { authHeader: "" };
|
|
237
254
|
}
|
|
238
255
|
else if (isAuthorizationHeader) {
|
|
256
|
+
// Use getAuthScheme (single source of truth) instead of capitalizing the raw slug.
|
|
257
|
+
// Pass explicitScheme so custom schemes from workspace.yml (e.g. "Ghost", "Hawk") are used.
|
|
258
|
+
const wsType = authTypeValue;
|
|
239
259
|
const resolvedScheme = (authTypeValue && authTypeValue !== "none")
|
|
240
|
-
?
|
|
260
|
+
? (getAuthScheme(wsType, explicitScheme) ?? "<scheme e.g. Bearer, Token or empty>")
|
|
241
261
|
: "<scheme e.g. Bearer, Token or empty>";
|
|
242
262
|
authParams = { authHeader: "Authorization", authScheme: resolvedScheme };
|
|
243
263
|
if (authTypeValue) {
|
|
244
264
|
authGuidance = `**Auth Scheme:** The workspace \`api.authType\` is \`"${authTypeValue}"\`.
|
|
245
265
|
**Where to find the scheme** (check in order):
|
|
246
266
|
1. **OpenAPI spec** \u2192 look at \`securitySchemes\` / \`securityDefinitions\` for \`type: http, scheme: bearer\` or \`type: apiKey\`
|
|
247
|
-
2. **Source code** \u2192 auth middleware
|
|
267
|
+
2. **Source code** \u2192 auth middleware signals: ${AUTH_MIDDLEWARE_PATTERNS_STR}
|
|
248
268
|
3. **Workspace config** \u2192 use \`api.authType\` value as the scheme if source is inconclusive
|
|
249
269
|
Pass the prefix as \`authScheme\` (e.g., \`"Bearer"\`, \`"Token"\`, \`"Basic"\`). If the API uses raw tokens with no prefix, pass \`authScheme: ""\`.
|
|
250
270
|
**Do NOT guess the scheme.**
|
|
@@ -254,7 +274,7 @@ To skip auth entirely, pass \`authHeader: ""\`.`;
|
|
|
254
274
|
authGuidance = `**Auth Scheme:** No \`api.authType\` in workspace config.
|
|
255
275
|
**Where to find the scheme** (check in order):
|
|
256
276
|
1. **OpenAPI spec** \u2192 look at \`securitySchemes\` / \`securityDefinitions\` for \`type: http, scheme: bearer\` or \`type: apiKey\`
|
|
257
|
-
2. **Source code** \u2192 auth middleware
|
|
277
|
+
2. **Source code** \u2192 auth middleware signals: ${AUTH_MIDDLEWARE_PATTERNS_STR}
|
|
258
278
|
3. **Fallback** \u2192 use \`"Bearer"\` only if the project clearly uses JWT or OAuth; otherwise pass \`authScheme: ""\`
|
|
259
279
|
Pass the prefix as \`authScheme\` (e.g., \`"Bearer"\`, \`"Token"\`, \`"Basic"\`). If the API uses raw tokens with no prefix, pass \`authScheme: ""\`.
|
|
260
280
|
**Do NOT guess the scheme.**
|
|
@@ -321,13 +341,14 @@ If an OpenAPI spec exists, ALSO pass \`apiSchema\` — it enables schema-aware v
|
|
|
321
341
|
Without a spec, \`endpointURL\` alone is sufficient.
|
|
322
342
|
${PATH_PARAM_UUID_GUIDANCE}
|
|
323
343
|
|
|
324
|
-
|
|
344
|
+
${CONSUMER_MODE_ENABLED ? `**Contract test mode selection — set based on this service's role at the boundary:**
|
|
325
345
|
- \`providerMode: true\` — this service IS the API; validates the implementation matches the spec.
|
|
326
346
|
Use for new or modified endpoints this codebase owns.
|
|
327
347
|
- \`consumerMode: true\` — this service CALLS another API; validates outbound requests conform to the downstream contract.
|
|
328
348
|
Use when the endpoint's implementation makes HTTP calls to external services (look for fetch/axios/httpx/http.Client/service clients).
|
|
329
349
|
A request-aware mock stands in for the real downstream service — no live dependency needed.
|
|
330
|
-
- **Both modes** (\`providerMode: true, consumerMode: true\`) — same output as omitting both flags. Generates both consumer and provider contract tests. Use when the diff contains BOTH provider signals (new/modified endpoint handlers) AND consumer signals (outbound HTTP client calls to another service)
|
|
350
|
+
- **Both modes** (\`providerMode: true, consumerMode: true\`) — same output as omitting both flags. Generates both consumer and provider contract tests. Use when the diff contains BOTH provider signals (new/modified endpoint handlers) AND consumer signals (outbound HTTP client calls to another service).` : `**Contract tests — provider-only:**
|
|
351
|
+
Only provider-side contract tests are supported. Pass \`providerMode: true\` for new or modified endpoints this codebase owns.`}
|
|
331
352
|
|
|
332
353
|
**For UI tests:**
|
|
333
354
|
1. \`browser_navigate\` to the target URL (from workspace \`api.baseUrl\`)
|