-
Notifications
You must be signed in to change notification settings - Fork 39
/
testplane.ts
136 lines (113 loc) · 5.23 KB
/
testplane.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
import os from 'os';
import path from 'path';
import type Testplane from 'testplane';
import type {TestResult as TestplaneTestResult} from 'testplane';
import _ from 'lodash';
import PQueue from 'p-queue';
import {CommanderStatic} from '@gemini-testing/commander';
import {TestplaneToolAdapter} from './lib/adapters/tool/testplane';
import {getStatus} from './lib/adapters/test-result/testplane';
import {commands as cliCommands} from './lib/cli';
import {parseConfig} from './lib/config';
import {ToolName} from './lib/constants';
import {StaticReportBuilder} from './lib/report-builder/static';
import {formatTestResult, logPathToHtmlReport, logError, getExpectedCacheKey} from './lib/server-utils';
import {SqliteClient} from './lib/sqlite-client';
import {ReporterOptions, TestSpecByPath} from './lib/types';
import {createWorkers, CreateWorkersRunner} from './lib/workers/create-workers';
import {SqliteImageStore} from './lib/image-store';
import {Cache} from './lib/cache';
import {ImagesInfoSaver} from './lib/images-info-saver';
export default (testplane: Testplane, opts: Partial<ReporterOptions>): void => {
if (testplane.isWorker()) {
return;
}
const config = parseConfig(opts);
if (!config.enabled) {
return;
}
const toolAdapter = TestplaneToolAdapter.create({toolName: ToolName.Testplane, tool: testplane, reporterConfig: config});
const {htmlReporter} = toolAdapter;
let isCliCommandLaunched = false;
let handlingTestResults: Promise<void>;
let staticReportBuilder: StaticReportBuilder;
const withMiddleware = <T extends (...args: unknown[]) => unknown>(fn: T):
(...args: Parameters<T>) => ReturnType<T> | undefined => {
return (...args: unknown[]) => {
// If any CLI command was launched, e.g. merge-reports, we need to interrupt regular flow
if (isCliCommandLaunched) {
return;
}
return fn.call(undefined, ...args) as ReturnType<T>;
};
};
testplane.on(testplane.events.CLI, (commander: CommanderStatic) => {
_.values(cliCommands).forEach((command: string) => {
// eslint-disable-next-line @typescript-eslint/no-var-requires
require(path.resolve(__dirname, 'lib/cli/commands', command))(commander, toolAdapter);
commander.prependListener(`command:${command}`, () => {
isCliCommandLaunched = true;
});
});
});
testplane.on(testplane.events.INIT, withMiddleware(async () => {
const dbClient = await SqliteClient.create({htmlReporter, reportPath: config.path});
const imageStore = new SqliteImageStore(dbClient);
const expectedPathsCache = new Cache<[TestSpecByPath, string | undefined], string>(getExpectedCacheKey);
const imagesInfoSaver = new ImagesInfoSaver({
imageFileSaver: htmlReporter.imagesSaver,
expectedPathsCache,
imageStore,
reportPath: htmlReporter.config.path
});
staticReportBuilder = StaticReportBuilder.create({
htmlReporter: toolAdapter.htmlReporter,
reporterConfig: config,
dbClient,
imagesInfoSaver
});
handlingTestResults = Promise.all([
staticReportBuilder.saveStaticFiles(),
handleTestResults(testplane, staticReportBuilder)
]).then(async () => {
await staticReportBuilder.finalize();
}).then(async () => {
await htmlReporter.emitAsync(htmlReporter.events.REPORT_SAVED, {reportPath: config.path});
});
htmlReporter.emit(htmlReporter.events.DATABASE_CREATED, dbClient.getRawConnection());
}));
testplane.on(testplane.events.RUNNER_START, withMiddleware((runner) => {
staticReportBuilder.registerWorkers(createWorkers(runner as unknown as CreateWorkersRunner));
}));
testplane.on(testplane.events.RUNNER_END, withMiddleware(async () => {
try {
await handlingTestResults;
logPathToHtmlReport(config);
} catch (e: unknown) {
logError(e as Error);
}
}));
};
async function handleTestResults(testplane: Testplane, reportBuilder: StaticReportBuilder): Promise<void> {
return new Promise((resolve, reject) => {
const queue = new PQueue({concurrency: os.cpus().length});
const promises: Promise<unknown>[] = [];
[
{eventName: testplane.events.TEST_PASS},
{eventName: testplane.events.RETRY},
{eventName: testplane.events.TEST_FAIL},
{eventName: testplane.events.TEST_PENDING}
].forEach(({eventName}) => {
type AnyTestplaneTestEvent = typeof testplane.events.TEST_PASS;
testplane.on(eventName as AnyTestplaneTestEvent, (testResult: TestplaneTestResult) => {
promises.push(queue.add(async () => {
const formattedResult = formatTestResult(testResult, getStatus(eventName, testplane.events, testResult));
await reportBuilder.addTestResult(formattedResult);
}).catch(reject));
});
});
testplane.on(testplane.events.RUNNER_END, () => {
return Promise.all(promises).then(() => resolve(), reject);
});
});
}