# AST Analysis
`playwright-ast-coverage` uses the Oxc parser to inspect JavaScript,
TypeScript, JSX, and TSX source. It does not run tests or execute project code.
Only static forms described here are detected.
## Playwright Config
The tool parses root-level `playwright*.config.*` files discovered under
`--root`, paths from analyzer config `playwrightConfig`, or repeated
`--playwright-config` options.
Supported config shapes:
```ts
export default { testDir: './tests' }
export default defineConfig({ testDir: './tests' })
const config = { testDir: './tests' }
export default config
module.exports = { testDir: './tests' }
module.exports = defineConfig({ testDir: './tests' })
```
It can follow top-level object bindings used as the exported config, the
`defineConfig(...)` argument, or the `use` object. Cyclic bindings are ignored.
Supported literal fields:
- `name`
- `testDir`
- `testMatch`
- `testIgnore`
- `baseURL` and `use.baseURL`
- `testIdAttribute` and `use.testIdAttribute`
- `projects`, when entries are object literals
`testDir`, `baseURL`, `testIdAttribute`, and `name` must be string literals or
expression-free template literals. `testMatch` and `testIgnore` may be a string
literal or an array of string literals. Regular-expression `testMatch` and
`testIgnore` patterns are not supported.
## Route Files
Routes are collected from Next.js App Router files under `frontendRoot`:
- `page.ts`
- `page.tsx`
- `page.js`
- `page.jsx`
Route groups like `(admin)` and parallel route segments like `@modal` are
ignored when building route patterns. Dynamic segments map as follows:
| `[id]` | `:id` |
| `[...rest]` | `*` |
| `[[...rest]]` | `**` |
## Test URL Detection
Tests cover routes when a detected URL normalizes to a local path and that path
matches a route pattern.
Detected AST forms:
```ts
await page.goto('/users/42');
await page.goto("http://localhost:3000/users/42");
await page.goto(`/users/${id}`);
await page.click('a[href="/settings"]');
await page.click(`a[href='/settings']`);
await expect(page).toHaveURL('/settings');
await expect(page).toHaveURL(new RegExp(`/users/${id}`));
await navigateTo(page, '/settings');
await testHelpers.openPath(page, '/settings');
```
Detection rules:
- Candidate URLs must start with `/`, `http://`, or `https://`.
- Protocol-relative URLs such as `//example.com/path` are treated as external.
- Absolute URLs only count when they start with a literal Playwright `baseURL`;
the base is stripped before route matching.
- External absolute URLs without a matching `baseURL` are ignored.
- `page.goto(...)` only contributes a URL when the first argument is a string or
template literal.
- `page.click(...)` only contributes a URL when the selector contains an
`href="..."` or `href='...'` value.
- Positive `.toHaveURL(...)` assertions and configured `navigationHelpers` use
the first URL-like string literal or template literal found anywhere in the
call arguments.
- Negative `.not.toHaveURL(...)` assertions are ignored.
## App Selector Collection
Selector coverage compares selectors declared in app JSX with selectors used by
Playwright tests. App selector source files may use these extensions:
- `.ts`
- `.tsx`
- `.js`
- `.jsx`
- `.mts`
- `.cts`
- `.mjs`
- `.cjs`
Supported JSX forms for configured `selectorAttributes`:
```tsx
<button data-testid="save" />
<button data-testid='save' />
<button data-testid={"save"} />
<button data-testid={'save'} />
<article data-testid={`user-${id}`} />
<button data-testid={id} />
```
Configured `componentSelectorAttributes` collect props from component JSX and
report them as the mapped DOM attribute:
```tsx
// componentSelectorAttributes: { dataPw: data-pw }
<SaveButton dataPw="save" />
<UI.Button dataPw="publish" />
```
Lowercase intrinsic elements do not use component mappings, so
`<button dataPw="save" />` is ignored unless `dataPw` is also listed in
`selectorAttributes`.
Quoted string values and quoted expression values are exact selectors. Template
literals with at least one static part are fuzzy selectors, so `user-${id}` can
be covered by a test selector such as `user-42`.
Dynamic expressions without static template text, such as `{id}` or `` `${id}` ``,
are reported with `unsupportedDynamic: true` and never count as covered.
## Playwright Selector Detection
Detected `getByTestId(...)` forms:
```ts
await page.getByTestId('save').click();
await page.getByTestId("save").click();
await page.getByTestId(`save`).click();
await page.getByTestId(/^user-/).click();
```
`getByTestId(...)` maps to each Playwright `testIdAttribute` discovered for the
test file. By default this covers `data-testid`. If Playwright config sets
`use.testIdAttribute: 'data-pw'`, then `getByTestId('save')` covers
`data-pw="save"`.
Detected CSS attribute selectors:
```ts
await page.locator('[data-testid="save"]').click();
await page.locator("[data-testid='save']").click();
await page.locator('[data-testid^="user-"]').click();
await page.locator('[data-testid$="-button"]').click();
await page.locator('[data-testid*="nav"]').click();
```
CSS attribute selectors are detected for configured `selectorAttributes` and
mapped DOM attributes from `componentSelectorAttributes`. Supported operators
are exact (`=`), prefix (`^=`), suffix (`$=`), and contains (`*=`).
The selector string must appear as a string or template literal argument to a
known Playwright selector method. Supported methods include `locator`, `click`,
`fill`, `hover`, `press`, `waitForSelector`, `$`, `$$`, `$eval`, `$$eval`,
`frameLocator`, and related selector-taking methods. `dragAndDrop(...)` checks
both selector arguments.
## Matching Behavior
A Playwright selector covers an app selector only when both the attribute and
value matcher match.
- Exact app selectors match exact, prefix, suffix, contains, or regex test
selectors according to the test selector semantics.
- Template app selectors match against their static parts.
- `getByTestId(/.../)` regex selectors are tested against static app selector
values and against a generated sample for template selectors where each
dynamic hole is replaced with `x`. JavaScript regex flags are ignored; the
pattern is evaluated as a Rust regex string.
- Unsupported dynamic app selectors never count as covered.
## Limitations
- Project code is never executed.
- Non-literal optional Playwright config values are ignored.
- Computed object properties and method properties in Playwright config are not
parsed as supported options.
- Regular-expression Playwright `testMatch` and `testIgnore` values are not
supported; use string globs.
- URL and selector values built through variables, function calls, string
concatenation, or conditionals are not detected unless a supported literal or
template literal appears in the inspected call.