chore: allow web server w/o waiting (#24609)

This commit is contained in:
Pavel Feldman 2023-08-04 12:05:16 -07:00 committed by GitHub
parent b3ce913551
commit 8fde110c61
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 25 additions and 23 deletions

View File

@ -584,8 +584,8 @@ export default defineConfig({
* since: v1.10
- type: ?<[Object]|[Array]<[Object]>>
- `command` <[string]> Shell command to start. For example `npm run start`..
- `port` ?<[int]> The port that your http server is expected to appear on. It does wait until it accepts connections. Exactly one of `port` or `url` is required.
- `url` ?<[string]> The url on your http server that is expected to return a 2xx, 3xx, 400, 401, 402, or 403 status code when the server is ready to accept connections. Redirects (3xx status codes) are being followed and the new location is checked. Exactly one of `port` or `url` is required.
- `port` ?<[int]> The port that your http server is expected to appear on. It does wait until it accepts connections. Either `port` or `url` should be specified.
- `url` ?<[string]> The url on your http server that is expected to return a 2xx, 3xx, 400, 401, 402, or 403 status code when the server is ready to accept connections. Redirects (3xx status codes) are being followed and the new location is checked. Either `port` or `url` should be specified.
- `ignoreHTTPSErrors` ?<[boolean]> Whether to ignore HTTPS errors when fetching the `url`. Defaults to `false`.
- `timeout` ?<[int]> How long to wait for the process to start up and be available in milliseconds. Defaults to 60000.
- `reuseExistingServer` ?<[boolean]> If true, it will re-use an existing server on the `port` or `url` when available. If no server is running on that `port` or `url`, it will run the command to start a new server. If `false`, it will throw if an existing process is listening on the `port` or `url`. This should be commonly set to `!process.env.CI` to allow the local dev server when running tests locally.

View File

@ -28,7 +28,7 @@ import type { ReporterV2 } from '../reporters/reporterV2';
export type WebServerPluginOptions = {
command: string;
url: string;
url?: string;
ignoreHTTPSErrors?: boolean;
timeout?: number;
reuseExistingServer?: boolean;
@ -45,7 +45,7 @@ const DEFAULT_ENVIRONMENT_VARIABLES = {
const debugWebServer = debug('pw:webserver');
export class WebServerPlugin implements TestRunnerPlugin {
private _isAvailable?: () => Promise<boolean>;
private _isAvailableCallback?: () => Promise<boolean>;
private _killProcess?: () => Promise<void>;
private _processExitedPromise!: Promise<any>;
private _options: WebServerPluginOptions;
@ -60,7 +60,7 @@ export class WebServerPlugin implements TestRunnerPlugin {
public async setup(config: FullConfig, configDir: string, reporter: ReporterV2) {
this._reporter = reporter;
this._isAvailable = getIsAvailableFunction(this._options.url, this._checkPortOnly, !!this._options.ignoreHTTPSErrors, this._reporter.onStdErr?.bind(this._reporter));
this._isAvailableCallback = this._options.url ? getIsAvailableFunction(this._options.url, this._checkPortOnly, !!this._options.ignoreHTTPSErrors, this._reporter.onStdErr?.bind(this._reporter)) : undefined;
this._options.cwd = this._options.cwd ? path.resolve(configDir, this._options.cwd) : configDir;
try {
await this._startProcess();
@ -79,12 +79,12 @@ export class WebServerPlugin implements TestRunnerPlugin {
let processExitedReject = (error: Error) => { };
this._processExitedPromise = new Promise((_, reject) => processExitedReject = reject);
const isAlreadyAvailable = await this._isAvailable!();
const isAlreadyAvailable = await this._isAvailableCallback?.();
if (isAlreadyAvailable) {
debugWebServer(`WebServer is already available`);
if (this._options.reuseExistingServer)
return;
const port = new URL(this._options.url);
const port = new URL(this._options.url!).port;
throw new Error(`${this._options.url ?? `http://localhost${port ? ':' + port : ''}`} is already used, make sure that nothing is running on the port/url or set reuseExistingServer:true in config.webServer.`);
}
@ -121,21 +121,21 @@ export class WebServerPlugin implements TestRunnerPlugin {
}
private async _waitForProcess() {
if (!this._isAvailableCallback) {
this._processExitedPromise.catch(() => {});
return;
}
debugWebServer(`Waiting for availability...`);
await this._waitForAvailability();
debugWebServer(`WebServer available`);
}
private async _waitForAvailability() {
const launchTimeout = this._options.timeout || 60 * 1000;
const cancellationToken = { canceled: false };
const { timedOut } = (await Promise.race([
raceAgainstDeadline(() => waitFor(this._isAvailable!, cancellationToken), monotonicTime() + launchTimeout),
raceAgainstDeadline(() => waitFor(this._isAvailableCallback!, cancellationToken), monotonicTime() + launchTimeout),
this._processExitedPromise,
]));
cancellationToken.canceled = true;
if (timedOut)
throw new Error(`Timed out waiting ${launchTimeout}ms from config.webServer.`);
debugWebServer(`WebServer available`);
}
}
@ -214,15 +214,17 @@ export const webServerPluginsForConfig = (config: FullConfigInternal): TestRunne
const shouldSetBaseUrl = !!config.config.webServer;
const webServerPlugins = [];
for (const webServerConfig of config.webServers) {
if ((!webServerConfig.port && !webServerConfig.url) || (webServerConfig.port && webServerConfig.url))
throw new Error(`Exactly one of 'port' or 'url' is required in config.webServer.`);
if (webServerConfig.port && webServerConfig.url)
throw new Error(`Either 'port' or 'url' should be specified in config.webServer.`);
const url = webServerConfig.url || `http://localhost:${webServerConfig.port}`;
// We only set base url when only the port is given. That's a legacy mode we have regrets about.
if (shouldSetBaseUrl && !webServerConfig.url)
process.env.PLAYWRIGHT_TEST_BASE_URL = url;
let url: string | undefined;
if (webServerConfig.port || webServerConfig.url) {
url = webServerConfig.url || `http://localhost:${webServerConfig.port}`;
// We only set base url when only the port is given. That's a legacy mode we have regrets about.
if (shouldSetBaseUrl && !webServerConfig.url)
process.env.PLAYWRIGHT_TEST_BASE_URL = url;
}
webServerPlugins.push(new WebServerPlugin({ ...webServerConfig, url }, webServerConfig.port !== undefined));
}

View File

@ -6735,15 +6735,15 @@ interface TestConfigWebServer {
command: string;
/**
* The port that your http server is expected to appear on. It does wait until it accepts connections. Exactly one of
* `port` or `url` is required.
* The port that your http server is expected to appear on. It does wait until it accepts connections. Either `port`
* or `url` should be specified.
*/
port?: number;
/**
* The url on your http server that is expected to return a 2xx, 3xx, 400, 401, 402, or 403 status code when the
* server is ready to accept connections. Redirects (3xx status codes) are being followed and the new location is
* checked. Exactly one of `port` or `url` is required.
* checked. Either `port` or `url` should be specified.
*/
url?: string;