# Queries

A query identifies an element. It is used by `clip`, by every entry in `marks`, `mask` and `check.ignore`, and by every step that acts on an element.

Keys combine: a source chooses candidates, filters narrow them, traversal moves from them, and selection picks one.

screenshots/recipes/order-row.yaml

```
clip:
  css: 'li, div'
  contains: Acme Corp
  matching: '\$\d'
  maxChildren: 12
  pick: smallest
```

## Sources

| Key | Matches |
| --- | --- |
| `css` | A CSS selector |
| `role` + `name` | ARIA role and accessible name |
| `label` | Form control by its label |
| `placeholder` | Input by placeholder text |
| `testid` | `data-testid` |
| `text` | Element whose trimmed text equals the value |
| `startsWith` | Element whose trimmed text begins with the value |
| `heading` | `h1`–`h6` with this exact text |

`text` and `startsWith` also narrow a source written alongside them. `exact: true` makes `role` + `name`, `label` and `placeholder` match the whole string, case-sensitively.

`role`, `label`, `placeholder` and `testid` cannot be used inside `span` or `within`. Use `css`, `text`, `startsWith` or `contains` there.

## Filters

| Key | Keeps elements that… |
| --- | --- |
| `contains` | contain this text |
| `containingAll` | contain all of these strings |
| `matching` | have text matching this regular expression |
| `maxChildren` / `minChildren` | have at most / at least this many children |
| `minWidth` / `maxWidth` | are within these widths |
| `minHeight` / `maxHeight` | are within these heights |
| `narrowerThan` / `widerThan` | are narrower / wider than this |
| `visible` | have a non-zero size |
| `within` | sit inside an already-resolved region |

Sizes take pixels (`400`) or viewport units (`95vw`, `50vh`). `matching` runs inside the page and is bounded by `site.timeout`.

### within

Takes a name or a query written out. Two kinds of name resolve:

- `clip`, the region being captured
- any mark declared above this one

A name that has not resolved yet is an error. `within` is available to `marks`, `mask` and `check.ignore`, which run after the clip is known. Steps cannot use it, because `setup` runs before anything is measured.

## Traversal

| Key | Moves to |
| --- | --- |
| `ancestor` | The first ancestor matching the filters given |
| `parent` | The parent element |
| `child: n` | The nth child; `-1` is the last |
| `children` | All children |

`ancestor` climbs from the element's parent, so an element is never its own ancestor. It takes `pick: nearest` (the default) or `pick: outermost`, which keeps climbing while the parent also matches.

screenshots/recipes/edit-order.yaml

```
marks:
  dialog:
    heading: Edit order
    ancestor: { narrowerThan: 95vw, pick: outermost }
```

## Selection and shape

| Key | Effect |
| --- | --- |
| `pick: first \| last \| smallest \| largest` | Which candidate to use |
| `nth: n` | The candidate at this position; `-1` is the last |
| `pad: n` | Grow the resulting box on all sides |
| `grow: { top, right, bottom, left }` | Grow it on specific sides |
| `span: [query, query]` | The bounding box of several queries |
| `rect: [x, y, width, height]` | A literal box, for `source: file` recipes |

`nth` and `child` count from the end when negative, as `Array.at` does. `pad` and `grow` cannot be negative.

## Frames

| Key | Effect |
| --- | --- |
| `frame` | The `<iframe>` to resolve inside, named by a query of its own |

Everything written beside `frame` resolves in that iframe's document — sources, filters, traversal and selection alike. The rect comes back in page coordinates.

screenshots/recipes/checkout.yaml

```
clip:
  frame: { css: 'iframe[title="Checkout"]' }
  css: '.order-total'
  pad: 12
```

|  |  |
| --- | --- |
| A frame inside a frame | Needs nothing extra |
| Cross-origin frames | Resolve; the browser does it, not the page |
| `within` naming a mark outside the frame | Refused |
| `transform` or `zoom` on the iframe | Not compensated for |

Steps take queries, so `click`, `fill` and `wait` reach inside a frame with the same key and no extra verb.

## Finders

A named query defined once in the configuration and called from any recipe. `$1` and `$2` stand for the arguments it is called with.

shotlist.config.yaml

```
finders:
  listRow:
    css: 'li, div'
    contains: $1
    matching: '\$\d'
    maxChildren: 12
    pick: smallest
```

screenshots/recipes/order-row.yaml

```
clip: { listRow: Acme Corp }
```

## Failure

A query that matches nothing stops the run, naming the recipe and the key. A mask covers every element its query matches; every other use resolves to exactly one.

For advice on which keys to prefer, see [Queries that survive a redesign](/docs/explanation/durable-queries/).

Written by Nicola Mustone · Applies to shotlist 0.6.0 · Maintained by Nicola Mustone

Published date unavailable · Updated date unavailable · [View source](https://github.com/SirDarcanos/shotlist.dev/blob/main/src/pages/docs/reference/queries.astro) · [Propose a correction](https://github.com/SirDarcanos/shotlist.dev/edit/main/src/pages/docs/reference/queries.astro)
