Skip to content

Commit 2d19312

Browse files
authored
Merge pull request #372 from GoogleChromeLabs/playwright-mvp
Playwright Test Suite: realtime-sine (MVP)
2 parents ec8dc80 + 87a4138 commit 2d19312

File tree

18 files changed

+2206
-4677
lines changed

18 files changed

+2206
-4677
lines changed

.github/workflows/playwright.yml

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
name: Playwright Tests
2+
on:
3+
push:
4+
branches: [ main, master ]
5+
pull_request:
6+
branches: [ main, master ]
7+
jobs:
8+
test:
9+
timeout-minutes: 60
10+
runs-on: ubuntu-latest
11+
steps:
12+
- uses: actions/checkout@v4
13+
- uses: actions/setup-node@v4
14+
with:
15+
node-version: lts/*
16+
- name: Install dependencies
17+
run: npm ci
18+
- name: Install Playwright Browsers
19+
run: npx playwright install --with-deps chromium
20+
- name: Run Playwright tests
21+
# Only test on Chromium
22+
# Each version of playwright is tied to the latest version of Chromium (at the time). The version is listed here:
23+
# https://github.com/microsoft/playwright?tab=readme-ov-file#documentation--api-reference
24+
run: npx playwright test --project=chromium
25+
- uses: actions/upload-artifact@v4
26+
if: always()
27+
with:
28+
name: playwright-report
29+
path: playwright-report/
30+
retention-days: 30

.gitignore

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,9 @@
11
.DS_Store
22
node_modules/
3-
_site/
3+
_site/
4+
5+
## Playwright-specific ignores
6+
/test-results/
7+
/playwright-report/
8+
/blob-report/
9+
/playwright/.cache/

package-lock.json

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

package.json

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,10 @@
1111
"start": "run-p start:*",
1212
"start:eleventy": "eleventy --serve",
1313
"start:postcss": "postcss src/styles/*.css --dir _site --watch",
14-
"format": "npx eslint --fix _site/audio-worklet/**/*.js && npx prettier --write --loglevel silent _site/audio-worklet/**/*.html"
14+
"format": "npx eslint --fix _site/audio-worklet/**/*.js && npx prettier --write --loglevel silent _site/audio-worklet/**/*.html",
15+
"test": "npx playwright test",
16+
"test-server": "npx http-server",
17+
"test-live": "npx http-server ./src/tests/playwright/pages/"
1518
},
1619
"license": "MIT",
1720
"dependencies": {
@@ -29,5 +32,9 @@
2932
"prettier": "^3.2.5",
3033
"rimraf": "^5.0.7",
3134
"tailwindcss": "^3.4.3"
35+
},
36+
"devDependencies": {
37+
"@playwright/test": "latest",
38+
"@types/node": "^20.12.13"
3239
}
3340
}

playwright.config.ts

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
import { defineConfig, devices } from '@playwright/test';
2+
3+
/**
4+
* See https://playwright.dev/docs/test-configuration.
5+
*/
6+
export default defineConfig({
7+
testDir: './src/tests/playwright',
8+
// Run tests in files in parallel
9+
fullyParallel: true,
10+
// Fail the build on CI if you accidentally left test.only in the source code.
11+
forbidOnly: !!process.env.CI,
12+
// Retry on CI only
13+
retries: process.env.CI ? 2 : 0,
14+
// Opt out of parallel tests on CI.
15+
workers: process.env.CI ? 1 : undefined,
16+
// Reporter to use. See https://playwright.dev/docs/test-reporters
17+
reporter: 'html',
18+
// Shared settings for all the projects below. See https://playwright.dev/docs/api/class-testoptions.
19+
use: {
20+
// Base URL to use in actions like `await page.goto('/')`.
21+
baseURL: 'http://127.0.0.1:8080/src/tests/playwright/',
22+
// Collect trace when retrying the failed test. See https://playwright.dev/docs/trace-viewer
23+
trace: 'on-first-retry',
24+
},
25+
26+
// Configure projects for major browsers
27+
projects: [
28+
{
29+
name: 'chromium',
30+
use: {
31+
...devices['Desktop Chrome'],
32+
launchOptions: {
33+
ignoreDefaultArgs: ['--mute-audio'],
34+
args: ['--autoplay-policy=no-user-gesture-required']
35+
},
36+
}
37+
}
38+
],
39+
40+
// Run your local dev server before starting the tests
41+
webServer: {
42+
command: 'npm run test-server',
43+
url: 'http://127.0.0.1:8080',
44+
reuseExistingServer: !process.env.CI,
45+
},
46+
});

src/_data/build_info.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
{"version":"3.2.0","revision":"c722df0","lastUpdated":"2024-05-14","copyrightYear":2024}
1+
{"version":"3.2.0","revision":"cb35e25","lastUpdated":"2024-05-17","copyrightYear":2024}

src/tests/playwright/pages/index.html

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
<!DOCTYPE html>
2+
<html lang="en">
3+
<head>
4+
<meta charset="UTF-8">
5+
<link rel="stylesheet" href="https://unpkg.com/terminal.css@0.7.4/dist/terminal.min.css" />
6+
<link rel="stylesheet" href="live-suite/style.css" />
7+
<script defer type="module" src="live-suite/scripts/main.js"></script>
8+
<title>Web Audio Test Suite</title>
9+
</head>
10+
<body class="terminal">
11+
<h1 class="terminal-prompt">Web Audio Test Suite</h1>
12+
<table>
13+
<thead>
14+
<tr>
15+
<th>Test Name</th>
16+
<th>Result</th>
17+
<th>Time</th>
18+
<th>Output</th>
19+
<th>Run</th>
20+
</tr>
21+
</thead>
22+
<tbody></tbody>
23+
</table>
24+
<template id="row">
25+
<tr>
26+
<td><slot name="name">Unnamed Test</slot></td>
27+
<td><slot name="result">---</slot></td>
28+
<td><slot name="time">---</slot></td>
29+
<td><pre><slot name="output">---</slot></pre></td>
30+
<td><button class="btn btn-block btn-primary">start</button></td>
31+
</tr>
32+
</template>
33+
</body>
34+
</html>
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
/**
2+
* @fileoverview Overrides console methods to capture outputs in `output` array
3+
* for debugging/tests. Handles common methods like log, error, info, and more,
4+
* storing { method, arguments }.
5+
*/
6+
export const output = [];
7+
8+
[
9+
'log', 'debug', 'info', 'error', 'warning', 'dir', 'dirxml',
10+
'table', 'trace', 'clear', 'group', 'groupCollapsed', 'groupEnd',
11+
'assert', 'profile', 'profileEnd', 'count', 'timeEnd',
12+
].forEach((method) => {
13+
const original = console[method];
14+
console[method] = (...args) => {
15+
(method !== 'assert' || (method === 'assert' && !args[0])) &&
16+
output.push({method, args});
17+
return original.apply(console, args);
18+
};
19+
});
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
/**
2+
* @fileoverview Initializes the Web Audio Test Suite by converting specified
3+
* HTML test files to interactive DOM elements.
4+
*/
5+
import {convertTestFiles} from './test-file-converter.js';
6+
7+
// Flag for live test suite environment
8+
window._isTestSuiteMode = true;
9+
10+
const files = [
11+
'realtime-sine.html',
12+
];
13+
14+
convertTestFiles(files);
Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
/**
2+
* @fileoverview Provides utilities for converting HTML test files to
3+
* interactive DOM elements in the Web Audio Test Suite, including dynamic test
4+
* execution, results display, and console output management.
5+
*/
6+
import {output} from './console-override.js';
7+
8+
export const convertTestFiles = async (tests) => {
9+
const htmls = await Promise.all(
10+
tests.map(async (t) => (await fetch(t)).text()));
11+
12+
const template = document.querySelector('#row');
13+
htmls.forEach((html) => {
14+
const dom = new DOMParser()
15+
.parseFromString(html, 'text/html');
16+
const scriptContent = dom.querySelector('script').innerText;
17+
18+
const tr = template.content.cloneNode(true);
19+
const id = dom.title
20+
.replace(/[^a-z0-9]/gi, '-')
21+
.toLowerCase();
22+
tr.childNodes[1].id = id;
23+
tr.querySelector('slot[name=name]').textContent = dom.title;
24+
tr.querySelector('button').addEventListener('click', async () => {
25+
document.querySelectorAll('button').forEach((b) => b.disabled = true);
26+
27+
const script = document.createElement('script');
28+
script.defer = true;
29+
script.async = true;
30+
script.type = 'module';
31+
script.textContent = scriptContent;
32+
document.head.appendChild(script);
33+
34+
// await until script loads in the live suite
35+
await new Promise((resolve) => window._webAudioTestIsRunning = resolve);
36+
const start = performance.now();
37+
await window._webAudioTest;
38+
const diff = performance.now() - start;
39+
40+
document.querySelector(`#${id} slot[name=result]`).textContent =
41+
await window.webAudioEvaluate() ? '✅': '❌';
42+
document.querySelector(`#${id} slot[name=time]`).textContent =
43+
`${(diff).toFixed(2)}ms`;
44+
document.querySelector(`#${id} pre slot[name=output]`).textContent =
45+
output.map(({method, args}) =>
46+
`${method}: ${args.join(' ')}`,
47+
).join('\n') || '---';
48+
49+
output.length = 0;
50+
51+
delete window._webAudioTest;
52+
delete window.webAudioEvaluate;
53+
document.head.removeChild(script);
54+
55+
document.querySelectorAll('button').forEach((b) => b.disabled = false);
56+
});
57+
58+
document.querySelector('tbody').appendChild(tr);
59+
});
60+
};

0 commit comments

Comments
 (0)