new changes

This commit is contained in:
nilesh.p
2026-10-10 21:57:24 +05:30
parent 56a4b44db8
commit 767127617d
7 changed files with 942 additions and 2 deletions
+26 -1
View File
@@ -37,7 +37,32 @@ exactly those screenshots and API calls, and the report must show them.
## 2. Automate it with Claude Code
From the repo root, use the prompt in
**The quick way — the `qa` tool.** It assembles a *grounded* generation prompt
(your card + the app model + the conventions) so Claude never guesses selectors,
routes, or endpoints:
```bash
npm run qa:index # build the app-model grounding index (do once / after UI changes)
node scripts/qa-tool.mjs list # list scenario cards
node scripts/qa-tool.mjs prompt BOOK-012 # print the grounded prompt — paste into Claude Code
node scripts/qa-tool.mjs run BOOK-012 # run the card's tests + open the evidence report
```
`npm run qa:index` reads the codebase and writes
[`app-model.md`](app-model.md) / `app-model.json`: the routes, page objects,
API endpoints, areas and accounts that actually exist. `prompt` embeds that plus
the card and [`e2e/CLAUDE.md`](../e2e/CLAUDE.md) into one ready-to-paste ask, so
the generated test is correct by construction. `run` wraps `run-tests.ps1` for
the card's layer and opens the evidence.
Generation stays human-in-the-loop: the tool prepares the grounded ask and runs
the result; you (with Claude Code) write and review the test.
**Prefer a screen?** `npm run qa:ui` opens a small local web UI (localhost:9500)
over the same commands: pick a card, copy its grounded prompt, hit **Run tests**,
and see the evidence report inline. Same engine, nicer front door.
**Or by hand:** use the template in
[`generate-test.prompt.md`](generate-test.prompt.md) with your card id. Claude
reads the card and the conventions in [`e2e/CLAUDE.md`](../e2e/CLAUDE.md) and
generates the spec using our page objects and fixtures.
+288
View File
@@ -0,0 +1,288 @@
{
"generatedAt": "2026-10-10T09:15:03.203Z",
"baseUrl": "http://localhost:4200",
"auth": {
"type": "basic",
"tenantHeader": "X-Tenant",
"note": "Credentials verified within the named tenant; take accounts from e2e/fixtures/accounts.ts, never hardcode.",
"accounts": [
{
"name": "SKYLINE_AGENT",
"email": "agent@skyline.test",
"tenant": "skyline",
"role": "agent"
},
{
"name": "SKYLINE_ADMIN",
"email": "admin@skyline.test",
"tenant": "skyline",
"role": "admin"
},
{
"name": "GLOBETROTTER_AGENT",
"email": "agent@globetrotter.test",
"tenant": "globetrotter",
"role": "agent"
},
{
"name": "GLOBETROTTER_ADMIN",
"email": "admin@globetrotter.test",
"tenant": "globetrotter",
"role": "admin"
}
]
},
"routes": [
"/login",
"/dashboard",
"/search",
"/book/:flightId",
"/bookings",
"/bookings/:bookingId"
],
"areas": [
{
"spec": "auth.spec.ts",
"title": "Signing in and sessions"
},
{
"spec": "search.spec.ts",
"title": "Searching for flights"
},
{
"spec": "booking.spec.ts",
"title": "Booking and cancelling"
},
{
"spec": "tenant-isolation.spec.ts",
"title": "Keeping agencies separate"
},
{
"spec": "dashboard.spec.ts",
"title": "Agency dashboard"
},
{
"spec": "simulation.spec.ts",
"title": "Behaviour when things go wrong"
},
{
"spec": "responsive.spec.ts",
"title": "Phones and small screens"
}
],
"api": [
{
"method": "GET",
"path": "/api/session",
"auth": true
},
{
"method": "GET",
"path": "/api/flights",
"auth": true
},
{
"method": "GET",
"path": "/api/flights/{id}",
"auth": true
},
{
"method": "GET",
"path": "/api/airports",
"auth": true
},
{
"method": "GET",
"path": "/api/airlines",
"auth": true
},
{
"method": "GET",
"path": "/api/bookings",
"auth": true
},
{
"method": "POST",
"path": "/api/bookings",
"auth": true
},
{
"method": "GET",
"path": "/api/bookings/{id}",
"auth": true
},
{
"method": "POST",
"path": "/api/bookings/{id}/cancel",
"auth": true
},
{
"method": "GET",
"path": "/api/dashboard",
"auth": true
},
{
"method": "GET",
"path": "/api/health",
"auth": false
},
{
"method": "GET",
"path": "/api/tenants",
"auth": false
}
],
"pageObjects": [
{
"file": "e2e/pages/bookings.page.ts",
"classes": [
"BookingsPage",
"BookingDetailPage"
],
"locators": [
"rows",
"emptyState",
"filterAll",
"filterConfirmed",
"filterCancelled",
"confirmationBanner",
"reference",
"statusBadge",
"travellerRows",
"totalPaid",
"cancelButton",
"confirmCancelButton",
"keepButton",
"cancelledNotice"
],
"methods": [
"goto()",
"settled()",
"rowFor(reference: string)",
"open(reference: string)",
"filterBy(status: 'all' | 'confirmed' | 'cancelled')",
"bookingReference()",
"cancel(expectedReference: string)"
]
},
{
"file": "e2e/pages/checkout.page.ts",
"classes": [
"CheckoutPage"
],
"locators": [
"travellerFieldsets",
"addTraveller",
"contactEmail",
"contactPhone",
"confirm",
"total",
"error",
"seatsLeft"
],
"methods": [
"waitUntilReady()",
"totalAmount()",
"fillTraveller(index: number, traveller: Traveller)",
"fillTravellers(travellers: Traveller[])",
"removeTraveller(index: number)",
"confirmBooking()"
]
},
{
"file": "e2e/pages/dashboard.page.ts",
"classes": [
"DashboardPage"
],
"locators": [
"greeting",
"tiles",
"recentRows",
"topRoutes"
],
"methods": [
"goto()",
"settled()",
"tileValue(label: string)"
]
},
{
"file": "e2e/pages/login.page.ts",
"classes": [
"LoginPage"
],
"locators": [
"tenantSelect",
"email",
"password",
"submit",
"error",
"demoChips"
],
"methods": [
"goto()",
"useDemoChip(account: Account)",
"fill(account: Account, password = account.password)",
"signIn(account: Account)",
"signInExpectingFailure(account: Account, password: string)"
]
},
{
"file": "e2e/pages/search.page.ts",
"classes": [
"FlightCard",
"SearchPage"
],
"locators": [
"root",
"origin",
"destination",
"date",
"cabin",
"travellers",
"searchButton",
"resetButton",
"sort",
"resultSummary",
"results",
"emptyState",
"nextPage",
"prevPage"
],
"methods": [
"fare()",
"seatsLeft()",
"flightNumber()",
"durationMinutes()",
"goto()",
"card(index = 0)",
"settled()",
"searchRoute(from: string, to: string)",
"setTravellers(n: number)",
"sortBy(value: 'price' | 'duration' | 'departure')",
"totalResults()",
"cardForFlight(flightNumber: string)"
]
},
{
"file": "e2e/pages/shell.page.ts",
"classes": [
"ShellPage"
],
"locators": [
"agencyName",
"agencySlug",
"accountName",
"accountMeta",
"signOut"
],
"methods": [
"goToDashboard()",
"goToSearch()",
"goToBookings()",
"signOutAndWait()"
]
}
],
"conventions": "e2e/CLAUDE.md"
}
+78
View File
@@ -0,0 +1,78 @@
# App model — grounding index for test generation
> Generated by `npm run qa:index` from the codebase. Do not edit by hand.
Base URL: `http://localhost:4200` · Auth: basic (header `X-Tenant`)
## Accounts
| const | email | tenant | role |
| --- | --- | --- | --- |
| `SKYLINE_AGENT` | agent@skyline.test | skyline | agent |
| `SKYLINE_ADMIN` | admin@skyline.test | skyline | admin |
| `GLOBETROTTER_AGENT` | agent@globetrotter.test | globetrotter | agent |
| `GLOBETROTTER_ADMIN` | admin@globetrotter.test | globetrotter | admin |
_Password: see `DEMO_PASSWORD` in accounts.ts. Credentials verified within the named tenant; take accounts from e2e/fixtures/accounts.ts, never hardcode._
## Routes
- `/login`
- `/dashboard`
- `/search`
- `/book/:flightId`
- `/bookings`
- `/bookings/:bookingId`
## Areas (spec file → report area)
- `auth.spec.ts` — Signing in and sessions
- `search.spec.ts` — Searching for flights
- `booking.spec.ts` — Booking and cancelling
- `tenant-isolation.spec.ts` — Keeping agencies separate
- `dashboard.spec.ts` — Agency dashboard
- `simulation.spec.ts` — Behaviour when things go wrong
- `responsive.spec.ts` — Phones and small screens
## API endpoints
| method | path | auth |
| --- | --- | --- |
| GET | `/api/session` | required |
| GET | `/api/flights` | required |
| GET | `/api/flights/{id}` | required |
| GET | `/api/airports` | required |
| GET | `/api/airlines` | required |
| GET | `/api/bookings` | required |
| POST | `/api/bookings` | required |
| GET | `/api/bookings/{id}` | required |
| POST | `/api/bookings/{id}/cancel` | required |
| GET | `/api/dashboard` | required |
| GET | `/api/health` | public |
| GET | `/api/tenants` | public |
## Page objects (use these — no raw selectors in specs)
### `e2e/pages/bookings.page.ts` — BookingsPage, BookingDetailPage
- locators: `rows`, `emptyState`, `filterAll`, `filterConfirmed`, `filterCancelled`, `confirmationBanner`, `reference`, `statusBadge`, `travellerRows`, `totalPaid`, `cancelButton`, `confirmCancelButton`, `keepButton`, `cancelledNotice`
- methods: `goto()`, `settled()`, `rowFor(reference: string)`, `open(reference: string)`, `filterBy(status: 'all' | 'confirmed' | 'cancelled')`, `bookingReference()`, `cancel(expectedReference: string)`
### `e2e/pages/checkout.page.ts` — CheckoutPage
- locators: `travellerFieldsets`, `addTraveller`, `contactEmail`, `contactPhone`, `confirm`, `total`, `error`, `seatsLeft`
- methods: `waitUntilReady()`, `totalAmount()`, `fillTraveller(index: number, traveller: Traveller)`, `fillTravellers(travellers: Traveller[])`, `removeTraveller(index: number)`, `confirmBooking()`
### `e2e/pages/dashboard.page.ts` — DashboardPage
- locators: `greeting`, `tiles`, `recentRows`, `topRoutes`
- methods: `goto()`, `settled()`, `tileValue(label: string)`
### `e2e/pages/login.page.ts` — LoginPage
- locators: `tenantSelect`, `email`, `password`, `submit`, `error`, `demoChips`
- methods: `goto()`, `useDemoChip(account: Account)`, `fill(account: Account, password = account.password)`, `signIn(account: Account)`, `signInExpectingFailure(account: Account, password: string)`
### `e2e/pages/search.page.ts` — FlightCard, SearchPage
- locators: `root`, `origin`, `destination`, `date`, `cabin`, `travellers`, `searchButton`, `resetButton`, `sort`, `resultSummary`, `results`, `emptyState`, `nextPage`, `prevPage`
- methods: `fare()`, `seatsLeft()`, `flightNumber()`, `durationMinutes()`, `goto()`, `card(index = 0)`, `settled()`, `searchRoute(from: string, to: string)`, `setTravellers(n: number)`, `sortBy(value: 'price' | 'duration' | 'departure')`, `totalResults()`, `cardForFlight(flightNumber: string)`
### `e2e/pages/shell.page.ts` — ShellPage
- locators: `agencyName`, `agencySlug`, `accountName`, `accountMeta`, `signOut`
- methods: `goToDashboard()`, `goToSearch()`, `goToBookings()`, `signOutAndWait()`