Skip to content

Commit c20a85d

Browse files
flavorjonesclaude
andcommitted
Add test progress reporting in local dev
WTR doesn't support per-test progress reporting, so let's use a middleware endpoint as a callback to emit minitest-style test status. In CI, though, we'll continue to use the default reporter because we want to see per-browser results (via Sauce Labs) and don't care about realtime progress. Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
1 parent 4df5db6 commit c20a85d

File tree

1 file changed

+58
-0
lines changed

1 file changed

+58
-0
lines changed

web-test-runner.config.mjs

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -89,6 +89,9 @@ const defaultBrowsers = [
8989
playwrightLauncher({ product: 'chromium' }),
9090
];
9191

92+
// Enable real-time progress reporting for local dev (single browser)
93+
const localDev = !sauceLabsConfig && !process.env.CI;
94+
9295
export default {
9396
// The test file(s)
9497
files: ['dist/test.js'],
@@ -113,8 +116,51 @@ export default {
113116
// Parallel browser execution
114117
concurrency: sauceLabsConfig ? 4 : 1,
115118

119+
// Use static logging for real-time progress (local dev only)
120+
staticLogging: localDev,
121+
122+
// Custom reporter for local dev; undefined falls back to default for CI
123+
reporters: localDev ? [
124+
{
125+
onTestRunFinished({ sessions }) {
126+
let passed = 0, failed = 0, skipped = 0;
127+
for (const session of sessions) {
128+
const countTests = (suite) => {
129+
for (const test of suite.tests || []) {
130+
if (test.skipped) skipped++;
131+
else if (test.passed) passed++;
132+
else failed++;
133+
}
134+
for (const child of suite.suites || []) countTests(child);
135+
};
136+
if (session.testResults) countTests(session.testResults);
137+
}
138+
const total = passed + failed + skipped;
139+
process.stdout.write(`\n\n${total} tests: ${passed} passed, ${failed} failed, ${skipped} skipped.\n\n`);
140+
},
141+
},
142+
] : undefined,
143+
116144
// Middleware to serve test fixtures and QUnit from local files
117145
middleware: [
146+
// Real-time test progress reporting
147+
async function testProgressReporter(context, next) {
148+
if (context.method === 'POST' && context.url === '/test-progress') {
149+
const chunks = [];
150+
for await (const chunk of context.req) {
151+
chunks.push(chunk);
152+
}
153+
const body = Buffer.concat(chunks).toString();
154+
const { status } = JSON.parse(body);
155+
// Match the same logic used in the reporter: skipped, failed, or passed (everything else)
156+
const progressIndicator = status === 'skipped' ? 'S' : status === 'failed' ? 'F' : '.';
157+
process.stdout.write(progressIndicator);
158+
context.status = 200;
159+
context.body = 'ok';
160+
return;
161+
}
162+
return next();
163+
},
118164
function serveLocalFiles(context, next) {
119165
// Serve test fixtures from src/test/test_helpers/fixtures/
120166
if (context.url.startsWith('/test_helpers/fixtures/')) {
@@ -177,6 +223,9 @@ export default {
177223
import { getConfig, sessionStarted, sessionFinished, sessionFailed }
178224
from '@web/test-runner-core/browser/session.js';
179225
226+
// Real-time progress reporting only for local dev (single browser)
227+
const reportProgress = ${localDev};
228+
180229
try {
181230
await sessionStarted();
182231
@@ -192,6 +241,15 @@ export default {
192241
});
193242
194243
QUnit.on('testEnd', (result) => {
244+
// POST progress to server for real-time output
245+
if (reportProgress) {
246+
fetch('/test-progress', {
247+
method: 'POST',
248+
headers: { 'Content-Type': 'application/json' },
249+
body: JSON.stringify({ status: result.status })
250+
}).catch(() => {});
251+
}
252+
195253
// Navigate to correct suite in hierarchy
196254
const modules = result.fullName.slice(0, -1);
197255
let currentSuite = testSuite;

0 commit comments

Comments
 (0)