# Files

> Check the files your services and apps write to a folder: an export directory, a volume they share with Axx, an app's own data.

Source: https://axx.nimbusxr.us/references/packs/files/

Check the files your services and apps write to a folder: an export directory, a volume they share with Axx, an app's own data.

Register the folder with `the {word} folder with the following properties:`. Its `path` is on this machine, relative to the directory of axx.yaml or absolute, or in the file context of its `owner`: `service:parcels` for a service of axx.yaml (relative to the folder Axx runs it in), `app:depot` for an app a scenario registers (`./` is where the app keeps its data, `~/` its home, wherever it runs). `${env:..}` and `${sys:..}` are expanded. A check names a file by its path in the folder, such as `manifests/M-KESTREL-0412/report.csv`, quoted when it has a space, and waits for it (10 seconds unless `within {duration}` says otherwise), since services write asynchronously.

The checks are those of the storage packs' objects: a file's exact content, its JSON properties, its text, read by its type (PDF, Word, Excel, CSV, JSON, XML, HTML or plain text), and a row of its table (CSV, TSV or Excel). What a service or an app takes away is checked too: a folder `has no file named` one, or `is empty`, which wait for the files to go. An image an app saves is compared with its screenshot, pixel by pixel, one for each platform: `packs.files.screenshots` sets their `folder`, `tolerance`, `update` and `platforms`, as the app screenshots' settings do.

A scenario puts files in a folder too, as a service or an app would find them: `is a copy of` a file of the project, or `has the content:` of a doc string. `the {word} folder is emptied` removes what is in it, hidden files and subfolders too: a folder of the project, a service's or an app's, never one elsewhere on the machine.

A service's folder keeps what earlier runs wrote there, and Axx empties no folder unless a scenario says so: whether a check needs a file of its scenario's own (named after data unique to it), or a folder emptied first, depends on the test. An app's files are its own in each scenario.

Add it to a project with `axx pack add files`.

## Settings

In `axx.yaml`, under `packs.files`:

| Setting | Takes | Values | Default |
|---|---|---|---|
| `screenshots.folder` | The folder of the images' screenshots, in the project. Unset, each feature keeps its screenshots in a screenshots folder next to its file. | text | |
| `screenshots.platforms` | The platforms whose screenshots the project keeps: each compares its own; elsewhere the step passes without comparing (default: every platform). | a list of `linux`, `darwin`, `windows` | |
| `screenshots.tolerance` | The share of an image's pixels that may differ. | a number, 0 to 1 | `0` |
| `screenshots.update` | Take every screenshot again, as the image is now, instead of comparing. | `true`, `false` | |

## Steps

### `files.folder`

```gherkin
Given the {word} folder with the following properties:
  | property | value |
```

The table may start with the row naming its columns, as above, or go straight to its rows.

Register a folder under a name. The folder may appear only when a service first writes to it.

| Parameter | Takes | For example |
|---|---|---|
| `{word}` | one word, with no spaces | `parcels`, `PX-4101` |

| Property | Takes | Default |
|---|---|---|
| `path` | the folder: with no `owner`, a path relative to the directory of axx.yaml, or absolute; with one, a path in its file context (`./exports`); `${env:..}` and `${sys:..}` are expanded | _required_ |
| `owner` | whose folder it is: `service:<name>`, a service of axx.yaml, whose paths are relative to the folder Axx runs it in; or `app:<name>`, an app the scenario registers, where `./` is where the app keeps its data and `~/` its home | `none: a folder on this machine` |

**Example:**

```gherkin
Given the exports folder with the following properties:
  | owner | service:parcels |
  | path  | ./exports       |
Given the desk folder with the following properties:
  | owner | app:depot              |
  | path  | "./Parcels/Depot desk" |
```

_Since 0.1.1._

### `files.copy`

```gherkin
Given the {path} file in the {word} folder is a copy of the {filepath} file
```

Put a copy of a file of the project in the folder, under the path given, as a service or an app would find it there; the folders it is in are made, and a file there is replaced.

| Parameter | Takes | For example |
|---|---|---|
| `{path}` | a path in a folder or a store, like `manifests/M-1/report.csv`, quoted when it has a space. A `*` stands for any characters but a slash, for a name a check cannot know (`snap-*.json`, named by the time): the path then names the one file it matches | `manifests/M-KESTREL-0412/report.csv`, `"Parcels/Depot desk/arrivals.json"` |
| `{word}` | one word, with no spaces | `parcels`, `PX-4101` |
| `{filepath}` | a file of the project: a path relative to the feature file's directory, the `resources` directories or axx.yaml's directory (looked for in that order), or an absolute path, quoted when it has a space | `seeds/parcels.yaml`, `kafka/scan-delivered.json` |

**Example:**

```gherkin
Given the manifests/M-KESTREL-0505/report.csv file in the exports folder is a copy of the reports/M-KESTREL-0505-earlier.csv file
```

_Since 0.2.3._

### `files.content`

```gherkin
Given the {path} file in the {word} folder has the content:
  """
  ...
  """
```

Put a file with the doc string's content in the folder, as a service or an app would find it there; the folders it is in are made, and a file there is replaced. `${env:..}` and `${sys:..}` are expanded.

| Parameter | Takes | For example |
|---|---|---|
| `{path}` | a path in a folder or a store, like `manifests/M-1/report.csv`, quoted when it has a space. A `*` stands for any characters but a slash, for a name a check cannot know (`snap-*.json`, named by the time): the path then names the one file it matches | `manifests/M-KESTREL-0412/report.csv`, `"Parcels/Depot desk/arrivals.json"` |
| `{word}` | one word, with no spaces | `parcels`, `PX-4101` |

**Example:**

```gherkin
Given the manifests/M-KESTREL-0505/summary.json file in the exports folder has the content:
  """json
  {"manifest": "M-KESTREL-0505", "lines": 1}
  """
```

_Since 0.2.3._

### `files.emptied`

```gherkin
Given the {word} folder is emptied
```

Remove everything in the folder, hidden files and subfolders too, and keep the folder: a folder of the project, a service's or an app's. A folder elsewhere on this machine is a person's, and is not emptied.

| Parameter | Takes | For example |
|---|---|---|
| `{word}` | one word, with no spaces | `parcels`, `PX-4101` |

**Example:**

```gherkin
Given the kestrel folder is emptied
```

_Since 0.2.3._

### `files.has`

```gherkin
Then [[within {duration} ]]the {word} folder has a(n) file named {path}
```

Check that the folder has a file with that name. The check waits for it: 10 seconds, or `within {duration}`.

| Parameter | Takes | For example |
|---|---|---|
| `{duration}` | a duration in seconds (`s`) or minutes (`m`) | `5s`, `2m` |
| `{word}` | one word, with no spaces | `parcels`, `PX-4101` |
| `{path}` | a path in a folder or a store, like `manifests/M-1/report.csv`, quoted when it has a space. A `*` stands for any characters but a slash, for a name a check cannot know (`snap-*.json`, named by the time): the path then names the one file it matches | `manifests/M-KESTREL-0412/report.csv`, `"Parcels/Depot desk/arrivals.json"` |

**Variants**, the parts in `[[...]]` said or left out:

- `the {word} folder has a(n) file named {path}`
- `within {duration} the {word} folder has a(n) file named {path}`

**Example:**

```gherkin
Then within 30s the exports folder has a file named disputes/kestrel-2026-09.csv
```

_Since 0.1.1._

### `files.identical`

```gherkin
Then [[within {duration} ]]the {path} file in the {word} folder is identical to the {filepath} file
```

Check that the file exists with exactly the content of the file. The check waits for it: 10 seconds, or `within {duration}`.

| Parameter | Takes | For example |
|---|---|---|
| `{duration}` | a duration in seconds (`s`) or minutes (`m`) | `5s`, `2m` |
| `{path}` | a path in a folder or a store, like `manifests/M-1/report.csv`, quoted when it has a space. A `*` stands for any characters but a slash, for a name a check cannot know (`snap-*.json`, named by the time): the path then names the one file it matches | `manifests/M-KESTREL-0412/report.csv`, `"Parcels/Depot desk/arrivals.json"` |
| `{word}` | one word, with no spaces | `parcels`, `PX-4101` |
| `{filepath}` | a file of the project: a path relative to the feature file's directory, the `resources` directories or axx.yaml's directory (looked for in that order), or an absolute path, quoted when it has a space | `seeds/parcels.yaml`, `kafka/scan-delivered.json` |

**Variants**, the parts in `[[...]]` said or left out:

- `the {path} file in the {word} folder is identical to the {filepath} file`
- `within {duration} the {path} file in the {word} folder is identical to the {filepath} file`

**Example:**

```gherkin
Then the disputes/kestrel-2026-09.csv file in the exports folder is identical to the expected/kestrel-disputes.csv file
```

_Since 0.1.1._

### `files.properties`

```gherkin
Then [[within {duration} ]]the {path} file in the {word} folder has the following properties:
  | path | value |
```

The table may start with the row naming its columns, as above, or go straight to its rows.

Check that the file holds JSON with those values at those paths. The check waits for it: 10 seconds, or `within {duration}`.

| Parameter | Takes | For example |
|---|---|---|
| `{duration}` | a duration in seconds (`s`) or minutes (`m`) | `5s`, `2m` |
| `{path}` | a path in a folder or a store, like `manifests/M-1/report.csv`, quoted when it has a space. A `*` stands for any characters but a slash, for a name a check cannot know (`snap-*.json`, named by the time): the path then names the one file it matches | `manifests/M-KESTREL-0412/report.csv`, `"Parcels/Depot desk/arrivals.json"` |
| `{word}` | one word, with no spaces | `parcels`, `PX-4101` |

Each row is a path into the JSON (a property name, a dotted path or a JSONPath) and the value it has, compared as text: `null` for null and `undefined` for absent, as in the other JSON property steps.

**Variants**, the parts in `[[...]]` said or left out:

- `the {path} file in the {word} folder has the following properties:`
- `within {duration} the {path} file in the {word} folder has the following properties:`

**Example:**

```gherkin
Then the summaries/kestrel-2026-09.json file in the exports folder has the following properties:
  | carrier         | KESTREL |
  | lines           | 14      |
  | totals.disputed | 5.25    |
```

_Since 0.1.1._

### `files.contains`

```gherkin
Then [[within {duration} ]]the {path} file in the {word} folder contains {string}
```

Check that the text of the file contains the text.

- The check waits for it: 10 seconds, or `within {duration}`.
- Case matters; runs of spaces and line breaks count as one space.
- The text is read by the file's type: the text of a PDF's pages, the paragraphs and tables of a Word document (.docx), the cells of every sheet of an Excel workbook (.xlsx), the text content (or the markup) of XML and HTML, or the text itself.

| Parameter | Takes | For example |
|---|---|---|
| `{duration}` | a duration in seconds (`s`) or minutes (`m`) | `5s`, `2m` |
| `{path}` | a path in a folder or a store, like `manifests/M-1/report.csv`, quoted when it has a space. A `*` stands for any characters but a slash, for a name a check cannot know (`snap-*.json`, named by the time): the path then names the one file it matches | `manifests/M-KESTREL-0412/report.csv`, `"Parcels/Depot desk/arrivals.json"` |
| `{word}` | one word, with no spaces | `parcels`, `PX-4101` |
| `{string}` | text in double or single quotes, which the step leaves out | `"Get a quote"`, `'Express'` |

**Variants**, the parts in `[[...]]` said or left out:

- `the {path} file in the {word} folder contains {string}`
- `within {duration} the {path} file in the {word} folder contains {string}`

**Example:**

```gherkin
Then the invoices/kestrel-2026-09.pdf file in the exports folder contains "Total due: 1284.50 EUR"
```

_Since 0.1.1._

### `files.row`

```gherkin
Then [[within {duration} ]]the {path} file in the {word} folder has a row where:
  | column | value |
```

The table may start with the row naming its columns, as above, or go straight to its rows.

Check that the table of the file has a row with those values in those columns.

- The check waits for it: 10 seconds, or `within {duration}`.
- The file is a CSV or TSV file, or an Excel workbook (.xlsx; its first sheet), whose first row names the columns.
- Cells compare as text, as the workbook shows them, with runs of spaces as one space; an empty value matches an empty cell.

| Parameter | Takes | For example |
|---|---|---|
| `{duration}` | a duration in seconds (`s`) or minutes (`m`) | `5s`, `2m` |
| `{path}` | a path in a folder or a store, like `manifests/M-1/report.csv`, quoted when it has a space. A `*` stands for any characters but a slash, for a name a check cannot know (`snap-*.json`, named by the time): the path then names the one file it matches | `manifests/M-KESTREL-0412/report.csv`, `"Parcels/Depot desk/arrivals.json"` |
| `{word}` | one word, with no spaces | `parcels`, `PX-4101` |

Each row names a column, as the file's first row names it, and the value in that column.

**Variants**, the parts in `[[...]]` said or left out:

- `the {path} file in the {word} folder has a row where:`
- `within {duration} the {path} file in the {word} folder has a row where:`

**Example:**

```gherkin
Then within 30s the disputes/kestrel-2026-09.csv file in the exports folder has a row where:
  | parcel | PX-5199          |
  | status | UNKNOWN_SHIPMENT |
  | billed | 4.10             |
```

_Since 0.1.1._

### `files.absent`

```gherkin
Then [[within {duration} ]]the {word} folder has no file named {path}
```

Check that the folder has no file with that name, or wait for it to go: 10 seconds, or `within {duration}`, for a service or an app that deletes or moves it.

| Parameter | Takes | For example |
|---|---|---|
| `{duration}` | a duration in seconds (`s`) or minutes (`m`) | `5s`, `2m` |
| `{word}` | one word, with no spaces | `parcels`, `PX-4101` |
| `{path}` | a path in a folder or a store, like `manifests/M-1/report.csv`, quoted when it has a space. A `*` stands for any characters but a slash, for a name a check cannot know (`snap-*.json`, named by the time): the path then names the one file it matches | `manifests/M-KESTREL-0412/report.csv`, `"Parcels/Depot desk/arrivals.json"` |

**Variants**, the parts in `[[...]]` said or left out:

- `the {word} folder has no file named {path}`
- `within {duration} the {word} folder has no file named {path}`

**Example:**

```gherkin
Then the exports folder has no file named manifests/M-KESTREL-0505/report.csv
```

_Since 0.2.3._

### `files.empty`

```gherkin
Then [[within {duration} ]]the {word} folder is empty
```

Check that the folder has no files, in its subfolders too, or wait for them to go: 10 seconds, or `within {duration}`. Hidden files (.DS_Store, .gitkeep) are not counted, and a folder that is not there is empty.

| Parameter | Takes | For example |
|---|---|---|
| `{duration}` | a duration in seconds (`s`) or minutes (`m`) | `5s`, `2m` |
| `{word}` | one word, with no spaces | `parcels`, `PX-4101` |

**Variants**, the parts in `[[...]]` said or left out:

- `the {word} folder is empty`
- `within {duration} the {word} folder is empty`

**Example:**

```gherkin
Then the exports folder is empty
```

_Since 0.2.3._

### `files.screenshot`

```gherkin
Then [[within {duration} ]]the {path} file in the {word} folder looks like the {string} screenshot
```

Check that an image file looks like its screenshot, pixel by pixel (anti-aliasing aside): an image an app saved, like an annotated capture (PNG or JPEG). The check waits for the file: 10 seconds, or `within {duration}`.

- Each platform draws images its own way, so each has its own screenshot, named after it (`checkout-annotated.darwin.png`), in a `screenshots` folder next to the feature file (or the folder `packs.files.screenshots` sets).
- Without one, the step takes it and fails: look at it, and keep it.
- When the image is different, the step attaches the screenshot, the image and their difference.

| Parameter | Takes | For example |
|---|---|---|
| `{duration}` | a duration in seconds (`s`) or minutes (`m`) | `5s`, `2m` |
| `{path}` | a path in a folder or a store, like `manifests/M-1/report.csv`, quoted when it has a space. A `*` stands for any characters but a slash, for a name a check cannot know (`snap-*.json`, named by the time): the path then names the one file it matches | `manifests/M-KESTREL-0412/report.csv`, `"Parcels/Depot desk/arrivals.json"` |
| `{word}` | one word, with no spaces | `parcels`, `PX-4101` |
| `{string}` | text in double or single quotes, which the step leaves out | `"Get a quote"`, `'Express'` |

**Variants**, the parts in `[[...]]` said or left out:

- `the {path} file in the {word} folder looks like the {string} screenshot`
- `within {duration} the {path} file in the {word} folder looks like the {string} screenshot`

**Example:**

```gherkin
Then the "snap-*.png" file in the inbox folder looks like the "checkout-annotated" screenshot
```

_Since 0.2.1._
