Skip to content

Commit 5ecb524

Browse files
authored
chore(ci): integrate playwright into CI (#4750)
Integrates playwright into CI. - CI: Changed cache paths - CI: removes old partioning scripts (playwright natively supports shards) - CI: updates artifact paths - config: I changed the config to start with CI-like/headless defaults, these then get overridden in the project `playwright.config.ts` - I'd like to keep things super clear and use "top-level" conditionals rather than embedded deeper down in config where possible. - config: I found a better way to pass through a baseURL from the playwright config. - config: I added a simple vite-base feature-file file watcher for running locally The only thing I'm unsure of here is the CI caching - I'm not totally sure if thats kicking in properly 👀 --------- Signed-off-by: John Cowen <john.cowen@konghq.com>
1 parent 26ccc5c commit 5ecb524

13 files changed

Lines changed: 95 additions & 107 deletions

File tree

.github/actions/bootstrap/action.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ runs:
2323
with:
2424
path: |
2525
**/node_modules
26-
/home/runner/.cache/Cypress
26+
~/.cache/ms-playwright
2727
# if you ever want to ignore or rebuild the cache you can temporarily
2828
# remove the restore-keys related comments here also see
2929
# https://github.com/actions/cache/blob/main/tips-and-workarounds.md#update-a-cache

.github/workflows/main.yml

Lines changed: 11 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -59,17 +59,7 @@ jobs:
5959
echo "e2e<<EOF" >> $GITHUB_OUTPUT
6060
make meta/e2e >> $GITHUB_OUTPUT
6161
echo "EOF" >> $GITHUB_OUTPUT
62-
- id: set-groups
63-
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8.0.0
64-
env:
65-
THREAD_COUNT: 4
66-
with:
67-
script: |
68-
const ci = require('@kumahq/config/ci')
69-
const json = {
70-
'@kumahq/kuma-gui': ci.getPartitionedTestFiles(process.env.THREAD_COUNT, 'packages/kuma-gui/'),
71-
}
72-
return json
62+
7363
lint-tests:
7464
timeout-minutes: 30
7565
needs:
@@ -111,25 +101,27 @@ jobs:
111101
fail-fast: false
112102
matrix:
113103
package: ${{ fromJSON(needs.install-dependencies.outputs.e2e) }}
114-
container: [0, 1, 2, 3]
104+
container: [1, 2, 3, 4]
115105
name: |
116106
Browser tests: ${{ matrix.package.name }}
117107
steps:
118108
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
119109
- uses: ./.github/actions/bootstrap/
120-
- run: |
110+
- env:
111+
KUMA_BASE_URL: 'http://localhost:5681/gui'
112+
KUMA_E2E_SHARD_CURRENT: ${{ matrix.container }}
113+
KUMA_E2E_SHARD_TOTAL: 4
114+
run: |
121115
make -C ${{ matrix.package.path }} run/e2e &
122-
make -C ${{ matrix.package.path }} \
123-
CYPRESS_SPEC="$(echo '${{ needs.install-dependencies.outputs.spec_groups }}' | jq -cMr '.["${{ matrix.package.name }}"]' | jq -cMr '.[${{ matrix.container }}]')" \
124-
test/e2e
116+
make -C ${{ matrix.package.path }} test/e2e
125117
- uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
126118
if: failure()
127119
with:
128-
name: cypress-artifacts-${{ matrix.package.slug }}-${{ matrix.container }}
120+
name: e2e-artifacts-${{ matrix.package.slug }}-${{ matrix.container }}
129121
retention-days: ${{ github.event_name == 'pull_request' && 3 || 30 }}
130122
path: |
131-
${{ matrix.package.path }}/cypress/screenshots
132-
${{ matrix.package.path }}/cypress/videos
123+
${{ matrix.package.path }}/test-results
124+
${{ matrix.package.path }}/playwright-report
133125
134126
post-checks:
135127
# There is a branch protection rule on the repo that requires "branch-protection" to

packages/config/package.json

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -15,9 +15,6 @@
1515
"./playwright": {
1616
"import": "./src/playwright/index.ts"
1717
},
18-
"./ci": {
19-
"require": "./scripts/ci.cjs"
20-
},
2118
"./vite": {
2219
"import": "./src/vite/index.ts"
2320
},

packages/config/scripts/ci.cjs

Lines changed: 0 additions & 36 deletions
This file was deleted.

packages/config/src/mk/playwright.mk

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,12 +3,17 @@ PLAYWRIGHT_MAKEFILE_DIR := $(dir $(abspath $(PLAYWRIGHT_MAKEFILE)))
33
PLAYWRIGHT ?= $(shell $(MAKE) resolve/bin PACKAGE=@playwright/test BIN=playwright)
44
BDDGEN ?= $(shell $(MAKE) resolve/bin PACKAGE=playwright-bdd BIN=bddgen)
55

6+
.PHONY: install
7+
install:
8+
@$(PLAYWRIGHT) install chromium --with-deps
9+
610
.PHONY: build
711
build:
812
@$(BDDGEN)
913

1014
.PHONY: test
11-
test: build
12-
@$(PLAYWRIGHT) test
15+
test: PLAYWRIGHT_FLAGS=""
16+
test: build install
17+
@$(PLAYWRIGHT) test $(PLAYWRIGHT_FLAGS)
1318

1419

packages/config/src/mk/test.mk

Lines changed: 0 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -21,21 +21,4 @@
2121

2222

2323
.PHONY: .test/e2e
24-
.test/e2e: CYPRESS ?= $(shell $(MAKE) resolve/bin BIN=cypress)
25-
.test/e2e: CYPRESS_SPEC?=**/*.feature
2624
.test/e2e:
27-
@$(CYPRESS) install
28-
ifdef KUMA_TEST_BROWSER
29-
@TZ=UTC \
30-
$(CYPRESS) \
31-
open \
32-
--e2e \
33-
--browser $(KUMA_TEST_BROWSER)
34-
else
35-
@while ! nc -z localhost 5681; do sleep 1; done;
36-
@CYPRESS_video=true \
37-
TZ=UTC \
38-
$(CYPRESS) \
39-
run \
40-
--spec $(CYPRESS_SPEC)
41-
endif
Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import { devices } from '@playwright/test'
12
import { defineBddConfig } from 'playwright-bdd'
23

34
import type { PlaywrightTestConfig } from '@playwright/test'
@@ -6,8 +7,8 @@ export const config = (config: Parameters<typeof defineBddConfig>[0] = {}) => {
67
const testDir = defineBddConfig(config)
78
return {
89
fullyParallel: true,
9-
maxFailures: process.env.CI ? 1 : 0,
10-
forbidOnly: !!process.env.CI,
10+
maxFailures: 1,
11+
forbidOnly: true,
1112
testDir,
1213
outputDir: './test-results',
1314
reporter: [
@@ -16,11 +17,18 @@ export const config = (config: Parameters<typeof defineBddConfig>[0] = {}) => {
1617
...(process.env.CI ? [['github'] as const] : []),
1718
],
1819
use: {
20+
headless: true,
1921
timezoneId: 'UTC',
2022
viewport: { width: 1920, height: 1080 },
2123
video: 'retain-on-failure',
2224
screenshot: 'only-on-failure',
2325
trace: 'retain-on-failure',
2426
},
27+
projects: [
28+
{
29+
name: 'chromium',
30+
use: { ...devices['Desktop Chrome'] },
31+
},
32+
],
2533
} satisfies PlaywrightTestConfig
2634
}

packages/config/src/vite/plugins/plugins.ts

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,25 @@
11
import { load, DEFAULT_SCHEMA, Type } from 'js-yaml'
22
import markdown from 'markdown-it'
3+
import { execSync } from 'node:child_process'
4+
import { dirname } from 'node:path'
5+
import { fileURLToPath } from 'node:url'
36

47
import type { Plugin } from 'vite'
58

9+
export const playwrightBdd = (src ='features/**/*.feature'): Plugin => {
10+
const playwrightBdd = dirname(fileURLToPath(import.meta.resolve('playwright-bdd')))
11+
return {
12+
name: 'playwrightBdd',
13+
configureServer(server) {
14+
server.watcher.add(src)
15+
server.watcher.on('change', (path) => {
16+
if(path.endsWith('.feature')) {
17+
execSync(`node ${playwrightBdd}/cli/index.js`, { stdio: 'inherit' })
18+
}
19+
})
20+
},
21+
}
22+
}
623

724
export const yamlLoader = (): Plugin => {
825
const md = markdown(
@@ -30,7 +47,7 @@ export const yamlLoader = (): Plugin => {
3047
name: 'yamlLoader',
3148
transform: async (code, filename) => {
3249
if (/\.ya?ml$/.test(filename)) {
33-
const json = load(code, { schema, filename, onWarning: (warning) => console.warn(warning.toString())})
50+
const json = load(code, { schema, filename, onWarning: (warning) => console.warn(warning.toString()) })
3451
return {
3552
code: `export default ${JSON.stringify(json)};`,
3653
map: { mappings: '' },

packages/gherkin-web/src/playwright/steps/index.ts

Lines changed: 20 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,24 +1,31 @@
11
import { createFetch } from '@kumahq/fake-api'
22
import { expect } from '@playwright/test'
3-
import { createBdd } from 'playwright-bdd'
3+
import { createBdd, test as base } from 'playwright-bdd'
44

55
import { getClient, YAML, merge, Cookie, routeToRegexp } from '../..'
66
import type { DataTable } from '@cucumber/cucumber'
77

88
type Options = {
9-
baseURL?: string
109
negativeTimeout?: number
1110
dependencies: any
1211
fs: any
1312
client?: ReturnType<typeof getClient>
1413
}
15-
export async function setupSteps({ dependencies, fs, baseURL = '', negativeTimeout = 4000, client = getClient() }: Options) {
14+
15+
export const test = base.extend<{
16+
baseURL: string
17+
}>({
18+
baseURL: async ({ baseURL }, use) => {
19+
await use(baseURL)
20+
},
21+
})
22+
23+
export async function setupSteps({ dependencies, fs, negativeTimeout = 4000, client = getClient() }: Options) {
1624
const _fetch = createFetch({
1725
dependencies,
1826
fs,
1927
})
2028
const config = {
21-
baseURL,
2229
negativeTimeout,
2330
fetch: async (...rest: Parameters<typeof _fetch>) => {
2431
const options = rest[1] ?? {}
@@ -33,8 +40,7 @@ export async function setupSteps({ dependencies, fs, baseURL = '', negativeTimeo
3340
return res
3441
},
3542
}
36-
37-
const { Given, When, Then, Before, After } = createBdd()
43+
const { Given, When, Then, Before, After } = createBdd(test)
3844

3945
const fetch = config.fetch
4046

@@ -60,7 +66,7 @@ export async function setupSteps({ dependencies, fs, baseURL = '', negativeTimeo
6066
let env: Record<string, string> = {}
6167
let selectors: Record<string, string> = {}
6268
let localStorage: Set<string>
63-
Before(async ({ context }) => {
69+
Before(async ({ context, baseURL }) => {
6470
client.reset()
6571
env = {}
6672
selectors = {}
@@ -70,7 +76,7 @@ export async function setupSteps({ dependencies, fs, baseURL = '', negativeTimeo
7076
{
7177
name: 'KUMA_MOCK_API_ENABLED',
7278
value: 'false',
73-
url: `${config.baseURL}`,
79+
url: `${baseURL}`,
7480
},
7581
])
7682
const p = Object.keys(fs).map(route => {
@@ -124,15 +130,15 @@ export async function setupSteps({ dependencies, fs, baseURL = '', negativeTimeo
124130
},
125131
)
126132
})
127-
Given('the environment', async ({ context }, yaml: string) => {
133+
Given('the environment', async ({ context, baseURL }, yaml: string) => {
128134
env = {
129135
...env,
130136
...YAML.parse(yaml) as object,
131137
}
132138
await context.addCookies(Object.entries(env).map(([key, value]) => ({
133139
name: key,
134140
value: String(value),
135-
url: `${config.baseURL}`,
141+
url: `${baseURL}`,
136142
})))
137143
})
138144

@@ -210,8 +216,8 @@ export async function setupSteps({ dependencies, fs, baseURL = '', negativeTimeo
210216

211217
// act
212218

213-
When('I visit the {string} URL', async ({ page }, path: string) => {
214-
const url = `${config.baseURL}${path}`
219+
When('I visit the {string} URL', async ({ page, baseURL }, path: string) => {
220+
const url = `${baseURL}${path}`
215221
await page.goto(url)
216222
await page.locator('[data-testid-root="mesh-app"]').waitFor({
217223
state: 'attached',
@@ -273,8 +279,8 @@ export async function setupSteps({ dependencies, fs, baseURL = '', negativeTimeo
273279

274280
// assert
275281

276-
Then('the URL is {string}', async ({ page }, expected: string) => {
277-
await page.waitForURL(`${config.baseURL}${expected}`)
282+
Then('the URL is {string}', async ({ page, baseURL }, expected: string) => {
283+
await page.waitForURL(`${baseURL}${expected}`)
278284
})
279285

280286
Then(/^the URL( doesn't | )contain[s]? "(.*)"$/, async ({ page }, assertion: string, str: string) => {

packages/kuma-gui/Makefile

Lines changed: 11 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,17 @@ test/unit: .test/unit ## Dev: run unit tests and exit
5151
test/unit/watch: .test/unit/watch ## Dev: run unit tests but watch for changes
5252

5353
.PHONY: test/e2e
54-
test/e2e: .test/e2e ## Run browser-based e2e tests against a running GUI, you may want to set KUMA_BASE_URL=http://localhost:8080/gui and KUMA_TEST_BROWSER=chrome
54+
test/e2e: ## Run browser-based e2e tests against a running GUI, you may want to set KUMA_TEST_BROWSER=chrome
55+
ifndef KUMA_TEST_BROWSER
56+
@while ! nc -z localhost 5681; do sleep 1; done;
57+
@$(MAKE) \
58+
-f $(MK)/playwright.mk \
59+
test
60+
else
61+
@$(MAKE) \
62+
-f $(MK)/playwright.mk \
63+
test PLAYWRIGHT_FLAGS="--ui"
64+
endif
5565

5666
.PHONY: build
5767
build: .build ## Dev: build a production artifact in `./dist`
@@ -74,10 +84,3 @@ release: KUMAHQ_KUMA_GUI=$(NPM_WORKSPACE_ROOT)/$(shell cat $(NPM_WORKSPACE_ROOT)
7484
release: SRC?=$(KUMAHQ_KUMA_GUI)/dist
7585
release: .release ## CI: 'releases' a built artifact, depends on the build artifact from 'make build'
7686

77-
.PHONY: playwright
78-
playwright: ## Run e2e tests with playwright
79-
@$(MAKE) \
80-
-f $(MK)/playwright.mk \
81-
test
82-
83-

0 commit comments

Comments
 (0)