@skyramp/mcp 0.3.6 → 0.3.7
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/prompts/code-reuse.js +17 -2
- package/build/prompts/enhance-assertions/sharedAssertionRules.js +1 -1
- package/build/prompts/modularization/ui-test-modularization.js +9 -6
- package/build/prompts/pom-aware-code-reuse.js +1 -1
- package/build/prompts/shared-helper-policy.js +5 -5
- package/build/services/TestGenerationService.js +15 -1
- package/build/tools/code-refactor/assertion-state.d.ts +91 -0
- package/build/tools/code-refactor/assertion-state.js +375 -0
- package/build/tools/code-refactor/codeReuseTool.js +6 -4
- package/build/tools/code-refactor/enhanceAssertionsTool.js +73 -18
- package/build/tools/code-refactor/retrofit-state.d.ts +53 -0
- package/build/tools/code-refactor/retrofit-state.js +162 -0
- package/build/tools/code-refactor/reuse-outcome.d.ts +7 -0
- package/build/tools/code-refactor/reuse-state.d.ts +9 -0
- package/build/tools/code-refactor/reuse-state.js +42 -4
- package/build/tools/code-refactor/utils-verify-gates.js +69 -15
- package/build/tools/executeSkyrampTestTool.js +19 -14
- package/build/tools/submitReportTool.js +81 -8
- package/build/types/AssertionOutcome.d.ts +68 -0
- package/build/types/AssertionOutcome.js +1 -0
- package/build/types/ReuseOutcome.d.ts +16 -0
- package/build/types/TestTypes.d.ts +4 -0
- package/build/types/TestTypes.js +8 -0
- package/build/types/TestbotReport.d.ts +13 -0
- package/build/types/index.d.ts +1 -1
- package/build/utils/AnalysisStateManager.d.ts +20 -7
- package/build/utils/assertion-verify/api-shared-lints.d.ts +5 -0
- package/build/utils/assertion-verify/api-shared-lints.js +315 -0
- package/build/utils/assertion-verify/contract-lints.d.ts +3 -0
- package/build/utils/assertion-verify/contract-lints.js +87 -0
- package/build/utils/assertion-verify/format.d.ts +5 -0
- package/build/utils/assertion-verify/format.js +65 -0
- package/build/utils/assertion-verify/helper-imports.d.ts +6 -0
- package/build/utils/assertion-verify/helper-imports.js +178 -0
- package/build/utils/assertion-verify/index.d.ts +3 -0
- package/build/utils/assertion-verify/index.js +7 -0
- package/build/utils/assertion-verify/integration-lints.d.ts +3 -0
- package/build/utils/assertion-verify/integration-lints.js +36 -0
- package/build/utils/assertion-verify/js-regex-blank.d.ts +1 -0
- package/build/utils/assertion-verify/js-regex-blank.js +153 -0
- package/build/utils/assertion-verify/lint-types.d.ts +33 -0
- package/build/utils/assertion-verify/lint-types.js +57 -0
- package/build/utils/assertion-verify/marker.d.ts +27 -0
- package/build/utils/assertion-verify/marker.js +61 -0
- package/build/utils/assertion-verify/metrics.d.ts +30 -0
- package/build/utils/assertion-verify/metrics.js +341 -0
- package/build/utils/assertion-verify/python-strip.d.ts +6 -0
- package/build/utils/assertion-verify/python-strip.js +75 -0
- package/build/utils/assertion-verify/strip-dispatch.d.ts +19 -0
- package/build/utils/assertion-verify/strip-dispatch.js +42 -0
- package/build/utils/assertion-verify/ui-lints.d.ts +8 -0
- package/build/utils/assertion-verify/ui-lints.js +244 -0
- package/build/utils/assertion-verify/verify.d.ts +61 -0
- package/build/utils/assertion-verify/verify.js +215 -0
- package/build/utils/featureFlags.d.ts +12 -2
- package/build/utils/featureFlags.js +33 -3
- package/build/utils/reportVerification.d.ts +4 -0
- package/build/utils/reportVerification.js +32 -4
- package/build/utils/utils-verify/allow.d.ts +22 -4
- package/build/utils/utils-verify/allow.js +8 -2
- package/build/utils/utils-verify/call-sites.d.ts +40 -1
- package/build/utils/utils-verify/call-sites.js +196 -30
- package/build/utils/utils-verify/importers.d.ts +31 -0
- package/build/utils/utils-verify/importers.js +78 -0
- package/build/utils/utils-verify/index.d.ts +1 -0
- package/build/utils/utils-verify/index.js +1 -0
- package/build/utils/utils-verify/language-spec.d.ts +13 -2
- package/build/utils/utils-verify/language-spec.js +12 -2
- package/build/utils/utils-verify/parse.d.ts +31 -3
- package/build/utils/utils-verify/parse.js +190 -9
- package/build/utils/utils-verify/retrofit-equivalence.d.ts +43 -0
- package/build/utils/utils-verify/retrofit-equivalence.js +218 -0
- package/build/utils/utils-verify/stage.d.ts +6 -0
- package/build/utils/utils-verify/stage.js +12 -2
- package/build/utils/utils-verify/verify.d.ts +54 -4
- package/build/utils/utils-verify/verify.js +224 -12
- package/node_modules/playwright/node_modules/playwright-core/lib/generated/injectedScriptSource.js +1 -1
- package/node_modules/playwright/node_modules/playwright-core/lib/vite/traceViewer/assets/{codeMirrorModule-CZfp96qZ.js → codeMirrorModule-LNgEKtdV.js} +1 -1
- package/node_modules/playwright/node_modules/playwright-core/lib/vite/traceViewer/assets/{defaultSettingsView-gpLo02E0.js → defaultSettingsView-Bwr1eMKC.js} +135 -135
- package/node_modules/playwright/node_modules/playwright-core/lib/vite/traceViewer/{index.Bq1r1URj.js → index.-Id052Lr.js} +1 -1
- package/node_modules/playwright/node_modules/playwright-core/lib/vite/traceViewer/index.html +2 -2
- package/node_modules/playwright/node_modules/playwright-core/lib/vite/traceViewer/{uiMode.VEfqi1qN.js → uiMode.BPopbasy.js} +1 -1
- package/node_modules/playwright/node_modules/playwright-core/lib/vite/traceViewer/uiMode.html +2 -2
- package/node_modules/playwright/node_modules/playwright-core/package.json +1 -1
- package/node_modules/playwright/node_modules/playwright-core/src/generated/injectedScriptSource.ts +1 -1
- package/node_modules/playwright/package.json +1 -1
- package/package.json +2 -2
- package/build/tools/code-refactor/enhance-state.d.ts +0 -49
- package/build/tools/code-refactor/enhance-state.js +0 -109
|
@@ -55,7 +55,7 @@ export async function uploadFileToRFI(page, fileName, tabName) {
|
|
|
55
55
|
These ARE near-duplicates. Same primitive sequence; the only material difference is the \`.first()\` qualifier on the locator. \`tabName\` is unused in the body — recorder noise, drop it. Correct merge:
|
|
56
56
|
|
|
57
57
|
\`\`\`ts
|
|
58
|
-
export async function uploadFileViaChooser(page, fileName) {
|
|
58
|
+
export async function uploadFileViaChooser(page: Page, fileName: string) {
|
|
59
59
|
await page.waitForTimeout(1500);
|
|
60
60
|
const responsePromise = page.waitForResponse("**/api/files/upload**");
|
|
61
61
|
const fileUploadPromise = page.waitForEvent("filechooser");
|
|
@@ -69,6 +69,15 @@ export async function uploadFileViaChooser(page, fileName) {
|
|
|
69
69
|
|
|
70
70
|
Both test files import and call \`uploadFileViaChooser(page, fileName)\`.`;
|
|
71
71
|
}
|
|
72
|
+
/** Every step that authors an import of the shared module states this: STEP 4b
|
|
73
|
+
* merges, STEP 5b seeds, STEP 5c rewires a sibling — and a run with no siblings
|
|
74
|
+
* jumps straight to 5b, so a rule living in one step only is skippable. The
|
|
75
|
+
* verifier is deterministic, so a skipped rule is a failed run. */
|
|
76
|
+
const pyImportRule = (fileName) => {
|
|
77
|
+
const stem = fileName.replace(/\.[^.]+$/, "");
|
|
78
|
+
return `**Import form (Python)**: if the test's directory contains \`__init__.py\`, a bare \`from ${stem} import …\` will NOT resolve when the customer runs pytest from their repo root — the test dies at collection. Write the fallback pair, which works there AND in the Skyramp executor: \`try:\` / \` from .${stem} import …\` / \`except ImportError:\` / \` from ${stem} import …\` — with \`…\` the names this test actually uses, unchanged between the two arms. Without \`__init__.py\` the bare form alone is correct — do not add the fallback.`;
|
|
79
|
+
};
|
|
80
|
+
const pyImportRuleLine = (fileName) => `- ${pyImportRule(fileName)}`;
|
|
72
81
|
export function getCodeReusePrompt(testFile, language, framework, scopedPoms, testType) {
|
|
73
82
|
const lang = language.toLowerCase();
|
|
74
83
|
if (isPomAwareTarget(lang, framework, testType)) {
|
|
@@ -111,6 +120,8 @@ export function getCodeReusePrompt(testFile, language, framework, scopedPoms, te
|
|
|
111
120
|
|
|
112
121
|
skyramp_modularization already ran on ${testFile} and extracted its steps into named helper functions. Those helpers belong in the shared \`${fileName}\` so later tests import them instead of re-defining them.
|
|
113
122
|
|
|
123
|
+
${pyImportRuleLine(fileName)}
|
|
124
|
+
|
|
114
125
|
For each helper function still defined locally in ${testFile} (i.e. not already merged by STEP 4b):
|
|
115
126
|
1. **CREATE** \`${fileName}\` if it does not exist, with the standard header:
|
|
116
127
|
\`\`\`${ext}
|
|
@@ -148,6 +159,8 @@ This step MOVES helpers that skyramp_modularization already created — it does
|
|
|
148
159
|
|
|
149
160
|
**Run this step EVEN IF STEP 3 already satisfied ${testFile} and STEPS 4–5b did nothing.** ${inlineRequestRule}
|
|
150
161
|
|
|
162
|
+
${pyImportRuleLine(fileName)} (this applies to the import you add to the SIBLING file too — its directory is what decides the form.)
|
|
163
|
+
|
|
151
164
|
1. Grep for the other Skyramp-generated test files exactly as in STEP 4 (codegen marker \`${codegenMarker}\`, excluding ${testFile} and \`${fileName}\`).
|
|
152
165
|
2. In each, find every inline request block whose method+path matches a helper now defined in \`${fileName}\`.
|
|
153
166
|
3. For each match: import the helper into that file and replace the block with the call, passing the block's literals (or expressions) as arguments; lift a literal the helper lacks to a defaulted parameter in \`${fileName}\`. Change NOTHING else in that file — its other steps, assertions and order stay verbatim.
|
|
@@ -286,7 +299,8 @@ For each helper defined locally in ${testFile}:
|
|
|
286
299
|
|
|
287
300
|
- **Conservative bias**: a wrong merge breaks both tests; a missed merge leaves a duplicate. When in doubt, do NOT merge.
|
|
288
301
|
- **No "cleanup"**: the merged body must mirror the original primitive sequence — same waits, same correlation patterns, same order. This is not refactoring.
|
|
289
|
-
|
|
302
|
+
${pyImportRuleLine(fileName)}
|
|
303
|
+
- **Typed parameters (TypeScript)**: the merged helper types every parameter (\`page: Page\`, values by their type); add \`import type { Page } from '@playwright/test'\` to the utils file if it is missing. Never \`page: any\` or an untyped \`page\` — that fails a repo that type-checks its tests under \`noImplicitAny\`.
|
|
290
304
|
- **Don't merge with helpers already in \`${fileName}\`**: those are STEP 3's territory. STEP 4b is strictly cross-test merge between sibling Skyramp-generated test files.
|
|
291
305
|
|
|
292
306
|
---
|
|
@@ -336,6 +350,7 @@ ${seedStepBlock}${siblingCallSitesStep}## STEP 6: VERIFY AND VALIDATE
|
|
|
336
350
|
8. **REMOVE** any helper functions that are not being used after refactoring
|
|
337
351
|
9. **NEVER** refactor, reorganize, or restructure existing source test files beyond moving helpers${inlineRequestRule ? " — and, in this flow, replacing an inline request block with a call to the shared helper it duplicates (STEP 3 / STEP 5c)" : ""}
|
|
338
352
|
10. **RUN TESTS** to ensure functionality is preserved after refactoring
|
|
353
|
+
11. **NEVER** drop an assertion or a cross-cutting guard (\`page.on('pageerror'...)\`, error collectors and their final \`expect\`) while moving code — the verify step compares the assertion count against the modularization hand-out and fails on a loss
|
|
339
354
|
|
|
340
355
|
**FINAL REMINDER: DO NOT CREATE HELPER FUNCTIONS FROM SCRATCH**
|
|
341
356
|
${finalReminderScope}
|
|
@@ -206,7 +206,7 @@ Before editing the given file, you must output a \`<thinking>\` block. The aim o
|
|
|
206
206
|
1. Scan the given test file and expected responses based on the test recommendations for the code change tested.
|
|
207
207
|
2. Classify each response first by its response status type and then assign the applicable assertion rules to the response.
|
|
208
208
|
1. Success with body (2xx with a response body): all assertion rules below may apply — echo-back of request fields, computed response fields, array / items validation, and chained values across steps.
|
|
209
|
-
2. Success with no body (204
|
|
209
|
+
2. Success with no body (200/202/204 with an empty body — e.g. logout/cancel/submit action endpoints): assert the status code only. Also apply chained-values rules if a follow-up step uses this response's ID.
|
|
210
210
|
3. Error response (4xx/5xx with a body): assert every error body field with its exact value plus array / items validation on the \`errors[]\` array (exact length + per-item fields + next index undefined). Status code alone is never sufficient when a body is present — for example, also assert \`errors.0.extensions.code == 'INVALID_PAYLOAD'\` and that \`errors.1\` is undefined.
|
|
211
211
|
3. For each in-scope response, output one JSON object using the template below. The output is an array — one object per in-scope response.
|
|
212
212
|
- \`step\`: the HTTP method, path, and response variable name for this request (e.g. \`POST /products → products_POST_response\`).
|
|
@@ -41,13 +41,13 @@ ${p.utilsReuse ? "6" : "5"}. **SKIP to Step ${_plan.labels.VERIFY}** for verific
|
|
|
41
41
|
- Examples: "create product", "fill form", "verify result"
|
|
42
42
|
- Must be cohesive - related operations that achieve one goal${p.utilsReuse
|
|
43
43
|
? `
|
|
44
|
-
- This project consolidates test helpers into a shared utils file
|
|
44
|
+
- This project consolidates test helpers into a shared utils file. Extract a user-action section as a helper when another scenario's test would perform it AS-IS with its own values — sign in, open an entity's form or dialog, add or remove an item, apply a filter or option, dismiss a banner — even if it appears once here. Do NOT extract a section that exists only in this scenario's narrative (a specific multi-step data entry, a check sequence over this test's own values) just because the utils file exists: a single-use helper adds code and hides this scenario's locators behind a name, and the shared file then carries it for every later run.`
|
|
45
45
|
: ""}
|
|
46
46
|
|
|
47
47
|
**Don't extract:**
|
|
48
48
|
- Code less than 5 lines (unless highly repetitive${p.utilsReuse
|
|
49
|
-
? ", or a
|
|
50
|
-
: ""})
|
|
49
|
+
? ", or a cross-test user action per B above — those become helpers regardless of length"
|
|
50
|
+
: ""})${p.utilsReuse ? "\n- A section only this scenario performs AND that appears once in this file — keep it inline (see B above; if it repeats 2+ times in this file, scenario A still applies and it is extracted)" : ""}
|
|
51
51
|
- Navigation sequences (\`navbar-\` clicks, \`page.goto\`)`)
|
|
52
52
|
.step("EXTRACT", "EXTRACT INTO HELPERS", (p) => `**GOLDEN RULE: Copy the original code exactly, only add parameters for values that differ**
|
|
53
53
|
|
|
@@ -123,8 +123,10 @@ async function createOrderWithItems(...) { }
|
|
|
123
123
|
|
|
124
124
|
**RIGHT - Good practices:**
|
|
125
125
|
\`\`\`typescript
|
|
126
|
+
import type { Page } from '@playwright/test';
|
|
127
|
+
|
|
126
128
|
// CORRECT - Extract with parameter, no new logic
|
|
127
|
-
async function addItem(page, name: string) {
|
|
129
|
+
async function addItem(page: Page, name: string) {
|
|
128
130
|
await page.fill("#name", name); // Replace literal with parameter
|
|
129
131
|
}
|
|
130
132
|
|
|
@@ -136,8 +138,8 @@ await addItem(page, "item3");
|
|
|
136
138
|
\`\`\``)
|
|
137
139
|
.step("USE", "CALL HELPERS WITH EXACT SAME VALUES", () => `Use the EXACT same values from the original test when calling helpers.`)
|
|
138
140
|
.step("VERIFY", "VERIFY - CRITICAL CHECKS", (p) => `**BUGS TO AVOID:**
|
|
139
|
-
- [ ] **
|
|
140
|
-
- [ ] **
|
|
141
|
+
- [ ] **EVERY PARAMETER IS TYPED (TypeScript)** - \`page: Page\` with \`import type { Page } from '@playwright/test'\` once at the top of the file; other parameters by their value type (\`name: string\`, \`quantity: number\`). An untyped parameter fails a repo that type-checks its tests under \`noImplicitAny\`. JavaScript files carry no annotations.
|
|
142
|
+
- [ ] **Return types are optional** - inferred returns are fine under strict mode; if you write one, it is the real type, never \`any\`
|
|
141
143
|
- [ ] **ALL field mappings verified** - EVERY \`.fill()\` uses the correct parameter. Check field gets quantity parameter, NOT hardcoded
|
|
142
144
|
- [ ] **No duplicate helpers** - Consolidate similar helpers (e.g., one createOrder vs createSimpleOrder + createOrderWithItems)
|
|
143
145
|
- [ ] **No nested helpers** - Helpers don't call other helpers unnecessarily
|
|
@@ -150,6 +152,7 @@ ${p.utilsReuse
|
|
|
150
152
|
- [ ] Every helper name is a canonical intent (\`openEditOrderForm\`, \`applyDiscount\`) with no scenario value or adjective in it
|
|
151
153
|
- [ ] No helper contains a value assertion (${BROWSER_VALUE_ASSERTIONS}) — each sits in the test body, right after the helper call it verifies
|
|
152
154
|
- [ ] Every selector inside a helper is byte-identical to the original test
|
|
155
|
+
- [ ] The assertion COUNT is unchanged: every \`expect(\` from the original is still in this file or inside a helper it calls, and the \`page.on('pageerror'...)\` guard and its final \`expect\` over the collected errors are still here — cross-cutting guards are never removed (the verify step counts both)
|
|
153
156
|
`
|
|
154
157
|
: ""}
|
|
155
158
|
**DATA INTEGRITY:**
|
|
@@ -559,7 +559,7 @@ For each helper defined locally in ${testFile}:
|
|
|
559
559
|
|
|
560
560
|
- **Conservative bias**: a wrong merge breaks both tests; a missed merge leaves a duplicate. When in doubt, do NOT merge.
|
|
561
561
|
- **No "cleanup"**: the merged body must mirror the original primitive sequence — same waits, same correlation patterns, same order. This is not refactoring.
|
|
562
|
-
- **
|
|
562
|
+
- **Typed parameters (TypeScript)**: the merged helper types every parameter (\`page: Page\`, values by their type); add \`import type { Page } from '@playwright/test'\` if the file lacks it. Never \`page: any\` or an untyped \`page\`.
|
|
563
563
|
- **Don't merge with helpers already in \`${utilsFile}\`**: those are STEP 3's territory. STEP 4b is strictly cross-test merge between sibling Skyramp-generated test files.
|
|
564
564
|
|
|
565
565
|
---
|
|
@@ -45,11 +45,11 @@ export const API_HELPERS = {
|
|
|
45
45
|
sameStep: "the SAME method+path",
|
|
46
46
|
liftableLiterals: API_LIFTABLE,
|
|
47
47
|
structuralNoise: "",
|
|
48
|
-
namingRule: "**NAME** each helper by method + resource ONLY
|
|
48
|
+
namingRule: "**NAME** each helper by method + resource ONLY, written in the naming convention of the language it is emitted in — Python `create_product`, `update_order`, `get_order`, `get_orders_list`; TypeScript/JavaScript `createProduct`, `updateOrder`, `getOrder`, `getOrdersList`. Match the surrounding test file: snake_case helpers in a camelCase TS suite read as foreign code the repo did not write. Scenario adjectives in names are FORBIDDEN (`create_discount_test_product`, `updateOrder50Discount`) — other tests define the same step, and a shared utils file can hold only one canonical name per step. Scenario data belongs in ARGUMENTS, never in the name.",
|
|
49
49
|
assertionRule: `**THE ONLY ASSERTION INSIDE A SHARED HELPER IS THE STATUS CODE**, written in the test's own assertion syntax (Python \`assert response.status_code == expected_status\`; TypeScript/JavaScript \`expect(response.status).toBe(expectedStatus)\`), with the expected status a parameter defaulted to this scenario's expected code. EVERY response-body assertion (echo-backs, computed totals, field checks) stays in the test body, applied to the response the helper returns. ${SHARED_HELPER_WHY}`,
|
|
50
50
|
relocationTarget: "the test body, applied to the response the helper returns",
|
|
51
51
|
moveRule: "",
|
|
52
|
-
namingExample: "E.g. `update_order
|
|
52
|
+
namingExample: "E.g. `update_order` (Python) / `updateOrder` (TypeScript), not `update_order_discount` or `updateOrder50Discount`.",
|
|
53
53
|
oneHelperRule: (fileName) => `One method+path = one helper in \`${fileName}\`.`,
|
|
54
54
|
// The API section below carries its own worked example; the generic
|
|
55
55
|
// Playwright one would teach intent-naming and locator merges to a Python prompt.
|
|
@@ -81,7 +81,7 @@ export const BROWSER_HELPERS = {
|
|
|
81
81
|
kind: "browser",
|
|
82
82
|
sameStep: "the SAME action sequence (same Playwright primitives in the same order, selectors identical)",
|
|
83
83
|
liftableLiterals: BROWSER_LIFTABLE,
|
|
84
|
-
structuralNoise: "`waitForTimeout(...)` lines and the `getValue(playwrightRequestN, ...)` value wrapper around a fill are RECORDER NOISE, not structure — two bodies that differ only by them perform the same step; keep the
|
|
84
|
+
structuralNoise: "`waitForTimeout(...)` lines and the `getValue(playwrightRequestN, ...)` value wrapper around a fill are RECORDER NOISE, not structure — two bodies that differ only by them perform the same step, so merge them; keep the body WITHOUT the `waitForTimeout` lines. A fixed sleep is not a structural wait: it is the one line of the pair that carries no meaning, and in a shared helper every importer pays it on every run. Where a step genuinely needs to wait, wait on the element the next line touches (`toBeVisible`, `toBeEnabled`)",
|
|
85
85
|
namingRule: "**NAME** each helper by the user INTENT it performs, shared across tests: `openEditOrderForm`, `applyDiscount`, `submitProductForm`, `removeOrderItem`. Scenario values or adjectives in names are FORBIDDEN (`applyFiftyPercentDiscount`, `editOrder1Discount`, `createExpensiveProduct`) — another test performs the same action with different data, and a shared utils file holds ONE canonical name per action. Scenario data belongs in ARGUMENTS, never in the name.",
|
|
86
86
|
assertionRule: `**A SHARED HELPER CONTAINS ACTIONS AND STRUCTURAL WAITS ONLY**: clicks, fills, selects, ${BROWSER_STRUCTURAL_WAITS}. EVERY VALUE ASSERTION (${BROWSER_VALUE_ASSERTIONS}) stays in the test body, placed immediately after the helper call it verifies, with its selector verbatim and its expected value unchanged. ${SHARED_HELPER_WHY}`,
|
|
87
87
|
relocationTarget: "the test body, immediately after the helper call, selector verbatim",
|
|
@@ -100,7 +100,7 @@ export async function applyDiscount(page) {
|
|
|
100
100
|
|
|
101
101
|
\`${testFile}\` (current) has:
|
|
102
102
|
\`\`\`ts
|
|
103
|
-
export async function applyTwentyPercentDiscount(page) {
|
|
103
|
+
export async function applyTwentyPercentDiscount(page: Page) {
|
|
104
104
|
await page.getByTestId("edit-order-input-discount").fill("20");
|
|
105
105
|
await page.getByTestId("edit-order-submit-btn").click();
|
|
106
106
|
}
|
|
@@ -109,7 +109,7 @@ export async function applyTwentyPercentDiscount(page) {
|
|
|
109
109
|
These ARE near-duplicates: same primitive sequence, selectors byte-identical, the only difference is the fill literal. Correct merge (name = intent, the literal becomes a parameter passed exactly as it was written — no conversion or other logic added, selectors untouched):
|
|
110
110
|
|
|
111
111
|
\`\`\`ts
|
|
112
|
-
export async function applyDiscount(page, discountPercent) {
|
|
112
|
+
export async function applyDiscount(page: Page, discountPercent: string) {
|
|
113
113
|
await page.getByTestId("edit-order-input-discount").fill(discountPercent);
|
|
114
114
|
await page.getByTestId("edit-order-submit-btn").click();
|
|
115
115
|
}
|
|
@@ -7,13 +7,14 @@ import { extractPathname, resolveQueryParamsForPath, contributesNothing, } from
|
|
|
7
7
|
import { getPathParameterValidationError, OUTPUT_DIR_FIELD_NAME, PATH_PARAMS_FIELD_NAME, QUERY_PARAMS_FIELD_NAME, FORM_PARAMS_FIELD_NAME, validateParams, validatePath, validateRequestData, } from "../utils/utils.js";
|
|
8
8
|
import { getEntryPoint } from "../utils/telemetry.js";
|
|
9
9
|
import { getLanguageSteps } from "../utils/language-helper.js";
|
|
10
|
-
import { TestType } from "../types/TestTypes.js";
|
|
10
|
+
import { ASSERTION_ENHANCEABLE_TEST_TYPES, TestType, MOCK_TYPE, } from "../types/TestTypes.js";
|
|
11
11
|
import { logger } from "../utils/logger.js";
|
|
12
12
|
import { normalizeLanguageParams } from "../utils/normalizeParams.js";
|
|
13
13
|
import { stageGeneratedPaths, resolveOutputDir } from "../utils/gitStaging.js";
|
|
14
14
|
import { isInsideExecutorWorkDir, executorWorkDirRefusal, generationTargets, } from "../utils/executorWorkDir.js";
|
|
15
15
|
import { getTestsRepoDir } from "../utils/AnalysisStateManager.js";
|
|
16
16
|
import { recordReuseHandOff } from "../tools/code-refactor/reuse-state.js";
|
|
17
|
+
import { recordAssertionBaseline } from "../tools/code-refactor/assertion-state.js";
|
|
17
18
|
import { isModularizeFirstTarget } from "../utils/reuseRouting.js";
|
|
18
19
|
/**
|
|
19
20
|
* Derive the framework actually used for generation from the (normalized)
|
|
@@ -106,7 +107,20 @@ export class TestGenerationService {
|
|
|
106
107
|
// The files this call wrote, as codegen reports them — the reuse hand-off is
|
|
107
108
|
// recorded per FILE (SKYR-4220).
|
|
108
109
|
const written = generatedFilesFrom(result);
|
|
110
|
+
// Seed the assertion-enhancement baseline SERVER-SIDE at generation time
|
|
111
|
+
// (same pattern as recordReuseHandOff below): the execute-time
|
|
112
|
+
// verification gate arms even if the agent never calls
|
|
113
|
+
// skyramp_enhance_assertions at all — the last prompt-mediated entry
|
|
114
|
+
// point. Best-effort; no run state means the gate stays open as before.
|
|
109
115
|
const testType = this.getTestType();
|
|
116
|
+
if (params.enhanceAssertions &&
|
|
117
|
+
testType !== MOCK_TYPE &&
|
|
118
|
+
ASSERTION_ENHANCEABLE_TEST_TYPES.includes(testType) &&
|
|
119
|
+
(testType !== TestType.CONTRACT || params.providerMode !== false)) {
|
|
120
|
+
for (const file of written) {
|
|
121
|
+
await recordAssertionBaseline(file, testType, "generation");
|
|
122
|
+
}
|
|
123
|
+
}
|
|
110
124
|
const languageSteps = getLanguageSteps({
|
|
111
125
|
language: params.language || "",
|
|
112
126
|
testType: testType,
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
import { type AssertionBaseline, type AssertionEnhanceType, type AssertionVerifyResult } from "../../utils/assertion-verify/index.js";
|
|
2
|
+
import type { AssertionOutcome, AssertionRecord } from "../../types/AssertionOutcome.js";
|
|
3
|
+
import type { TestType } from "../../types/TestTypes.js";
|
|
4
|
+
import type { RepoCheckout } from "../../utils/reportVerification.js";
|
|
5
|
+
/**
|
|
6
|
+
* Persistence for the assertion-enhancement verification loop, keyed by the
|
|
7
|
+
* spec's CANONICAL absolute path in `UnifiedAnalysisState.assertionOutcomes`.
|
|
8
|
+
* Path keying (not basename, unlike reuseOutcomes — nothing here needs to
|
|
9
|
+
* match report rows) removes the multi-repo basename-collision class outright,
|
|
10
|
+
* and canonicalization closes the alias bypass: `dir//x.spec.ts` and
|
|
11
|
+
* `dir/./x.spec.ts` must hit the same record as `dir/x.spec.ts`.
|
|
12
|
+
*
|
|
13
|
+
* Every write is best-effort: verification must never be able to fail an
|
|
14
|
+
* enhancement that otherwise succeeded, so a missing/unreadable state file
|
|
15
|
+
* degrades to stateless checks and open gates — always with a warning.
|
|
16
|
+
*/
|
|
17
|
+
/** Canonical identity of a test file — all keying and ownership comparisons
|
|
18
|
+
* go through this so a non-normalized agent-supplied path cannot fork or
|
|
19
|
+
* bypass a record. realpath also collapses symlinked directories; a path
|
|
20
|
+
* that does not (yet) exist falls back to plain resolution. */
|
|
21
|
+
export declare function canonicalTestPath(testFile: string): string;
|
|
22
|
+
/** A record only counts for `testFile` when its stored path canonicalizes to
|
|
23
|
+
* the same spec — the single home for the ownership invariant (previously
|
|
24
|
+
* copied in baselineFrom, recordAssertionVerdict, and the enhance tool). */
|
|
25
|
+
export declare function trustedRecord(record: AssertionRecord | undefined, testFile: string): AssertionRecord | undefined;
|
|
26
|
+
/** One spec's record, or undefined when there is no run state / no record. */
|
|
27
|
+
export declare function readAssertionRecord(testFile: string, explicitStateFile?: string): Promise<AssertionRecord | undefined>;
|
|
28
|
+
/**
|
|
29
|
+
* Snapshot the file at instruction hand-out time — the baseline verify measures
|
|
30
|
+
* against. Keep-first rule: while no Passed verdict exists for the same file and
|
|
31
|
+
* enhanceType, a repeat hand-out does NOT re-baseline (re-calling the tool after
|
|
32
|
+
* half-editing must not shrink the required delta). A hand-out after a Passed
|
|
33
|
+
* verdict, or with a different enhanceType, starts a new enhancement round.
|
|
34
|
+
*/
|
|
35
|
+
export declare function recordAssertionBaseline(testFile: string, testType: TestType, enhanceType: AssertionEnhanceType, explicitStateFile?: string): Promise<void>;
|
|
36
|
+
/** The baseline verify measures against, from a record; undefined when the
|
|
37
|
+
* record carries no snapshot or belongs to a different canonical path. */
|
|
38
|
+
export declare function baselineFrom(record: AssertionRecord | undefined, testFile: string): AssertionBaseline | undefined;
|
|
39
|
+
/** Record what one verify pass established. The verdict is written to the
|
|
40
|
+
* state file the record actually LIVES in (not blindly to the explicit path —
|
|
41
|
+
* a mismatched `stateFile` would otherwise split verdict from baseline).
|
|
42
|
+
* Never writes onto a record owned by a different canonical path, and never
|
|
43
|
+
* fails silently. */
|
|
44
|
+
export declare function recordAssertionVerdict(testFile: string, result: AssertionVerifyResult, explicitStateFile?: string): Promise<void>;
|
|
45
|
+
/**
|
|
46
|
+
* Deterministic execute-time verification: skyramp_execute_test calls this
|
|
47
|
+
* itself, so the check runs whether or not the agent ever called `verify: true`
|
|
48
|
+
* — a prose instruction can be ignored, a server-side call cannot. Returns the
|
|
49
|
+
* feedback text when the assertions are insufficient (the caller returns it
|
|
50
|
+
* instead of executing), or undefined to proceed.
|
|
51
|
+
*
|
|
52
|
+
* Self-clearing: the checks run on the file's CURRENT content, so fixing the
|
|
53
|
+
* assertions and re-executing passes — no separate verify call is required.
|
|
54
|
+
* Skipped once a verdict has PASSED: sanctioned later rewrites (a `.raw.bak`
|
|
55
|
+
* restore during execution fix-up) would otherwise false-fail the differential
|
|
56
|
+
* gates. Modularization needs no special handling: the shared-helper policy
|
|
57
|
+
* keeps every value assertion in the test body, so the baseline comparison
|
|
58
|
+
* stays valid across it. Fails open on no baseline and unreadable state/file.
|
|
59
|
+
*/
|
|
60
|
+
export declare function assertionFeedbackForExecution(testFile: string, explicitStateFile?: string): Promise<string | undefined>;
|
|
61
|
+
/**
|
|
62
|
+
* Count one execution of this spec (called by skyramp_execute_test after its
|
|
63
|
+
* gates pass, best-effort). Creates a minimal record when none exists so an
|
|
64
|
+
* executed-but-never-enhanced spec is still visible to the report cross-check.
|
|
65
|
+
*/
|
|
66
|
+
export declare function recordAssertionExecution(testFile: string, testType: TestType, explicitStateFile?: string): Promise<void>;
|
|
67
|
+
/** A repo checkout as listRepoCheckouts returns it — the canonical type from
|
|
68
|
+
* reportVerification, re-exported (a local re-declaration had drifted:
|
|
69
|
+
* `root` weakened to optional, forcing a redundant guard at each use). */
|
|
70
|
+
export type RepoCheckoutRef = RepoCheckout;
|
|
71
|
+
/** The record for a report row, matched by the canonical-path keys' basenames
|
|
72
|
+
* and then GUARDED: the record's testType must match the row's, and when the
|
|
73
|
+
* run's checkouts are known, the record's path must live in the row's repo
|
|
74
|
+
* (primary when the row names none). Without the guards, a basename shared
|
|
75
|
+
* across repos published one spec's proof-of-work under another's name — and
|
|
76
|
+
* the victim was exactly the never-enhanced spec the field exists to expose.
|
|
77
|
+
* Ambiguity after filtering still fails open: wrong attribution is worse
|
|
78
|
+
* than omission. */
|
|
79
|
+
export declare function findAssertionRecordByFileName(outcomes: Record<string, AssertionRecord>, row: {
|
|
80
|
+
fileName: string;
|
|
81
|
+
testType?: string;
|
|
82
|
+
repository?: string;
|
|
83
|
+
}, checkouts: RepoCheckoutRef[]): AssertionRecord | undefined;
|
|
84
|
+
/**
|
|
85
|
+
* Report-facing proof-of-work summary re-derived from the spec as delivered
|
|
86
|
+
* NOW — final metrics come from the file, not from verify-time state, so
|
|
87
|
+
* post-verify edits cannot publish stale counts. Always returns the status
|
|
88
|
+
* fields (verification, declinedReason, executionCount — they come from the
|
|
89
|
+
* record); the metric fields are omitted when the file cannot supply them.
|
|
90
|
+
*/
|
|
91
|
+
export declare function rederiveAssertionOutcome(record: AssertionRecord): Promise<AssertionOutcome>;
|