chore: iterate towards tele reporter reuse in vscode (2) (#29812)
This commit is contained in:
parent
5eb8fea616
commit
e314b83e56
|
|
@ -122,10 +122,9 @@ export type JsonEvent = {
|
|||
};
|
||||
|
||||
type TeleReporterReceiverOptions = {
|
||||
pathSeparator: string;
|
||||
mergeProjects: boolean;
|
||||
mergeTestCases: boolean;
|
||||
internString?: StringIntern;
|
||||
resolvePath: (rootDir: string, relativePath: string) => string;
|
||||
configOverrides?: Pick<reporterTypes.FullConfig, 'configFile' | 'quiet' | 'reportSlowTests' | 'reporter'>;
|
||||
};
|
||||
|
||||
|
|
@ -242,7 +241,6 @@ export class TeleReporterReceiver {
|
|||
testResult.workerIndex = payload.workerIndex;
|
||||
testResult.parallelIndex = payload.parallelIndex;
|
||||
testResult.setStartTimeNumber(payload.startTime);
|
||||
testResult.statusEx = 'running';
|
||||
this._reporter.onTestBegin?.(test, testResult);
|
||||
}
|
||||
|
||||
|
|
@ -251,22 +249,21 @@ export class TeleReporterReceiver {
|
|||
test.timeout = testEndPayload.timeout;
|
||||
test.expectedStatus = testEndPayload.expectedStatus;
|
||||
test.annotations = testEndPayload.annotations;
|
||||
const result = test.resultsMap.get(payload.id)!;
|
||||
const result = test._resultsMap.get(payload.id)!;
|
||||
result.duration = payload.duration;
|
||||
result.status = payload.status;
|
||||
result.statusEx = payload.status;
|
||||
result.errors = payload.errors;
|
||||
result.error = result.errors?.[0];
|
||||
result.attachments = this._parseAttachments(payload.attachments);
|
||||
this._reporter.onTestEnd?.(test, result);
|
||||
// Free up the memory as won't see these step ids.
|
||||
result.stepMap = new Map();
|
||||
result._stepMap = new Map();
|
||||
}
|
||||
|
||||
private _onStepBegin(testId: string, resultId: string, payload: JsonTestStepStart) {
|
||||
const test = this._tests.get(testId)!;
|
||||
const result = test.resultsMap.get(resultId)!;
|
||||
const parentStep = payload.parentStepId ? result.stepMap.get(payload.parentStepId) : undefined;
|
||||
const result = test._resultsMap.get(resultId)!;
|
||||
const parentStep = payload.parentStepId ? result._stepMap.get(payload.parentStepId) : undefined;
|
||||
|
||||
const location = this._absoluteLocation(payload.location);
|
||||
const step = new TeleTestStep(payload, parentStep, location);
|
||||
|
|
@ -274,14 +271,14 @@ export class TeleReporterReceiver {
|
|||
parentStep.steps.push(step);
|
||||
else
|
||||
result.steps.push(step);
|
||||
result.stepMap.set(payload.id, step);
|
||||
result._stepMap.set(payload.id, step);
|
||||
this._reporter.onStepBegin?.(test, result, step);
|
||||
}
|
||||
|
||||
private _onStepEnd(testId: string, resultId: string, payload: JsonTestStepEnd) {
|
||||
const test = this._tests.get(testId)!;
|
||||
const result = test.resultsMap.get(resultId)!;
|
||||
const step = result.stepMap.get(payload.id)!;
|
||||
const result = test._resultsMap.get(resultId)!;
|
||||
const step = result._stepMap.get(payload.id)!;
|
||||
step.duration = payload.duration;
|
||||
step.error = payload.error;
|
||||
this._reporter.onStepEnd?.(test, result, step);
|
||||
|
|
@ -294,7 +291,7 @@ export class TeleReporterReceiver {
|
|||
private _onStdIO(type: JsonStdIOType, testId: string | undefined, resultId: string | undefined, data: string, isBase64: boolean) {
|
||||
const chunk = isBase64 ? ((globalThis as any).Buffer ? Buffer.from(data, 'base64') : atob(data)) : data;
|
||||
const test = testId ? this._tests.get(testId) : undefined;
|
||||
const result = test && resultId ? test.resultsMap.get(resultId) : undefined;
|
||||
const result = test && resultId ? test._resultsMap.get(resultId) : undefined;
|
||||
if (type === 'stdout') {
|
||||
result?.stdout.push(chunk);
|
||||
this._reporter.onStdOut?.(chunk, test, result);
|
||||
|
|
@ -407,11 +404,7 @@ export class TeleReporterReceiver {
|
|||
private _absolutePath(relativePath?: string): string | undefined {
|
||||
if (relativePath === undefined)
|
||||
return;
|
||||
return this._internString(this._rootDir + this._options.pathSeparator + relativePath);
|
||||
}
|
||||
|
||||
private _internString(s: string): string {
|
||||
return this._options.internString ? this._options.internString(s) : s;
|
||||
return this._options.resolvePath(this._rootDir, relativePath);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -475,7 +468,7 @@ export class TeleTestCase implements reporterTypes.TestCase {
|
|||
repeatEachIndex = 0;
|
||||
id: string;
|
||||
|
||||
resultsMap = new Map<string, TeleTestResult>();
|
||||
_resultsMap = new Map<string, TeleTestResult>();
|
||||
|
||||
constructor(id: string, title: string, location: reporterTypes.Location, repeatEachIndex: number) {
|
||||
this.id = id;
|
||||
|
|
@ -515,13 +508,13 @@ export class TeleTestCase implements reporterTypes.TestCase {
|
|||
|
||||
_clearResults() {
|
||||
this.results = [];
|
||||
this.resultsMap.clear();
|
||||
this._resultsMap.clear();
|
||||
}
|
||||
|
||||
_createTestResult(id: string): TeleTestResult {
|
||||
const result = new TeleTestResult(this.results.length);
|
||||
this.results.push(result);
|
||||
this.resultsMap.set(id, result);
|
||||
this._resultsMap.set(id, result);
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
|
@ -571,8 +564,7 @@ class TeleTestResult implements reporterTypes.TestResult {
|
|||
errors: reporterTypes.TestResult['errors'] = [];
|
||||
error: reporterTypes.TestResult['error'];
|
||||
|
||||
stepMap: Map<string, reporterTypes.TestStep> = new Map();
|
||||
statusEx: reporterTypes.TestResult['status'] | 'scheduled' | 'running' = 'scheduled';
|
||||
_stepMap: Map<string, reporterTypes.TestStep> = new Map();
|
||||
|
||||
private _startTime: number = 0;
|
||||
|
||||
|
|
|
|||
|
|
@ -50,7 +50,7 @@ export class BlobReporter extends TeleReporterEmitter {
|
|||
private _reportName!: string;
|
||||
|
||||
constructor(options: BlobReporterOptions) {
|
||||
super(message => this._messages.push(message), false);
|
||||
super(message => this._messages.push(message));
|
||||
this._options = options;
|
||||
if (this._options.fileName && !this._options.fileName.endsWith('.zip'))
|
||||
throw new Error(`Blob report file name must end with .zip extension: ${this._options.fileName}`);
|
||||
|
|
|
|||
|
|
@ -54,10 +54,9 @@ export async function createMergedReport(config: FullConfigInternal, dir: string
|
|||
// If explicit config is provided, use platform path separator, otherwise use the one from the report (if any).
|
||||
const pathSeparator = rootDirOverride ? path.sep : (eventData.pathSeparatorFromMetadata ?? path.sep);
|
||||
const receiver = new TeleReporterReceiver(multiplexer, {
|
||||
pathSeparator,
|
||||
mergeProjects: false,
|
||||
mergeTestCases: false,
|
||||
internString: s => stringPool.internString(s),
|
||||
resolvePath: (rootDir, relativePath) => stringPool.internString(rootDir + pathSeparator + relativePath),
|
||||
configOverrides: config.config,
|
||||
});
|
||||
printStatus(`processing test events`);
|
||||
|
|
|
|||
|
|
@ -21,14 +21,19 @@ import type * as teleReceiver from '../isomorphic/teleReceiver';
|
|||
import { serializeRegexPatterns } from '../isomorphic/teleReceiver';
|
||||
import type { ReporterV2 } from './reporterV2';
|
||||
|
||||
export type TeleReporterEmitterOptions = {
|
||||
omitOutput?: boolean;
|
||||
omitBuffers?: boolean;
|
||||
};
|
||||
|
||||
export class TeleReporterEmitter implements ReporterV2 {
|
||||
private _messageSink: (message: teleReceiver.JsonEvent) => void;
|
||||
private _rootDir!: string;
|
||||
private _skipBuffers: boolean;
|
||||
private _emitterOptions: TeleReporterEmitterOptions;
|
||||
|
||||
constructor(messageSink: (message: teleReceiver.JsonEvent) => void, skipBuffers: boolean) {
|
||||
constructor(messageSink: (message: teleReceiver.JsonEvent) => void, options: TeleReporterEmitterOptions = {}) {
|
||||
this._messageSink = messageSink;
|
||||
this._skipBuffers = skipBuffers;
|
||||
this._emitterOptions = options;
|
||||
}
|
||||
|
||||
version(): 'v2' {
|
||||
|
|
@ -113,6 +118,8 @@ export class TeleReporterEmitter implements ReporterV2 {
|
|||
}
|
||||
|
||||
private _onStdIO(type: teleReceiver.JsonStdIOType, chunk: string | Buffer, test: void | reporterTypes.TestCase, result: void | reporterTypes.TestResult): void {
|
||||
if (this._emitterOptions.omitOutput)
|
||||
return;
|
||||
const isBase64 = typeof chunk !== 'string';
|
||||
const data = isBase64 ? chunk.toString('base64') : chunk;
|
||||
this._messageSink({
|
||||
|
|
@ -224,7 +231,7 @@ export class TeleReporterEmitter implements ReporterV2 {
|
|||
return {
|
||||
...a,
|
||||
// There is no Buffer in the browser, so there is no point in sending the data there.
|
||||
base64: (a.body && !this._skipBuffers) ? a.body.toString('base64') : undefined,
|
||||
base64: (a.body && !this._emitterOptions.omitBuffers) ? a.body.toString('base64') : undefined,
|
||||
};
|
||||
});
|
||||
}
|
||||
|
|
|
|||
|
|
@ -88,7 +88,7 @@ export async function createReporterForTestServer(config: FullConfigInternal, fi
|
|||
function reporterOptions(config: FullConfigInternal, mode: 'list' | 'test' | 'ui' | 'merge', send?: (message: any) => void) {
|
||||
return {
|
||||
configDir: config.configDir,
|
||||
send,
|
||||
_send: send,
|
||||
_mode: mode,
|
||||
};
|
||||
}
|
||||
|
|
|
|||
|
|
@ -114,13 +114,13 @@ class Dispatcher implements TestServerInterface {
|
|||
async listTests(params: {
|
||||
configFile: string;
|
||||
locations: string[];
|
||||
reporters: { file: string, event: string }[];
|
||||
reporter: string;
|
||||
env: NodeJS.ProcessEnv;
|
||||
}) {
|
||||
const config = await this._loadConfig(params.configFile);
|
||||
config.cliArgs = params.locations || [];
|
||||
const wireReporters = await this._wireReporters(config, 'list', params.reporters);
|
||||
const reporter = new InternalReporter(new Multiplexer(wireReporters));
|
||||
const wireReporter = await createReporterForTestServer(config, params.reporter, 'list', message => this._dispatchEvent('report', message));
|
||||
const reporter = new InternalReporter(new Multiplexer([wireReporter]));
|
||||
const taskRunner = createTaskRunnerForList(config, reporter, 'out-of-process', { failOnLoadErrors: true });
|
||||
const testRun = new TestRun(config, reporter);
|
||||
reporter.onConfigure(config.config);
|
||||
|
|
@ -138,7 +138,7 @@ class Dispatcher implements TestServerInterface {
|
|||
async test(params: {
|
||||
configFile: string;
|
||||
locations: string[];
|
||||
reporters: { file: string, event: string }[];
|
||||
reporter: string;
|
||||
env: NodeJS.ProcessEnv;
|
||||
headed?: boolean;
|
||||
oneWorker?: boolean;
|
||||
|
|
@ -169,9 +169,9 @@ class Dispatcher implements TestServerInterface {
|
|||
config.cliGrep = params.grep;
|
||||
config.cliProjectFilter = params.projects?.length ? params.projects : undefined;
|
||||
|
||||
const wireReporters = await this._wireReporters(config, 'test', params.reporters);
|
||||
const wireReporter = await createReporterForTestServer(config, params.reporter, 'test', message => this._dispatchEvent('report', message));
|
||||
const configReporters = await createReporters(config, 'test');
|
||||
const reporter = new InternalReporter(new Multiplexer([...configReporters, ...wireReporters]));
|
||||
const reporter = new InternalReporter(new Multiplexer([...configReporters, wireReporter]));
|
||||
const taskRunner = createTaskRunnerForTestServer(config, reporter);
|
||||
const testRun = new TestRun(config, reporter);
|
||||
reporter.onConfigure(config.config);
|
||||
|
|
@ -186,14 +186,6 @@ class Dispatcher implements TestServerInterface {
|
|||
await run;
|
||||
}
|
||||
|
||||
private async _wireReporters(config: FullConfigInternal, mode: 'test' | 'list', reporters: { file: string, event: string }[]) {
|
||||
return await Promise.all(reporters.map(r => {
|
||||
return createReporterForTestServer(config, r.file, mode, message => {
|
||||
this._dispatchEvent(r.event, message);
|
||||
});
|
||||
}));
|
||||
}
|
||||
|
||||
async findRelatedTestFiles(params: {
|
||||
configFile: string;
|
||||
files: string[];
|
||||
|
|
|
|||
|
|
@ -33,13 +33,13 @@ export interface TestServerInterface {
|
|||
listTests(params: {
|
||||
configFile: string;
|
||||
locations: string[];
|
||||
reporters: { file: string, event: string }[];
|
||||
reporter: string;
|
||||
}): Promise<void>;
|
||||
|
||||
test(params: {
|
||||
configFile: string;
|
||||
locations: string[];
|
||||
reporters: { file: string, event: string }[];
|
||||
reporter: string;
|
||||
headed?: boolean;
|
||||
oneWorker?: boolean;
|
||||
trace?: 'on' | 'off';
|
||||
|
|
|
|||
|
|
@ -167,7 +167,7 @@ class UIMode {
|
|||
}
|
||||
|
||||
private async _listTests() {
|
||||
const reporter = new InternalReporter(new TeleReporterEmitter(e => this._dispatchEvent('listReport', e), true));
|
||||
const reporter = new InternalReporter(new TeleReporterEmitter(e => this._dispatchEvent('listReport', e), { omitBuffers: true }));
|
||||
this._config.cliListOnly = true;
|
||||
this._config.testIdMatcher = undefined;
|
||||
const taskRunner = createTaskRunnerForList(this._config, reporter, 'out-of-process', { failOnLoadErrors: false });
|
||||
|
|
@ -195,7 +195,7 @@ class UIMode {
|
|||
this._config.testIdMatcher = id => !testIdSet || testIdSet.has(id);
|
||||
|
||||
const reporters = await createReporters(this._config, 'ui');
|
||||
reporters.push(new TeleReporterEmitter(e => this._dispatchEvent('testReport', e), true));
|
||||
reporters.push(new TeleReporterEmitter(e => this._dispatchEvent('testReport', e), { omitBuffers: true }));
|
||||
const reporter = new InternalReporter(new Multiplexer(reporters));
|
||||
const taskRunner = createTaskRunnerForWatch(this._config, reporter);
|
||||
const testRun = new TestRun(this._config, reporter);
|
||||
|
|
|
|||
|
|
@ -22,7 +22,7 @@ import { TreeView } from '@web/components/treeView';
|
|||
import type { TreeState } from '@web/components/treeView';
|
||||
import { baseFullConfig, TeleReporterReceiver, TeleSuite } from '@testIsomorphic/teleReceiver';
|
||||
import type { TeleTestCase } from '@testIsomorphic/teleReceiver';
|
||||
import type { FullConfig, Suite, TestCase, Location, TestError } from 'playwright/types/testReporter';
|
||||
import type { FullConfig, Suite, TestCase, Location, TestError, TestResult } from 'playwright/types/testReporter';
|
||||
import { SplitView } from '@web/components/splitView';
|
||||
import { idForAction, MultiTraceModel } from './modelUtil';
|
||||
import type { SourceLocation } from './modelUtil';
|
||||
|
|
@ -151,7 +151,8 @@ export const UIModeView: React.FC<{}> = ({
|
|||
for (const test of testModel.rootSuite?.allTests() || []) {
|
||||
if (testIds.has(test.id)) {
|
||||
(test as TeleTestCase)._clearResults();
|
||||
(test as TeleTestCase)._createTestResult('pending');
|
||||
const result = (test as TeleTestCase)._createTestResult('pending');
|
||||
(result as any)[statusEx] = 'scheduled';
|
||||
}
|
||||
}
|
||||
setTestModel({ ...testModel });
|
||||
|
|
@ -655,9 +656,9 @@ const refreshRootSuite = (eraseResults: boolean): Promise<void> => {
|
|||
lastRunReceiver = undefined;
|
||||
}
|
||||
}, {
|
||||
pathSeparator,
|
||||
mergeProjects: true,
|
||||
mergeTestCases: false
|
||||
mergeTestCases: false,
|
||||
resolvePath: (rootDir, relativePath) => rootDir + pathSeparator + relativePath,
|
||||
});
|
||||
},
|
||||
|
||||
|
|
@ -675,17 +676,19 @@ const refreshRootSuite = (eraseResults: boolean): Promise<void> => {
|
|||
throttleUpdateRootSuite(config, rootSuite, loadErrors, progress, true);
|
||||
},
|
||||
|
||||
onTestBegin: () => {
|
||||
onTestBegin: (test: TestCase, testResult: TestResult) => {
|
||||
(testResult as any)[statusEx] = 'running';
|
||||
throttleUpdateRootSuite(config, rootSuite, loadErrors, progress);
|
||||
},
|
||||
|
||||
onTestEnd: (test: TestCase) => {
|
||||
onTestEnd: (test: TestCase, testResult: TestResult) => {
|
||||
if (test.outcome() === 'skipped')
|
||||
++progress.skipped;
|
||||
else if (test.outcome() === 'unexpected')
|
||||
++progress.failed;
|
||||
else
|
||||
++progress.passed;
|
||||
(testResult as any)[statusEx] = testResult.status;
|
||||
throttleUpdateRootSuite(config, rootSuite, loadErrors, progress);
|
||||
},
|
||||
|
||||
|
|
@ -705,9 +708,9 @@ const refreshRootSuite = (eraseResults: boolean): Promise<void> => {
|
|||
onStepBegin: () => {},
|
||||
onStepEnd: () => {},
|
||||
}, {
|
||||
pathSeparator,
|
||||
mergeProjects: true,
|
||||
mergeTestCases: true,
|
||||
resolvePath: (rootDir, relativePath) => rootDir + pathSeparator + relativePath,
|
||||
});
|
||||
receiver._setClearPreviousResultsWhenTestBegins();
|
||||
return sendMessage('list', {});
|
||||
|
|
@ -906,11 +909,11 @@ function createTree(rootSuite: Suite | undefined, loadErrors: TestError[], proje
|
|||
parentGroup.children.push(testCaseItem);
|
||||
}
|
||||
|
||||
const result = (test as TeleTestCase).results[0];
|
||||
const result = test.results[0];
|
||||
let status: 'none' | 'running' | 'scheduled' | 'passed' | 'failed' | 'skipped' = 'none';
|
||||
if (result?.statusEx === 'scheduled')
|
||||
if ((result as any)?.[statusEx] === 'scheduled')
|
||||
status = 'scheduled';
|
||||
else if (result?.statusEx === 'running')
|
||||
else if ((result as any)?.[statusEx] === 'running')
|
||||
status = 'running';
|
||||
else if (result?.status === 'skipped')
|
||||
status = 'skipped';
|
||||
|
|
@ -1053,3 +1056,4 @@ async function loadSingleTraceFile(url: string): Promise<MultiTraceModel> {
|
|||
}
|
||||
|
||||
const pathSeparator = navigator.userAgent.toLowerCase().includes('windows') ? '\\' : '/';
|
||||
const statusEx = Symbol('statusEx');
|
||||
|
|
|
|||
Loading…
Reference in a new issue