diff --git a/src/playwrightTest.ts b/src/playwrightTest.ts index 8388a9fad..1f8c0f767 100644 --- a/src/playwrightTest.ts +++ b/src/playwrightTest.ts @@ -94,16 +94,12 @@ export class PlaywrightTest { } async listFiles(config: TestConfig): Promise { - const configFolder = path.dirname(config.configFile); - const configFile = path.basename(config.configFile); - const allArgs = [config.cli, 'list-files', '-c', configFile]; - { - // For tests. - this._log(`${escapeRegex(path.relative(config.workspaceFolder, configFolder))}> playwright list-files -c ${configFile}`); - } try { - const output = await this._runNode(allArgs, configFolder); - const result = JSON.parse(output) as ConfigListFilesReport; + let result: ConfigListFilesReport; + if (this._useTestServer(config)) + result = await this._listFilesServer(config); + else + result = await this._listFilesCLI(config); // TODO: merge getPlaywrightInfo and listFiles to avoid this. // Override the cli entry point with the one obtained from the config. if (result.cliEntryPoint) @@ -112,7 +108,7 @@ export class PlaywrightTest { } catch (error: any) { return { error: { - location: { file: configFile, line: 0, column: 0 }, + location: { file: config.configFile, line: 0, column: 0 }, message: error.message, }, projects: [], @@ -120,6 +116,26 @@ export class PlaywrightTest { } } + private async _listFilesCLI(config: TestConfig): Promise { + const configFolder = path.dirname(config.configFile); + const configFile = path.basename(config.configFile); + const allArgs = [config.cli, 'list-files', '-c', configFile]; + { + // For tests. + this._log(`${escapeRegex(path.relative(config.workspaceFolder, configFolder))}> playwright list-files -c ${configFile}`); + } + const output = await this._runNode(allArgs, configFolder); + const result = JSON.parse(output) as ConfigListFilesReport; + return result; + } + + private async _listFilesServer(config: TestConfig): Promise { + const testServer = await this._testServerController.testServerFor(config); + if (!testServer) + throw new Error('Internal error: unable to connect to the test server'); + return await testServer.listFiles({ configFile: config.configFile }); + } + async runTests(config: TestConfig, projectNames: string[], locations: string[] | null, listener: TestListener, parametrizedTestTitle: string | undefined, token: vscodeTypes.CancellationToken) { const locationArg = locations ? locations : []; if (token?.isCancellationRequested) @@ -252,13 +268,11 @@ export class PlaywrightTest { if (token?.isCancellationRequested) return; if (!testServer) - return this._testWithCLI(config, locations, mode, options, listener, token); - if (token?.isCancellationRequested) return; const env = await reporterServer.env({ selfDestruct: false }); const reporter = reporterServer.reporterFile(); if (mode === 'list') - testServer.list({ configFile: config.configFile, locations, reporter, env }); + testServer.listTests({ configFile: config.configFile, locations, reporter, env }); if (mode === 'run') { testServer.test({ configFile: config.configFile, locations, reporter, env, ...options }); token.onCancellationRequested(() => { @@ -308,7 +322,7 @@ export class PlaywrightTest { async _findRelatedTestFilesServer(config: TestConfig, files: string[]): Promise { const testServer = await this._testServerController.testServerFor(config); if (!testServer) - return await this._findRelatedTestFilesCLI(config, files); + return { testFiles: files, errors: [{ message: 'Internal error: unable to connect to the test server' }] }; return await testServer.findRelatedTestFiles({ configFile: config.configFile, files }); } diff --git a/src/testServerController.ts b/src/testServerController.ts index b4f32f403..6afd82c1b 100644 --- a/src/testServerController.ts +++ b/src/testServerController.ts @@ -17,13 +17,12 @@ import { BackendClient, BackendServer } from './backend'; import { ConfigFindRelatedTestFilesReport } from './listTests'; import { TestConfig } from './playwrightTest'; -import type { TestError } from './reporter'; +import type { TestServerEvents, TestServerInterface } from './testServerInterface'; import * as vscodeTypes from './vscodeTypes'; export class TestServerController implements vscodeTypes.Disposable { private _vscode: vscodeTypes.VSCode; private _instancePromise: Promise | undefined; - private _instance: TestServer | null = null; private _envProvider: () => NodeJS.ProcessEnv; constructor(vscode: vscodeTypes.VSCode, envProvider: () => NodeJS.ProcessEnv) { @@ -53,7 +52,6 @@ export class TestServerController implements vscodeTypes.Disposable { dumpIO: false, }); const testServer = await testServerBackend.start(); - this._instance = testServer; return testServer; } @@ -65,54 +63,19 @@ export class TestServerController implements vscodeTypes.Disposable { if (this._instancePromise) this._instancePromise.then(server => server?.closeGracefully()); this._instancePromise = undefined; - this._instance = null; } } -interface TestServerInterface { - list(params: { - configFile: string; - locations: string[]; - reporter: string; - env: NodeJS.ProcessEnv; - }): Promise; - - test(params: { - configFile: string; - locations: string[]; - reporter: string; - env: NodeJS.ProcessEnv; - headed?: boolean; - oneWorker?: boolean; - trace?: 'on' | 'off'; - projects?: string[]; - grep?: string; - reuseContext?: boolean; - connectWsEndpoint?: string; - }): Promise; - - findRelatedTestFiles(params: { - configFile: string; - files: string[]; - }): Promise<{ testFiles: string[]; errors?: TestError[]; }>; - - stop(params: { - configFile: string; - }): Promise; - - closeGracefully(): Promise; -} - -interface TestServerEvents { - on(event: 'stdio', listener: (params: { type: 'stdout' | 'stderr', text?: string, buffer?: string }) => void): void; -} - class TestServer extends BackendClient implements TestServerInterface, TestServerEvents { override async initialize(): Promise { } - async list(params: any) { - await this.send('list', params); + async listFiles(params: any) { + return await this.send('listFiles', params); + } + + async listTests(params: any) { + await this.send('listTests', params); } findRelatedTestFiles(params: { files: string[]; }): Promise { diff --git a/src/testServerInterface.ts b/src/testServerInterface.ts new file mode 100644 index 000000000..d7040a03d --- /dev/null +++ b/src/testServerInterface.ts @@ -0,0 +1,68 @@ +/** + * 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. + */ + +import type { TestError } from './reporter'; + +export interface TestServerInterface { + listFiles(params: { + configFile: string; + }): Promise<{ + projects: { + name: string; + testDir: string; + use: { testIdAttribute?: string }; + files: string[]; + }[]; + cliEntryPoint?: string; + error?: TestError; + }>; + + listTests(params: { + configFile: string; + locations: string[]; + reporter: string; + env: NodeJS.ProcessEnv; + }): Promise; + + test(params: { + configFile: string; + locations: string[]; + reporter: string; + env: NodeJS.ProcessEnv; + headed?: boolean; + oneWorker?: boolean; + trace?: 'on' | 'off'; + projects?: string[]; + grep?: string; + reuseContext?: boolean; + connectWsEndpoint?: string; + }): Promise; + + findRelatedTestFiles(params: { + configFile: string; + files: string[]; + }): Promise<{ testFiles: string[]; errors?: TestError[]; }>; + + stop(params: { + configFile: string; + }): Promise; + + closeGracefully(): Promise; +} + +export interface TestServerEvents { + on(event: 'stdio', listener: (params: { type: 'stdout' | 'stderr', text?: string, buffer?: string }) => void): void; +} diff --git a/tests/utils.ts b/tests/utils.ts index 0b9e012fb..6f43d8d3b 100644 --- a/tests/utils.ts +++ b/tests/utils.ts @@ -83,9 +83,6 @@ export const test = baseTest.extend({ } else { await vscode.addWorkspaceFolder(options?.rootDir || testInfo.outputDir, files); } - const extension = new Extension(vscode); - vscode.extensions.push(extension); - await vscode.activate(); if (showBrowser) { const configuration = vscode.workspace.getConfiguration('playwright'); @@ -96,6 +93,10 @@ export const test = baseTest.extend({ configuration.update('useTestServer', true); } + const extension = new Extension(vscode); + vscode.extensions.push(extension); + await vscode.activate(); + instances.push(vscode); return { vscode, diff --git a/tests/watch.spec.ts b/tests/watch.spec.ts index ca4d79498..6e22beabd 100644 --- a/tests/watch.spec.ts +++ b/tests/watch.spec.ts @@ -88,7 +88,7 @@ test('should unwatch all tests', async ({ activate }) => { expect(testRuns).toHaveLength(0); - expect(vscode.renderExecLog(' ')).toBe(` + expect(vscode).toHaveExecLog(` > playwright list-files -c playwright.config.js `); });