# Generated File Structures — Reference

Read this file when you need to understand the exact TypeScript output format or
run commands. It is not loaded into context automatically.

---

## locators.ts

```typescript
/**
 * Locators: <ScreenName>Page
 * SOURCE: Playwright MCP AX tree snapshot — <app-url>/<route>
 * Stability: data-testid(100) > role+name(90) > id(80) > aria-label(70) > placeholder(60)
 * ⚠ stability:0 entries were NOT reached in the live session — get user sign-off before using.
 */
export const <ScreenName>Locators = {
  // stability: 100 — data-testid, survives refactors
  addUsersButton: {
    selector  : "[data-testid='person_header-button-add_users']",
    intent    : "Add Users button that opens the slide-out panel",
    stability : 100,
  },
  // stability: 90 — aria-role+name, visualIntent: screenshot at assertions
  exportListButton: {
    selector     : "button:has-text('Export List')",
    intent       : "Export List button on the failure summary screen",
    stability    : 90,
    visualIntent : true,
  },
  // ⚠ stability: 0 — NOT VERIFIED: could not reach this screen (reason: <why>)
  // Get user sign-off before using in automation.
  someUnreachedElement: {
    selector  : "[data-testid='TODO']",
    intent    : "...",
    stability : 0,
  },
} as const;

export type LocatorKey = keyof typeof <ScreenName>Locators;
```

---

## {ScreenName}Page.ts

```typescript
import { Page, expect, Download } from '@playwright/test';
import { fixture }                from '@hooks/pageFixture';
import { <ScreenName>Locators }   from './locators';

/**
 * <ScreenName>Page — Three-Layer Self-Healing Page Object
 * Layer 1 (LocatorHealer):  primary → role → label → text → AI Vision → AX tree
 * Layer 2 (TimingHealer):   network-trace drift → auto-adjusted timeouts
 * Layer 3 (VisualIntentChecker): element screenshot diff at key assertions
 * HealingDashboard: http://localhost:7890
 *
 * RULES:
 *   • Never use page.click / page.fill / page.locator directly.
 *   • Access healers via fixture(): fixture().locatorHealer / .locatorTiming / .locatorVisual.
 *     A single instance per scenario lives on the World — do NOT new them up per page object.
 *   • Don't capture a locator into a const just to pass its selector to the healer;
 *     pass the selector from the locators object directly. Use captured locators only
 *     when you actually call `.click()`, `expect()`, or `.waitFor()` on the variable.
 */
export default class <ScreenName>Page {
  private readonly loc = <ScreenName>Locators;
  private readonly page: Page;

  constructor(page?: Page) {
    this.page = page ?? fixture().page;
    // Register this screen's locators with the shared repository so the
    // healer can resolve them by key from anywhere in the scenario.
    Object.entries(this.loc).forEach(([k, v]) =>
      fixture().locatorRepository.register(k, v.selector, v.intent),
    );
  }

  async login(email: string, password: string): Promise<void> {
    await this.page.goto(process.env.APP_BASE_URL!, { waitUntil: 'networkidle' });
    await fixture().locatorHealer.fillWithHealing('emailInput',    this.loc.emailInput.selector,    email,    this.loc.emailInput.intent);
    await fixture().locatorHealer.fillWithHealing('passwordInput', this.loc.passwordInput.selector, password, this.loc.passwordInput.intent);
    await fixture().locatorHealer.clickWithHealing('loginButton',  this.loc.loginButton.selector,             this.loc.loginButton.intent);
    await fixture().locatorTiming.waitForNetworkIdle('login');
  }

  // All interactions go through fixture().locatorHealer — never raw page.click / page.fill
}
```

---

## {feature}.steps.ts

```typescript
import { Given, When, Then, Before } from '@cucumber/cucumber';
import { expect }                    from '@playwright/test';
import { fixture }                   from '@hooks/pageFixture';
import <ScreenName>Page              from '@pages/<screenName>/<ScreenName>Page';

let page_: <ScreenName>Page;

Before(async () => { page_ = new <ScreenName>Page(fixture().page); });

Given('I am logged in to {word} as {string}', async (_app: string, email: string) => {
  await page_.login(email, process.env.APP_PASSWORD!);
});

// Steps reach the healers via fixture() when needed — never new them up per step:
//   const { locatorHealer, locatorTiming } = fixture();
//   await locatorHealer.clickWithHealing(...);
// Page-object methods do the same; only fall back to fixture() in steps when there
// isn't already a page-object method for the action.
//
// Only net-new steps — never re-register steps already in existing files.
```

---

## Run Commands

```bash
ENABLE_SELF_HEALING=true \
HEALING_DASHBOARD_PORT=7890 \
APP_BASE_URL=https://account.myamplify.io \
APP_EMAIL=<email> \
APP_PASSWORD=<password> \
cucumber-js --config=config/cucumber.js -p <feature_profile>

# Smoke only:
cucumber-js --config=config/cucumber.js -p <feature_profile> --tags "@smoke"
```
