fix(ui mode): make sure that reload does correctly restart the webserver (#32263)

Fixes #32103.
This commit is contained in:
Dmitry Gozman 2024-08-22 05:48:31 -07:00 committed by GitHub
parent dc4a8e48eb
commit 7758b330b1
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
3 changed files with 52 additions and 12 deletions

View File

@ -73,7 +73,9 @@ export class WebServerPlugin implements TestRunnerPlugin {
}
public async teardown() {
debugWebServer(`Terminating the WebServer`);
await this._killProcess?.();
debugWebServer(`Terminated the WebServer`);
}
private async _startProcess(): Promise<void> {

View File

@ -114,14 +114,11 @@ class TestServerDispatcher implements TestServerInterface {
}
async initialize(params: Parameters<TestServerInterface['initialize']>[0]): ReturnType<TestServerInterface['initialize']> {
if (params.serializer)
this._serializer = params.serializer;
if (params.closeOnDisconnect)
this._closeOnDisconnect = true;
if (params.interceptStdio)
await this._setInterceptStdio(true);
if (params.watchTestDirs)
this._watchTestDirs = true;
// Note: this method can be called multiple times, for example from a new connection after UI mode reload.
this._serializer = params.serializer || require.resolve('./uiModeReporter');
this._closeOnDisconnect = !!params.closeOnDisconnect;
await this._setInterceptStdio(!!params.interceptStdio);
this._watchTestDirs = !!params.watchTestDirs;
}
async ping() {}
@ -161,7 +158,6 @@ class TestServerDispatcher implements TestServerInterface {
return { status: 'failed', report };
}
webServerPluginsForConfig(config).forEach(p => config.plugins.push({ factory: p }));
const { collectingReporter, report } = await this._collectingReporter();
const listReporter = new ListReporter();
const taskRunner = createTaskRunnerForWatchSetup(config, [collectingReporter, listReporter]);
@ -418,10 +414,12 @@ class TestServerDispatcher implements TestServerInterface {
try {
const config = await loadConfig(this._configLocation, overrides);
// Preserve plugin instances between setup and build.
if (!this._plugins)
if (!this._plugins) {
webServerPluginsForConfig(config).forEach(p => config.plugins.push({ factory: p }));
this._plugins = config.plugins || [];
else
} else {
config.plugins.splice(0, config.plugins.length, ...this._plugins);
}
return { config };
} catch (e) {
return { config: null, error: serializeError(e) };

View File

@ -15,6 +15,7 @@
*/
import { test, expect, retries, dumpTestTree } from './ui-mode-fixtures';
import path from 'path';
test.describe.configure({ mode: 'parallel', retries });
@ -245,4 +246,43 @@ for (const useWeb of [true, false]) {
]);
});
});
}
}
test('should restart webserver on reload', async ({ runUITest }) => {
const SIMPLE_SERVER_PATH = path.join(__dirname, 'assets', 'simple-server.js');
const port = test.info().workerIndex * 2 + 10500;
const { page } = await runUITest({
'playwright.config.ts': `
import { defineConfig } from '@playwright/test';
export default defineConfig({
webServer: {
command: 'node ${JSON.stringify(SIMPLE_SERVER_PATH)} ${port}',
port: ${port},
reuseExistingServer: false,
},
});
`,
'a.test.js': `
import { test, expect } from '@playwright/test';
test('should work', async ({ page }) => {
await page.goto('http://localhost:${port}');
});
`
}, { DEBUG: 'pw:webserver' });
await page.getByTitle('Run all').click();
await expect(page.getByTestId('status-line')).toHaveText('1/1 passed (100%)');
await page.getByTitle('Toggle output').click();
await expect(page.getByTestId('output')).toContainText('[WebServer] listening');
await page.getByTitle('Clear output').click();
await expect(page.getByTestId('output')).not.toContainText('[WebServer] listening');
await page.getByTitle('Reload').click();
await expect(page.getByTestId('output')).toContainText('[WebServer] listening');
await expect(page.getByTestId('output')).not.toContainText('set reuseExistingServer:true');
await page.getByTitle('Run all').click();
await expect(page.getByTestId('status-line')).toHaveText('1/1 passed (100%)');
});