Skip to content

Commit 15e8ee2

Browse files
authored
test(e2e): create initial e2e github action (#562)
1 parent c8b0b28 commit 15e8ee2

File tree

10 files changed

+207
-19
lines changed

10 files changed

+207
-19
lines changed

.github/workflows/e2e.yml

Lines changed: 125 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,125 @@
1+
name: End-to-End Tests
2+
permissions:
3+
contents: read # allow checkout
4+
pull-requests: write # allow commenting later if desired
5+
6+
# note: should we have env datasets declared like in monorepoe2e.yml?
7+
8+
on:
9+
# run on PRs, even drafts
10+
pull_request:
11+
# run on main branch when it changes
12+
push:
13+
branches: [main]
14+
15+
# Cancel in-progress runs from the same PR/branch
16+
concurrency:
17+
group: e2e-${{ github.workflow }}-${{ github.head_ref || github.run_id }}
18+
cancel-in-progress: true
19+
20+
jobs:
21+
# run / share the same install step for all browsers
22+
install:
23+
runs-on: ubuntu-latest
24+
steps:
25+
- name: 📥 Checkout repository
26+
uses: actions/checkout@v4
27+
28+
- name: 🧰 Setup pnpm
29+
uses: pnpm/action-setup@v4
30+
31+
- name: 🛠️ Setup Node.js
32+
uses: actions/setup-node@v4
33+
with:
34+
cache: "pnpm"
35+
node-version: "lts/*"
36+
37+
- name: 📦 Install dependencies
38+
run: pnpm install --frozen-lockfile
39+
40+
# see if the Playwright version we're using is already cached -- skip download if so
41+
- name: 🔍 Determine Playwright version
42+
id: pw-version
43+
run: echo "version=$(npx playwright --version | sed 's/Version //')" >> "$GITHUB_OUTPUT"
44+
45+
- name: 💾 Cache Playwright browsers
46+
id: cache-playwright-browsers
47+
uses: actions/cache@v4
48+
with:
49+
path: ~/.cache/ms-playwright
50+
key: ${{ runner.os }}-${{ steps.pw-version.outputs.version }}-playwright-browsers
51+
52+
- name: Install Playwright browsers
53+
if: steps.cache-playwright-browsers.outputs.cache-hit != 'true'
54+
run: npx playwright install --with-deps
55+
56+
playwright-tests:
57+
needs: install
58+
timeout-minutes: 30
59+
runs-on: ubuntu-latest
60+
61+
# Browser matrix to parallelise executions.
62+
strategy:
63+
fail-fast: false
64+
matrix:
65+
browser: [chromium, firefox, webkit]
66+
67+
# Shared env required by the SDK E2E helpers. Configure these in the repo → Settings → Secrets / Variables
68+
env:
69+
SDK_E2E_PROJECT_ID: ${{ secrets.SDK_E2E_PROJECT_ID }}
70+
SDK_E2E_DATASET_0: ${{ github.event_name == 'pull_request' && format('pr-{0}-{1}-{2}', github.event.number, matrix.browser, github.run_id) || format('main-{0}-{1}', matrix.browser, github.run_id) }}
71+
SDK_E2E_DATASET_1: ${{ github.event_name == 'pull_request' && format('pr-{0}-{1}-secondary-{2}', github.event.number, matrix.browser, github.run_id) || format('main-{0}-secondary-{1}', matrix.browser, github.run_id) }}
72+
SDK_E2E_SESSION_TOKEN: ${{ secrets.SDK_E2E_SESSION_TOKEN }}
73+
SDK_E2E_USER_ID: ${{ secrets.SDK_E2E_USER_ID }}
74+
SDK_E2E_USER_PASSWORD: ${{ secrets.SDK_E2E_USER_PASSWORD }}
75+
RECAPTCHA_E2E_STAGING_KEY: ${{ secrets.RECAPTCHA_E2E_STAGING_KEY }}
76+
77+
steps:
78+
- name: 📥 Checkout repository
79+
uses: actions/checkout@v4
80+
81+
- name: 🧰 Setup pnpm
82+
uses: pnpm/action-setup@v4
83+
84+
- name: 🛠️ Setup Node.js
85+
uses: actions/setup-node@v4
86+
with:
87+
cache: "pnpm"
88+
node-version: "lts/*"
89+
90+
- name: 📦 Install dependencies
91+
run: pnpm install --frozen-lockfile
92+
93+
- name: 🔍 Determine Playwright version
94+
id: pw-version
95+
run: echo "version=$(npx playwright --version | sed 's/Version //')" >> "$GITHUB_OUTPUT"
96+
97+
- name: 💾 Restore cached Playwright browsers
98+
id: cache-playwright-browsers
99+
uses: actions/cache/restore@v4
100+
with:
101+
path: ~/.cache/ms-playwright
102+
key: ${{ runner.os }}-${{ steps.pw-version.outputs.version }}-playwright-browsers
103+
104+
- name: Install Playwright browsers
105+
if: steps.cache-playwright-browsers.outputs.cache-hit != 'true'
106+
run: npx playwright install --with-deps
107+
108+
- name: Install WebKit Dependencies
109+
if: matrix.browser == 'webkit'
110+
run: pnpm exec playwright install-deps webkit
111+
112+
- name: 🧪 Run E2E tests
113+
run: pnpm test:e2e -- --project ${{ matrix.browser }}
114+
115+
- name: 📤 Upload Playwright HTML report
116+
if: always()
117+
uses: actions/upload-artifact@v4
118+
with:
119+
name: playwright-report-${{ matrix.browser }}
120+
path: apps/kitchensink-react/e2e/test-report
121+
retention-days: 30
122+
123+
- name: 🧹 Cleanup E2E datasets
124+
if: always()
125+
run: pnpm cleanup:e2e

apps/kitchensink-react/package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
"install": "npm run schema:extract && npm run types:generate",
1212
"lint": "eslint .",
1313
"paramour": "npx paramour --config=./src/css/css.config.js --output=./src/css/paramour.css",
14+
"preview": "vite preview",
1415
"schema:extract": "sanity schema extract --workspace ppsg7ml5-test --path schema.ppsg7ml5.test.json && sanity schema extract --workspace d45jg133-production --path schema.d45jg133.production.json",
1516
"test:e2e": "playwright test",
1617
"tsc": "tsc --noEmit",

apps/kitchensink-react/playwright.config.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,9 @@ export default createPlaywrightConfig({
44
testDir: './e2e',
55
/* Run your local dev server before starting the tests */
66
webServer: {
7-
command: 'pnpm dev --mode e2e',
7+
command: process.env['CI']
8+
? 'pnpm build --mode e2e && pnpm preview --mode e2e --port 3333'
9+
: 'pnpm dev --mode e2e',
810
url: 'http://localhost:3333',
911
reuseExistingServer: !process.env['CI'],
1012
stdout: 'pipe',

package.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,8 +18,8 @@
1818
"scripts": {
1919
"all": "turbo run build && turbo run lint && turbo run test:coverage && pnpm build:docs && pnpm depcheck",
2020
"build": "turbo run build --filter='./packages/*' --filter='./apps/*'",
21-
"build:docs": "turbo run docs && typedoc",
2221
"build:bundle": "turbo run build:bundle --filter='./packages/*' --filter='!./packages/@repo/*'",
22+
"build:docs": "turbo run docs && typedoc",
2323
"build:packages": "turbo run build --filter='./packages/*' --filter='!./packages/@repo/*'",
2424
"build:visualizer": "VISUALIZER=true turbo run build --filter='./packages/*'",
2525
"clean": "run-s clean:build clean:deps",
@@ -49,6 +49,7 @@
4949
"@commitlint/config-conventional": "^19.8.0",
5050
"@commitlint/types": "^19.8.0",
5151
"@google-cloud/storage": "^7.16.0",
52+
"@playwright/test": "^1.52.0",
5253
"@repo/config-eslint": "workspace:*",
5354
"@repo/config-test": "workspace:*",
5455
"@repo/tsconfig": "workspace:*",

packages/@repo/e2e/src/helpers/getE2EEnv.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,11 @@ function readEnv(name: KnownEnvVar): string {
3333
}
3434
const val = findEnv(name)
3535
if (val === undefined) {
36+
// eslint-disable-next-line no-console
37+
console.error(
38+
`Environment variable "${name}" not found. Available env vars:`,
39+
Object.keys(process.env),
40+
)
3641
throw new Error(
3742
`Missing required environment variable "${name}". Make sure to copy \`.env.example\` to \`.env.local\``,
3843
)

packages/@repo/e2e/src/helpers/loadEnvFiles.ts

Lines changed: 20 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,25 @@
11
import fs from 'node:fs'
22
import path from 'node:path'
3-
import {fileURLToPath} from 'node:url'
43

54
import dotenv from 'dotenv'
65

6+
/**
7+
* Find the monorepo root directory by looking for the root package.json
8+
*/
9+
function findMonorepoRoot(): string {
10+
let currentDir = process.cwd()
11+
while (currentDir !== '/') {
12+
if (fs.existsSync(path.join(currentDir, 'package.json'))) {
13+
const pkg = JSON.parse(fs.readFileSync(path.join(currentDir, 'package.json'), 'utf-8'))
14+
if (pkg.name === 'sdk-root') {
15+
return currentDir
16+
}
17+
}
18+
currentDir = path.dirname(currentDir)
19+
}
20+
throw new Error('Could not find monorepo root directory')
21+
}
22+
723
/**
824
* Load environment variables from .env files, mirroring the behavior of Vite.
925
*
@@ -14,12 +30,10 @@ export function loadEnvFiles(): string[] {
1430
const envFiles = ['.env', '.env.local', `.env.${mode}`, `.env.${mode}.local`]
1531
const loaded: string[] = []
1632

17-
// Get the directory path using import.meta.url
18-
const __filename = fileURLToPath(import.meta.url)
19-
const __dirname = path.dirname(__filename)
20-
33+
// Load from monorepo root directory
34+
const rootDir = findMonorepoRoot()
2135
for (const file of envFiles) {
22-
const envFilePath = path.join(__dirname, '..', '..', '..', '..', file)
36+
const envFilePath = path.join(rootDir, file)
2337
if (!fs.existsSync(envFilePath)) {
2438
continue
2539
}

packages/@repo/e2e/src/index.ts

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -53,12 +53,21 @@ export const basePlaywrightConfig: PlaywrightTestConfig = {
5353
testDir: TEARDOWN_DIR,
5454
testMatch: /.*\.teardown\.ts/,
5555
},
56-
// we can add as many different projects as we like here
5756
{
5857
name: 'chromium',
5958
use: {...devices['Desktop Chrome'], storageState: AUTH_FILE},
6059
dependencies: ['setup'],
6160
},
61+
{
62+
name: 'firefox',
63+
use: {...devices['Desktop Firefox'], storageState: AUTH_FILE},
64+
dependencies: ['setup'],
65+
},
66+
{
67+
name: 'webkit',
68+
use: {...devices['Desktop Safari'], storageState: AUTH_FILE},
69+
dependencies: ['setup'],
70+
},
6271
],
6372
}
6473

packages/@repo/e2e/src/scripts/cleanup-datasets.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,8 +12,13 @@ const env = getE2EEnv()
1212
async function cleanupDatasets() {
1313
const primaryDataset = sanitizeDatasetName(env.SDK_E2E_DATASET_0)
1414
const secondaryDataset = sanitizeDatasetName(env.SDK_E2E_DATASET_1)
15+
if (!env.CI) {
16+
console.log('Skipping cleanup in non-CI environment')
17+
return
18+
}
1519

1620
const client = getClient()
21+
1722
const timer = startTimer('Cleaning up test datasets')
1823

1924
try {

pnpm-lock.yaml

Lines changed: 9 additions & 6 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

turbo.json

Lines changed: 27 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,19 @@
11
{
22
"$schema": "https://turbo.build/schema.json",
3-
"globalEnv": ["DEV", "VISUALIZER", "PKG_VERSION", "NPM_CONFIG_PROVENANCE", "CI"],
3+
"globalEnv": [
4+
"DEV",
5+
"VISUALIZER",
6+
"PKG_VERSION",
7+
"NPM_CONFIG_PROVENANCE",
8+
"CI",
9+
"SDK_E2E_PROJECT_ID",
10+
"SDK_E2E_DATASET_0",
11+
"SDK_E2E_DATASET_1",
12+
"SDK_E2E_SESSION_TOKEN",
13+
"SDK_E2E_USER_ID",
14+
"SDK_E2E_USER_PASSWORD",
15+
"RECAPTCHA_E2E_STAGING_KEY"
16+
],
417
"tasks": {
518
"build": {
619
"dependsOn": ["^build"],
@@ -18,6 +31,18 @@
1831
"dependsOn": ["^check-types"]
1932
},
2033
"clean": {},
34+
"cleanup": {
35+
"cache": false,
36+
"env": [
37+
"SDK_E2E_PROJECT_ID",
38+
"SDK_E2E_DATASET_0",
39+
"SDK_E2E_DATASET_1",
40+
"SDK_E2E_SESSION_TOKEN"
41+
]
42+
},
43+
"depcheck": {
44+
"env": ["DEPCHECK"]
45+
},
2146
"dev": {
2247
"persistent": true,
2348
"cache": false
@@ -59,10 +84,8 @@
5984
"dependsOn": ["@repo/e2e#build"],
6085
"outputs": ["e2e/test-results/**", "e2e/test-report/**"]
6186
},
62-
"depcheck": {
63-
"env": ["DEPCHECK"]
64-
},
6587
"@repo/e2e#build": {
88+
"dependsOn": ["^build"],
6689
"outputs": ["dist/**"],
6790
"cache": true
6891
}

0 commit comments

Comments
 (0)