From 423894d87047f2a2dc8b0ea5f9a970008edbca3a Mon Sep 17 00:00:00 2001 From: finalerock44 <77282157+finalerock44@users.noreply.github.com> Date: Fri, 18 Sep 2026 14:35:55 +0100 Subject: [PATCH 1/8] docs: visual testing with assertScreenshot, and includedPaths Co-Authored-By: Claude Opus 5 (1M context) --- SUMMARY.md | 1 + advanced/visual-testing.md | 134 ++++++++++++++++++++++++++++++ configuration/workspace-config.md | 30 +++++++ 3 files changed, 165 insertions(+) create mode 100644 advanced/visual-testing.md diff --git a/SUMMARY.md b/SUMMARY.md index 8929180..8175fb5 100644 --- a/SUMMARY.md +++ b/SUMMARY.md @@ -74,6 +74,7 @@ ## Advanced * [Async Execution](advanced/async-execution.md) +* [Visual Testing](advanced/visual-testing.md) * [Retry Strategies](advanced/retry-strategies.md) * [Chrome Onboarding](advanced/chrome-onboarding.md) * [Exit Codes](advanced/exit-codes.md) diff --git a/advanced/visual-testing.md b/advanced/visual-testing.md new file mode 100644 index 0000000..094ac52 --- /dev/null +++ b/advanced/visual-testing.md @@ -0,0 +1,134 @@ +# Visual Testing + +Maestro's `assertScreenshot` command compares the current screen against a reference image — a **baseline** — that you commit to your repository. + +```yaml +- assertScreenshot: screenshots/home.png +``` + +DeviceCloud runs these assertions like any other command. The one thing you need to set up is getting your baseline images onto the device. + +{% hint style="warning" %} +**Requires Maestro 2.3.0 or later.** The command exists in 2.2.0, but earlier versions resolve the baseline path against a different working directory and will report `Screenshot file not found` no matter where you put the file. Pass `--maestro-version 2.3.0` (or newer) if your runs still default to an older version. +{% endhint %} + +## Uploading your baselines + +Baselines are not referenced by any command that the CLI inspects, so you must declare them in your workspace [`config.yaml`](../configuration/workspace-config.md): + +```yaml +# config.yaml +flows: + - ./**/*.yaml + +includedPaths: + - screenshots/** +``` + +That is the whole setup. Without it, the baseline never reaches the device and the run fails with `Screenshot file not found`. + +Baselines referenced by a plain literal path are also picked up automatically, so a simple layout often needs no configuration at all. You still need `includedPaths` whenever the path contains a variable — see [per-device baselines](#per-device-baselines) below. + +## Where baselines live + +Maestro resolves the path **relative to the flow file's own directory**, so this layout works: + +``` +.maestro/ + config.yaml + visual.yaml - assertScreenshot: screenshots/home.png + screenshots/ + home.png +``` + +You can climb out of the flow's folder with `../` if you prefer to keep baselines in one shared place. + +The file extension is optional. `assertScreenshot: screenshots/home` looks for `screenshots/home.png`. + +## Creating your first baseline + +Maestro has no "record" mode, so you have to capture the first baseline yourself. A baseline must match the device's screen dimensions **exactly**, so capture it on a local emulator or simulator configured identically to the DeviceCloud device you intend to test on — a `pixel-7` / API 34 AVD for `--android-device pixel-7 --android-api-level 34`, for example. + +1. Boot that local device and run the flow with `takeScreenshot` in place of the assertion: + + ```yaml + - takeScreenshot: home + ``` + + ```bash + maestro test .maestro/visual.yaml + ``` + +2. Copy the PNG out of Maestro's output directory into `screenshots/home.png` and commit it. + +3. Swap the command to `assertScreenshot` and add `includedPaths` to your config. + +{% hint style="warning" %} +**Don't use `--download-artifacts` to create a baseline.** Screenshots and videos from a run are downscaled and re-encoded for the console — a 1080x2400 screen comes back as a 486x1080 JPEG — so they will never match at full size. They are for looking at, not for asserting against. +{% endhint %} + +If the dimensions are wrong you will see a size mismatch naming both sizes, which tells you exactly what to capture at: + +``` +Screenshot size mismatch: expected 486x1080, actual 1080x2400. +Screenshots must have the same dimensions to compare. +``` + +## Tuning the threshold + +```yaml +- assertScreenshot: + path: screenshots/home.png + thresholdPercentage: 98 +``` + +`thresholdPercentage` is the **percentage of pixels that must match**, defaulting to `95`. It is a floor, not a tolerance — the assertion passes when the measured match is at or above it, so a **lower** number is more permissive. Start at the default and lower it only if a screen has genuinely unstable content. + +A non-numeric value fails the step rather than falling back to the default. + +{% hint style="warning" %} +**A size mismatch always fails, whatever the threshold.** Baselines are locked to the resolution they were captured at, so a baseline taken on one device will never pass on a device with a different screen size. If you use `cropOn`, the baseline must have been captured with the same `cropOn`. +{% endhint %} + +## Per-device baselines + +Because baselines are resolution-locked, a [device matrix](../configuration/device-matrix.md) needs one baseline per device. Every run exposes the device profile as `DCD_DEVICE`, so you can select the right baseline from the path: + +```yaml +- assertScreenshot: screenshots/${DCD_DEVICE}/home.png +``` + +``` +screenshots/ + pixel-7-api-34/home.png + iphone-16-ios-18/home.png +``` + +```yaml +# config.yaml +includedPaths: + - screenshots/** +``` + +`DCD_DEVICE` is a lowercased, hyphenated form of the device profile you requested — `pixel-7-api-34` on Android, `iphone-16-ios-18` on iOS. `DCD_PLATFORM` (`android` or `ios`) is available too. Setting either yourself as an environment variable overrides the built-in value. + +{% hint style="info" %} +A variable in the path means the CLI cannot work out which file is needed ahead of the run, so `includedPaths` is required here — the automatic pickup only handles literal paths. +{% endhint %} + +## Reading a failure + +A failed comparison reports the match it measured, so you can tell a real regression from a threshold that is set too high: + +``` +Comparison error: Assert screenshot matches screenshots/home.png (threshold: 90%) + - threshold not met, current: 7.78% +``` + +Maestro also writes a diff image highlighting the changed regions. DeviceCloud collects it at full resolution and shows it in the run's media strip alongside the video and screenshots, so you can see what moved without re-running anything. + +The diff is only produced for a pixel mismatch. A size mismatch fails before any comparison happens, and reports the expected and actual dimensions in the step error instead. + +{% hint style="warning" %} +Maestro writes that diff next to the baseline **in your working directory** when you run locally, as `_diff.png`. Add `**/*_diff.png` to your `.gitignore` so local runs don't leave them lying around your repository. +{% endhint %} diff --git a/configuration/workspace-config.md b/configuration/workspace-config.md index 485e158..05a0777 100644 --- a/configuration/workspace-config.md +++ b/configuration/workspace-config.md @@ -33,6 +33,33 @@ Files named `config.yaml` / `config.yml` and paths containing `.app` path segmen If `flows` is omitted, all `.yaml` / `.yml` files in the directory (except config files) are included. +### `includedPaths` + +Glob patterns selecting extra **non-flow** files to upload alongside your flows. + +The CLI works out what to upload by reading your flows, so it only picks up files a command actually references — `addMedia`, `runFlow` and `runScript` arguments. Anything else your test needs on the device is invisible to it and silently absent from the run. `includedPaths` is how you declare those files. + +```yaml +includedPaths: + - screenshots/** # assertScreenshot baselines + - fixtures/*.json # test data read by a script + - certs/test-ca.pem +``` + +Patterns use the same [NPM glob](https://www.npmjs.com/package/glob) syntax as `flows`, and are always resolved **relative to the workspace folder you pass to `dcd cloud`** — not relative to the config file, even when you load it with `--config`. + +Matched files keep their position relative to your flows when they are uploaded, so a baseline at `screenshots/home.png` sitting next to `visual.yaml` arrives next to that flow on the device. + +{% hint style="warning" %} +Patterns cannot escape the workspace folder. A pattern resolving to a file outside it (`../secrets.json`) fails the run rather than uploading it. +{% endhint %} + +{% hint style="info" %} +Run with `--debug` to list exactly which files were matched and uploaded. + +If your included files sit **beside** your flows folder rather than inside it, the upload root moves up to cover both, and flow paths shown in the console gain a leading folder (`login.yaml` becomes `flows/login.yaml`). This is expected — it is what keeps the relative path between a flow and its files intact — and affects display only. +{% endhint %} + ### `includeTags` / `excludeTags` Filter flows by their Maestro `tags`. Values here are **merged** with any tags set using the CLI flags. @@ -120,6 +147,9 @@ platform: flows: - ./**/*.yaml +includedPaths: + - screenshots/** + includeTags: - smoke From 5d2d076f74b2381f1561267d36ab55611d2e464d Mon Sep 17 00:00:00 2001 From: finalerock44 Date: Wed, 23 Sep 2026 15:13:50 +0100 Subject: [PATCH 2/8] docs(visual-testing): recommend Maestro 2.10.0 and require CLI 5.6.0 Maestro 2.3.0 doesn't exist on DeviceCloud (2.2.0 is followed by 2.5.0), and the 2.2.0 default is too old for assertScreenshot. Point at --maestro-version 2.10.0 or latest until the default moves on 19 October, and say baselines are only uploaded from CLI 5.6.0. --- advanced/visual-testing.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/advanced/visual-testing.md b/advanced/visual-testing.md index 094ac52..117c43c 100644 --- a/advanced/visual-testing.md +++ b/advanced/visual-testing.md @@ -9,7 +9,9 @@ Maestro's `assertScreenshot` command compares the current screen against a refer DeviceCloud runs these assertions like any other command. The one thing you need to set up is getting your baseline images onto the device. {% hint style="warning" %} -**Requires Maestro 2.3.0 or later.** The command exists in 2.2.0, but earlier versions resolve the baseline path against a different working directory and will report `Screenshot file not found` no matter where you put the file. Pass `--maestro-version 2.3.0` (or newer) if your runs still default to an older version. +**Requires DeviceCloud CLI 5.6.0 or later**, the first version that uploads your baselines. + +**Use Maestro 2.10.0.** The default version, 2.2.0, resolves the baseline path against a different working directory and reports `Screenshot file not found` no matter where you put the file. Pass `--maestro-version 2.10.0` (or `--maestro-version latest`) until the default becomes 2.10.0 on 19 October 2026 — see [Maestro Versions](../configuration/maestro-versions.md). {% endhint %} ## Uploading your baselines From 53355ad75215583b06b351775d2a64a37056c150 Mon Sep 17 00:00:00 2001 From: finalerock44 Date: Wed, 23 Sep 2026 15:14:12 +0100 Subject: [PATCH 3/8] docs(visual-testing): literal baseline paths are bundled automatically CLI 5.6.0 walks assertScreenshot like addMedia and runScript, so a literal baseline path needs no config. includedPaths is for paths with a variable and for other files. Add assertScreenshot to the auto-discovered lists in workspace-config and flows-and-workspaces. --- advanced/visual-testing.md | 10 +++++----- configuration/workspace-config.md | 4 ++-- getting-started/flows-and-workspaces.md | 2 +- 3 files changed, 8 insertions(+), 8 deletions(-) diff --git a/advanced/visual-testing.md b/advanced/visual-testing.md index 117c43c..5006ae4 100644 --- a/advanced/visual-testing.md +++ b/advanced/visual-testing.md @@ -16,7 +16,9 @@ DeviceCloud runs these assertions like any other command. The one thing you need ## Uploading your baselines -Baselines are not referenced by any command that the CLI inspects, so you must declare them in your workspace [`config.yaml`](../configuration/workspace-config.md): +The CLI reads your flows before it uploads them, and bundles every baseline that an `assertScreenshot` command names with a literal path, the same way it bundles `addMedia` and `runScript` files. The example above needs no configuration: `screenshots/home.png` is uploaded with the flow. + +When the path contains a variable, the CLI can't tell which file you mean, so declare the baselines with `includedPaths` in your workspace [`config.yaml`](../configuration/workspace-config.md) — see [per-device baselines](#per-device-baselines) below: ```yaml # config.yaml @@ -27,9 +29,7 @@ includedPaths: - screenshots/** ``` -That is the whole setup. Without it, the baseline never reaches the device and the run fails with `Screenshot file not found`. - -Baselines referenced by a plain literal path are also picked up automatically, so a simple layout often needs no configuration at all. You still need `includedPaths` whenever the path contains a variable — see [per-device baselines](#per-device-baselines) below. +A baseline that isn't uploaded never reaches the device, and the assertion fails with `Screenshot file not found`. The CLI doesn't stop you uploading a flow whose baseline doesn't exist yet; the error comes from the run. ## Where baselines live @@ -63,7 +63,7 @@ Maestro has no "record" mode, so you have to capture the first baseline yourself 2. Copy the PNG out of Maestro's output directory into `screenshots/home.png` and commit it. -3. Swap the command to `assertScreenshot` and add `includedPaths` to your config. +3. Swap the command to `assertScreenshot`. A literal path needs nothing else; a path with a variable also needs `includedPaths` in your config. {% hint style="warning" %} **Don't use `--download-artifacts` to create a baseline.** Screenshots and videos from a run are downscaled and re-encoded for the console — a 1080x2400 screen comes back as a 486x1080 JPEG — so they will never match at full size. They are for looking at, not for asserting against. diff --git a/configuration/workspace-config.md b/configuration/workspace-config.md index 05a0777..dc4eaeb 100644 --- a/configuration/workspace-config.md +++ b/configuration/workspace-config.md @@ -37,11 +37,11 @@ If `flows` is omitted, all `.yaml` / `.yml` files in the directory (except confi Glob patterns selecting extra **non-flow** files to upload alongside your flows. -The CLI works out what to upload by reading your flows, so it only picks up files a command actually references — `addMedia`, `runFlow` and `runScript` arguments. Anything else your test needs on the device is invisible to it and silently absent from the run. `includedPaths` is how you declare those files. +The CLI works out what to upload by reading your flows, so it only picks up files a command actually references by a literal path — `addMedia`, `assertScreenshot`, `runFlow` and `runScript` arguments. A path that contains a variable (`screenshots/${DCD_DEVICE}/home.png`), and anything else your test needs on the device, is invisible to it and silently absent from the run. `includedPaths` is how you declare those files. ```yaml includedPaths: - - screenshots/** # assertScreenshot baselines + - screenshots/** # per-device assertScreenshot baselines - fixtures/*.json # test data read by a script - certs/test-ca.pem ``` diff --git a/getting-started/flows-and-workspaces.md b/getting-started/flows-and-workspaces.md index 79659dd..81334e5 100644 --- a/getting-started/flows-and-workspaces.md +++ b/getting-started/flows-and-workspaces.md @@ -60,4 +60,4 @@ See [Workspace Configuration](../configuration/workspace-config.md) for more inf ### Referencing flows -As of version 2.0.0, the CLI will search for all nested dependencies referenced by your YAML flows using Maestro keywords (`addMedia`, `runFlow`, `runScript`). +As of version 2.0.0, the CLI will search for all nested dependencies referenced by your YAML flows using Maestro keywords (`addMedia`, `runFlow`, `runScript`). From version 5.6.0 it also picks up `assertScreenshot` baselines named by a literal path. Files it can't find this way, such as a path containing a variable, can be added with [`includedPaths`](../configuration/workspace-config.md#includedpaths). From 397c0f4e967c12654726a7bda656e5471655334e Mon Sep 17 00:00:00 2001 From: finalerock44 Date: Wed, 23 Sep 2026 15:14:45 +0100 Subject: [PATCH 4/8] docs(visual-testing): list DCD_DEVICE values and add built-in variables Show DCD_DEVICE as it reads once the device key splits on the last ' - ': iphone-16-ios-18, the full iPad model name, pixel-7-api-34, pixel-7-api-34-play for Google Play and the tablet's emulator profile. Tell users to print it once to confirm. Document DCD_DEVICE and DCD_PLATFORM under Built-in Variables, with -e overriding them. --- advanced/visual-testing.md | 16 ++++++++++++++-- configuration/environment-variables.md | 11 +++++++++++ 2 files changed, 25 insertions(+), 2 deletions(-) diff --git a/advanced/visual-testing.md b/advanced/visual-testing.md index 5006ae4..8d661c1 100644 --- a/advanced/visual-testing.md +++ b/advanced/visual-testing.md @@ -94,7 +94,7 @@ A non-numeric value fails the step rather than falling back to the default. ## Per-device baselines -Because baselines are resolution-locked, a [device matrix](../configuration/device-matrix.md) needs one baseline per device. Every run exposes the device profile as `DCD_DEVICE`, so you can select the right baseline from the path: +Because baselines are resolution-locked, a [device matrix](../configuration/device-matrix.md) needs one baseline per device. Every run exposes the device it runs on as `DCD_DEVICE`, so you can select the right baseline from the path: ```yaml - assertScreenshot: screenshots/${DCD_DEVICE}/home.png @@ -112,7 +112,19 @@ includedPaths: - screenshots/** ``` -`DCD_DEVICE` is a lowercased, hyphenated form of the device profile you requested — `pixel-7-api-34` on Android, `iphone-16-ios-18` on iOS. `DCD_PLATFORM` (`android` or `ios`) is available too. Setting either yourself as an environment variable overrides the built-in value. +`DCD_DEVICE` is the device and OS version, lowercased and hyphenated: + +| Device | `DCD_DEVICE` | +| --- | --- | +| `--android-device pixel-7 --android-api-level 34` | `pixel-7-api-34` | +| The same with `--google-play` | `pixel-7-api-34-play` | +| `--android-device generic-tablet --android-api-level 36` | `13-5in-freeform-api-36` | +| `--ios-device iphone-16 --ios-version 18` | `iphone-16-ios-18` | +| `--ios-device ipad-pro-6th-gen --ios-version 26` | `ipad-pro-12-9-inch-6th-generation-ios-26` | + +iPads use their full model name and the generic tablet its emulator profile, so the value isn't always your device flag. Print it once before you name your folders: add `- evalScript: ${console.log('DCD_DEVICE=' + DCD_DEVICE)}` to a flow and look for the `JsConsole` line in the run's Maestro log. + +`DCD_PLATFORM` (`android` or `ios`) is available too. Passing either yourself with `--env` overrides the built-in value. See [Built-in Variables](../configuration/environment-variables.md#built-in-variables). {% hint style="info" %} A variable in the path means the CLI cannot work out which file is needed ahead of the run, so `includedPaths` is required here — the automatic pickup only handles literal paths. diff --git a/configuration/environment-variables.md b/configuration/environment-variables.md index 611f1ef..4fda436 100644 --- a/configuration/environment-variables.md +++ b/configuration/environment-variables.md @@ -53,6 +53,17 @@ let apiUrl = ProcessInfo.processInfo.environment["API_URL"] This is useful for feature flags, environment switching, or any value your app reads at launch without needing to rebuild the binary. +## Built-in Variables + +DeviceCloud also sets these variables on every run, so a flow can adapt to the device it runs on: + +| Variable | Value | +| --- | --- | +| `DCD_DEVICE` | The device and OS version, lowercased and hyphenated — for example `pixel-7-api-34`, `pixel-7-api-34-play` on a Google Play device, `iphone-16-ios-18` or `ipad-pro-12-9-inch-6th-generation-ios-26`. | +| `DCD_PLATFORM` | `android` or `ios`. | + +Passing your own value with `-e` overrides the built-in one. [Visual Testing](../advanced/visual-testing.md#per-device-baselines) uses `DCD_DEVICE` to pick a baseline per device. + ## Best Practices - Never commit sensitive values From 7b8ee932a05ac9639ed2b2f9f5646f56490e976c Mon Sep 17 00:00:00 2001 From: finalerock44 Date: Wed, 23 Sep 2026 15:15:13 +0100 Subject: [PATCH 5/8] docs(workspace-config): includedPaths anchoring, containment and flow keys A single flow file passed with --config anchors patterns on that file's folder. Patterns can't leave the workspace, and the CLI errors before uploading. Raising the upload root changes the paths flows are recorded under, and so flow history and Flows API fileName matches, not just what the console shows. --- advanced/visual-testing.md | 2 +- configuration/workspace-config.md | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/advanced/visual-testing.md b/advanced/visual-testing.md index 8d661c1..5f8d235 100644 --- a/advanced/visual-testing.md +++ b/advanced/visual-testing.md @@ -43,7 +43,7 @@ Maestro resolves the path **relative to the flow file's own directory**, so this home.png ``` -You can climb out of the flow's folder with `../` if you prefer to keep baselines in one shared place. +You can climb out of the flow's folder with `../` if you prefer to keep baselines in one shared place. Keep them inside the workspace folder you pass to `dcd cloud`, because `includedPaths` can't reach outside it. Baselines outside the folder that holds your flows also move the upload root up, which changes the paths your flows are recorded under — see [`includedPaths`](../configuration/workspace-config.md#includedpaths). The file extension is optional. `assertScreenshot: screenshots/home` looks for `screenshots/home.png`. diff --git a/configuration/workspace-config.md b/configuration/workspace-config.md index dc4eaeb..42ed44e 100644 --- a/configuration/workspace-config.md +++ b/configuration/workspace-config.md @@ -46,18 +46,18 @@ includedPaths: - certs/test-ca.pem ``` -Patterns use the same [NPM glob](https://www.npmjs.com/package/glob) syntax as `flows`, and are always resolved **relative to the workspace folder you pass to `dcd cloud`** — not relative to the config file, even when you load it with `--config`. +Patterns use the same [NPM glob](https://www.npmjs.com/package/glob) syntax as `flows`, and are resolved **relative to the workspace folder you pass to `dcd cloud`** — not relative to the config file, even when you load it with `--config`. If you pass a single flow file with `--config` instead of a folder, patterns are resolved relative to that flow file's folder. Matched files keep their position relative to your flows when they are uploaded, so a baseline at `screenshots/home.png` sitting next to `visual.yaml` arrives next to that flow on the device. {% hint style="warning" %} -Patterns cannot escape the workspace folder. A pattern resolving to a file outside it (`../secrets.json`) fails the run rather than uploading it. +Patterns cannot escape the workspace folder. A pattern resolving to a file outside it (`../secrets.json`) stops the CLI with an error before anything is uploaded. {% endhint %} {% hint style="info" %} Run with `--debug` to list exactly which files were matched and uploaded. -If your included files sit **beside** your flows folder rather than inside it, the upload root moves up to cover both, and flow paths shown in the console gain a leading folder (`login.yaml` becomes `flows/login.yaml`). This is expected — it is what keeps the relative path between a flow and its files intact — and affects display only. +If included or referenced files sit **beside** your flows folder rather than inside it, the upload root moves up to cover both, and the paths your flows are recorded under gain a leading folder (`login.yaml` becomes `flows/login.yaml`). This is what keeps the relative path between a flow and its files intact, but it is more than cosmetic: a flow without a `name:` starts a new history under its new path, and anything that matches on the old path, such as `fileName` in the [Flows API](../api/flows.md), needs updating. {% endhint %} ### `includeTags` / `excludeTags` From 9387bc974428665e940df12df7dda0f202f158fc Mon Sep 17 00:00:00 2001 From: finalerock44 Date: Wed, 23 Sep 2026 15:15:22 +0100 Subject: [PATCH 6/8] docs(visual-testing): stale local diffs get uploaded; name is home_diff.png The runner collects every *_diff.png in the uploaded workspace, so a diff left by a local Maestro run and matched by includedPaths shows up as if the cloud run produced it. Say to delete them first, and give the real file name. --- advanced/visual-testing.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/advanced/visual-testing.md b/advanced/visual-testing.md index 5f8d235..cf44bf2 100644 --- a/advanced/visual-testing.md +++ b/advanced/visual-testing.md @@ -144,5 +144,5 @@ Maestro also writes a diff image highlighting the changed regions. DeviceCloud c The diff is only produced for a pixel mismatch. A size mismatch fails before any comparison happens, and reports the expected and actual dimensions in the step error instead. {% hint style="warning" %} -Maestro writes that diff next to the baseline **in your working directory** when you run locally, as `_diff.png`. Add `**/*_diff.png` to your `.gitignore` so local runs don't leave them lying around your repository. +When you run locally, Maestro writes that diff next to the baseline **in your working directory**, named after it: `screenshots/home_diff.png` for `screenshots/home.png`. Delete these before you run `dcd cloud` from the same folder. An `includedPaths` pattern such as `screenshots/**` uploads them, and DeviceCloud shows every `*_diff.png` it finds as a diff from the run. Add `**/*_diff.png` to your `.gitignore` too, so they never reach your repository. {% endhint %} From 24641c6b0712f09c61466e9ca1636cb920604dec Mon Sep 17 00:00:00 2001 From: finalerock44 Date: Wed, 23 Sep 2026 15:15:30 +0100 Subject: [PATCH 7/8] docs(artifacts): screenshots include assertScreenshot diff images --- artifacts/artifacts.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/artifacts/artifacts.md b/artifacts/artifacts.md index 5087002..2500e6e 100644 --- a/artifacts/artifacts.md +++ b/artifacts/artifacts.md @@ -7,7 +7,7 @@ DeviceCloud captures various artifacts during test execution that can help debug Each test run generates: * Logs -* Screenshots +* Screenshots, including the diff image from a failed [`assertScreenshot`](../advanced/visual-testing.md#reading-a-failure) comparison * Videos * Test reports From 4d4ca7c3bb8d299d11f7bed4c4dcbf8ca11dd0d8 Mon Sep 17 00:00:00 2001 From: finalerock44 Date: Wed, 23 Sep 2026 15:16:03 +0100 Subject: [PATCH 8/8] docs(workspace-config): includedPaths needs CLI 5.6.0 --- configuration/workspace-config.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/configuration/workspace-config.md b/configuration/workspace-config.md index 42ed44e..73742d9 100644 --- a/configuration/workspace-config.md +++ b/configuration/workspace-config.md @@ -35,7 +35,7 @@ If `flows` is omitted, all `.yaml` / `.yml` files in the directory (except confi ### `includedPaths` -Glob patterns selecting extra **non-flow** files to upload alongside your flows. +Glob patterns selecting extra **non-flow** files to upload alongside your flows. Requires DeviceCloud CLI 5.6.0 or later; older versions warn that the key is unknown and upload nothing extra. The CLI works out what to upload by reading your flows, so it only picks up files a command actually references by a literal path — `addMedia`, `assertScreenshot`, `runFlow` and `runScript` arguments. A path that contains a variable (`screenshots/${DCD_DEVICE}/home.png`), and anything else your test needs on the device, is invisible to it and silently absent from the run. `includedPaths` is how you declare those files.