import { generateSkyrampHeader, SKYRAMP_UTILS_HEADER } from "../utils/utils.js";
const TS_UTILS_FILE = "skyrampUtils.ts";
const TS_CODEGEN_MARKER = "// Generated by Skyramp v";
export function getPomAwareCodeReusePrompt(testFile) {
    return `# POM-AWARE CODE REUSE — TYPESCRIPT/PLAYWRIGHT

## COMPLIANCE RULES — READ THIS FIRST, APPLY TO EVERY STEP

You are about to execute a multi-step process. These rules govern your behavior for the entire session:

1. **Read the ENTIRE guidance before taking any action**
2. **Execute steps in order** — never skip, combine, or reorder steps
3. **Output all intermediate artifacts** (POM catalog, mapping table) to chat before proceeding to the next step
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
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.
6. **Never pause for user input** between steps — execute the full sequence autonomously from start to finish.

If you catch yourself about to skip a step, stop and execute it.

---

## DEFINITIONS

**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.

This is NOT the same as SkyrampUtils consolidation. Do NOT create a SkyrampUtils file on the POM path.

---

## STEP 1: DETECT POM CLASSES IN THE PROJECT

Use the Glob tool to search for POM files starting from the directory containing \`${testFile}\`:
- Pattern 1: \`**/pageobjects/**/*.ts\`
- Pattern 2: \`**/page-objects/**/*.ts\`
- Pattern 3: \`**/pages/**/*.ts\`
- Pattern 4: \`**/*.page.ts\`
- Pattern 5: \`**/*Page.ts\`
- Pattern 6: \`**/pom/**/*.ts\`

Exclude any file with \`test\`, \`spec\`, \`.test.\`, or \`.spec.\` in its name.
Also exclude \`node_modules\`.

**Decision:**
- Zero matches found → **SKIP TO STEP 6 (FALLBACK)**
- One or more matches found → continue to STEP 2

---

## STEP 2: READ AND CATALOG ALL REUSABLE MODULES

**Step 2a: Check for existing catalog (MANDATORY FIRST STEP)**

Check if \`skyramp-pom-catalog.md\` exists in the same directory as \`tsconfig.json\`:
- **If it exists:** Read it and verify it starts with \`<!-- skyramp-pom-catalog: auto-generated -->\`
  - Valid → Output: *"Using existing POM catalog from /path/to/skyramp-pom-catalog.md"* and **skip to STEP 2b**
  - Invalid → Continue to full catalog generation below
- **If it doesn't exist:** Continue to full catalog generation below

---

**Step 2 (full catalog generation — only if catalog doesn't exist or is invalid):**

Before writing a single line of refactored code, do the following:

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.

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.

3. **Read every POM file found in STEP 1** — For each file, document:
   - Class name, import path (using tsconfig aliases), and what page/component it represents
   - Iframe selector used in the constructor (e.g. \`page.frameLocator('iframe#asset-list')\`), or "top-level page" if no iframe
   - Every property (locator): name and its exact selector string from the constructor assignment
   - Every method: name, parameters, what action it performs
   - **For each method, explicitly check and record:**
     - Any \`waitForTimeout\` or \`sleep\` calls inside the method body and their durations
     - Any \`waitForResponse\` calls inside the method body and their URL patterns
     - 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**
   - Flag methods as **timing-aware**, **network-aware**, or **wrapping** as appropriate — all three are high-value substitution targets

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.

**BLOCKING: Do not proceed to STEP 2b until you have written this file and confirmed the path in a single output line:**
*"POM catalog written to /path/to/skyramp-pom-catalog.md"*

**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.

Use this exact format for the file:

\`\`\`markdown
<!-- skyramp-pom-catalog: auto-generated by skyramp_reuse_code -->

## ClassName
- **Import:** \`@pageobjects/path/to/file\`
- **Iframe:** \`iframe#selector\` | top-level page
- **Properties:**
  - \`propertyName\` — selector: \`exact-selector-string\`
- **Methods:**
  - \`methodName(param1, param2)\` — description; ⏱ timing-aware: waitForTimeout(Xms) + sleep(Ys) | 🌐 network-aware: waitForResponse('**/pattern**') | (omit tags if neither)
\`\`\`

---

## STEP 2b: BUILD AND OUTPUT A MAPPING TABLE

**This step is MANDATORY. Do not write any code until it is complete.**

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.

### Matching criteria

Use **selector-string evidence** as the primary signal — not naming similarity or context guessing:

**PREFERRED** (highest priority — always substitute, even if raw test has explicit waits or correlations for the same sequence):
- 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
- 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
- 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

**HIGH confidence** (substitute in refactored test):
- 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("資産登録")')\`
- A POM method's internal steps clearly and completely cover the entire raw action sequence with no gaps
- **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.

**LOW / UNCERTAIN confidence** (leave inline):
- You can only infer the match from naming or context alone — no selector string overlap AND no convention evidence from existing refactored tests
- The POM property's selector is different, no existing test uses this method for this action, and you are not sure the elements match

**UNMAPPED** (leave inline):
- No POM property or method found that covers this locator or action

### Iframe context gate — apply BEFORE assigning any confidence

Before considering any POM match, verify the iframe context matches:
- Identify the iframe the raw locator uses (e.g., \`frameLocator('iframe#asset-list')\`)
- Read the POM class constructor to find which iframe it scopes to
- If the iframe IDs differ → the match is UNMAPPED regardless of selector similarity
- **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\`.
- When filtering POM candidates for a raw action, first narrow to classes whose iframe matches, then look for methods within those classes only.

### Mapping table format

Output the table in this format:

\`\`\`
| Raw locator / action | POM class | Property / Method | Selector evidence | Confidence |
|---|---|---|---|---|
| goto + fill email/password + click login | LoginPage | login(email, password) | method is timing-aware (waitForTimeout+sleep) covering login sequence | PREFERRED |
| frameLocator('iframe#asset-list').locator('button:has-text("資産登録")') | AssetListPage | assetEntryBtn | iframe.locator('button:has-text("資産登録")') | HIGH |
| page.locator('input[name="email"]').fill(...) | LoginPage | emailInput | getByPlaceholder('メールアドレス（半角）') | LOW |
| frameLocator('iframe#wf-input').locator('input[name="changeQty"]') | — | — | No match | UNMAPPED |
\`\`\`

### Rules after completing the table

- **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
- **HIGH entries MUST be substituted** — use the POM method or property; do NOT write an inline locator
- **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.
- **Do NOT upgrade LOW or UNCERTAIN** because the substitution seems reasonable
- **Conservative default**: an incorrect substitution that breaks the test is worse than leaving something inline — when in doubt, leave it inline
- If the table has ZERO PREFERRED or HIGH entries → **SKIP TO STEP 6 (FALLBACK)**
- If AT LEAST ONE PREFERRED or HIGH entry exists → continue to STEP 3

---

## STEP 3: ANALYZE AND APPLY TRANSFORMATION RULES

Read through \`${testFile}\` and apply the following rules:

### 3a. Skyramp Infrastructure — PRESERVE EXACTLY, NO EXCEPTIONS

The following must appear in the refactored test exactly as they are in the original. Never remove, alter, or move them:

\`\`\`typescript
// Generated by Skyramp ...
// Command: skyramp generate ui rest \\
//     ...
\`\`\`

\`\`\`typescript
import { test } from '@playwright/test';
import { newSkyrampPlaywrightPage, expect } from '@skyramp/skyramp';

test.use({
    viewport: { width: 1450, height: 900 },
});

// Inside the test body:
test.setTimeout(1000000);
page.setDefaultTimeout(pageTimeout);
const skyrampPage = newSkyrampPlaywrightPage(page, testInfo);
\`\`\`

> **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.

### 3b. Test Data — Extract to Named Constants

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.

**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:
\`\`\`typescript
const uniqueSuffix = Date.now();
const workflowName = "MyWorkflow_" + uniqueSuffix;
\`\`\`

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.

### 3c. Module Instantiation — Declare All Together at the Top of the Test Body

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).

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.

### 3d. Import Organization — Match the Repository's Conventions

Match the import grouping and labeling style of the existing test files exactly:
- Keep Skyramp imports (\`@playwright/test\` and \`@skyramp/skyramp\`) together and unchanged
- Group POM imports separately, consistent with the repo's pattern
- Use whatever path alias convention the repository defines — never introduce a new path style

### 3e. \`waitForTimeout\` Calls

**Always prefer the POM method over preserving a raw wait:**
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.

- Call the POM method
- Drop any \`waitForTimeout\` that falls *inside* the method's covered sequence
- Add a comment: \`// raw waitForTimeout(Xms) dropped — POM method used instead\`

**How to determine inside vs outside a method's scope:**
- **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.
- **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.

**Preserve waits that fall between POM calls:**
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(...)\`.

### 3f. \`waitForResponse\` Network Intercepts

**If the action maps to a PREFERRED method (network-aware):**
- Call the POM method — its internal \`waitForResponse\` handles the correlation
- Drop the raw test's entire \`const responsePromiseN / await action / const responseN\` block
- Add a comment: \`// method handles network correlation internally — raw responsePromise dropped\`

**If no POM method is network-aware for this action:**
- Leave the \`waitForResponse\` block inline as-is. Do NOT create a new POM method.

**Side-effect endpoints — always remove regardless:**
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:
- \`**/live/**\` — live chat / messaging (e.g. \`getAFTalkRelations\`)
- \`**/notification/**\`, \`**/notifications/**\`
- \`**/analytics/**\`, \`**/tracking/**\`, \`**/telemetry/**\`

\`\`\`typescript
// Before — remove this
const responsePromise1 = skyrampPage.waitForResponse("**/live/getAFTalkRelations**");
await someAction.click();
const response1 = await responsePromise1;

// After — just the action
await someAction.click();
\`\`\`

### 3g. Iframe and Frame Context — Match Constructor Expectations

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.

**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.

### 3h. Dynamic Locators — Prefer Positional Module Methods

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.

**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.

### 3i. Checkbox Interactions — Follow Repo Pattern

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.

### 3j. Screenshot Assertions — Preserve Exact Position

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\`

---

## STEP 4: WRITE THE REFACTORED TEST

Using your catalog from STEP 2 and analysis from STEP 3, produce the refactored test.

**CRITICAL — Consult the mapping table for every single action before writing it:**
Before writing each line of refactored code, look up that action in your STEP 2b mapping table:
- If it is marked **PREFERRED** → use the POM method and drop any raw waits/correlations inside its scope. This is the highest-value substitution.
- If it is marked **HIGH** → you MUST use the POM method or property. Do NOT write an inline locator.
- If it is marked **LOW / UNCERTAIN / UNMAPPED** → write it inline exactly as in the original test.

Do not skip this lookup for any action. The mapping table is your source of truth — not the raw test's locators.

## STEP 4b: VERIFY AGAINST MAPPING TABLE

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.

**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 -----*/\`.

**Modularization Priority:**
1. **Existing module method** — always preferred; replaces entire action sequences
2. **Existing module property** — use directly in \`expect(...)\` or chained calls when no method wraps it
3. **Inline raw locator** — only when no modular equivalent exists; keep as-is

**Assertions:**
- Module locator assertions: \`expect(module.someLocator).toBeVisible()\`
- Screenshot assertions (\`toHaveScreenshot\`): always keep inline; if mid-method sequence, inline that method's steps around it
- Page readiness checks: use module methods such as \`pageToBeVisible()\` if they exist

**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.

---

## STEP 5: WRITE THE OUTPUT

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.

Do not output a section breakdown, modularization summary, or any analysis to the chat. Perform all analysis internally.

**Do NOT run the test. Do NOT call any other tools. Your work is complete once the file is written.**

---

## STEP 6: FALLBACK — STANDARD SKYRAMPUTILS PATH

**You should only reach this step if:**
- No POM files were detected in STEP 1, OR
- Zero locators/actions in the test mapped to any existing module in STEP 2

Inform the user: *"No POM layer found in this project — falling back to standard SkyrampUtils code reuse."*

Then follow the standard SkyrampUtils process below exactly:

---

# CODE REUSE - 6 CLEAR STEPS
**CRITICAL WARNING: VIOLATION OF THESE RULES WILL RESULT IN ERROR**

## DEFINITIONS (READ FIRST):
**What is a "Helper Function"?**
- A helper function is a STANDALONE, DISTINCT function that is ALREADY DEFINED in the code
- It has a clear function signature (e.g., \`async function createProduct(page, data) { ... }\`)
- It is NOT just repetitive inline test code or patterns
- Example of helper function: \`async function loginUser(page, email, password) { ... }\`
- Example of NOT a helper function: Repetitive \`await page.click()\` statements scattered in test

**What is "Repetitive Code Pattern"?**
- Sequential test steps that look similar but are NOT extracted into standalone functions
- Example: Multiple sequences of \`await page.fill()\`, \`await page.click()\` directly in test body
- These patterns should NOT be extracted during code reuse - that's what modularization is for

** CODE REUSE ≠ REFACTORING**
- Code reuse = Finding EXISTING helper functions and reusing them
- Refactoring = Creating NEW helper functions from patterns (handled by modularization tool)

## STEP 1: READ TEST FILE
Read ${testFile} and look for ALREADY DEFINED helper functions (not just repetitive patterns).

## STEP 2: FIND EXISTING UTILS
Use the Grep tool to search for files containing "${SKYRAMP_UTILS_HEADER}":
- Pattern: "${SKYRAMP_UTILS_HEADER}"
- Type: "ts"
- Output mode: "files_with_matches"
**CRITICAL: Only look for util files with header ${SKYRAMP_UTILS_HEADER}** - exclude test files (files with "test", "spec", ".test.", ".spec." in the name).
Read the matching non-test files and check if any helpers can be reused in ${testFile}.

## STEP 3: USE EXISTING HELPERS FROM UTILS SOURCE FILES FOUND IN STEP 2
If helpers exist in util file that can be reused in ${testFile} without modifying them:
- Import them into ${testFile}
- Remove any duplicate code in ${testFile}
- Test that ${testFile} still works without any errors and logical is same as original test file.

## STEP 4: FIND LOCAL HELPERS IN OTHER SKYRAMP-GENERATED TEST FILES

You are looking for sibling test files generated by Skyramp's codegen. They start with the comment line \`${TS_CODEGEN_MARKER}…\` (note: the version suffix is what distinguishes them from the SkyrampUtils header). The substring \`${SKYRAMP_UTILS_HEADER}\` is contained in that line, so:

Use the Grep tool to search for other test files containing "${SKYRAMP_UTILS_HEADER}":
- Pattern: "${SKYRAMP_UTILS_HEADER}"
- Type: "ts"
- Output mode: "files_with_matches"

**CRITICAL: Exclude ${testFile} from the results** — only look at OTHER files, not the current file.

The result will mix two kinds of files:
- **Utils file(s)** — already handled by STEP 2/STEP 3.
- **Other Skyramp-generated test files** — these are the targets of STEP 4 / 4b / 5. Identify them by the \`${TS_CODEGEN_MARKER}\` codegen marker on line 1 (NOT the bare utils header).

**STOP HERE IF NO OTHER SKYRAMP-GENERATED TEST FILES FOUND**
**IF NO OTHER SKYRAMP-GENERATED TEST FILES ARE FOUND, SKIP TO STEP 6 - DO NOT CREATE ANY UTILS FILES.**

If other Skyramp-generated test files are found, read those files and look for ALREADY DEFINED helper functions with clear function signatures.

**How to identify helper functions in other test files:**
 HELPER FUNCTION (move to utils):
   - Has function signature: \`async function doSomething(params) { ... }\`
   - Can be called multiple times with different parameters
   - Example: \`async function fillProductForm(page, product) { ... }\`

NOT A HELPER FUNCTION (do not extract):
   - Just repetitive inline test code in test body
   - No function definition/signature
   - Example: Multiple \`await page.getByTestId("xyz").click()\` directly in test

**IF OTHER TEST FILES ONLY CONTAIN REPETITIVE PATTERNS (NO ACTUAL HELPER FUNCTIONS), SKIP TO STEP 6**
**IF OTHER SKYRAMP-GENERATED TEST FILES ARE BYTE-FOR-BYTE IDENTICAL TO ${testFile}, SKIP TO STEP 6** (note: helpers being NEAR-duplicates does NOT count as identical — proceed to STEP 4b)

## STEP 4b: PARAMETERIZE NEAR-DUPLICATE HELPERS ACROSS SKYRAMP-GENERATED TEST FILES

**This step handles the cross-test case: ${testFile} AND another Skyramp-generated test file each define their own helper that does the SAME shape of work with only literal-value differences. These should become ONE parameterized helper in \`${TS_UTILS_FILE}\`.**

### Definition: "near-duplicate" helpers

Two helpers (one in ${testFile}, one in another Skyramp-generated test file found in STEP 4) are near-duplicates when **ALL** of the following are true:

1. **Same Playwright primitive sequence** — they call the same Playwright APIs in the same order (e.g. both: \`getByText(...).click()\` → \`waitForEvent("filechooser")\` → \`fileChooser.setFiles(...)\` → \`waitForResponse(...)\`).
2. **Differences are limited to literal values** — selectors, role names, test IDs, URL patterns, button labels, or other string/number literals. NO structural difference (no extra step in one and not the other, no different control flow, no extra arguments that change behavior).
3. **The differing literals are trivially liftable to parameters** — no logic needs to be invented to derive them.

If any of the three is false → NOT near-duplicates. Leave them alone.

### Worked example

\`flow-1.spec.ts\` has:
\`\`\`ts
export async function upload_translation_file(page, fileName) {
    await page.waitForTimeout(1500);
    const responsePromise1 = page.waitForResponse("**/api/files/upload**");
    const fileUploadPromise0 = page.waitForEvent("filechooser");
    await page.getByText("Choose a file").first().click();
    const fileChooser0 = await fileUploadPromise0;
    await fileChooser0.setFiles(fileName);
    const response1 = await responsePromise1;
    await page.waitForTimeout(1500);
}
\`\`\`

\`${testFile}\` (current) has:
\`\`\`ts
export async function uploadFileToRFI(page, fileName, tabName) {
    await page.waitForTimeout(1500);
    const responsePromise1 = page.waitForResponse("**/api/files/upload**");
    const fileUploadPromise0 = page.waitForEvent("filechooser");
    await page.getByText("Choose a file").click();
    const fileChooser0 = await fileUploadPromise0;
    await fileChooser0.setFiles(fileName);
    const response1 = await responsePromise1;
    await page.waitForTimeout(1500);
}
\`\`\`

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:

\`\`\`ts
export async function uploadFileViaChooser(page, fileName) {
    await page.waitForTimeout(1500);
    const responsePromise = page.waitForResponse("**/api/files/upload**");
    const fileUploadPromise = page.waitForEvent("filechooser");
    await page.getByText("Choose a file").first().click();
    const fileChooser = await fileUploadPromise;
    await fileChooser.setFiles(fileName);
    await responsePromise;
    await page.waitForTimeout(1500);
}
\`\`\`

Both test files import and call \`uploadFileViaChooser(page, fileName)\`.

### Procedure

For each helper defined locally in ${testFile}:

1. **Scan** each OTHER Skyramp-generated test file from STEP 4 for a helper that matches the near-duplicate criteria above.
2. **If no match** → skip this helper, move on.
3. **If exactly one match** → parameterize and merge:
   - **Name**: pick a name describing INTENT, not the originating test. E.g. \`uploadFileViaChooser\`, not \`upload_translation_file\` or \`uploadFileToRFI\`.
   - **Parameters**: the literal values that differ across the two call sites. Drop any parameter that is unused in the body (recorder noise).
   - **Body**: the common primitive sequence with the differing literals replaced by parameters.
   - **Forbidden**: do NOT add if/else, ternaries, defaults, or any logic to bridge the differences. If a single body can't cover both call sites without new logic, they are NOT near-duplicates — leave them alone.
4. **If multiple matches** → process each pair independently. Merge every pair that clearly meets all three near-duplicate criteria. Skip any pair you're uncertain about — a missed merge is recoverable, a wrong merge breaks both tests.
5. **Write** the merged helper into \`${TS_UTILS_FILE}\` (create the file with the standard header if it doesn't exist; same header as STEP 5 below).
6. **Delete** both original helpers from their test files.
7. **Import** the merged helper from \`${TS_UTILS_FILE}\` into both test files.
8. **Replace** the two original call sites with calls to the merged helper, passing the values that were originally hardcoded or argument-passed in each test.

### Hard rules

- **Conservative bias**: a wrong merge breaks both tests; a missed merge leaves a duplicate. When in doubt, do NOT merge.
- **No "cleanup"**: the merged body must mirror the original primitive sequence — same waits, same correlation patterns, same order. This is not refactoring.
- **Preserve typing style**: if originals used \`page: any\` (or untyped \`page\`), keep that convention.
- **Don't merge with helpers already in \`${TS_UTILS_FILE}\`**: those are STEP 3's territory. STEP 4b is strictly cross-test merge between sibling Skyramp-generated test files.

---

## 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

**This step covers the EXACT-DUPLICATE / move-as-is case. STEP 4b already handled the near-duplicate case — do NOT re-process helpers that STEP 4b already merged.**

**ONLY PROCEED WITH STEP 5 IF ALL CONDITIONS ARE MET:**
- You found OTHER test files in STEP 4 (not just ${testFile})
- Those test files contain ACTUAL HELPER FUNCTIONS with function signatures (not just repetitive patterns)
- The helper functions are ALREADY IMPLEMENTED and working in those OTHER test files
- The helper in the OTHER test file is byte-for-byte usable in ${testFile} without any modification — if a helper would need parameterizing to fit both call sites, it is a near-duplicate and STEP 4b owns it; do NOT process those helpers here

**IF ANY CONDITION IS NOT MET, SKIP TO STEP 6 - DO NOT CREATE ANY UTILS FILES.**

**CRITICAL:** For helpers found in STEP 4:
1. **CREATE** \`${TS_UTILS_FILE}\` file if it doesn't exist with the following header:
   \`\`\`ts
   ${generateSkyrampHeader("typescript")}
   \`\`\`
**CRITICAL: IF \`${TS_UTILS_FILE}\` is already big, create a new file with a different name and add the header to the new file.**
2. **COPY** those local helper functions from original test source files to \`${TS_UTILS_FILE}\` without modifying them
3. **DELETE** those local helper functions from test source files (REMOVE THEM COMPLETELY) WITHOUT ANY OTHER CHANGES.
4. **IMPORT** those local helper functions from \`${TS_UTILS_FILE}\` back into test source files WITHOUT MAKING ANY OTHER CHANGE.
5. **IMPORT** those local helper functions from \`${TS_UTILS_FILE}\` into ${testFile}
6. **REPLACE** duplicate code in ${testFile} with calls to imported helper functions WITHOUT MAKING ANY OTHER CHANGE.

**CRITICAL: DO NOT CREATE UNNECESSARY HELPER FUNCTIONS**
**DO NOT CREATE HELPERS BASED ON PATTERNS IN THE CURRENT FILE**

## STEP 6: VERIFY AND VALIDATE
1. **BEFORE** making any changes, read the original test file and identify ALL helper functions
2. **AFTER** copying helpers to utils, read the original file again to confirm helpers are GONE
3. **VERIFY** that helper functions are NO LONGER in original test files
4. **VERIFY** that the original test files only have import statements and no duplicate code
5. **VERIFY** that both original and new test files import from utils and use the helper functions
5a. **VERIFY STEP 4b OUTCOMES** — for every near-duplicate pair you merged in STEP 4b: the merged helper exists in \`${TS_UTILS_FILE}\` exactly once; BOTH original helpers are deleted from their respective test files; BOTH test files import the merged helper from \`${TS_UTILS_FILE}\`; BOTH original call sites are replaced with calls to the merged helper. If any of these is false, fix it before finishing.
6. **VERIFY** that no unnecessary helper functions were created
7. **VERIFY** that all helper functions in utils are actually imported and used in the test files
8. **REMOVE** any helper functions that are not being used after refactoring
9. **NEVER** refactor, reorganize, or restructure existing source test files beyond moving helpers
10. **Do NOT run the test** — your work is complete once the file is written

**Do NOT call skyramp_modularization.** Code reuse is complete. The test file has been refactored in place — no further tools should be called.

SUMMARIZE THE CODE REUSE PROCESS AND THE RESULTS.
`;
}
