Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: add possibility to pass additional data to the reporter plugin (closes #3584) #7562

Merged
merged 16 commits into from
Mar 30, 2023
5 changes: 5 additions & 0 deletions src/api/test-controller/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,7 @@ import {
SkipJsErrorsCommand,
AddRequestHooksCommand,
RemoveRequestHooksCommand,
ReportCommand,
} from '../../test-run/commands/actions';

import {
Expand Down Expand Up @@ -629,6 +630,10 @@ export default class TestController {
return this.enqueueCommand(RemoveRequestHooksCommand, { hooks });
}

[delegatedAPI(ReportCommand.methodName)] (...args) {
return this.enqueueCommand(ReportCommand, { args });
}

static enableDebugForNonDebugCommands () {
inDebug = true;
}
Expand Down
62 changes: 58 additions & 4 deletions src/reporter/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@ interface TaskInfo {
}

interface TestInfo {
reportData: Dictionary<any[]>;
fixture: Fixture;
test: Test;
testRunIds: string[];
Expand Down Expand Up @@ -86,6 +87,7 @@ interface BrowserRunInfo extends Browser {
interface TestRunInfo {
errs: TestRunErrorFormattableAdapter[];
warnings: string[];
reportData: Dictionary<any[]>;
durationMs: number;
unstable: boolean;
screenshotPath: string;
Expand Down Expand Up @@ -123,6 +125,11 @@ interface ReportWarningEventArguments {
actionId?: string;
}

interface ReportDataEventArgs {
data: any[];
testRun: TestRun
}

const debugLog = debug('testcafe:reporter');

export default class Reporter {
Expand Down Expand Up @@ -194,6 +201,8 @@ export default class Reporter {

messageBus.on('warning-add', async e => await this._onWarningAddHandler(e));

messageBus.on('report-data', async e => await this._onReportDataHandler(e));

messageBus.once('start', async (task: Task) => await this._onceTaskStartHandler(task));

messageBus.on('test-run-start', async testRun => await this._onTaskTestRunStartHandler(testRun));
Expand Down Expand Up @@ -299,6 +308,7 @@ export default class Reporter {
pendingTestRunDonePromise: Reporter._createPendingPromise(),
pendingTestRunStartPromise: Reporter._createPendingPromise(),
browsers: [],
reportData: {},
};
}

Expand All @@ -312,6 +322,7 @@ export default class Reporter {
return {
errs: sortBy(reportItem.errs, ['userAgent', 'code']),
warnings: reportItem.warnings,
reportData: reportItem.reportData,
durationMs: +new Date() - (reportItem.startTime as number), //eslint-disable-line @typescript-eslint/no-extra-parens
unstable: reportItem.unstable,
screenshotPath: reportItem.screenshotPath as string,
Expand Down Expand Up @@ -544,10 +555,12 @@ export default class Reporter {

reportItem.browsers.push(browser);

reportItem.pendingRuns = isTestRunStoppedTaskExecution ? 0 : reportItem.pendingRuns - 1;
reportItem.unstable = reportItem.unstable || testRun.unstable;
reportItem.errs = reportItem.errs.concat(testRun.errs);
reportItem.warnings = testRun.warningLog ? union(reportItem.warnings, testRun.warningLog.messages) : [];
reportItem.pendingRuns = isTestRunStoppedTaskExecution ? 0 : reportItem.pendingRuns - 1;
reportItem.unstable = reportItem.unstable || testRun.unstable;
reportItem.errs = reportItem.errs.concat(testRun.errs);
reportItem.warnings = testRun.warningLog ? union(reportItem.warnings, testRun.warningLog.messages) : [];
reportItem.reportData = reportItem.reportData || {};
reportItem.reportData[testRun.id] = testRun.reportDataLog ? testRun.reportDataLog.data : [];

if (testRun.quarantine) {
reportItem.quarantine = reportItem.quarantine || {};
Expand Down Expand Up @@ -635,4 +648,45 @@ export default class Reporter {

(this.taskInfo.pendingTaskDonePromise.resolve as Function)();
}

private _prepareReportDataEventArgs (testRun: TestRun): any {
const { test, browser, id } = testRun;
const fixture = test.fixture;

const testInfo = {
name: test.name,
id: test.id,
meta: test.meta,
};

const fixtureInfo = {
name: fixture?.name,
id: fixture?.id,
meta: fixture?.meta,
path: fixture?.path,
};

return {
test: testInfo,
fixture: fixtureInfo,
testRunId: id,
browser,
};
}

private async _onReportDataHandler ({ testRun, data }: ReportDataEventArgs): Promise<void> {
Artem-Babich marked this conversation as resolved.
Show resolved Hide resolved
if (!this.taskInfo)
return;

const testRunInfo = this._prepareReportDataEventArgs(testRun);

await this.dispatchToPlugin({
method: ReporterPluginMethod.reportData as string,
initialObject: this.taskInfo.task,
args: [
testRunInfo,
...data,
],
});
}
}
1 change: 1 addition & 0 deletions src/reporter/interfaces.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ export interface ReporterPlugin {
reportTestDone(): void;
reportTaskDone(): void;
reportWarnings?(): void;
reportData? (): void
}

export interface ReporterSource {
Expand Down
4 changes: 4 additions & 0 deletions src/reporter/plugin-host.ts
Original file line number Diff line number Diff line change
Expand Up @@ -194,4 +194,8 @@ export default class ReporterPluginHost {
// NOTE: It's an optional method
public async reportWarnings (/* warnings */): Promise<void> { // eslint-disable-line @typescript-eslint/no-empty-function
}

// NOTE: It's an optional method
public async reportData (/* testRun, ...data */): Promise<void> { // eslint-disable-line @typescript-eslint/no-empty-function
}
}
1 change: 1 addition & 0 deletions src/reporter/plugin-methods.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ const ReporterPluginMethod: EnumFromPropertiesOf<ReporterPlugin> = {
reportTestDone: 'reportTestDone',
reportTaskDone: 'reportTaskDone',
reportWarnings: 'reportWarnings',
reportData: 'reportData',
};

export default ReporterPluginMethod;
32 changes: 32 additions & 0 deletions src/reporter/report-data-log.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
import MessageBus from '../utils/message-bus';
import TestRun from '../test-run';

type ReportDataLogCallback = (data: any[]) => Promise<void>;

export default class ReportDataLog {
private readonly _data: any[];
public callback?: ReportDataLogCallback;

public constructor (callback?: ReportDataLogCallback) {
this._data = [];
this.callback = callback;
}

public get data (): any[] {
return this._data;
}

public async addData (data: any[]): Promise<void> {
if (this.callback)
await this.callback(data);

this._data.push(...data);
}

public static createAddDataCallback (messageBus: MessageBus | undefined, testRun: TestRun): ReportDataLogCallback {
return async (data: any[]) => {
if (messageBus)
await messageBus.emit('report-data', { data, testRun });
};
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@ import {
SkipJsErrorsCommand,
AddRequestHooksCommand,
RemoveRequestHooksCommand,
ReportCommand,
} from '../../../../../test-run/commands/actions';

import { AssertionCommand } from '../../../../../test-run/commands/assertion';
Expand Down Expand Up @@ -115,6 +116,7 @@ const COMMAND_CONSTRUCTORS = new Map<string, CommandConstructor>([
[CommandType.skipJsErrors, SkipJsErrorsCommand],
[CommandType.addRequestHooks, AddRequestHooksCommand],
[CommandType.removeRequestHooks, RemoveRequestHooksCommand],
[CommandType.report, ReportCommand],
]);

export default COMMAND_CONSTRUCTORS;
5 changes: 5 additions & 0 deletions src/test-run/commands/actions.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -285,3 +285,8 @@ export class RunCustomActionCommand extends ActionCommandBase {
public args: any;
}

export class ReportCommand extends ActionCommandBase {
public constructor (obj: object, testRun: TestRun, validateProperties: boolean);
public args: any[];
}

13 changes: 13 additions & 0 deletions src/test-run/commands/actions.js
Original file line number Diff line number Diff line change
Expand Up @@ -813,3 +813,16 @@ export class RemoveRequestHooksCommand extends ActionCommandBase {
}
}

export class ReportCommand extends ActionCommandBase {
static methodName = camelCase(TYPE.report);

constructor (obj, testRun, validateProperties) {
super(obj, testRun, TYPE.report, validateProperties);
}

getAssignableProperties () {
return [
{ name: 'args', required: true },
];
}
}
1 change: 1 addition & 0 deletions src/test-run/commands/type.js
Original file line number Diff line number Diff line change
Expand Up @@ -72,4 +72,5 @@ export default {
addRequestHooks: 'add-request-hooks',
removeRequestHooks: 'remove-request-hooks',
runCustomAction: 'run-custom-action',
report: 'report',
};
12 changes: 9 additions & 3 deletions src/test-run/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -143,6 +143,7 @@ import {

import ProxylessRequestPipeline from '../proxyless/request-pipeline';
import Proxyless from '../proxyless';
import ReportDataLog from '../reporter/report-data-log';

const lazyRequire = require('import-lazy')(require);
const ClientFunctionBuilder = lazyRequire('../client-functions/client-function-builder');
Expand Down Expand Up @@ -227,6 +228,7 @@ interface OpenedWindowInformation {
export default class TestRun extends AsyncEventEmitter {
private [testRunMarker]: boolean;
public readonly warningLog: WarningLog;
public readonly reportDataLog: ReportDataLog;
private readonly opts: Dictionary<OptionValue>;
public readonly test: Test;
public readonly browserConnection: BrowserConnection;
Expand Down Expand Up @@ -290,6 +292,7 @@ export default class TestRun extends AsyncEventEmitter {
this[testRunMarker] = true;
this._messageBus = messageBus;
this.warningLog = new WarningLog(globalWarningLog, WarningLog.createAddWarningCallback(messageBus, this));
this.reportDataLog = new ReportDataLog(ReportDataLog.createAddDataCallback(messageBus, this));
this.opts = opts;
this.test = test;
this.browserConnection = browserConnection;
Expand All @@ -307,8 +310,8 @@ export default class TestRun extends AsyncEventEmitter {
this.pageLoadTimeout = this._getPageLoadTimeout(test, opts);
this.testExecutionTimeout = this._getTestExecutionTimeout(opts);

this.disablePageReloads = test.disablePageReloads || opts.disablePageReloads as boolean && test.disablePageReloads !== false;
this.disablePageCaching = test.disablePageCaching || opts.disablePageCaching as boolean;
this.disablePageReloads = test.disablePageReloads || opts.disablePageReloads as boolean && test.disablePageReloads !== false;
this.disablePageCaching = test.disablePageCaching || opts.disablePageCaching as boolean;

this.disableMultipleWindows = opts.disableMultipleWindows as boolean;

Expand Down Expand Up @@ -350,7 +353,7 @@ export default class TestRun extends AsyncEventEmitter {

this.debugLog = new TestRunDebugLog(this.browserConnection.userAgent);

this.quarantine = null;
this.quarantine = null;

this.debugLogger = this.opts.debugLogger;

Expand Down Expand Up @@ -1308,6 +1311,9 @@ export default class TestRun extends AsyncEventEmitter {
return await wrappedFn(this, args);
}

if (command.type === COMMAND_TYPE.report)
return await this.reportDataLog.addData(command.args as any[]);

if (command.type === COMMAND_TYPE.assertion)
return this._executeAssertion(command as AssertionCommand, callsite as CallsiteRecord);

Expand Down
55 changes: 55 additions & 0 deletions test/functional/fixtures/reporter/test.js
Original file line number Diff line number Diff line change
Expand Up @@ -888,6 +888,61 @@ const experimentalDebug = !!process.env.EXPERIMENTAL_DEBUG;
});
});

describe('Report Data', () => {
let reportDataInfos = {};
let testDoneInfos = {};
let reporter = null;

const createReportDataReporter = () => {
return createReporter({
reportData: ({ browser }, ...data) => {
const alias = browser.alias;

if (!reportDataInfos[alias])
reportDataInfos[alias] = [];

reportDataInfos[alias].push(data);
},
reportTestDone: (name, { reportData, browsers }) => {
browsers.forEach(({ testRunId, alias }) => {
testDoneInfos[alias] = reportData[testRunId];
});
},
});
};

beforeEach(() => {
reportDataInfos = {};
testDoneInfos = {};

reporter = createReportDataReporter();
});

it('Should raise reportData twice with different argument count and types', async () => {
Artem-Babich marked this conversation as resolved.
Show resolved Hide resolved
const expectedReportData = [1, true, 'string', { 'reportResult': 'test' }];

await runTests('testcafe-fixtures/report-data-test.js', 'Run t.report action with object val', {
reporter,
});

const reportDataBrowserInfos = Object.entries(reportDataInfos);
const testDoneBrowserInfos = Object.entries(testDoneInfos);

expect(reportDataBrowserInfos.length).eql(config.browsers.length);
expect(testDoneBrowserInfos.length).eql(config.browsers.length);

reportDataBrowserInfos.forEach(([alias, reportData]) => {
expect(reportData.flat()).eql(testDoneInfos[alias]);
});

testDoneBrowserInfos.forEach(([, reportData]) => {
const [, ...rest] = reportData;

expect(rest).eql(expectedReportData);
});
});
});

describe('Warnings', () => {
let warningResult = {};
let reporter = null;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ async function errorCheck (t) {

test('Simple test', async t => {
await t.wait(1);
await t.report();
Artem-Babich marked this conversation as resolved.
Show resolved Hide resolved
});

test('Simple command test', async t => {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
fixture`Report Data API`
.page('../pages/index.html');

test('Run t.report action with object val', async t => {
Artem-Babich marked this conversation as resolved.
Show resolved Hide resolved
await t
.report(t.browser.alias)
.report(1, true, 'string', { 'reportResult': 'test' });
});
Loading