test(ci): run tests in docker container (#28893)

Issue number: Internal

---------

<!-- Please do not submit updates to dependencies unless it fixes an
issue. -->

<!-- Please try to limit your pull request to one type (bugfix, feature,
etc). Submit multiple pull requests if needed. -->

## What is the current behavior?
<!-- Please describe the current behavior that you are modifying. -->

The team currently faces two challenges:

1. Verifying visual changes locally is difficult because we cannot use
the existing ground truths as they were generated in Linux environments
and most of our team uses either macOS or Windows. While team members
can generate ground truths in the correct environment, they need to
remember to do that first before making changes.
2. Updating visual diffs is time consuming and can only be done by team
members. Our GitHub Action runs the entire test suite which can take ~10
even if only a handful of screenshots are generated. Additionally, this
job can only be run by team members meaning community contributors
cannot update/add screenshots. This limits them to non-visual tasks when
contributing. In the event that they do want to make visual changes, the
team needs to copy all their code into a branch and manually run
screenshot diffs for them.

## What is the new behavior?
<!-- Please describe the behavior or changes that are being added by
this PR. -->

- This PR introduces the ability to run all Playwright tests inside of a
Docker container using an image with Playwright dependencies. The
container will have access to the local project, so developers can make
changes and then run tests in the container after the changes are
compiled. This enables anyone to propose new screenshot changes.
However, the "update screenshot" job will still be available for folks
who do not want/are unable to use docker.
- There are some typeface differences between GH Actions and the Docker
image which is why there are a handful of screenshots that needed to be
updated.

One risk here is that the Playwright npm and Docker image versions must
be kept in sync. As a result, I also updatRenovate to allow us to auto
update the npm and Docker image versions at the same time.

## Does this introduce a breaking change?

- [ ] Yes
- [x] No

<!--
  If this introduces a breaking change:
1. Describe the impact and migration path for existing applications
below.
  3. Update the BREAKING.md file with the breaking change.
4. Add "BREAKING CHANGE: [...]" to the commit description when merging.
See
https://github.com/ionic-team/ionic-framework/blob/main/.github/CONTRIBUTING.md#footer
for more information.
-->


## Other information

<!-- Any other information that is important to this PR such as
screenshots of how the component looks before and after the change. -->


⚠️ There are still some issues I need to sort out with mounting the
local project on Windows. However, using Ubuntu with the linux subsystem
for windows can be used as a workaround. I'd like to merge this so we
can start testing it in our day-to-day workflow and ironing out any
bugs.

---------

Co-authored-by: ionitron <hi@ionicframework.com>
This commit is contained in:
Liam DeBeasi 2024-03-18 10:08:30 -04:00 committed by GitHub
parent 02f89bbc42
commit e98620ee99
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
44 changed files with 142 additions and 17 deletions

View File

@ -21,8 +21,8 @@ runs:
name: ionic-core
path: ./core
filename: CoreBuild.zip
- name: Install Playwright Dependencies
run: npm install && npx playwright install && npx playwright install-deps
- name: Install Dependencies
run: npm install
shell: bash
working-directory: ./core
- id: clean-component-name
@ -47,7 +47,7 @@ runs:
shell: bash
- name: Test
if: inputs.update != 'true'
run: npm run test.e2e ${{ steps.set-test-file.outputs.testFile }} -- --shard=${{ inputs.shard }}/${{ inputs.totalShards }}
run: npm run test.e2e.docker.ci ${{ steps.set-test-file.outputs.testFile }} -- --shard=${{ inputs.shard }}/${{ inputs.totalShards }}
shell: bash
working-directory: ./core
- name: Test and Update
@ -69,7 +69,7 @@ runs:
# which is why we not using the upload-archive
# composite step here.
run: |
npm run test.e2e ${{ steps.set-test-file.outputs.testFile }} -- --shard=${{ inputs.shard }}/${{ inputs.totalShards }} --update-snapshots
npm run test.e2e.docker.ci ${{ steps.set-test-file.outputs.testFile }} -- --shard=${{ inputs.shard }}/${{ inputs.totalShards }} --update-snapshots
git add src/\*.png --force
mkdir updated-screenshots
cd ../ && rsync -R --progress $(git diff --name-only --cached) core/updated-screenshots

View File

@ -25,12 +25,9 @@ runs:
# Configure user as Ionitron
# and push only the changed .png snapshots
# to the remote branch.
# Screenshots are in .gitignore
# Non-Linux screenshots are in .gitignore
# to prevent local screenshots from getting
# pushed to Git. As a result, we need --force
# here so that CI generated screenshots can
# get added to git. Screenshot ground truths
# should only be added via this CI process.
# pushed to Git.
run: |
git config user.name ionitron
git config user.email hi@ionicframework.com
@ -38,7 +35,7 @@ runs:
# This adds an empty entry for new
# screenshot files so we can track them with
# git diff
git add src/\*.png --force -N
git add src/\*.png -N
if git diff --exit-code; then
echo -e "\033[1;31m⚠ Error: No new screenshots generated ⚠️\033[0m"
@ -48,7 +45,7 @@ runs:
else
# This actually adds the contents
# of the screenshots (including new ones)
git add src/\*.png --force
git add src/\*.png
git commit -m "chore(): add updated snapshots"
git push
fi

11
.gitignore vendored
View File

@ -68,7 +68,16 @@ core/www/
# playwright
core/test-results/
core/playwright-report/
core/**/*-snapshots
# ground truths generated outside of docker should not be committed to the repo
core/**/*-snapshots/*
# new ground truths should only be generated inside of docker which will result in -linux.png screenshots
!core/**/*-snapshots/*-linux.png
# these files are going to be different per-developer environment so do not add them to the repo
core/docker-display.txt
core/docker-display-volume.txt
# angular
packages/angular/css/

5
core/Dockerfile Normal file
View File

@ -0,0 +1,5 @@
# Get Playwright
FROM mcr.microsoft.com/playwright:v1.42.1
# Set the working directory
WORKDIR /ionic

View File

@ -94,7 +94,12 @@
"test.e2e.update-snapshots": "npm run test.e2e -- --update-snapshots",
"test.watch": "jest --watch --no-cache",
"test.treeshake": "node scripts/treeshaking.js dist/index.js",
"validate": "npm run lint && npm run test && npm run build && npm run test.treeshake"
"validate": "npm run lint && npm run test && npm run build && npm run test.treeshake",
"docker.build": "docker build -t ionic-playwright .",
"test.e2e.docker": "npm run docker.build && docker run -it --rm -e DISPLAY=$(cat docker-display.txt) -v $(cat docker-display-volume.txt) --ipc=host --mount=type=bind,source=./,target=/ionic ionic-playwright npm run test.e2e --",
"test.e2e.docker.update-snapshots": "npm run test.e2e.docker -- --update-snapshots",
"test.e2e.docker.ci": "npm run docker.build && docker run -e CI='true' --rm --ipc=host --mount=type=bind,source=./,target=/ionic ionic-playwright npm run test.e2e --",
"test.report": "npx playwright show-report"
},
"author": "Ionic Team",
"license": "MIT",

Binary file not shown.

Before

Width:  |  Height:  |  Size: 26 KiB

After

Width:  |  Height:  |  Size: 27 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 18 KiB

After

Width:  |  Height:  |  Size: 19 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 26 KiB

After

Width:  |  Height:  |  Size: 27 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 18 KiB

After

Width:  |  Height:  |  Size: 19 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 33 KiB

After

Width:  |  Height:  |  Size: 35 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 22 KiB

After

Width:  |  Height:  |  Size: 21 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 33 KiB

After

Width:  |  Height:  |  Size: 35 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 22 KiB

After

Width:  |  Height:  |  Size: 21 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 19 KiB

After

Width:  |  Height:  |  Size: 19 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 10 KiB

After

Width:  |  Height:  |  Size: 10 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 19 KiB

After

Width:  |  Height:  |  Size: 19 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 10 KiB

After

Width:  |  Height:  |  Size: 10 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 19 KiB

After

Width:  |  Height:  |  Size: 19 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 11 KiB

After

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 19 KiB

After

Width:  |  Height:  |  Size: 19 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 11 KiB

After

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 40 KiB

After

Width:  |  Height:  |  Size: 38 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 42 KiB

After

Width:  |  Height:  |  Size: 42 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 31 KiB

After

Width:  |  Height:  |  Size: 31 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 43 KiB

After

Width:  |  Height:  |  Size: 42 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 47 KiB

After

Width:  |  Height:  |  Size: 47 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 33 KiB

After

Width:  |  Height:  |  Size: 33 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 52 KiB

After

Width:  |  Height:  |  Size: 47 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 52 KiB

After

Width:  |  Height:  |  Size: 49 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 43 KiB

After

Width:  |  Height:  |  Size: 42 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 53 KiB

After

Width:  |  Height:  |  Size: 47 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 53 KiB

After

Width:  |  Height:  |  Size: 50 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 43 KiB

After

Width:  |  Height:  |  Size: 42 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 51 KiB

After

Width:  |  Height:  |  Size: 46 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 52 KiB

After

Width:  |  Height:  |  Size: 48 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 42 KiB

After

Width:  |  Height:  |  Size: 40 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 51 KiB

After

Width:  |  Height:  |  Size: 45 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 52 KiB

After

Width:  |  Height:  |  Size: 48 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 42 KiB

After

Width:  |  Height:  |  Size: 40 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 30 KiB

After

Width:  |  Height:  |  Size: 29 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 31 KiB

After

Width:  |  Height:  |  Size: 30 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 26 KiB

After

Width:  |  Height:  |  Size: 25 KiB

View File

@ -5,6 +5,7 @@ E2E tests verify Ionic components in a real browser. This is useful for testing
## Table of Contents
- [Installing Dependencies](#installing-dependencies)
- [Configuring Docker](#configuring-docker)
- [Running Tests](#running-tests)
- [Managing Screenshots](#managing-screenshots)
- [Further Reading](#further-reading)
@ -16,6 +17,54 @@ Follow these steps to install Playwright dependencies. These steps must also be
1. Install the Playwright dependency in the `core` directory: `npm ci`
2. Download the correct browsers: `npx playwright install`
## Configuring Docker
Ionic uses [Docker](https://www.docker.com) to provide a way to run tests locally in the same environment that is used on CI. Using Docker is **optional** as all tests can be run locally on your host machine, but there are a few reasons why you might want to use Docker to run tests locally:
1. You want to run screenshot tests against the same ground truths used on CI to test your work. Screenshots must be tested in a consistent environment otherwise there will be screenshot mismatches. Without Docker, you would first need to generate ground truths in your local environment.
2. You want to update ground truths locally. Ground truths can be updated using a GitHub Action on CI, but this can take ~15 minutes.
3. You are a community contributor and you do not have access to the GitHub Action to update ground truths.
4. You want to debug an issue that only happen on CI. While this is rare, there are sometimes Linux-specific issues that pop up during development.
The [Running Tests](#running-tests) and [Managing Screenshots](#managing-screenshots) sections show how to perform various tasks in Playwright with Docker. The section below shows how to configure your environment to get set up with Docker.
### Installing Docker
Docker can be installed by [following the steps on the Docker website](https://docs.docker.com/get-docker/).
### Configuring Docker for Headed Tests (Optional)
Additional software is needed to run headed tests inside of Docker. The Docker-specific test commands such as `npm run test.e2e.docker` are configured to use this additional software, but it is up to the developer to ensure that the software is installed and running.
Playwright relies on [XServer](https://www.x.org/wiki/XServer/), a windowing system used to draw and move windows on a display, in order to run tests in headed mode. Follow the steps below to install XServer on your computer.
> [!NOTE]
> The following instructions are based off https://www.oddbird.net/2022/11/30/headed-playwright-in-docker/
#### macOS
macOS uses [XQuartz](https://www.xquartz.org) to use XServer on macOS.
1. Install [Homebrew](https://brew.sh) if not already installed. You can run `brew --version` to check if Homebrew is installed.
2. Install XQuartz: `brew install --cask xquartz`
3. Open XQuartz, go to `Preferences > Security`, and check “Allow connections from network clients”.
4. Restart your computer.
5. Start XQuartz from the command line: `xhost +localhost`
6. Open Docker Desktop and edit settings to give access to `/tmp/.X11-unix` in `Preferences > Resources > File sharing`.
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.
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.
#### Windows
Windows has a native XServer called [WSLg](https://github.com/microsoft/wslg#readme) that is included as part of the [Windows Subsystem for Linux (WSL)](https://apps.microsoft.com/store/detail/9P9TQF7MRM4R?hl=en-us&gl=US). If you are running Docker Desktop on Windows 10 or 11 you likely already have both WSL and WSLg installed. The following steps show how to verify the WSL and WSLg are installed as well as how to configure your environment for headed tests in Docker.
If either of the below verification checks fail, then developers should [download the latest version of WSL](https://apps.microsoft.com/store/detail/9P9TQF7MRM4R?hl=en-us&gl=US).
1. To verify WSL is installed, launch "WSL" from the start menu. If "WSL" does not show up in the start menu then you do not have WSL installed.
2. With WSL open, verify that WSLg is installed: `ls -a -w 1 /mnt/wslg`. If the command fails with `No such file or directory` then your system is either missing WSL or running an old version.
3. In the `core` directory run `echo :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.
4. 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.
## Running Tests
### Running All Test Files
@ -46,11 +95,62 @@ npm run test.e2e src/components/button/test/basic/button.e2e.ts src/components/b
npm run test.e2e src/components/button/test
```
### Running Tests Inside Docker
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. This command supports all the same features as `npm run test.e2e` detailed in the previous section.
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.
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 `npm run test.report` outside of Docker to open the most recent test report.
> [!NOTE]
> Additional setup is needed to run Playwright tests with headed mode in Docker. See below for more information.
### Headed vs. Headless Tests
Playwright tests in Ionic are run in headless mode by default. This means that a visual representation of the browser does not appear on your computer while running.
No additional steps are needed in order to run the tests in headless mode:
```shell
# Will run tests in headless mode
npm run test.e2e src/components/chip
```
Playwright supports the `--headed` flag to run in headed mode which causes the visual representation of the browser to appear:
```shell
# Will run tests in headed mode
npm run test.e2e src/components/chip -- --headed
```
## Managing Screenshots
### Generating or Updating Ground Truths (Local Development)
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.
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. Alternatively, if the reference branch has changed since the last time you generated ground truths you may need to update your local ground truths.
### Generating or Updating Ground Truths With Docker (Local Development)
We recommend generating ground truths inside of [Docker](https://www.docker.com). This allows anyone contributing to Ionic Framework to create or update ground truths.
To create or update ground truths, run the following command:
```shell
npm run test.e2e.docker.update-snapshots
```
Optionally, you can pass a directory to only update the ground truths for that directory & subdirectories. This is useful when working on a specific component.
```shell
npm run test.e2e.docker.update-snapshots src/components/alert/
```
The resulting screenshots should be committed and pushed to your branch.
### Generating or Updating Ground Truths Without Docker (Local Development)
While we recommend generating ground truths inside of Docker it is possible to generate ground truths without it. Note that these generated ground truths can only be used for local testing and will not update the ground truths stored in the repo.
If the reference branch has changed since the last time you generated ground truths you may need to update your local ground truths.
For most types of work the reference branch is typically `main`. Features are merged into a different branch, so developers should use that as the reference branch. For example, if branch `foo` will be merged into `bar`, then the reference branch is `bar`.
@ -73,10 +173,10 @@ From here, you can switch back to your branch and run the tests.
> [!NOTE]
> Locally generated ground truths should not be committed to the repo. The `.gitignore` file prevents this from accidentally happening.
### Updating Ground Truths (CI)
### Generating or Updating Ground Truths (CI)
> [!IMPORTANT]
> Only Ionic Team members can update ground truths on the main repo. Ground truths cannot be updated on forked versions of the repo.
> Only Ionic Team members can update ground truths on the main repo. Ground truths cannot be updated on forked versions of the repo. Instead, we recommend generating ground truths in Docker.
When making an intentional visual change, you will need to update the ground truth screenshots or add new ones. It is important that the ground truth and comparison screenshots are taken in the same environment, so do not update the ground truth screenshots locally and commit them to the repo.

View File

@ -29,6 +29,15 @@
"core/package.json"
]
},
{
matchDatasources: ["docker"],
matchPackageNames: ["mcr.microsoft.com/playwright"],
groupName: "playwright",
matchFileNames: [
"core/Dockerfile"
],
versioning: "semver"
},
{
matchPackagePatterns: ["ionicons"],
groupName: "ionicons",