Skip to content

Commit d84a710

Browse files
committed
test(scratch-gui): add Playwright cross-browser test infrastructure
scratch-gui's existing browser-based tests use Selenium WebDriver and are hard-coded to Chrome, so cross-browser regressions (Firefox-specific pointer-event behavior, for example) only get noticed once they hit staging. Add @playwright/test as parallel infrastructure that runs the same tests in both Chromium and Firefox. This change is pure infrastructure: it does not replace the existing Selenium tests, and the only included Playwright test is a smoke check that loads build/index.html and asserts the editor mounts. Follow-up PRs can add richer interaction tests on top of this foundation. The new test:playwright script is chained into `npm test` so the full test suite runs in a single command, both locally and in CI. Devs need to run `npx playwright install` once before their first run; CI does this in the test-package action. CI changes: - Unify the existing scratch-render Playwright install step with a scratch-gui install step. Both packages now share one install step that pulls Chromium + Firefox; scratch-render carries Firefox as unused weight rather than maintaining a per-package browser list. - No new test step is needed because `npm test` runs the Playwright suite as part of its normal chain.
1 parent e9738b2 commit d84a710

5 files changed

Lines changed: 180 additions & 4 deletions

File tree

‎.github/actions/test-package/action.yml‎

Lines changed: 20 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -11,9 +11,14 @@ runs:
1111
using: composite
1212
steps:
1313
- name: Install Playwright
14-
if: ${{ inputs.package_name == 'scratch-render' }}
14+
if: ${{ inputs.package_name == 'scratch-render' || inputs.package_name == 'scratch-gui' }}
1515
shell: bash
16-
run: npx playwright install --with-deps chromium
16+
# Install both browsers for any package that needs Playwright,
17+
# even if a particular package only uses one of them — keeping
18+
# the install step uniform is much simpler than maintaining a
19+
# per-package browser list, and Firefox in scratch-render is
20+
# cheap unused weight rather than a correctness problem.
21+
run: npx playwright install --with-deps chromium firefox
1722
- name: Test
1823
working-directory: ./packages/${{ inputs.package_name }}
1924
shell: bash
@@ -27,7 +32,19 @@ runs:
2732
if: ${{ !cancelled() }}
2833
uses: EnricoMi/publish-unit-test-result-action@34d7c956a59aed1bfebf31df77b8de55db9bbaaf # v2
2934
with:
30-
files: "./packages/${{ inputs.package_name }}/test-results/**/*"
35+
# Exclude Playwright's HTML report and per-test artifact
36+
# directories. Without these exclusions the action would try to
37+
# parse HTML, CSS, JS, PNG, JSON, etc. as test result files.
38+
#
39+
# We exclude by directory rather than by file extension because
40+
# the action supports JSON test results (Dart, Mocha) and
41+
# Playwright also writes .json files (e.g. .last-run.json) —
42+
# extension-based exclusion would either break JSON test report
43+
# support or let Playwright metadata through to the parser.
44+
files: |
45+
./packages/${{ inputs.package_name }}/test-results/**/*
46+
!./packages/${{ inputs.package_name }}/test-results/playwright-html/**
47+
!./packages/${{ inputs.package_name }}/test-results/playwright-artifacts/**
3148
check_name: "Test report for ${{ inputs.package_name }}"
3249
test_file_prefix: "+packages/${{ inputs.package_name }}/"
3350
- name: Store Test Results

‎package-lock.json‎

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

‎packages/scratch-gui/package.json‎

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -58,9 +58,10 @@
5858
"prepare": "node scripts/prepare.mjs",
5959
"prepublishOnly": "echo \"Please publish through CI only.\" && exit 1",
6060
"start": "webpack serve",
61-
"test": "npm run test:lint && npm run test:unit && npm run test:integration",
61+
"test": "npm run test:lint && npm run test:unit && npm run test:integration && npm run test:playwright",
6262
"test:integration": "cross-env JEST_JUNIT_OUTPUT_NAME=integration-tests-results.xml jest --maxWorkers=4 test[\\\\/]integration",
6363
"test:lint": "eslint",
64+
"test:playwright": "playwright test",
6465
"test:smoke": "jest --runInBand test[\\\\/]smoke",
6566
"test:unit": "cross-env JEST_JUNIT_OUTPUT_NAME=unit-tests-results.xml jest test[\\\\/]unit",
6667
"watch": "webpack --watch"
@@ -197,6 +198,7 @@
197198
"@babel/core": "7.29.0",
198199
"@babel/preset-env": "7.29.2",
199200
"@babel/preset-react": "7.28.5",
201+
"@playwright/test": "1.59.1",
200202
"@testing-library/jest-dom": "5.17.0",
201203
"@testing-library/react": "14.3.1",
202204
"@types/jest": "25.2.3",
Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
// @ts-check
2+
const path = require('path');
3+
const {defineConfig, devices} = require('@playwright/test');
4+
5+
/**
6+
* Playwright configuration for scratch-gui browser tests.
7+
*
8+
* These tests run in both Chromium and Firefox so cross-browser issues
9+
* (e.g., differences in how each browser interprets pointer event spec
10+
* compatibility-mouse-event suppression) are caught in CI rather than
11+
* being noticed by users on staging.
12+
*
13+
* Tests load the built playground from `build/` via a `file://` URL,
14+
* mirroring the existing Selenium-based integration tests in
15+
* `test/integration/`. CI builds scratch-gui before running the test
16+
* job, so the build artifact is always present when this config runs.
17+
* Locally, run `npm run build` in scratch-gui first.
18+
*
19+
* Test files reference pages relative to the build directory via
20+
* Playwright's `baseURL`, e.g. `await page.goto('index.html')`.
21+
*/
22+
module.exports = defineConfig({
23+
testDir: './test/playwright',
24+
25+
// Tests in this suite are isolated and stateless, so they can
26+
// safely run in parallel.
27+
fullyParallel: true,
28+
29+
// Fail the build if a developer accidentally commits `test.only`.
30+
forbidOnly: !!process.env.CI,
31+
32+
// CI gets a single retry for flake tolerance; locally, fail fast.
33+
retries: process.env.CI ? 1 : 0,
34+
35+
// Keep all Playwright output under the same `test-results/`
36+
// directory the rest of scratch-gui's test pipeline writes to,
37+
// so the CI publish-unit-test-result-action and upload-artifact
38+
// steps pick everything up with their existing globs.
39+
//
40+
// The HTML report has to live in its own subdirectory because
41+
// the HTML reporter clears its output folder on each run, and
42+
// Playwright refuses to point it at the same folder as
43+
// `outputDir` (which holds per-test artifacts like traces and
44+
// screenshots).
45+
outputDir: 'test-results/playwright-artifacts',
46+
reporter: [
47+
['list'],
48+
['junit', {outputFile: 'test-results/playwright-junit.xml'}],
49+
['html', {outputFolder: 'test-results/playwright-html', open: 'never'}]
50+
],
51+
52+
use: {
53+
// Resolve relative `page.goto()` URLs against the built
54+
// playground so individual tests don't need to compute paths
55+
// relative to their own location on disk.
56+
baseURL: `file://${path.resolve(__dirname, 'build')}/`,
57+
58+
// Capture a trace on the first retry so CI failures are
59+
// debuggable without re-running tests locally.
60+
trace: 'on-first-retry'
61+
},
62+
63+
projects: [
64+
{
65+
name: 'chromium',
66+
use: {...devices['Desktop Chrome']}
67+
},
68+
{
69+
name: 'firefox',
70+
use: {...devices['Desktop Firefox']}
71+
}
72+
]
73+
});
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
// @ts-check
2+
const {test, expect} = require('@playwright/test');
3+
4+
// `baseURL` in playwright.config.js points at the built playground
5+
// (`packages/scratch-gui/build/`), so relative URLs here resolve
6+
// against that directory. CI downloads the build artifact before
7+
// running this suite; locally, run `npm run build` in scratch-gui
8+
// first.
9+
10+
test.describe('editor smoke test', () => {
11+
test('the editor loads and the Backpack header is visible', async ({page}) => {
12+
// Capture uncaught exceptions so a broken bundle fails loudly with
13+
// the underlying error instead of silently timing out on the
14+
// visibility assertion below. Note: we deliberately do NOT capture
15+
// `console.error` events. The current scratch-gui playground emits
16+
// a flood of React `defaultProps` deprecation warnings on mount,
17+
// which would drown out any real signal. Uncaught exceptions
18+
// (`pageerror`) are the right granularity for catching things
19+
// like a bundle that fails to evaluate at all.
20+
const pageErrors = [];
21+
page.on('pageerror', err => pageErrors.push(err.message));
22+
23+
await page.goto('index.html');
24+
25+
// The Backpack header is always rendered in the playground build,
26+
// even when no backpack host is configured (it just gets wrapped in
27+
// a "Coming Soon" tooltip). It is a stable, user-visible marker
28+
// that the editor mounted and rendered the menu bar / footer area.
29+
await expect(page.getByText('Backpack', {exact: true})).toBeVisible();
30+
31+
expect(pageErrors, 'uncaught exceptions during editor load').toEqual([]);
32+
});
33+
});

0 commit comments

Comments
 (0)