chore(test runner): extract LastRunReporter (#32540)
This commit is contained in:
parent
b5d968fa0e
commit
356517cddb
|
|
@ -53,6 +53,7 @@ export class FullConfigInternal {
|
|||
cliListOnly = false;
|
||||
cliPassWithNoTests?: boolean;
|
||||
cliFailOnFlakyTests?: boolean;
|
||||
cliLastFailed?: boolean;
|
||||
testIdMatcher?: Matcher;
|
||||
defineConfigWasUsed = false;
|
||||
|
||||
|
|
|
|||
|
|
@ -19,7 +19,7 @@
|
|||
import type { Command } from 'playwright-core/lib/utilsBundle';
|
||||
import fs from 'fs';
|
||||
import path from 'path';
|
||||
import { Runner, readLastRunInfo } from './runner/runner';
|
||||
import { Runner } from './runner/runner';
|
||||
import { stopProfiling, startProfiling, gracefullyProcessExitDoNotHang } from 'playwright-core/lib/utils';
|
||||
import { serializeError } from './util';
|
||||
import { showHTMLReport } from './reporters/html';
|
||||
|
|
@ -207,11 +207,6 @@ async function runTests(args: string[], opts: { [key: string]: any }) {
|
|||
if (!config)
|
||||
return;
|
||||
|
||||
if (opts.lastFailed) {
|
||||
const lastRunInfo = await readLastRunInfo(config);
|
||||
config.testIdMatcher = id => lastRunInfo.failedTests.includes(id);
|
||||
}
|
||||
|
||||
config.cliArgs = args;
|
||||
config.cliGrep = opts.grep as string | undefined;
|
||||
config.cliOnlyChanged = opts.onlyChanged === true ? 'HEAD' : opts.onlyChanged;
|
||||
|
|
@ -220,6 +215,7 @@ async function runTests(args: string[], opts: { [key: string]: any }) {
|
|||
config.cliProjectFilter = opts.project || undefined;
|
||||
config.cliPassWithNoTests = !!opts.passWithNoTests;
|
||||
config.cliFailOnFlakyTests = !!opts.failOnFlakyTests;
|
||||
config.cliLastFailed = !!opts.lastFailed;
|
||||
|
||||
const runner = new Runner(config);
|
||||
const status = await runner.runAllTests();
|
||||
|
|
|
|||
71
packages/playwright/src/runner/lastRun.ts
Normal file
71
packages/playwright/src/runner/lastRun.ts
Normal file
|
|
@ -0,0 +1,71 @@
|
|||
/**
|
||||
* Copyright Microsoft Corporation. All rights reserved.
|
||||
*
|
||||
* 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 fs from 'fs';
|
||||
import path from 'path';
|
||||
import type { FullResult, Suite } from '../../types/testReporter';
|
||||
import { filterProjects } from './projectUtils';
|
||||
import type { FullConfigInternal } from '../common/config';
|
||||
import type { ReporterV2 } from '../reporters/reporterV2';
|
||||
|
||||
type LastRunInfo = {
|
||||
status: FullResult['status'];
|
||||
failedTests: string[];
|
||||
};
|
||||
|
||||
export class LastRunReporter implements ReporterV2 {
|
||||
private _config: FullConfigInternal;
|
||||
private _lastRunFile: string | undefined;
|
||||
private _suite: Suite | undefined;
|
||||
|
||||
constructor(config: FullConfigInternal) {
|
||||
this._config = config;
|
||||
const [project] = filterProjects(config.projects, config.cliProjectFilter);
|
||||
if (project)
|
||||
this._lastRunFile = path.join(project.project.outputDir, '.last-run.json');
|
||||
}
|
||||
|
||||
async filterLastFailed() {
|
||||
if (!this._lastRunFile)
|
||||
return;
|
||||
try {
|
||||
const lastRunInfo = JSON.parse(await fs.promises.readFile(this._lastRunFile, 'utf8')) as LastRunInfo;
|
||||
this._config.testIdMatcher = id => lastRunInfo.failedTests.includes(id);
|
||||
} catch {
|
||||
}
|
||||
}
|
||||
|
||||
version(): 'v2' {
|
||||
return 'v2';
|
||||
}
|
||||
|
||||
printsToStdio() {
|
||||
return false;
|
||||
}
|
||||
|
||||
onBegin(suite: Suite) {
|
||||
this._suite = suite;
|
||||
}
|
||||
|
||||
async onEnd(result: FullResult) {
|
||||
if (!this._lastRunFile || this._config.cliListOnly)
|
||||
return;
|
||||
await fs.promises.mkdir(path.dirname(this._lastRunFile), { recursive: true });
|
||||
const failedTests = this._suite?.allTests().filter(t => !t.ok()).map(t => t.id);
|
||||
const lastRunReport = JSON.stringify({ status: result.status, failedTests }, undefined, 2);
|
||||
await fs.promises.writeFile(this._lastRunFile, lastRunReport);
|
||||
}
|
||||
}
|
||||
|
|
@ -15,8 +15,6 @@
|
|||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import fs from 'fs';
|
||||
import path from 'path';
|
||||
import { monotonicTime } from 'playwright-core/lib/utils';
|
||||
import type { FullResult, TestError } from '../../types/testReporter';
|
||||
import { webServerPluginsForConfig } from '../plugins/webServerPlugin';
|
||||
|
|
@ -26,6 +24,7 @@ import { TestRun, createTaskRunner, createTaskRunnerForClearCache, createTaskRun
|
|||
import type { FullConfigInternal } from '../common/config';
|
||||
import { affectedTestFiles } from '../transform/compilationCache';
|
||||
import { InternalReporter } from '../reporters/internalReporter';
|
||||
import { LastRunReporter } from './lastRun';
|
||||
|
||||
type ProjectConfigWithFiles = {
|
||||
name: string;
|
||||
|
|
@ -76,7 +75,11 @@ export class Runner {
|
|||
webServerPluginsForConfig(config).forEach(p => config.plugins.push({ factory: p }));
|
||||
|
||||
const reporters = await createReporters(config, listOnly ? 'list' : 'test', false);
|
||||
const reporter = new InternalReporter(reporters);
|
||||
const lastRun = new LastRunReporter(config);
|
||||
if (config.cliLastFailed)
|
||||
await lastRun.filterLastFailed();
|
||||
|
||||
const reporter = new InternalReporter([...reporters, lastRun]);
|
||||
const taskRunner = listOnly ? createTaskRunnerForList(
|
||||
config,
|
||||
reporter,
|
||||
|
|
@ -94,9 +97,6 @@ export class Runner {
|
|||
if (modifiedResult && modifiedResult.status)
|
||||
status = modifiedResult.status;
|
||||
|
||||
if (!listOnly)
|
||||
await writeLastRunInfo(testRun, status);
|
||||
|
||||
await reporter.onExit();
|
||||
|
||||
// Calling process.exit() might truncate large stdout/stderr output.
|
||||
|
|
@ -143,33 +143,3 @@ export class Runner {
|
|||
return { status };
|
||||
}
|
||||
}
|
||||
|
||||
export type LastRunInfo = {
|
||||
status: FullResult['status'];
|
||||
failedTests: string[];
|
||||
};
|
||||
|
||||
async function writeLastRunInfo(testRun: TestRun, status: FullResult['status']) {
|
||||
const [project] = filterProjects(testRun.config.projects, testRun.config.cliProjectFilter);
|
||||
if (!project)
|
||||
return;
|
||||
const outputDir = project.project.outputDir;
|
||||
await fs.promises.mkdir(outputDir, { recursive: true });
|
||||
const lastRunReportFile = path.join(outputDir, '.last-run.json');
|
||||
const failedTests = testRun.rootSuite?.allTests().filter(t => !t.ok()).map(t => t.id);
|
||||
const lastRunReport = JSON.stringify({ status, failedTests }, undefined, 2);
|
||||
await fs.promises.writeFile(lastRunReportFile, lastRunReport);
|
||||
}
|
||||
|
||||
export async function readLastRunInfo(config: FullConfigInternal): Promise<LastRunInfo> {
|
||||
const [project] = filterProjects(config.projects, config.cliProjectFilter);
|
||||
if (!project)
|
||||
return { status: 'passed', failedTests: [] };
|
||||
const outputDir = project.project.outputDir;
|
||||
try {
|
||||
const lastRunReportFile = path.join(outputDir, '.last-run.json');
|
||||
return JSON.parse(await fs.promises.readFile(lastRunReportFile, 'utf8')) as LastRunInfo;
|
||||
} catch {
|
||||
}
|
||||
return { status: 'passed', failedTests: [] };
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue