chore: move dev server to config-based framework extensibility (#30234)
This commit is contained in:
parent
e18a358bc6
commit
5043bd55dc
3
package-lock.json
generated
3
package-lock.json
generated
|
|
@ -8220,9 +8220,6 @@
|
|||
"playwright-core": "1.44.0-next",
|
||||
"vite": "^5.0.13"
|
||||
},
|
||||
"bin": {
|
||||
"playwright": "cli.js"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=16"
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,19 +0,0 @@
|
|||
#!/usr/bin/env node
|
||||
/**
|
||||
* Copyright (c) Microsoft Corporation.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
const { program } = require('./lib/program');
|
||||
program.parse(process.argv);
|
||||
|
|
@ -16,7 +16,7 @@
|
|||
|
||||
const { test: baseTest, expect, devices, defineConfig: originalDefineConfig } = require('playwright/test');
|
||||
const { fixtures } = require('./lib/mount');
|
||||
const { clearCacheCommand, findRelatedTestFilesCommand } = require('./lib/cliOverrides');
|
||||
const { clearCacheCommand, runDevServerCommand, findRelatedTestFilesCommand } = require('./lib/cliOverrides');
|
||||
const { createPlugin } = require('./lib/vitePlugin');
|
||||
|
||||
const defineConfig = (...configs) => {
|
||||
|
|
@ -31,6 +31,7 @@ const defineConfig = (...configs) => {
|
|||
],
|
||||
cli: {
|
||||
'clear-cache': clearCacheCommand,
|
||||
'dev-server': runDevServerCommand,
|
||||
'find-related-test-files': findRelatedTestFilesCommand,
|
||||
},
|
||||
}
|
||||
|
|
|
|||
|
|
@ -29,8 +29,5 @@
|
|||
"playwright-core": "1.44.0-next",
|
||||
"vite": "^5.0.13",
|
||||
"playwright": "1.44.0-next"
|
||||
},
|
||||
"bin": {
|
||||
"playwright": "cli.js"
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -20,6 +20,8 @@ import { affectedTestFiles, cacheDir } from 'playwright/lib/transform/compilatio
|
|||
import { buildBundle } from './vitePlugin';
|
||||
import { resolveDirs } from './viteUtils';
|
||||
import type { FullConfig, Suite } from 'playwright/types/testReporter';
|
||||
import { runDevServer } from './devServer';
|
||||
import type { FullConfigInternal } from 'playwright/lib/common/config';
|
||||
|
||||
export async function clearCacheCommand(config: FullConfig, configDir: string) {
|
||||
const dirs = await resolveDirs(configDir, config);
|
||||
|
|
@ -32,3 +34,7 @@ export async function findRelatedTestFilesCommand(files: string[], config: Full
|
|||
await buildBundle(config, configDir, suite);
|
||||
return { testFiles: affectedTestFiles(files) };
|
||||
}
|
||||
|
||||
export async function runDevServerCommand(config: FullConfigInternal) {
|
||||
return await runDevServer(config);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -17,18 +17,14 @@
|
|||
import fs from 'fs';
|
||||
import path from 'path';
|
||||
import { Watcher } from 'playwright/lib/fsWatcher';
|
||||
import { loadConfigFromFileRestartIfNeeded } from 'playwright/lib/common/configLoader';
|
||||
import { Runner } from 'playwright/lib/runner/runner';
|
||||
import type { PluginContext } from 'rollup';
|
||||
import { source as injectedSource } from './generated/indexSource';
|
||||
import { createConfig, populateComponentsFromTests, resolveDirs, transformIndexFile, frameworkConfig } from './viteUtils';
|
||||
import type { ComponentRegistry } from './viteUtils';
|
||||
import type { FullConfigInternal } from 'playwright/lib/common/config';
|
||||
|
||||
export async function runDevServer(configFile: string) {
|
||||
const config = await loadConfigFromFileRestartIfNeeded(configFile);
|
||||
if (!config)
|
||||
return;
|
||||
|
||||
export async function runDevServer(config: FullConfigInternal): Promise<() => Promise<void>> {
|
||||
const { registerSourceFile, frameworkPluginFactory } = frameworkConfig(config.config);
|
||||
const runner = new Runner(config);
|
||||
await runner.loadAllTests();
|
||||
|
|
@ -39,7 +35,7 @@ export async function runDevServer(configFile: string) {
|
|||
if (!dirs) {
|
||||
// eslint-disable-next-line no-console
|
||||
console.log(`Template file playwright/index.html is missing.`);
|
||||
return;
|
||||
return async () => {};
|
||||
}
|
||||
const registerSource = injectedSource + '\n' + await fs.promises.readFile(registerSourceFile, 'utf-8');
|
||||
const viteConfig = await createConfig(dirs, config.config, frameworkPluginFactory, false);
|
||||
|
|
@ -56,7 +52,7 @@ export async function runDevServer(configFile: string) {
|
|||
await devServer.listen();
|
||||
const protocol = viteConfig.server.https ? 'https:' : 'http:';
|
||||
// eslint-disable-next-line no-console
|
||||
console.log(`Test Server listening on ${protocol}//${viteConfig.server.host || 'localhost'}:${viteConfig.server.port}`);
|
||||
console.log(`Dev Server listening on ${protocol}//${viteConfig.server.host || 'localhost'}:${viteConfig.server.port}`);
|
||||
|
||||
const projectDirs = new Set<string>();
|
||||
const projectOutputs = new Set<string>();
|
||||
|
|
@ -85,4 +81,5 @@ export async function runDevServer(configFile: string) {
|
|||
devServer.moduleGraph.onFileChange(rootModule.file!);
|
||||
});
|
||||
globalWatcher.update([...projectDirs], [...projectOutputs], false);
|
||||
return () => Promise.all([devServer.close(), globalWatcher.close()]).then(() => {});
|
||||
}
|
||||
|
|
|
|||
|
|
@ -14,19 +14,4 @@
|
|||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import type { Command } from 'playwright-core/lib/utilsBundle';
|
||||
|
||||
import { program } from 'playwright/lib/program';
|
||||
import { runDevServer } from './devServer';
|
||||
export { program } from 'playwright/lib/program';
|
||||
|
||||
function addDevServerCommand(program: Command) {
|
||||
const command = program.command('dev-server', { hidden: true });
|
||||
command.description('start dev server');
|
||||
command.option('-c, --config <file>', `Configuration file, or a test directory with optional "playwright.config.{m,c}?{js,ts}"`);
|
||||
command.action(options => {
|
||||
runDevServer(options.config);
|
||||
});
|
||||
}
|
||||
|
||||
addDevServerCommand(program);
|
||||
|
|
|
|||
|
|
@ -96,7 +96,7 @@ export async function buildBundle(config: FullConfig, configDir: string, suite:
|
|||
const url = new URL(`${protocol}//${endpoint.host}:${endpoint.port}`);
|
||||
if (await isURLAvailable(url, true)) {
|
||||
// eslint-disable-next-line no-console
|
||||
console.log(`Test Server is already running at ${url.toString()}, using it.\n`);
|
||||
console.log(`Dev Server is already running at ${url.toString()}, using it.\n`);
|
||||
process.env.PLAYWRIGHT_TEST_BASE_URL = url.toString();
|
||||
return null;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -63,6 +63,10 @@ export class Watcher {
|
|||
});
|
||||
}
|
||||
|
||||
async close() {
|
||||
await this._fsWatcher?.close();
|
||||
}
|
||||
|
||||
private _reportEventsIfAny() {
|
||||
if (this._collector.length)
|
||||
this._onChange(this._collector.slice());
|
||||
|
|
|
|||
|
|
@ -19,6 +19,8 @@ import type { Metadata } from '../../types/test';
|
|||
import type * as reporterTypes from '../../types/testReporter';
|
||||
import type { ReporterV2 } from '../reporters/reporterV2';
|
||||
|
||||
// -- Reuse boundary -- Everything below this line is reused in the vscode extension.
|
||||
|
||||
export type StringIntern = (s: string) => string;
|
||||
export type JsonLocation = reporterTypes.Location;
|
||||
export type JsonError = string;
|
||||
|
|
|
|||
|
|
@ -17,6 +17,8 @@
|
|||
import type { TestServerInterface, TestServerInterfaceEvents } from '@testIsomorphic/testServerInterface';
|
||||
import * as events from './events';
|
||||
|
||||
// -- Reuse boundary -- Everything below this line is reused in the vscode extension.
|
||||
|
||||
export class TestServerConnection implements TestServerInterface, TestServerInterfaceEvents {
|
||||
readonly onClose: events.Event<void>;
|
||||
readonly onReport: events.Event<any>;
|
||||
|
|
@ -155,6 +157,14 @@ export class TestServerConnection implements TestServerInterface, TestServerInte
|
|||
return await this._sendMessage('runGlobalTeardown', params);
|
||||
}
|
||||
|
||||
async startDevServer(params: Parameters<TestServerInterface['startDevServer']>[0]): ReturnType<TestServerInterface['startDevServer']> {
|
||||
return await this._sendMessage('startDevServer', params);
|
||||
}
|
||||
|
||||
async stopDevServer(params: Parameters<TestServerInterface['stopDevServer']>[0]): ReturnType<TestServerInterface['stopDevServer']> {
|
||||
return await this._sendMessage('stopDevServer', params);
|
||||
}
|
||||
|
||||
async listFiles(params: Parameters<TestServerInterface['listFiles']>[0]): ReturnType<TestServerInterface['listFiles']> {
|
||||
return await this._sendMessage('listFiles', params);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -18,6 +18,8 @@ import type * as reporterTypes from '../../types/testReporter';
|
|||
import type { Event } from './events';
|
||||
import type { JsonEvent } from './teleReceiver';
|
||||
|
||||
// -- Reuse boundary -- Everything below this line is reused in the vscode extension.
|
||||
|
||||
export type ReportEntry = JsonEvent;
|
||||
|
||||
export interface TestServerInterface {
|
||||
|
|
@ -52,6 +54,16 @@ export interface TestServerInterface {
|
|||
status: reporterTypes.FullResult['status']
|
||||
}>;
|
||||
|
||||
startDevServer(params: {}): Promise<{
|
||||
report: ReportEntry[];
|
||||
status: reporterTypes.FullResult['status']
|
||||
}>;
|
||||
|
||||
stopDevServer(params: {}): Promise<{
|
||||
report: ReportEntry[];
|
||||
status: reporterTypes.FullResult['status']
|
||||
}>;
|
||||
|
||||
listFiles(params: {
|
||||
projects?: string[];
|
||||
}): Promise<{
|
||||
|
|
|
|||
|
|
@ -17,6 +17,8 @@
|
|||
export type TestItemStatus = 'none' | 'running' | 'scheduled' | 'passed' | 'failed' | 'skipped';
|
||||
import type * as reporterTypes from '../../types/testReporter';
|
||||
|
||||
// -- Reuse boundary -- Everything below this line is reused in the vscode extension.
|
||||
|
||||
export type TreeItemBase = {
|
||||
kind: 'root' | 'group' | 'case' | 'test',
|
||||
id: string;
|
||||
|
|
|
|||
|
|
@ -105,6 +105,25 @@ function addFindRelatedTestFilesCommand(program: Command) {
|
|||
});
|
||||
}
|
||||
|
||||
function addDevServerCommand(program: Command) {
|
||||
const command = program.command('dev-server', { hidden: true });
|
||||
command.description('start dev server');
|
||||
command.option('-c, --config <file>', `Configuration file, or a test directory with optional "playwright.config.{m,c}?{js,ts}"`);
|
||||
command.action(async options => {
|
||||
const configInternal = await loadConfigFromFileRestartIfNeeded(options.config);
|
||||
if (!configInternal)
|
||||
return;
|
||||
const { config } = configInternal;
|
||||
const implementation = (config as any)['@playwright/test']?.['cli']?.['dev-server'];
|
||||
if (implementation) {
|
||||
await implementation(configInternal);
|
||||
} else {
|
||||
console.log(`DevServer is not available in the package you are using. Did you mean to use component testing?`);
|
||||
gracefullyProcessExitDoNotHang(1);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function addTestServerCommand(program: Command) {
|
||||
const command = program.command('test-server', { hidden: true });
|
||||
command.description('start test server');
|
||||
|
|
@ -362,4 +381,5 @@ addListFilesCommand(program);
|
|||
addMergeReportsCommand(program);
|
||||
addClearCacheCommand(program);
|
||||
addFindRelatedTestFilesCommand(program);
|
||||
addDevServerCommand(program);
|
||||
addTestServerCommand(program);
|
||||
|
|
|
|||
|
|
@ -21,6 +21,8 @@ import type * as teleReceiver from '../isomorphic/teleReceiver';
|
|||
import { serializeRegexPatterns } from '../isomorphic/teleReceiver';
|
||||
import type { ReporterV2 } from './reporterV2';
|
||||
|
||||
// -- Reuse boundary -- Everything below this line is reused in the vscode extension.
|
||||
|
||||
export type TeleReporterEmitterOptions = {
|
||||
omitOutput?: boolean;
|
||||
omitBuffers?: boolean;
|
||||
|
|
|
|||
|
|
@ -74,6 +74,7 @@ class TestServerDispatcher implements TestServerInterface {
|
|||
private _serializer = require.resolve('./uiModeReporter');
|
||||
private _watchTestDirs = false;
|
||||
private _closeOnDisconnect = false;
|
||||
private _devServerHandle: (() => Promise<void>) | undefined;
|
||||
|
||||
constructor(configFile: string | undefined) {
|
||||
this._configFile = configFile;
|
||||
|
|
@ -172,6 +173,43 @@ class TestServerDispatcher implements TestServerInterface {
|
|||
return { status, report: globalSetup?.report || [] };
|
||||
}
|
||||
|
||||
async startDevServer(params: Parameters<TestServerInterface['startDevServer']>[0]): ReturnType<TestServerInterface['startDevServer']> {
|
||||
if (this._devServerHandle)
|
||||
return { status: 'failed', report: [] };
|
||||
const { reporter, report } = await this._collectingReporter();
|
||||
const { config, error } = await this._loadConfig(this._configFile);
|
||||
if (!config) {
|
||||
reporter.onError(error!);
|
||||
return { status: 'failed', report };
|
||||
}
|
||||
const devServerCommand = (config.config as any)['@playwright/test']?.['cli']?.['dev-server'];
|
||||
if (!devServerCommand) {
|
||||
reporter.onError({ message: 'No dev-server command found in the configuration' });
|
||||
return { status: 'failed', report };
|
||||
}
|
||||
try {
|
||||
this._devServerHandle = await devServerCommand(config);
|
||||
return { status: 'passed', report };
|
||||
} catch (e) {
|
||||
reporter.onError(serializeError(e));
|
||||
return { status: 'failed', report };
|
||||
}
|
||||
}
|
||||
|
||||
async stopDevServer(params: Parameters<TestServerInterface['stopDevServer']>[0]): ReturnType<TestServerInterface['stopDevServer']> {
|
||||
if (!this._devServerHandle)
|
||||
return { status: 'failed', report: [] };
|
||||
try {
|
||||
await this._devServerHandle();
|
||||
this._devServerHandle = undefined;
|
||||
return { status: 'passed', report: [] };
|
||||
} catch (e) {
|
||||
const { reporter, report } = await this._collectingReporter();
|
||||
reporter.onError(serializeError(e));
|
||||
return { status: 'failed', report };
|
||||
}
|
||||
}
|
||||
|
||||
async listFiles(params: Parameters<TestServerInterface['listFiles']>[0]): ReturnType<TestServerInterface['listFiles']> {
|
||||
const { reporter, report } = await this._collectingReporter();
|
||||
const { config, error } = await this._loadConfig(this._configFile);
|
||||
|
|
|
|||
Loading…
Reference in a new issue