chore: implement server-based list files (#29633)
This commit is contained in:
parent
2ca45ff948
commit
a3aea813bc
|
|
@ -27,7 +27,7 @@ import { createMergedReport } from './reporters/merge';
|
||||||
import { loadConfigFromFileRestartIfNeeded, loadEmptyConfigForMergeReports } from './common/configLoader';
|
import { loadConfigFromFileRestartIfNeeded, loadEmptyConfigForMergeReports } from './common/configLoader';
|
||||||
import type { ConfigCLIOverrides } from './common/ipc';
|
import type { ConfigCLIOverrides } from './common/ipc';
|
||||||
import type { FullResult, TestError } from '../types/testReporter';
|
import type { FullResult, TestError } from '../types/testReporter';
|
||||||
import type { FullConfig, TraceMode } from '../types/test';
|
import type { TraceMode } from '../types/test';
|
||||||
import { builtInReporters, defaultReporter, defaultTimeout } from './common/config';
|
import { builtInReporters, defaultReporter, defaultTimeout } from './common/config';
|
||||||
import { program } from 'playwright-core/lib/cli/program';
|
import { program } from 'playwright-core/lib/cli/program';
|
||||||
export { program } from 'playwright-core/lib/cli/program';
|
export { program } from 'playwright-core/lib/cli/program';
|
||||||
|
|
@ -176,7 +176,7 @@ async function runTests(args: string[], opts: { [key: string]: any }) {
|
||||||
gracefullyProcessExitDoNotHang(exitCode);
|
gracefullyProcessExitDoNotHang(exitCode);
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function withRunnerAndMutedWrite(configFile: string | undefined, callback: (runner: Runner, config: FullConfig, configDir: string) => Promise<any>) {
|
export async function withRunnerAndMutedWrite(configFile: string | undefined, callback: (runner: Runner) => Promise<any>) {
|
||||||
// Redefine process.stdout.write in case config decides to pollute stdio.
|
// Redefine process.stdout.write in case config decides to pollute stdio.
|
||||||
const stdoutWrite = process.stdout.write.bind(process.stdout);
|
const stdoutWrite = process.stdout.write.bind(process.stdout);
|
||||||
process.stdout.write = ((a: any, b: any, c: any) => process.stderr.write(a, b, c)) as any;
|
process.stdout.write = ((a: any, b: any, c: any) => process.stderr.write(a, b, c)) as any;
|
||||||
|
|
@ -185,7 +185,7 @@ export async function withRunnerAndMutedWrite(configFile: string | undefined, ca
|
||||||
if (!config)
|
if (!config)
|
||||||
return;
|
return;
|
||||||
const runner = new Runner(config);
|
const runner = new Runner(config);
|
||||||
const result = await callback(runner, config.config, config.configDir);
|
const result = await callback(runner);
|
||||||
stdoutWrite(JSON.stringify(result, undefined, 2), () => {
|
stdoutWrite(JSON.stringify(result, undefined, 2), () => {
|
||||||
gracefullyProcessExitDoNotHang(0);
|
gracefullyProcessExitDoNotHang(0);
|
||||||
});
|
});
|
||||||
|
|
@ -199,9 +199,8 @@ export async function withRunnerAndMutedWrite(configFile: string | undefined, ca
|
||||||
}
|
}
|
||||||
|
|
||||||
async function listTestFiles(opts: { [key: string]: any }) {
|
async function listTestFiles(opts: { [key: string]: any }) {
|
||||||
await withRunnerAndMutedWrite(opts.config, async (runner, config) => {
|
await withRunnerAndMutedWrite(opts.config, async runner => {
|
||||||
const frameworkPackage = (config as any)['@playwright/test']?.['packageJSON'];
|
return await runner.listTestFiles();
|
||||||
return await runner.listTestFiles(frameworkPackage, opts.project);
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -57,8 +57,9 @@ export class Runner {
|
||||||
this._config = config;
|
this._config = config;
|
||||||
}
|
}
|
||||||
|
|
||||||
async listTestFiles(frameworkPackage: string | undefined, projectNames: string[] | undefined): Promise<ConfigListFilesReport> {
|
async listTestFiles(): Promise<ConfigListFilesReport> {
|
||||||
const projects = filterProjects(this._config.projects, projectNames);
|
const frameworkPackage = (this._config.config as any)['@playwright/test']?.['packageJSON'];
|
||||||
|
const projects = filterProjects(this._config.projects);
|
||||||
const report: ConfigListFilesReport = {
|
const report: ConfigListFilesReport = {
|
||||||
projects: [],
|
projects: [],
|
||||||
cliEntryPoint: frameworkPackage ? path.join(path.dirname(frameworkPackage), 'cli.js') : undefined,
|
cliEntryPoint: frameworkPackage ? path.join(path.dirname(frameworkPackage), 'cli.js') : undefined,
|
||||||
|
|
|
||||||
|
|
@ -29,6 +29,9 @@ import type { ConfigCLIOverrides } from '../common/ipc';
|
||||||
import { Runner } from './runner';
|
import { Runner } from './runner';
|
||||||
import type { FindRelatedTestFilesReport } from './runner';
|
import type { FindRelatedTestFilesReport } from './runner';
|
||||||
import type { FullConfigInternal } from '../common/config';
|
import type { FullConfigInternal } from '../common/config';
|
||||||
|
import type { TestServerInterface } from './testServerInterface';
|
||||||
|
import { serializeError } from '../util';
|
||||||
|
import { prepareErrorStack } from '../reporters/base';
|
||||||
|
|
||||||
export async function runTestServer() {
|
export async function runTestServer() {
|
||||||
if (restartWithExperimentalTsEsm(undefined, true))
|
if (restartWithExperimentalTsEsm(undefined, true))
|
||||||
|
|
@ -60,44 +63,6 @@ export async function runTestServer() {
|
||||||
process.stdin.on('close', () => gracefullyProcessExitDoNotHang(0));
|
process.stdin.on('close', () => gracefullyProcessExitDoNotHang(0));
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface TestServerInterface {
|
|
||||||
list(params: {
|
|
||||||
configFile: string;
|
|
||||||
locations: string[];
|
|
||||||
reporter: string;
|
|
||||||
env: NodeJS.ProcessEnv;
|
|
||||||
}): Promise<void>;
|
|
||||||
|
|
||||||
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<void>;
|
|
||||||
|
|
||||||
findRelatedTestFiles(params: {
|
|
||||||
configFile: string;
|
|
||||||
files: string[];
|
|
||||||
}): Promise<{ testFiles: string[]; errors?: TestError[]; }>;
|
|
||||||
|
|
||||||
stop(params: {
|
|
||||||
configFile: string;
|
|
||||||
}): Promise<void>;
|
|
||||||
|
|
||||||
closeGracefully(): Promise<void>;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface TestServerEvents {
|
|
||||||
on(event: 'stdio', listener: (params: { type: 'stdout' | 'stderr', text?: string, buffer?: string }) => void): void;
|
|
||||||
}
|
|
||||||
|
|
||||||
class Dispatcher implements TestServerInterface {
|
class Dispatcher implements TestServerInterface {
|
||||||
private _testRun: { run: Promise<FullResult['status']>, stop: ManualPromise<void> } | undefined;
|
private _testRun: { run: Promise<FullResult['status']>, stop: ManualPromise<void> } | undefined;
|
||||||
private _ws: WebSocket;
|
private _ws: WebSocket;
|
||||||
|
|
@ -123,7 +88,30 @@ class Dispatcher implements TestServerInterface {
|
||||||
}) as any;
|
}) as any;
|
||||||
}
|
}
|
||||||
|
|
||||||
async list(params: {
|
async listFiles(params: {
|
||||||
|
configFile: string;
|
||||||
|
}): Promise<{
|
||||||
|
projects: {
|
||||||
|
name: string;
|
||||||
|
testDir: string;
|
||||||
|
use: { testIdAttribute?: string };
|
||||||
|
files: string[];
|
||||||
|
}[];
|
||||||
|
cliEntryPoint?: string;
|
||||||
|
error?: TestError;
|
||||||
|
}> {
|
||||||
|
try {
|
||||||
|
const config = await this._loadConfig(params.configFile);
|
||||||
|
const runner = new Runner(config);
|
||||||
|
return runner.listTestFiles();
|
||||||
|
} catch (e) {
|
||||||
|
const error: TestError = serializeError(e);
|
||||||
|
error.location = prepareErrorStack(e.stack).location;
|
||||||
|
return { projects: [], error };
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async listTests(params: {
|
||||||
configFile: string;
|
configFile: string;
|
||||||
locations: string[];
|
locations: string[];
|
||||||
reporter: string;
|
reporter: string;
|
||||||
|
|
|
||||||
68
packages/playwright/src/runner/testServerInterface.ts
Normal file
68
packages/playwright/src/runner/testServerInterface.ts
Normal file
|
|
@ -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 '../../types/testReporter';
|
||||||
|
|
||||||
|
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<void>;
|
||||||
|
|
||||||
|
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<void>;
|
||||||
|
|
||||||
|
findRelatedTestFiles(params: {
|
||||||
|
configFile: string;
|
||||||
|
files: string[];
|
||||||
|
}): Promise<{ testFiles: string[]; errors?: TestError[]; }>;
|
||||||
|
|
||||||
|
stop(params: {
|
||||||
|
configFile: string;
|
||||||
|
}): Promise<void>;
|
||||||
|
|
||||||
|
closeGracefully(): Promise<void>;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface TestServerEvents {
|
||||||
|
on(event: 'stdio', listener: (params: { type: 'stdout' | 'stderr', text?: string, buffer?: string }) => void): void;
|
||||||
|
}
|
||||||
|
|
@ -48,30 +48,6 @@ test('should list files', async ({ runCLICommand }) => {
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
test('should support wildcard list files', async ({ runCLICommand }) => {
|
|
||||||
const result = await runCLICommand({
|
|
||||||
'playwright.config.ts': `
|
|
||||||
module.exports = { projects: [{ name: 'foo' }, { name: 'bar' }] };
|
|
||||||
`,
|
|
||||||
'a.test.js': ``
|
|
||||||
}, 'list-files', ['--project', 'f*o']);
|
|
||||||
expect(result.exitCode).toBe(0);
|
|
||||||
|
|
||||||
const data = JSON.parse(result.stdout);
|
|
||||||
expect(data).toEqual({
|
|
||||||
projects: [
|
|
||||||
{
|
|
||||||
name: 'foo',
|
|
||||||
testDir: expect.stringContaining('list-files-should-support-wildcard-list-files-playwright-test'),
|
|
||||||
use: {},
|
|
||||||
files: [
|
|
||||||
expect.stringContaining('a.test.js')
|
|
||||||
]
|
|
||||||
}
|
|
||||||
]
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
test('should include testIdAttribute', async ({ runCLICommand }) => {
|
test('should include testIdAttribute', async ({ runCLICommand }) => {
|
||||||
const result = await runCLICommand({
|
const result = await runCLICommand({
|
||||||
'playwright.config.ts': `
|
'playwright.config.ts': `
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue