Skip to content

Commit 990f620

Browse files
committed
Switch to running QUnit tests via Playwright instead of Puppeteer.
1 parent 2183f23 commit 990f620

4 files changed

Lines changed: 106 additions & 22 deletions

File tree

.github/workflows/reusable-javascript-tests.yml

Lines changed: 0 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -5,12 +5,6 @@ name: JavaScript tests
55

66
on:
77
workflow_call:
8-
inputs:
9-
disable-apparmor:
10-
description: 'Whether to disable AppArmor.'
11-
required: false
12-
type: 'boolean'
13-
default: false
148

159
# Disable permissions for all available scopes by default.
1610
# Any needed permissions should be configured at the job level.
@@ -55,15 +49,6 @@ jobs:
5549
- name: Install npm Dependencies
5650
run: npm ci
5751

58-
# Older branches using outdated versions of Puppeteer fail on newer versions of the `ubuntu-24` image.
59-
# This disables AppArmor in order to work around those failures.
60-
#
61-
# See https://issues.chromium.org/issues/373753919
62-
# and https://chromium.googlesource.com/chromium/src/+/main/docs/security/apparmor-userns-restrictions.md
63-
- name: Disable AppArmor
64-
if: ${{ inputs.disable-apparmor }}
65-
run: echo 0 | sudo tee /proc/sys/kernel/apparmor_restrict_unprivileged_userns
66-
6752
- name: Run QUnit tests
6853
run: npm run grunt qunit:compiled
6954

Gruntfile.js

Lines changed: 14 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -115,7 +115,6 @@ module.exports = function(grunt) {
115115
'cssmin',
116116
'imagemin',
117117
'jshint',
118-
'qunit',
119118
'uglify',
120119
'watch'
121120
],
@@ -1010,12 +1009,6 @@ module.exports = function(grunt) {
10101009
}
10111010
}
10121011
},
1013-
qunit: {
1014-
files: [
1015-
'tests/qunit/**/*.html',
1016-
'!tests/qunit/editor/**'
1017-
]
1018-
},
10191012
phpunit: {
10201013
'default': {
10211014
args: ['--verbose', '-c', 'phpunit.xml.dist']
@@ -2172,6 +2165,20 @@ module.exports = function(grunt) {
21722165
}, this.async());
21732166
});
21742167

2168+
grunt.registerTask( 'qunit', 'Runs QUnit tests.', function() {
2169+
var done = this.async();
2170+
grunt.util.spawn( {
2171+
cmd: 'node_modules/.bin/playwright',
2172+
args: [ 'test', '--config', 'tests/qunit/playwright.config.js' ],
2173+
opts: { stdio: 'inherit' }
2174+
}, function( error, result, code ) {
2175+
if ( code !== 0 ) {
2176+
grunt.fail.warn( 'QUnit tests failed.' );
2177+
}
2178+
done();
2179+
} );
2180+
} );
2181+
21752182
grunt.registerTask( 'qunit:compiled', 'Runs QUnit tests on compiled as well as uncompiled scripts.',
21762183
['build', 'copy:qunit', 'qunit']
21772184
);

tests/qunit/playwright.config.js

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
/**
2+
* Playwright configuration for QUnit tests.
3+
*/
4+
const path = require( 'path' );
5+
const { defineConfig } = require( '@playwright/test' );
6+
7+
module.exports = defineConfig( {
8+
testDir: __dirname,
9+
outputDir: path.join( __dirname, '..', '..', 'artifacts', 'test-results' ),
10+
testMatch: 'qunit.js',
11+
timeout: 30_000,
12+
workers: 1,
13+
use: {
14+
headless: true,
15+
channel: 'chrome',
16+
},
17+
reporter: process.env.CI ? 'github' : 'list',
18+
} );

tests/qunit/qunit.js

Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
const { test, expect } = require( '@playwright/test' );
2+
const path = require( 'path' );
3+
const glob = require( 'fast-glob' );
4+
5+
const qunitDir = path.resolve( __dirname );
6+
const htmlFiles = glob.sync( [ '**/*.html' ], {
7+
cwd: qunitDir,
8+
absolute: true,
9+
} );
10+
11+
for ( const file of htmlFiles ) {
12+
const name = path.relative( qunitDir, file );
13+
14+
test( `QUnit: ${ name }`, async ( { page } ) => {
15+
// Inject a QUnit.done hook before any page scripts run.
16+
await page.addInitScript( () => {
17+
window.__qunitResults = new Promise( ( resolve ) => {
18+
window.__qunitResolve = resolve;
19+
} );
20+
21+
// Keep checking for QUnit to become available.
22+
const observer = new MutationObserver( () => {
23+
if ( typeof QUnit !== 'undefined' && ! window.__qunitHooked ) {
24+
window.__qunitHooked = true;
25+
observer.disconnect();
26+
27+
const failures = [];
28+
QUnit.testDone( ( details ) => {
29+
if ( details.failed > 0 ) {
30+
failures.push(
31+
`${ details.module } > ${ details.name } (${ details.failed } assertion(s))`
32+
);
33+
}
34+
} );
35+
36+
QUnit.done( ( details ) => {
37+
window.__qunitResolve( {
38+
passed: details.passed,
39+
failed: details.failed,
40+
total: details.total,
41+
runtime: details.runtime,
42+
failures,
43+
} );
44+
} );
45+
}
46+
} );
47+
observer.observe( document, {
48+
childList: true,
49+
subtree: true,
50+
} );
51+
} );
52+
53+
// Navigate to the test file.
54+
await page.goto( 'file://' + file, { waitUntil: 'domcontentloaded' } );
55+
56+
// Wait for QUnit to complete.
57+
const results = await page.evaluate( () => window.__qunitResults );
58+
59+
// Log summary.
60+
// eslint-disable-next-line no-console
61+
console.log(
62+
` ${ results.passed }/${ results.total } passed, ${ results.failed } failed, ${ results.runtime }ms`
63+
);
64+
65+
if ( results.failures.length > 0 ) {
66+
// eslint-disable-next-line no-console
67+
console.log(
68+
results.failures.map( ( f ) => ` FAIL: ${ f }` ).join( '\n' )
69+
);
70+
}
71+
72+
expect( results.failed ).toBe( 0 );
73+
} );
74+
}

0 commit comments

Comments
 (0)