Skip to content

Commit 5bfbc7f

Browse files
docs(contributing): update screenshot commands and Docker usage (#31441)
Updated the contributing guide to replace the outdated commands (`npm run test.screenshot`) with the current commands: `npm run test.e2e.docker.update-snapshots` and `npm run test.e2e.docker`. Also moved most of the explanation into the proper testing files and link to it instead of having it in the main contributing guide. Restructures the steps for running tests and removes old Docker instructions. --------- Co-authored-by: Brandy Smith <6577830+brandyscarney@users.noreply.github.com>
1 parent d88023e commit 5bfbc7f

3 files changed

Lines changed: 114 additions & 32 deletions

File tree

‎docs/CONTRIBUTING.md‎

Lines changed: 19 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -86,7 +86,7 @@ Without a reliable code reproduction, it is unlikely we will be able to resolve
8686

8787
To contribute on Windows, do the following:
8888

89-
- Configure VS Code to read/save files using line breaks (LF) instead of carriage returns (CRLF). Set it globally by navigating to: Settings -> Text Editor -> Files -> Eol. Set to `\n`.
89+
- Configure VS Code to read/save files using line breaks (LF) instead of carriage returns (CRLF). Set it globally by navigating to: Settings → Text Editor → Files → Eol. Set to `\n`.
9090

9191
- You can optionally use the following settings in your `.vscode/settings.json`:
9292
```json
@@ -290,20 +290,29 @@ npm install file:/~/ionic-vue-router-7.0.1.tgz
290290

291291
1. Locate the test to modify inside the `test/` folder in the component's directory.
292292
2. If a test exists, modify the test by adding an example to reproduce the problem fixed or feature added.
293-
3. If a new test is needed, the easiest way is to copy the `basic/` directory from the component's `test/` directory, rename it, and edit the content in both the `index.html` and `e2e.ts` file (see [Screenshot Tests](#screenshot-tests) for more information on this file).
293+
3. If a new test is needed, the easiest way is to copy the `basic/` directory from the component's `test/` directory, rename it, and edit the content in both the `index.html` and `*.e2e.ts` file (see [Screenshot Tests](#screenshot-tests) for more information on this file).
294294
4. The `preview/` directory is used in the documentation as a demo. Only update this test if there is a bug in the test or if the API has a change that hasn't been updated in the test.
295295

296-
Refer to [Ionic's E2E testing guide](/core/src/utils/test/playwright/docs/README.md) for information regarding the tools you can use to test Ionic.
296+
Refer to [Ionic's E2E testing guide](/docs/core/testing/README.md) for information regarding the tools you can use to test Ionic.
297297

298298
##### Screenshot Tests
299299

300-
1. If the test exists in screenshot, there will be a file named `e2e.ts` in the directory of the test.
301-
2. A screenshot test can be added by including this file and adding one or more `test()` calls that include a call to `page.compareScreenshot()`. See [Stencil end-to-end testing](https://stenciljs.com/docs/end-to-end-testing) and existing tests in `core/` for examples.
302-
3. **Important:** each `test()` should have only one screenshot (`page.compareScreenshot()`) call **or** it should check the expect at the end of each test. If there is a mismatch it will fail the test which will prevent the rest of the test from running, i.e. if the first screenshot fails the remaining screenshot calls would not be called _unless_ they are in a separate test or all of the expects are called at the end.
303-
4. To run screenshot locally, use the following command: `npm run test.screenshot`.
304-
- To run screenshot for a specific test, pass the path to the test or a string to search for.
305-
- For example, running all `alert` tests: `npm run test.screenshot alert`.
306-
- Or, running the basic `alert` tests: `npm run test.screenshot src/components/alert/test/basic/e2e.ts`.
300+
Screenshot tests live in the same `*.e2e.ts` files as a component's other E2E tests and assert with `toHaveScreenshot()`. They compare against ground truth screenshots that are generated in Docker, so both generating and running them use the Docker commands from the `core` directory:
301+
302+
```shell
303+
# Generate or update the ground truths for a component
304+
npm run test.e2e.docker.update-snapshots src/components/alert/
305+
306+
# Run the tests against the committed ground truths
307+
npm run test.e2e.docker src/components/alert
308+
```
309+
310+
To learn more:
311+
312+
- [Managing Screenshots](/docs/core/testing/usage-instructions.md#managing-screenshots) covers why Docker is required, which screenshots are committed, and how Ionic team members update ground truths on CI.
313+
- [Best Practices](/docs/core/testing/best-practices.md) covers the conventions screenshot tests follow, including using one screenshot assertion per test.
314+
- [Playwright Test Utils](/docs/core/testing/api.md) documents `configs`, `screenshot`, and the other helpers.
315+
- [Playwright Visual Comparisons](https://playwright.dev/docs/test-snapshots) documents Playwright's screenshot comparison APIs, including `toHaveScreenshot()` and its options.
307316

308317

309318
#### Building Changes

‎docs/core/testing/best-practices.md‎

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ This guide details best practices that should be followed when writing E2E tests
1414
- [Break up large or slow-running tests across multiple files](#practice-slow-tests)
1515
- [Use standard viewport sizes](#practice-viewport)
1616
- [Avoid using screenshots as a way of verifying functionality](#practice-screenshot-functionality)
17+
- [Use one screenshot assertion per test](#practice-one-screenshot)
1718
- [Avoid tests that compare computed values](#practice-test-computed)
1819
- [Test for positive and negative cases](#practice-positive-negative)
1920
- [Start your test with the configuration or layout in place if possible](#practice-test-config)
@@ -247,6 +248,51 @@ configs().forEach(({ config, title }) => {
247248
});
248249
```
249250

251+
<h2 id="practice-one-screenshot">Use one screenshot assertion per test</h2>
252+
253+
A failed `toHaveScreenshot()` assertion ends the test, so anything after it never runs. When a test takes several screenshots, only the first mismatch is reported and the remaining screenshots are never compared. An intentional visual change then takes several runs of the suite to fully verify.
254+
255+
Give each screenshot its own `test()`. If the screenshots must share setup, take them all and assert at the end of the test.
256+
257+
❌ Incorrect
258+
259+
A mismatch on `button-solid` means `button-outline` is never compared.
260+
261+
```typescript
262+
configs().forEach(({ config, screenshot, title }) => {
263+
test.describe(title('button: fill'), () => {
264+
test('should not have visual regressions', async ({ page }) => {
265+
await page.goto('/src/components/button/test/fill', config);
266+
267+
await expect(page.locator('#solid')).toHaveScreenshot(screenshot('button-solid'));
268+
await expect(page.locator('#outline')).toHaveScreenshot(screenshot('button-outline'));
269+
});
270+
});
271+
});
272+
```
273+
274+
✅ Correct
275+
276+
Each screenshot is compared independently, and a failure names the state that changed.
277+
278+
```typescript
279+
configs().forEach(({ config, screenshot, title }) => {
280+
test.describe(title('button: fill'), () => {
281+
test('should not have visual regressions for solid buttons', async ({ page }) => {
282+
await page.goto('/src/components/button/test/fill', config);
283+
284+
await expect(page.locator('#solid')).toHaveScreenshot(screenshot('button-solid'));
285+
});
286+
287+
test('should not have visual regressions for outline buttons', async ({ page }) => {
288+
await page.goto('/src/components/button/test/fill', config);
289+
290+
await expect(page.locator('#outline')).toHaveScreenshot(screenshot('button-outline'));
291+
});
292+
});
293+
});
294+
```
295+
250296
<h2 id="practice-test-computed">Avoid tests that compare computed values</h2>
251297

252298
All browsers render web content in slightly different manners. Instead of testing computed values such as exact pixel values, screenshots are a great way to ensure that elements are being rendered in a consistent manner across browsers.

‎docs/core/testing/usage-instructions.md‎

Lines changed: 49 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -80,12 +80,14 @@ macOS uses [XQuartz](https://www.xquartz.org) to use XServer on macOS.
8080

8181
1. Install [Homebrew](https://brew.sh) if not already installed. You can run `brew --version` to check if Homebrew is installed.
8282
2. Install XQuartz: `brew install --cask xquartz`
83-
3. Open XQuartz, go to `Preferences > Security`, and check "Allow connections from network clients".
83+
3. Open XQuartz, go to `Settings → Security`, and check "Allow connections from network clients".
8484
4. Restart your computer.
8585
5. Start XQuartz from the command line: `xhost +localhost`
86-
6. Open Docker Desktop and edit settings to give access to `/tmp/.X11-unix` in `Preferences > Resources > File sharing`.
87-
7. In the `core` directory run `echo host.docker.internal:0 > docker-display.txt`. This information is used to set the `DISPLAY` environment variable which tells Playwright how to render a headed UI from the Docker container.
88-
8. In the `core` directory run `echo /tmp/.X11-unix:/tmp/.X11-unix > docker-display-volume.txt`. This information is used to make XServer available inside of the Docker container.
86+
6. In the `core` directory run `echo host.docker.internal:0 > docker-display.txt`. This information is used to set the `DISPLAY` environment variable which tells Playwright how to render a headed UI from the Docker container.
87+
7. In the `core` directory run `echo /tmp/.X11-unix:/tmp/.X11-unix > docker-display-volume.txt`. This information is used to make XServer available inside of the Docker container.
88+
89+
> [!NOTE]
90+
> Unlike Docker Desktop, Rancher Desktop needs no file sharing configuration for this. It shares `/private/tmp` by default, which is where `/tmp` points on macOS.
8991
9092
#### Windows
9193

@@ -99,44 +101,58 @@ Windows has a native XServer called [WSLg](https://github.com/microsoft/wslg#rea
99101

100102
## Running Tests
101103

102-
### Running All Test Files
104+
Tests are run from the `core` directory with `npm run test.e2e.docker`, which runs them inside the Docker environment provided by the Ionic team through [Rancher Desktop](#installing-rancher-desktop). Any test that takes a screenshot must be run this way so that it compares against the ground truths committed to the repository. See [Managing Screenshots](#managing-screenshots) for more information.
103105

104-
All E2E tests can be run using the following command:
106+
This command builds a Docker image before tests run. It will also re-build the Docker image in the event that a Playwright update was merged into the repo.
105107

106-
```shell
107-
npm run test.e2e
108-
```
108+
Note that the Playwright report will not automatically open in your web browser when tests are complete because the tests were run in Docker. Run `npx playwright show-report` outside of Docker to open the most recent test report.
109109

110110
> [!NOTE]
111-
> This command is a wrapper for `npx playwright test`. All data passed to `npm run test.e2e` can also be passed to `npx playwright test`.
111+
> Additional setup is needed to run Playwright tests with headed mode in Docker. See [Configuring Docker for Headed Tests](#configuring-docker-for-headed-tests-optional) for more information.
112112
113113
### Running Specific Test Files
114114

115-
Specific test files can be run by passing the file paths or a directory that contains multiple test files. See [Managing Screenshots](#managing-screenshots) for generating ground truths before running screenshot tests.
115+
Scope each run to the tests you are working on by passing file paths, a directory that contains multiple test files, or a component name.
116116

117117
**Specific Test Files**
118118

119119
```shell
120-
npm run test.e2e src/components/button/test/basic/button.e2e.ts src/components/button/test/a11y/button.e2e.ts
120+
npm run test.e2e.docker src/components/button/test/basic/button.e2e.ts src/components/button/test/a11y/button.e2e.ts
121121
```
122122

123123
**Test Directory with Multiple Files**
124124

125125
```shell
126126
# Will run all the test files in the `test` directory
127-
npm run test.e2e src/components/button/test
127+
npm run test.e2e.docker src/components/button/test
128128
```
129129

130-
### Running Tests Inside Docker
130+
**Component Names**
131131

132-
While `npm run test.e2e` can be used to run tests in the same environment that you are developing in, `npm run test.e2e.docker` can be used to run tests in a Docker environment provided by the Ionic team through [Rancher Desktop](#installing-rancher-desktop). This command supports all the same features as `npm run test.e2e` detailed in the previous section.
132+
The argument is a Playwright filter, so a bare component name matches every test file whose path contains it.
133133

134-
This command builds a Docker image before tests run. It will also re-build the Docker image in the event that a Playwright update was merged into the repo.
134+
```shell
135+
npm run test.e2e.docker checkbox radio toggle
136+
```
135137

136-
Note that the Playwright report will not automatically open in your web browser when tests are complete because the tests were run in Docker. Run `npx playwright show-report` outside of Docker to open the most recent test report.
138+
### Running All Test Files
139+
140+
Omitting the filter runs every E2E test file:
141+
142+
```shell
143+
npm run test.e2e.docker
144+
```
145+
146+
There are over 400 E2E test files, which CI runs in parallel across 20 shards. A single machine runs them one shard at a time, so prefer scoping a local run to the component you changed and let CI cover the rest.
147+
148+
### Running Tests Outside of Docker
149+
150+
`npm run test.e2e` runs the tests directly in the environment you are developing in. It accepts all of the same arguments as `npm run test.e2e.docker`.
137151

138152
> [!NOTE]
139-
> Additional setup is needed to run Playwright tests with headed mode in Docker. See [Configuring Docker for Headed Tests](#configuring-docker-for-headed-tests-optional) for more information.
153+
> This command is a wrapper for `npx playwright test`. All data passed to `npm run test.e2e` can also be passed to `npx playwright test`.
154+
155+
Use this only for tests that take no screenshots. Because screenshots are resolved per platform, a screenshot test run outside of Docker compares against a ground truth that is not in the repository. See [Managing Screenshots](#managing-screenshots) for why this passes locally and fails on CI.
140156

141157
### Headed vs. Headless Tests
142158

@@ -146,14 +162,14 @@ No additional steps are needed in order to run the tests in headless mode:
146162

147163
```shell
148164
# Will run tests in headless mode
149-
npm run test.e2e src/components/chip
165+
npm run test.e2e.docker src/components/chip
150166
```
151167

152168
Playwright supports the `--headed` flag to run in headed mode which causes the visual representation of the browser to appear:
153169

154170
```shell
155171
# Will run tests in headed mode
156-
npm run test.e2e src/components/chip -- --headed
172+
npm run test.e2e.docker src/components/chip -- --headed
157173
```
158174

159175
### Debugging Tests
@@ -205,11 +221,18 @@ This is especially useful when CI reports a failure you cannot reproduce on your
205221
**Example:**
206222

207223
```shell
208-
npm run test.e2e.docker.update-snapshots src/components/radio/test/a11y/radio.e2e.ts -- --repeat-each=10
224+
npm run test.e2e.docker src/components/radio/test/a11y/radio.e2e.ts -- --repeat-each=10
209225
```
210226

211227
This runs the test 10 times, increasing the chance of catching the flaky behavior.
212228

229+
> [!WARNING]
230+
> Reproduce a flaky failure with `test.e2e.docker`, not
231+
> `test.e2e.docker.update-snapshots`. On a mismatch the update variant overwrites
232+
> the ground truth and reports the test as **passing**, so the run goes green with
233+
> no diff images and the flaky screenshot is left in your working tree. Check
234+
> `git status` if you suspect this happened.
235+
213236
#### 4. Pausing Test Execution
214237

215238
Additionally, you can pause execution of a test by using the `page.pause()` method. This pauses the script execution and allows you to manually inspect the page in the browser.
@@ -238,6 +261,10 @@ test('example test', async ({ page }) => {
238261

239262
If you are running a test that takes a screenshot, you must first generate the reference screenshot from your reference branch. This is known as generating a "ground truth screenshot". All other screenshots will be compared to this ground truth.
240263

264+
Playwright appends the browser and platform to every screenshot name, so the same test resolves a different file per operating system. Example: `button-expand-md-ltr-Mobile-Chrome-linux.png`. The ground truths committed to the repository are the `-linux.png` files generated in Docker, and `.gitignore` excludes every other platform's.
265+
266+
This is why screenshot tests should be run with `npm run test.e2e.docker`. Running them natively on macOS or Windows looks for a `-darwin.png` or `-win32.png` ground truth that is not in the repository. Playwright writes that file, fails the test once, and passes on every run afterward against a baseline that git ignores and CI never sees. The result is a test that passes locally and fails on CI.
267+
241268
### Generating or Updating Ground Truths With Docker (Local Development)
242269

243270
We recommend generating ground truths inside of [Docker](https://www.docker.com) using [Rancher Desktop](#installing-rancher-desktop). This allows anyone contributing to Ionic Framework to create or update ground truths in a consistent environment.
@@ -319,7 +346,7 @@ test-results-[current shard]-[total shards]
319346
320347
Example:
321348
322-
test-results-2-5 --> Test results from job runner 2 out of 5.
349+
test-results-2-5 → Test results from job runner 2 out of 5.
323350
```
324351

325352
Download the appropriate artifact and unzip the file.

0 commit comments

Comments
 (0)