chore: prepare to reuse test server from ui mode (6) (#30008)
This commit is contained in:
parent
2bd3e104ab
commit
48ccc9cbcd
|
|
@ -135,7 +135,6 @@ export class TeleReporterReceiver {
|
|||
private _reporter: Partial<ReporterV2>;
|
||||
private _tests = new Map<string, TeleTestCase>();
|
||||
private _rootDir!: string;
|
||||
private _listOnly = false;
|
||||
private _config!: reporterTypes.FullConfig;
|
||||
|
||||
constructor(reporter: Partial<ReporterV2>, options: TeleReporterReceiverOptions) {
|
||||
|
|
@ -144,11 +143,16 @@ export class TeleReporterReceiver {
|
|||
this._reporter = reporter;
|
||||
}
|
||||
|
||||
dispatch(mode: 'list' | 'test', message: JsonEvent): Promise<void> | void {
|
||||
reset() {
|
||||
this._rootSuite.suites = [];
|
||||
this._rootSuite.tests = [];
|
||||
this._tests.clear();
|
||||
}
|
||||
|
||||
dispatch(message: JsonEvent): Promise<void> | void {
|
||||
const { method, params } = message;
|
||||
if (method === 'onConfigure') {
|
||||
this._onConfigure(params.config);
|
||||
this._listOnly = mode === 'list';
|
||||
return;
|
||||
}
|
||||
if (method === 'onProject') {
|
||||
|
|
@ -205,23 +209,6 @@ export class TeleReporterReceiver {
|
|||
// Always update project in watch mode.
|
||||
projectSuite._project = this._parseProject(project);
|
||||
this._mergeSuitesInto(project.suites, projectSuite);
|
||||
|
||||
// Remove deleted tests when listing. Empty suites will be auto-filtered
|
||||
// in the UI layer.
|
||||
if (this._listOnly) {
|
||||
const testIds = new Set<string>();
|
||||
const collectIds = (suite: JsonSuite) => {
|
||||
suite.tests.map(t => t.testId).forEach(testId => testIds.add(testId));
|
||||
suite.suites.forEach(collectIds);
|
||||
};
|
||||
project.suites.forEach(collectIds);
|
||||
|
||||
const filterTests = (suite: TeleSuite) => {
|
||||
suite.tests = suite.tests.filter(t => testIds.has(t.id));
|
||||
suite.suites.forEach(filterTests);
|
||||
};
|
||||
filterTests(projectSuite);
|
||||
}
|
||||
}
|
||||
|
||||
private _onBegin() {
|
||||
|
|
|
|||
|
|
@ -20,16 +20,14 @@ import * as events from './events';
|
|||
|
||||
export class TestServerConnection implements TestServerInterface, TestServerInterfaceEvents {
|
||||
readonly onClose: events.Event<void>;
|
||||
readonly onListReport: events.Event<any>;
|
||||
readonly onTestReport: events.Event<any>;
|
||||
readonly onReport: events.Event<any>;
|
||||
readonly onStdio: events.Event<{ type: 'stderr' | 'stdout'; text?: string | undefined; buffer?: string | undefined; }>;
|
||||
readonly onListChanged: events.Event<void>;
|
||||
readonly onTestFilesChanged: events.Event<{ testFiles: string[] }>;
|
||||
readonly onLoadTraceRequested: events.Event<{ traceUrl: string }>;
|
||||
|
||||
private _onCloseEmitter = new events.EventEmitter<void>();
|
||||
private _onListReportEmitter = new events.EventEmitter<any>();
|
||||
private _onTestReportEmitter = new events.EventEmitter<any>();
|
||||
private _onReportEmitter = new events.EventEmitter<any>();
|
||||
private _onStdioEmitter = new events.EventEmitter<{ type: 'stderr' | 'stdout'; text?: string | undefined; buffer?: string | undefined; }>();
|
||||
private _onListChangedEmitter = new events.EventEmitter<void>();
|
||||
private _onTestFilesChangedEmitter = new events.EventEmitter<{ testFiles: string[] }>();
|
||||
|
|
@ -42,8 +40,7 @@ export class TestServerConnection implements TestServerInterface, TestServerInte
|
|||
|
||||
constructor(wsURL: string) {
|
||||
this.onClose = this._onCloseEmitter.event;
|
||||
this.onListReport = this._onListReportEmitter.event;
|
||||
this.onTestReport = this._onTestReportEmitter.event;
|
||||
this.onReport = this._onReportEmitter.event;
|
||||
this.onStdio = this._onStdioEmitter.event;
|
||||
this.onListChanged = this._onListChangedEmitter.event;
|
||||
this.onTestFilesChanged = this._onTestFilesChangedEmitter.event;
|
||||
|
|
@ -94,10 +91,8 @@ export class TestServerConnection implements TestServerInterface, TestServerInte
|
|||
}
|
||||
|
||||
private _dispatchEvent(method: string, params?: any) {
|
||||
if (method === 'listReport')
|
||||
this._onListReportEmitter.fire(params);
|
||||
else if (method === 'testReport')
|
||||
this._onTestReportEmitter.fire(params);
|
||||
if (method === 'report')
|
||||
this._onReportEmitter.fire(params);
|
||||
else if (method === 'stdio')
|
||||
this._onStdioEmitter.fire(params);
|
||||
else if (method === 'listChanged')
|
||||
|
|
@ -142,9 +137,10 @@ export class TestServerConnection implements TestServerInterface, TestServerInte
|
|||
return await this._sendMessage('listFiles');
|
||||
}
|
||||
|
||||
async listTests(params: { reporter?: string | undefined; fileNames?: string[] | undefined; }): Promise<void> {
|
||||
await this._sendMessage('listTests', params);
|
||||
async listTests(params: { reporter?: string | undefined; fileNames?: string[] | undefined; }): Promise<{ report: any[] }> {
|
||||
return await this._sendMessage('listTests', params);
|
||||
}
|
||||
|
||||
async runTests(params: { reporter?: string | undefined; locations?: string[] | undefined; grep?: string | undefined; testIds?: string[] | undefined; headed?: boolean | undefined; oneWorker?: boolean | undefined; trace?: 'off' | 'on' | undefined; projects?: string[] | undefined; reuseContext?: boolean | undefined; connectWsEndpoint?: string | undefined; }): Promise<void> {
|
||||
await this._sendMessage('runTests', params);
|
||||
}
|
||||
|
|
@ -153,8 +149,8 @@ export class TestServerConnection implements TestServerInterface, TestServerInte
|
|||
return await this._sendMessage('findRelatedTestFiles', params);
|
||||
}
|
||||
|
||||
async stop(): Promise<void> {
|
||||
await this._sendMessage('stop');
|
||||
async stopTests(): Promise<void> {
|
||||
await this._sendMessage('stopTests');
|
||||
}
|
||||
|
||||
async closeGracefully(): Promise<void> {
|
||||
|
|
|
|||
|
|
@ -47,10 +47,13 @@ export interface TestServerInterface {
|
|||
error?: reporterTypes.TestError;
|
||||
}>;
|
||||
|
||||
/**
|
||||
* Returns list of teleReporter events.
|
||||
*/
|
||||
listTests(params: {
|
||||
reporter?: string;
|
||||
fileNames?: string[];
|
||||
}): Promise<void>;
|
||||
}): Promise<{ report: any[] }>;
|
||||
|
||||
runTests(params: {
|
||||
reporter?: string;
|
||||
|
|
@ -69,15 +72,14 @@ export interface TestServerInterface {
|
|||
files: string[];
|
||||
}): Promise<{ testFiles: string[]; errors?: reporterTypes.TestError[]; }>;
|
||||
|
||||
stop(): Promise<void>;
|
||||
stopTests(): Promise<void>;
|
||||
|
||||
closeGracefully(): Promise<void>;
|
||||
}
|
||||
|
||||
export interface TestServerInterfaceEvents {
|
||||
onClose: Event<void>;
|
||||
onListReport: Event<any>;
|
||||
onTestReport: Event<any>;
|
||||
onReport: Event<any>;
|
||||
onStdio: Event<{ type: 'stdout' | 'stderr', text?: string, buffer?: string }>;
|
||||
onListChanged: Event<void>;
|
||||
onTestFilesChanged: Event<{ testFiles: string[] }>;
|
||||
|
|
@ -86,8 +88,7 @@ export interface TestServerInterfaceEvents {
|
|||
|
||||
export interface TestServerInterfaceEventEmitters {
|
||||
dispatchEvent(event: 'close', params: {}): void;
|
||||
dispatchEvent(event: 'listReport', params: any): void;
|
||||
dispatchEvent(event: 'testReport', params: any): void;
|
||||
dispatchEvent(event: 'report', params: any): void;
|
||||
dispatchEvent(event: 'stdio', params: { type: 'stdout' | 'stderr', text?: string, buffer?: string }): void;
|
||||
dispatchEvent(event: 'listChanged', params: {}): void;
|
||||
dispatchEvent(event: 'testFilesChanged', params: { testFiles: string[] }): void;
|
||||
|
|
|
|||
|
|
@ -65,7 +65,7 @@ export async function createMergedReport(config: FullConfigInternal, dir: string
|
|||
for (const event of events) {
|
||||
if (event.method === 'onEnd')
|
||||
printStatus(`building final report`);
|
||||
await receiver.dispatch('test', event);
|
||||
await receiver.dispatch(event);
|
||||
if (event.method === 'onEnd')
|
||||
printStatus(`finished building report`);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -89,7 +89,6 @@ function reporterOptions(config: FullConfigInternal, mode: 'list' | 'test' | 'ui
|
|||
return {
|
||||
configDir: config.configDir,
|
||||
_send: send,
|
||||
_mode: mode,
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -14,6 +14,8 @@
|
|||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import fs from 'fs';
|
||||
import path from 'path';
|
||||
import { registry, startTraceViewerServer } from 'playwright-core/lib/server';
|
||||
import { ManualPromise, gracefullyProcessExitDoNotHang, isUnderTest } from 'playwright-core/lib/utils';
|
||||
import type { Transport, HttpServer } from 'playwright-core/lib/utils';
|
||||
|
|
@ -172,12 +174,17 @@ class TestServerDispatcher implements TestServerInterface {
|
|||
}
|
||||
|
||||
async listTests(params: { reporter?: string; fileNames: string[]; }) {
|
||||
this._queue = this._queue.then(() => this._innerListTests(params)).catch(printInternalError);
|
||||
let report: any[] = [];
|
||||
this._queue = this._queue.then(async () => {
|
||||
report = await this._innerListTests(params);
|
||||
}).catch(printInternalError);
|
||||
await this._queue;
|
||||
return { report };
|
||||
}
|
||||
|
||||
private async _innerListTests(params: { reporter?: string; fileNames?: string[]; }) {
|
||||
const wireReporter = await createReporterForTestServer(this._config, params.reporter || require.resolve('./uiModeReporter'), 'list', e => this._dispatchEvent('listReport', e));
|
||||
const report: any[] = [];
|
||||
const wireReporter = await createReporterForTestServer(this._config, params.reporter || require.resolve('./uiModeReporter'), 'list', e => report.push(e));
|
||||
const reporter = new InternalReporter(wireReporter);
|
||||
this._config.cliArgs = params.fileNames || [];
|
||||
this._config.cliListOnly = true;
|
||||
|
|
@ -195,7 +202,15 @@ class TestServerDispatcher implements TestServerInterface {
|
|||
projectDirs.add(p.project.testDir);
|
||||
projectOutputs.add(p.project.outputDir);
|
||||
}
|
||||
|
||||
const result = await resolveCtDirs(this._config);
|
||||
if (result) {
|
||||
projectDirs.add(result.templateDir);
|
||||
projectOutputs.add(result.outDir);
|
||||
}
|
||||
|
||||
this._globalWatcher.update([...projectDirs], [...projectOutputs], false);
|
||||
return report;
|
||||
}
|
||||
|
||||
async runTests(params: { reporter?: string; locations?: string[] | undefined; grep?: string | undefined; testIds?: string[] | undefined; headed?: boolean | undefined; oneWorker?: boolean | undefined; trace?: 'off' | 'on' | undefined; projects?: string[] | undefined; reuseContext?: boolean | undefined; connectWsEndpoint?: string | undefined; }) {
|
||||
|
|
@ -204,7 +219,7 @@ class TestServerDispatcher implements TestServerInterface {
|
|||
}
|
||||
|
||||
private async _innerRunTests(params: { reporter?: string; locations?: string[] | undefined; grep?: string | undefined; testIds?: string[] | undefined; headed?: boolean | undefined; oneWorker?: boolean | undefined; trace?: 'off' | 'on' | undefined; projects?: string[] | undefined; reuseContext?: boolean | undefined; connectWsEndpoint?: string | undefined; }) {
|
||||
await this.stop();
|
||||
await this.stopTests();
|
||||
const { testIds, projects, locations, grep } = params;
|
||||
|
||||
const testIdSet = testIds ? new Set<string>(testIds) : null;
|
||||
|
|
@ -215,7 +230,7 @@ class TestServerDispatcher implements TestServerInterface {
|
|||
this._config.testIdMatcher = id => !testIdSet || testIdSet.has(id);
|
||||
|
||||
const reporters = await createReporters(this._config, 'ui');
|
||||
reporters.push(await createReporterForTestServer(this._config, params.reporter || require.resolve('./uiModeReporter'), 'list', e => this._dispatchEvent('testReport', e)));
|
||||
reporters.push(await createReporterForTestServer(this._config, params.reporter || require.resolve('./uiModeReporter'), 'list', e => this._dispatchEvent('report', e)));
|
||||
const reporter = new InternalReporter(new Multiplexer(reporters));
|
||||
const taskRunner = createTaskRunnerForWatch(this._config, reporter);
|
||||
const testRun = new TestRun(this._config, reporter);
|
||||
|
|
@ -246,7 +261,7 @@ class TestServerDispatcher implements TestServerInterface {
|
|||
return runner.findRelatedTestFiles('out-of-process', params.files);
|
||||
}
|
||||
|
||||
async stop() {
|
||||
async stopTests() {
|
||||
this._testRun?.stop?.resolve();
|
||||
await this._testRun?.run;
|
||||
}
|
||||
|
|
@ -306,3 +321,17 @@ function printInternalError(e: Error) {
|
|||
// eslint-disable-next-line no-console
|
||||
console.error('Internal error:', e);
|
||||
}
|
||||
|
||||
// TODO: remove CT dependency.
|
||||
export async function resolveCtDirs(config: FullConfigInternal) {
|
||||
const use = config.config.projects[0].use as any;
|
||||
const relativeTemplateDir = use.ctTemplateDir || 'playwright';
|
||||
const templateDir = await fs.promises.realpath(path.normalize(path.join(config.configDir, relativeTemplateDir))).catch(() => undefined);
|
||||
if (!templateDir)
|
||||
return null;
|
||||
const outDir = use.ctCacheDir ? path.resolve(config.configDir, use.ctCacheDir) : path.resolve(templateDir, '.cache');
|
||||
return {
|
||||
outDir,
|
||||
templateDir
|
||||
};
|
||||
}
|
||||
|
|
|
|||
|
|
@ -128,11 +128,16 @@ export class TeleSuiteUpdater {
|
|||
};
|
||||
}
|
||||
|
||||
dispatch(mode: 'test' | 'list', message: any) {
|
||||
processListReport(report: any[]) {
|
||||
this._receiver.reset();
|
||||
for (const message of report)
|
||||
this._receiver.dispatch(message);
|
||||
}
|
||||
|
||||
processTestReport(message: any) {
|
||||
// The order of receiver dispatches matters here, we want to assign `lastRunTestCount`
|
||||
// before we use it.
|
||||
if (mode === 'test')
|
||||
this._lastRunReceiver?.dispatch('test', message)?.catch(() => {});
|
||||
this._receiver.dispatch(mode, message)?.catch(() => {});
|
||||
this._lastRunReceiver?.dispatch(message)?.catch(() => {});
|
||||
this._receiver.dispatch(message)?.catch(() => {});
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -184,7 +184,7 @@ export const UIModeView: React.FC<{}> = ({
|
|||
const onShortcutEvent = (e: KeyboardEvent) => {
|
||||
if (e.code === 'F6') {
|
||||
e.preventDefault();
|
||||
testServerConnection?.stop().catch(() => {});
|
||||
testServerConnection?.stopTests().catch(() => {});
|
||||
} else if (e.code === 'F5') {
|
||||
e.preventDefault();
|
||||
reloadTests();
|
||||
|
|
@ -278,7 +278,7 @@ export const UIModeView: React.FC<{}> = ({
|
|||
<div>Running {progress.passed}/{runningState.testIds.size} passed ({(progress.passed / runningState.testIds.size) * 100 | 0}%)</div>
|
||||
</div>}
|
||||
<ToolbarButton icon='play' title='Run all' onClick={() => runTests('bounce-if-busy', visibleTestIds)} disabled={isRunningTest || isLoading}></ToolbarButton>
|
||||
<ToolbarButton icon='debug-stop' title='Stop' onClick={() => testServerConnection?.stop()} disabled={!isRunningTest || isLoading}></ToolbarButton>
|
||||
<ToolbarButton icon='debug-stop' title='Stop' onClick={() => testServerConnection?.stopTests()} disabled={!isRunningTest || isLoading}></ToolbarButton>
|
||||
<ToolbarButton icon='eye' title='Watch all' toggled={watchAll} onClick={() => {
|
||||
setWatchedTreeIds({ value: new Set() });
|
||||
setWatchAll(!watchAll);
|
||||
|
|
@ -648,12 +648,14 @@ const refreshRootSuite = async (testServerConnection: TestServerConnection): Pro
|
|||
},
|
||||
pathSeparator,
|
||||
});
|
||||
return testServerConnection.listTests({});
|
||||
const { report } = await testServerConnection.listTests({});
|
||||
teleSuiteUpdater?.processListReport(report);
|
||||
};
|
||||
|
||||
const wireConnectionListeners = (testServerConnection: TestServerConnection) => {
|
||||
testServerConnection.onListChanged(() => {
|
||||
testServerConnection.listTests({}).catch(() => {});
|
||||
testServerConnection.onListChanged(async () => {
|
||||
const { report } = await testServerConnection.listTests({});
|
||||
teleSuiteUpdater?.processListReport(report);
|
||||
});
|
||||
|
||||
testServerConnection.onTestFilesChanged(params => {
|
||||
|
|
@ -669,12 +671,8 @@ const wireConnectionListeners = (testServerConnection: TestServerConnection) =>
|
|||
}
|
||||
});
|
||||
|
||||
testServerConnection.onListReport(params => {
|
||||
teleSuiteUpdater?.dispatch('list', params);
|
||||
});
|
||||
|
||||
testServerConnection.onTestReport(params => {
|
||||
teleSuiteUpdater?.dispatch('test', params);
|
||||
testServerConnection.onReport(params => {
|
||||
teleSuiteUpdater?.processTestReport(params);
|
||||
});
|
||||
|
||||
xtermDataSource.resize = (cols, rows) => {
|
||||
|
|
|
|||
Loading…
Reference in a new issue