Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add e2e tests #694

Merged
merged 24 commits into from
Aug 6, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
7d5d181
initial commit on template-infra from git diff
rylew1 Jul 16, 2024
4029926
Merge branch 'main' into rylew/358-e2e-tests
rylew1 Jul 16, 2024
57191e9
update docs reference
rylew1 Jul 16, 2024
d745223
add bash markdown language id
rylew1 Jul 22, 2024
2227326
rename file and update e2e-setup-ci
rylew1 Jul 22, 2024
a7d7ef9
fix docs
rylew1 Jul 22, 2024
0273c40
quick docs update
rylew1 Jul 22, 2024
0c15bc4
remove steps that are already in e2e-setup-ci
rylew1 Jul 22, 2024
4fda1f8
add errors if app_name or base_url are not passed to e2e-test make ta…
rylew1 Jul 22, 2024
ab96fa3
remove safari in derived config
rylew1 Jul 22, 2024
cc79aa4
add deepMerge for playwright configs
rylew1 Jul 22, 2024
977ffd3
remove unneeded devices playwright reference
rylew1 Jul 22, 2024
9b28220
link to browsers
rylew1 Jul 22, 2024
8705294
remove comment in makefile
rylew1 Jul 22, 2024
0f542b7
add proper deepMerge import
rylew1 Jul 23, 2024
28f829a
remove .env.example
rylew1 Jul 23, 2024
8dfdb4d
update e2e-test package.json script
rylew1 Jul 23, 2024
1b6bd95
update docs to reflect new e2e-test package.json script
rylew1 Jul 23, 2024
61fc9bd
Merge branch 'main' into rylew/358-e2e-tests
rylew1 Jul 29, 2024
213c59d
add error handling (via shell script) for npm run script
rylew1 Jul 29, 2024
1f4956d
fix shell script comments
rylew1 Jul 29, 2024
91d5a1a
remove unnecessary env definitions
rylew1 Aug 2, 2024
a40af2b
remove extra && statement in makefile
rylew1 Aug 2, 2024
9acb1d0
Merge branch 'main' into rylew/358-e2e-tests
rylew1 Aug 2, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions .github/workflows/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,8 @@ Each app should have:
- `ci-[app_name]`: must be created; should run linting and testing
- `ci-[app_name]-vulnerability-scans`: calls `vulnerability-scans`
- Based on [ci-app-vulnerability-scans](https://github.com/navapbc/template-infra/blob/main/.github/workflows/ci-app-vulnerability-scans.yml)
- `ci-[app_name]-pr-environment-update.yml`: calls `pr-environment-update.yml` to create or update a pull request environment (see [pull request environments](/docs/infra/pull-request-environments.md))
- Based on [ci-app-pr-environment-update.yml](https://github.com/navapbc/template-infra/blob/main/.github/workflows/ci-app-pr-environment-update.yml)
- `ci-[app_name]-pr-environment-checks.yml`: calls `pr-environment-checks.yml` to create or update a pull request environment (see [pull request environments](/docs/infra/pull-request-environments.md))
- Based on [ci-app-pr-environment-checks.yml](/.github/workflows/ci-app-pr-environment-checks.yml)
- `ci-[app_name]-pr-environment-destroy.yml`: calls `pr-environment-destroy.yml` to destroy the pull request environment (see [pull request environments](/docs/infra/pull-request-environments.md))
- Based on [ci-app-pr-environment-destroy.yml](https://github.com/navapbc/template-infra/blob/main/.github/workflows/ci-app-pr-environment-destroy.yml)

Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
name: CI App PR Environment Update
name: CI App PR Environment Checks
on:
workflow_dispatch:
inputs:
Expand All @@ -13,7 +13,7 @@ on:
jobs:
update:
name: " " # GitHub UI is noisy when calling reusable workflows, so use whitespace for name to reduce noise
uses: ./.github/workflows/pr-environment-update.yml
uses: ./.github/workflows/pr-environment-checks.yml
with:
app_name: "app"
environment: "dev"
Expand Down
37 changes: 37 additions & 0 deletions .github/workflows/e2e-tests.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
name: E2E Tests

on:
workflow_call:
inputs:
service_endpoint:
required: true
type: string
app_name:
required: false
type: string

jobs:
e2e:
name: " " # GitHub UI is noisy when calling reusable workflows, so use whitespace for name to reduce noise
runs-on: ubuntu-latest

steps:
- name: Checkout code
uses: actions/checkout@v4

- name: Set up Node.js
uses: actions/setup-node@v4
with:
node-version: '20'

- name: Install Playwright browsers
run: make e2e-setup-ci

- name: Run e2e tests
run: make e2e-test APP_NAME=${{ inputs.app_name }} BASE_URL=${{ inputs.service_endpoint }}

- name: Upload Playwright report
uses: actions/upload-artifact@v4
with:
name: playwright-report
path: ./e2e/playwright-report
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,9 @@ jobs:

concurrency: pr-environment-${{ inputs.pr_number }}

outputs:
service_endpoint: ${{ steps.update-environment.outputs.service_endpoint }}

steps:
- uses: actions/checkout@v4

Expand All @@ -52,6 +55,19 @@ jobs:
environment: ${{ inputs.environment }}

- name: Update environment
run: ./bin/update-pr-environment "${{ inputs.app_name }}" "${{ inputs.environment }}" "${{ inputs.pr_number }}" "${{ inputs.commit_hash }}"
id: update-environment
run: |
./bin/update-pr-environment "${{ inputs.app_name }}" "${{ inputs.environment }}" "${{ inputs.pr_number }}" "${{ inputs.commit_hash }}"
service_endpoint=$(terraform -chdir="infra/${{ inputs.app_name }}/service" output -raw service_endpoint)
echo "service_endpoint=${service_endpoint}"
echo "service_endpoint=${service_endpoint}" >> "$GITHUB_OUTPUT"
env:
GH_TOKEN: ${{ github.token }}

e2e-tests:
name: Run E2E Tests
needs: [update]
uses: ./.github/workflows/e2e-tests.yml
with:
service_endpoint: ${{ needs.update.outputs.service_endpoint }}
app_name: ${{ inputs.app_name }}
28 changes: 25 additions & 3 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -59,9 +59,9 @@ __check_defined = \
release-image-name \
release-image-tag \
release-publish \
release-run-database-migrations


release-run-database-migrations \
e2e-setup \
e2e-test

infra-set-up-account: ## Configure and create resources for current AWS profile and save tfbackend file to infra/accounts/$ACCOUNT_NAME.ACCOUNT_ID.s3.tfbackend
@:$(call check_defined, ACCOUNT_NAME, human readable name for account e.g. "prod" or the AWS account alias)
Expand Down Expand Up @@ -222,6 +222,28 @@ release-image-name: ## Prints the image name of the release image
release-image-tag: ## Prints the image tag of the release image
@echo $(IMAGE_TAG)

##############################
## End-to-end (E2E) Testing ##
##############################

e2e-setup: ## Setup end-to-end tests
@cd e2e && npm install
@cd e2e && npx playwright install --with-deps

e2e-setup-ci: ## Install system dependencies, Node dependencies, and Playwright browsers
sudo apt-get update
sudo apt-get install -y libwoff1 libopus0 libvpx7 libevent-2.1-7 libopus0 libgstreamer1.0-0 \
libgstreamer-plugins-base1.0-0 libgstreamer-plugins-good1.0-0 libharfbuzz-icu0 libhyphen0 \
libenchant-2-2 libflite1 libgles2 libx264-dev
cd e2e && npm ci
cd e2e && npx playwright install --with-deps


e2e-test: ## Run end-to-end tests
@:$(call check_defined, APP_NAME, You must pass in a specific APP_NAME)
@:$(call check_defined, BASE_URL, You must pass in a BASE_URL)
@cd e2e/$(APP_NAME) && APP_NAME=$(APP_NAME) BASE_URL=$(BASE_URL) npx playwright test $(E2E_ARGS)

########################
## Scripts and Helper ##
########################
Expand Down
73 changes: 73 additions & 0 deletions docs/e2e/e2e-checks.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
# End-to-End (E2E) Tests

## Overview

This repository uses [Playwright](https://playwright.dev/) to perform end-to-end (E2E) tests. The tests can be run locally, but also run on [Pull Request preview environments](../infra/pull-request-environments.md). This ensures that any new code changes are validated through E2E tests before being merged.

## Folder Structure
In order to support e2e for multiple apps, the folder structure will include a base playwright config (`./e2e/playwright.config.js`), and app-specific derived playwright config that override the base config. See the example folder structure below:
```
- e2e
- playwright.config.js
- app/
- playwright.config.js
- tests/
- index.spec.js
- app2/
- playwright.config.js
- tests/
- index.spec.js
```

Some highlights:
- By default, the base config is defined to run on a minimal browser-set (desktop and mobile chrome). Browsers can be added in the app-specific playwright config.
- Snapshots will be output locally or in the artifacts of the CI job
- HTML reports are output to the `playwright-report` folder
- Parallelism limited on CI to ensure stable execution
- Accessibility testing can be performed using the `@axe-core/playwright` package (https://playwright.dev/docs/accessibility-testing)


## Running Locally

### Running Locally From the Root Directory

Make targets are setup to easily pass in a particular app name and URL to run tests against

```bash
make e2e-setup # install playwright deps
make e2e-test APP_NAME=app BASE_URL=http://localhost:3000 # run tests on a particular app
```

### Running Locally From the `./e2e` Directory

If you prefer to run package.json run scripts, you can do so from the e2e folder:

```
cd e2e

npm install

APP_NAME=app npm run e2e-test
```

### PR Environments

The E2E tests are triggered in PR preview environments on each PR update. For more information on how PR environments work, please refer to [PR Environments Documentation](../infra/pull-request-environments.md).

### Workflows

The following workflows trigger E2E tests:
- [PR Environment Update](../../.github/workflows/pr-environment-checks.yml)
- [E2E Tests Workflow](../../.github/workflows/e2e-tests.yml)

The [E2E Tests Workflow](../../.github/workflows/e2e-tests.yml) takes a `service_endpoint` URL and an `app_name` to run the tests against specific configurations for your app.

## Configuration

The E2E tests are configured using the following files:
- [Base Configuration](../../e2e/playwright.config.js)
- [App-specific Configuration](../../e2e/app/playwright.config.js)

The app-specific configuration files extend the common base configuration.

By default when running `make e2e-test APP_NAME=app BASE_URL=http://localhost:3000 ` - you don't necessarily need to pass an `BASE_URL` since the default is defined in the app-specific playwright config (`./e2e/app/playwright.config.js`).
6 changes: 3 additions & 3 deletions docs/infra/pull-request-environments.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,12 +23,12 @@ This guidance is not strict. It is still okay to combine database migrations and

Pull request environments are created by GitHub Actions workflows. There are two reusable callable workflows that manage pull request environments:

- [pr-environment-update.yml](/.github/workflows/pr-environment-update.yml) - creates or updates a temporary environment in a separate Terraform workspace for a given application and pull request
- [pr-environment-checks.yml](/.github/workflows/pr-environment-checks.yml) - creates or updates a temporary environment in a separate Terraform workspace for a given application and pull request
- [pr-environment-destroy.yml](/.github/workflows/pr-environment-destroy.yml) - destroys a temporary environment and workspace for a given application and pull request

Using these reusable workflows, configure PR environments for each application with application-specific workflows:

- `ci-[app_name]-pr-environment-update.yml`
- Based on [ci-app-pr-environment-update.yml](https://github.com/navapbc/template-infra/blob/main/.github/workflows/ci-app-pr-environment-update.yml)
- `ci-[app_name]-pr-environment-checks.yml`
- Based on [ci-app-pr-environment-checks.yml](https://github.com/navapbc/template-infra/blob/main/.github/workflows/ci-app-pr-environment-checks.yml)
- `ci-[app_name]-pr-environment-destroy.yml`
- Based on [ci-app-pr-environment-destroy.yml](https://github.com/navapbc/template-infra/blob/main/.github/workflows/ci-app-pr-environment-destroy.yml)
6 changes: 6 additions & 0 deletions e2e/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
node_modules/
/test-results/
/playwright-report/
/blob-report/
/playwright/.cache/
*.png*
12 changes: 12 additions & 0 deletions e2e/app/playwright.config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import baseConfig from '../playwright.config';
import { deepMerge } from '../util';
import { defineConfig } from '@playwright/test';

export default defineConfig(deepMerge(
baseConfig,
{
use: {
baseUrl: baseConfig.use.baseUrl || "localhost:3000"
},
}
));
31 changes: 31 additions & 0 deletions e2e/app/tests/index.spec.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
const { test, expect } = require('@playwright/test');

import AxeBuilder from '@axe-core/playwright';

test.describe('Generic Webpage Tests', () => {
test('should load the webpage successfully', async ({ page }) => {
const response = await page.goto('/');
const title = await page.title();
await expect(response.status()).toBe(200);
});

test('should take a screenshot of the webpage', async ({ page }) => {
await page.goto('/');
await page.screenshot({ path: 'example-screenshot.png', fullPage: true });
});

// https://playwright.dev/docs/accessibility-testing
test('should not have any automatically detectable accessibility issues', async ({ page }) => {
await page.goto('/');
const accessibilityScanResults = await new AxeBuilder({ page }).analyze();
expect(accessibilityScanResults.violations).toEqual([]);
});

// Example test of finding a an html element on the index/home page
// test('should check for an element to be visible', async ({ page }) => {
// await page.goto('/');
// const element = page.locator('h1');
// await expect(element).toBeVisible();
// });

});
Loading
Loading