creevey 0.10.0-beta.37 → 0.10.0-beta.39
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/client/web/assets/{index-B0Xv0lOY.js → index-C47njyZV.js} +2 -2
- package/dist/client/web/index.html +1 -1
- package/dist/server/index.js +14 -12
- package/dist/server/index.js.map +1 -1
- package/dist/server/master/pool.js +1 -1
- package/dist/server/master/pool.js.map +1 -1
- package/dist/server/master/runner.d.ts +2 -0
- package/dist/server/master/runner.js +62 -0
- package/dist/server/master/runner.js.map +1 -1
- package/dist/server/reporter.d.ts +4 -19
- package/dist/server/reporter.js +22 -17
- package/dist/server/reporter.js.map +1 -1
- package/dist/server/worker/start.js +23 -62
- package/dist/server/worker/start.js.map +1 -1
- package/dist/types.d.ts +13 -1
- package/dist/types.js +2 -0
- package/dist/types.js.map +1 -1
- package/package.json +1 -1
- package/src/server/index.ts +13 -11
- package/src/server/master/pool.ts +1 -1
- package/src/server/master/runner.ts +78 -0
- package/src/server/reporter.ts +26 -29
- package/src/server/worker/start.ts +24 -71
- package/src/types.ts +16 -1
@@ -11,9 +11,16 @@ import {
|
|
11
11
|
TestStatus,
|
12
12
|
ServerTest,
|
13
13
|
TestMeta,
|
14
|
+
TEST_EVENTS,
|
15
|
+
FakeSuite,
|
16
|
+
FakeTest,
|
14
17
|
} from '../../types.js';
|
15
18
|
import Pool from './pool.js';
|
16
19
|
import { WorkerQueue } from './queue.js';
|
20
|
+
import { getTestPath } from '../utils.js';
|
21
|
+
|
22
|
+
// NOTE: This is workaround to fix parallel tests running with mocha-junit-reporter
|
23
|
+
let isJUnit = false;
|
17
24
|
|
18
25
|
export default class Runner extends EventEmitter {
|
19
26
|
private failFast: boolean;
|
@@ -22,6 +29,8 @@ export default class Runner extends EventEmitter {
|
|
22
29
|
private browsers: string[];
|
23
30
|
private scheduler: WorkerQueue;
|
24
31
|
private pools: Record<string, Pool> = {};
|
32
|
+
private fakeRunner: EventEmitter;
|
33
|
+
private config: Config;
|
25
34
|
tests: Partial<Record<string, ServerTest>> = {};
|
26
35
|
public get isRunning(): boolean {
|
27
36
|
return Object.values(this.pools).some((pool) => pool.isRunning);
|
@@ -29,11 +38,24 @@ export default class Runner extends EventEmitter {
|
|
29
38
|
constructor(config: Config, gridUrl?: string) {
|
30
39
|
super();
|
31
40
|
|
41
|
+
this.config = config;
|
32
42
|
this.failFast = config.failFast;
|
33
43
|
this.screenDir = config.screenDir;
|
34
44
|
this.reportDir = config.reportDir;
|
35
45
|
this.scheduler = new WorkerQueue(config.useWorkerQueue);
|
36
46
|
this.browsers = Object.keys(config.browsers);
|
47
|
+
|
48
|
+
class FakeRunner extends EventEmitter {}
|
49
|
+
const runner = new FakeRunner();
|
50
|
+
const Reporter = config.reporter;
|
51
|
+
|
52
|
+
if (Reporter.name == 'MochaJUnitReporter') {
|
53
|
+
isJUnit = true;
|
54
|
+
}
|
55
|
+
|
56
|
+
new Reporter(runner, { reporterOptions: config.reporterOptions });
|
57
|
+
this.fakeRunner = runner;
|
58
|
+
|
37
59
|
this.browsers
|
38
60
|
.map((browser) => (this.pools[browser] = new Pool(this.scheduler, config, browser, gridUrl)))
|
39
61
|
.map((pool) => pool.on('test', this.handlePoolMessage));
|
@@ -45,10 +67,38 @@ export default class Runner extends EventEmitter {
|
|
45
67
|
|
46
68
|
if (!test) return;
|
47
69
|
const { browser, testName, storyPath, storyId } = test;
|
70
|
+
|
71
|
+
const fakeSuite: FakeSuite = {
|
72
|
+
title: test.storyPath.slice(0, -1).join('/'),
|
73
|
+
fullTitle: () => fakeSuite.title,
|
74
|
+
titlePath: () => [fakeSuite.title],
|
75
|
+
tests: [],
|
76
|
+
};
|
77
|
+
|
78
|
+
const fakeTest: FakeTest = {
|
79
|
+
parent: fakeSuite,
|
80
|
+
title: [test.story.name, testName, browser].filter(isDefined).join('/'),
|
81
|
+
fullTitle: () => getTestPath(test).join('/'),
|
82
|
+
titlePath: () => getTestPath(test),
|
83
|
+
currentRetry: () => result?.retries,
|
84
|
+
retires: () => this.config.maxRetries,
|
85
|
+
slow: () => 1000,
|
86
|
+
creevey: {
|
87
|
+
reportDir: this.reportDir,
|
88
|
+
sessionId: id, // TODO SessionId
|
89
|
+
browserName: browser,
|
90
|
+
willRetry: (result?.retries ?? 0) < this.config.maxRetries,
|
91
|
+
images: result?.images ?? {},
|
92
|
+
},
|
93
|
+
};
|
94
|
+
|
95
|
+
fakeSuite.tests.push(fakeTest);
|
96
|
+
|
48
97
|
// TODO Handle 'retrying' status
|
49
98
|
test.status = status == 'retrying' ? 'failed' : status;
|
50
99
|
if (!result) {
|
51
100
|
// NOTE: Running status
|
101
|
+
this.fakeRunner.emit(TEST_EVENTS.TEST_BEGIN, fakeTest);
|
52
102
|
this.sendUpdate({ tests: { [id]: { id, browser, testName, storyPath, status: test.status, storyId } } });
|
53
103
|
return;
|
54
104
|
}
|
@@ -59,6 +109,32 @@ export default class Runner extends EventEmitter {
|
|
59
109
|
test.approved = null;
|
60
110
|
}
|
61
111
|
|
112
|
+
const { duration, attachments } = result;
|
113
|
+
|
114
|
+
fakeTest.duration = duration;
|
115
|
+
fakeTest.attachments = attachments;
|
116
|
+
fakeTest.state = result.status === 'failed' ? 'failed' : 'passed';
|
117
|
+
if (duration !== undefined) {
|
118
|
+
fakeTest.speed = duration > fakeTest.slow() ? 'slow' : duration / 2 > fakeTest.slow() ? 'medium' : 'fast';
|
119
|
+
}
|
120
|
+
|
121
|
+
if (isJUnit) {
|
122
|
+
this.fakeRunner.emit(TEST_EVENTS.SUITE_BEGIN, fakeSuite);
|
123
|
+
}
|
124
|
+
|
125
|
+
if (result.status === 'failed') {
|
126
|
+
fakeTest.err = result.error;
|
127
|
+
this.fakeRunner.emit(TEST_EVENTS.TEST_FAIL, fakeTest, result.error);
|
128
|
+
} else {
|
129
|
+
this.fakeRunner.emit(TEST_EVENTS.TEST_PASS, fakeTest);
|
130
|
+
}
|
131
|
+
|
132
|
+
if (isJUnit) {
|
133
|
+
this.fakeRunner.emit(TEST_EVENTS.SUITE_END, fakeSuite);
|
134
|
+
}
|
135
|
+
|
136
|
+
this.fakeRunner.emit(TEST_EVENTS.TEST_END, fakeTest);
|
137
|
+
|
62
138
|
this.sendUpdate({
|
63
139
|
tests: {
|
64
140
|
[id]: {
|
@@ -79,6 +155,7 @@ export default class Runner extends EventEmitter {
|
|
79
155
|
|
80
156
|
private handlePoolStop = (): void => {
|
81
157
|
if (!this.isRunning) {
|
158
|
+
this.fakeRunner.emit(TEST_EVENTS.RUN_END);
|
82
159
|
this.sendUpdate({ isRunning: false });
|
83
160
|
this.emit('stop');
|
84
161
|
}
|
@@ -148,6 +225,7 @@ export default class Runner extends EventEmitter {
|
|
148
225
|
};
|
149
226
|
}, {});
|
150
227
|
|
228
|
+
this.fakeRunner.emit(TEST_EVENTS.RUN_BEGIN);
|
151
229
|
this.browsers.forEach((browser) => {
|
152
230
|
const pool = this.pools[browser];
|
153
231
|
const tests = testsByBrowser[browser];
|
package/src/server/reporter.ts
CHANGED
@@ -4,14 +4,6 @@ import prefix from 'loglevel-plugin-prefix';
|
|
4
4
|
import { FakeTest, Images, isDefined, isImageError, TEST_EVENTS } from '../types.js';
|
5
5
|
import EventEmitter from 'events';
|
6
6
|
|
7
|
-
interface ReporterOptions {
|
8
|
-
reportDir: string;
|
9
|
-
sessionId: string;
|
10
|
-
browserName: string;
|
11
|
-
willRetry: boolean;
|
12
|
-
images: Partial<Record<string, Partial<Images>>>;
|
13
|
-
}
|
14
|
-
|
15
7
|
const testLevels: Record<string, string> = {
|
16
8
|
INFO: chalk.green('PASS'),
|
17
9
|
WARN: chalk.yellow('START'),
|
@@ -19,36 +11,43 @@ const testLevels: Record<string, string> = {
|
|
19
11
|
};
|
20
12
|
|
21
13
|
export class CreeveyReporter {
|
14
|
+
private logger: Logger.Logger | null = null;
|
22
15
|
// TODO Output in better way, like vitest, maybe
|
23
|
-
constructor(runner: EventEmitter
|
24
|
-
const { sessionId, browserName } = options.reporterOptions.creevey;
|
25
|
-
const testLogger = Logger.getLogger(sessionId);
|
26
|
-
|
27
|
-
prefix.apply(testLogger, {
|
28
|
-
format(level) {
|
29
|
-
return `[${browserName}:${chalk.gray(process.pid)}] ${testLevels[level]} => ${chalk.gray(sessionId)}`;
|
30
|
-
},
|
31
|
-
});
|
32
|
-
|
16
|
+
constructor(runner: EventEmitter) {
|
33
17
|
runner.on(TEST_EVENTS.TEST_BEGIN, (test: FakeTest) => {
|
34
|
-
|
18
|
+
this.getLogger(test.creevey).warn(chalk.cyan(test.fullTitle()));
|
35
19
|
});
|
36
20
|
runner.on(TEST_EVENTS.TEST_PASS, (test: FakeTest) => {
|
37
|
-
|
21
|
+
this.getLogger(test.creevey).info(chalk.cyan(test.fullTitle()), chalk.gray(`(${test.duration} ms)`));
|
38
22
|
});
|
39
23
|
runner.on(TEST_EVENTS.TEST_FAIL, (test: FakeTest, error) => {
|
40
|
-
|
24
|
+
this.getLogger(test.creevey).error(
|
41
25
|
chalk.cyan(test.fullTitle()),
|
42
26
|
chalk.gray(`(${test.duration} ms)`),
|
43
27
|
'\n ',
|
44
28
|
this.getErrors(
|
45
29
|
error,
|
46
|
-
(error, imageName) => `${chalk.bold(imageName ?? browserName)}:${error}`,
|
30
|
+
(error, imageName) => `${chalk.bold(imageName ?? test.creevey.browserName)}:${error}`,
|
47
31
|
(error) => error.stack ?? error.message,
|
48
32
|
).join('\n '),
|
49
33
|
);
|
50
34
|
});
|
51
35
|
}
|
36
|
+
|
37
|
+
private getLogger(options: { sessionId: string; browserName: string }) {
|
38
|
+
if (this.logger) return this.logger;
|
39
|
+
const { sessionId, browserName } = options;
|
40
|
+
const testLogger = Logger.getLogger(sessionId);
|
41
|
+
|
42
|
+
this.logger = prefix.apply(testLogger, {
|
43
|
+
format(level) {
|
44
|
+
return `[${browserName}:${chalk.gray(process.pid)}] ${testLevels[level]} => ${chalk.gray(sessionId)}`;
|
45
|
+
},
|
46
|
+
});
|
47
|
+
|
48
|
+
return this.logger;
|
49
|
+
}
|
50
|
+
|
52
51
|
private getErrors(
|
53
52
|
error: unknown,
|
54
53
|
imageErrorToString: (error: string, imageName?: string) => string,
|
@@ -72,10 +71,7 @@ export class CreeveyReporter {
|
|
72
71
|
}
|
73
72
|
|
74
73
|
export class TeamcityReporter {
|
75
|
-
constructor(runner: EventEmitter
|
76
|
-
const browserName = this.escape(options.reporterOptions.creevey.browserName);
|
77
|
-
const reporterOptions = options.reporterOptions.creevey;
|
78
|
-
|
74
|
+
constructor(runner: EventEmitter) {
|
79
75
|
runner.on(TEST_EVENTS.TEST_BEGIN, (test: FakeTest) => {
|
80
76
|
console.log(`##teamcity[testStarted name='${this.escape(test.fullTitle())}' flowId='${process.pid}']`);
|
81
77
|
});
|
@@ -85,7 +81,8 @@ export class TeamcityReporter {
|
|
85
81
|
});
|
86
82
|
|
87
83
|
runner.on(TEST_EVENTS.TEST_FAIL, (test: FakeTest, error: Error) => {
|
88
|
-
|
84
|
+
const browserName = this.escape(test.creevey.browserName);
|
85
|
+
Object.entries(test.creevey.images).forEach(([name, image]) => {
|
89
86
|
if (!image) return;
|
90
87
|
const filePath = test
|
91
88
|
.titlePath()
|
@@ -99,7 +96,7 @@ export class TeamcityReporter {
|
|
99
96
|
.filter(isDefined)
|
100
97
|
.forEach((fileName) => {
|
101
98
|
console.log(
|
102
|
-
`##teamcity[publishArtifacts '${
|
99
|
+
`##teamcity[publishArtifacts '${test.creevey.reportDir}/${filePath}/${fileName} => report/${filePath}']`,
|
103
100
|
);
|
104
101
|
console.log(
|
105
102
|
`##teamcity[testMetadata testName='${this.escape(
|
@@ -112,7 +109,7 @@ export class TeamcityReporter {
|
|
112
109
|
// Output failed test as passed due TC don't support retry mechanic
|
113
110
|
// https://teamcity-support.jetbrains.com/hc/en-us/community/posts/207216829-Count-test-as-successful-if-at-least-one-try-is-successful?page=1#community_comment_207394125
|
114
111
|
|
115
|
-
if (
|
112
|
+
if (test.creevey.willRetry)
|
116
113
|
console.log(`##teamcity[testFinished name='${this.escape(test.fullTitle())}' flowId='${process.pid}']`);
|
117
114
|
else
|
118
115
|
console.log(
|
@@ -1,16 +1,12 @@
|
|
1
1
|
import chai from 'chai';
|
2
|
-
import EventEmitter from 'events';
|
3
2
|
import {
|
4
3
|
BaseCreeveyTestContext,
|
5
4
|
Config,
|
6
5
|
CreeveyWebdriver,
|
7
|
-
FakeSuite,
|
8
|
-
FakeTest,
|
9
|
-
Images,
|
10
6
|
Options,
|
11
7
|
ServerTest,
|
12
|
-
TEST_EVENTS,
|
13
8
|
TestMessage,
|
9
|
+
TestResult,
|
14
10
|
isDefined,
|
15
11
|
isImageError,
|
16
12
|
} from '../../types.js';
|
@@ -45,9 +41,10 @@ async function getTestsFromStories(
|
|
45
41
|
return testsById;
|
46
42
|
}
|
47
43
|
|
48
|
-
function runHandler(browserName: string,
|
44
|
+
function runHandler(browserName: string, result: Omit<TestResult, 'status'>, error?: unknown): void {
|
49
45
|
// TODO How handle browser corruption?
|
50
|
-
|
46
|
+
const { images } = result;
|
47
|
+
if (images != null && isImageError(error)) {
|
51
48
|
if (typeof error.images == 'string') {
|
52
49
|
const image = images[browserName];
|
53
50
|
if (image) image.error = error.images;
|
@@ -60,25 +57,31 @@ function runHandler(browserName: string, images: Partial<Record<string, Images>>
|
|
60
57
|
}
|
61
58
|
}
|
62
59
|
|
63
|
-
if (error || Object.values(images).some((image) => image?.error != null)) {
|
64
|
-
|
60
|
+
if (error || (images != null && Object.values(images).some((image) => image?.error != null))) {
|
61
|
+
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
62
|
+
const errorMessage = result.error!;
|
65
63
|
|
66
64
|
const isUnexpectedError =
|
67
65
|
hasTimeout(errorMessage) ||
|
68
66
|
hasDisconnected(errorMessage) ||
|
69
|
-
Object.values(images).some((image) => hasTimeout(image?.error));
|
67
|
+
(images != null && Object.values(images).some((image) => hasTimeout(image?.error)));
|
70
68
|
if (isUnexpectedError) emitWorkerMessage({ type: 'error', payload: { subtype: 'unknown', error: errorMessage } });
|
71
69
|
else
|
72
70
|
emitTestMessage({
|
73
71
|
type: 'end',
|
74
72
|
payload: {
|
75
73
|
status: 'failed',
|
76
|
-
|
77
|
-
error: errorMessage,
|
74
|
+
...result,
|
78
75
|
},
|
79
76
|
});
|
80
77
|
} else {
|
81
|
-
emitTestMessage({
|
78
|
+
emitTestMessage({
|
79
|
+
type: 'end',
|
80
|
+
payload: {
|
81
|
+
status: 'success',
|
82
|
+
...result,
|
83
|
+
},
|
84
|
+
});
|
82
85
|
}
|
83
86
|
}
|
84
87
|
|
@@ -112,7 +115,6 @@ function hasTimeout(str: string | null | undefined): boolean {
|
|
112
115
|
}
|
113
116
|
|
114
117
|
export async function start(browser: string, gridUrl: string, config: Config, options: Options): Promise<void> {
|
115
|
-
let retries = 0;
|
116
118
|
const imagesContext: ImageContext = {
|
117
119
|
attachments: [],
|
118
120
|
testFullPath: [],
|
@@ -123,26 +125,6 @@ export async function start(browser: string, gridUrl: string, config: Config, op
|
|
123
125
|
|
124
126
|
if (!webdriver || !sessionId) return;
|
125
127
|
|
126
|
-
const reporterOptions = {
|
127
|
-
...config.reporterOptions,
|
128
|
-
creevey: {
|
129
|
-
sessionId,
|
130
|
-
reportDir: config.reportDir,
|
131
|
-
browserName: browser,
|
132
|
-
get willRetry() {
|
133
|
-
return retries < config.maxRetries;
|
134
|
-
},
|
135
|
-
get images() {
|
136
|
-
return imagesContext.images;
|
137
|
-
},
|
138
|
-
},
|
139
|
-
};
|
140
|
-
|
141
|
-
class FakeRunner extends EventEmitter {}
|
142
|
-
const runner = new FakeRunner();
|
143
|
-
const Reporter = config.reporter;
|
144
|
-
new Reporter(runner, { reporterOptions });
|
145
|
-
|
146
128
|
const { matchImage, matchImages } = options.odiff
|
147
129
|
? getOdiffMatchers(imagesContext, config)
|
148
130
|
: await getMatchers(imagesContext, config);
|
@@ -196,32 +178,9 @@ export async function start(browser: string, gridUrl: string, config: Config, op
|
|
196
178
|
imagesContext.testFullPath = getTestPath(test);
|
197
179
|
imagesContext.images = {};
|
198
180
|
|
199
|
-
retries = message.payload.retries;
|
200
181
|
let error = undefined;
|
201
182
|
|
202
|
-
const fakeSuite: FakeSuite = {
|
203
|
-
title: test.storyPath.slice(0, -1).join('/'),
|
204
|
-
fullTitle: () => fakeSuite.title,
|
205
|
-
titlePath: () => [fakeSuite.title],
|
206
|
-
tests: [],
|
207
|
-
};
|
208
|
-
|
209
|
-
const fakeTest: FakeTest = {
|
210
|
-
parent: fakeSuite,
|
211
|
-
title: [test.story.name, test.testName, test.browser].filter(isDefined).join('/'),
|
212
|
-
fullTitle: () => getTestPath(test).join('/'),
|
213
|
-
titlePath: () => getTestPath(test),
|
214
|
-
currentRetry: () => retries,
|
215
|
-
retires: () => config.maxRetries,
|
216
|
-
slow: () => 1000,
|
217
|
-
};
|
218
|
-
|
219
|
-
fakeSuite.tests.push(fakeTest);
|
220
|
-
|
221
183
|
void (async () => {
|
222
|
-
runner.emit(TEST_EVENTS.RUN_BEGIN);
|
223
|
-
runner.emit(TEST_EVENTS.TEST_BEGIN, fakeTest);
|
224
|
-
|
225
184
|
let timeout;
|
226
185
|
let isRejected = false;
|
227
186
|
const start = Date.now();
|
@@ -241,22 +200,9 @@ export async function start(browser: string, gridUrl: string, config: Config, op
|
|
241
200
|
]);
|
242
201
|
} catch (testError) {
|
243
202
|
error = testError;
|
244
|
-
fakeTest.err = error;
|
245
203
|
}
|
246
204
|
const duration = Date.now() - start;
|
247
205
|
clearTimeout(timeout);
|
248
|
-
fakeTest.attachments = imagesContext.attachments;
|
249
|
-
fakeTest.state = error ? 'failed' : 'passed';
|
250
|
-
fakeTest.duration = duration;
|
251
|
-
fakeTest.speed = duration > fakeTest.slow() ? 'slow' : duration / 2 > fakeTest.slow() ? 'medium' : 'fast';
|
252
|
-
|
253
|
-
if (error) {
|
254
|
-
runner.emit(TEST_EVENTS.TEST_FAIL, fakeTest, error);
|
255
|
-
} else {
|
256
|
-
runner.emit(TEST_EVENTS.TEST_PASS, fakeTest);
|
257
|
-
}
|
258
|
-
runner.emit(TEST_EVENTS.TEST_END, fakeTest);
|
259
|
-
runner.emit(TEST_EVENTS.RUN_END);
|
260
206
|
|
261
207
|
await webdriver.afterTest(test);
|
262
208
|
|
@@ -267,7 +213,14 @@ export async function start(browser: string, gridUrl: string, config: Config, op
|
|
267
213
|
payload: { subtype: 'unknown', error: serializeError(error) },
|
268
214
|
});
|
269
215
|
} else {
|
270
|
-
|
216
|
+
const result = {
|
217
|
+
images: imagesContext.images,
|
218
|
+
error: serializeError(error),
|
219
|
+
duration,
|
220
|
+
attachments: imagesContext.attachments,
|
221
|
+
retries: message.payload.retries,
|
222
|
+
};
|
223
|
+
runHandler(baseContext.browserName, result, error);
|
271
224
|
}
|
272
225
|
})().catch((error: unknown) => {
|
273
226
|
logger().error('Unexpected error:', error);
|
package/src/types.ts
CHANGED
@@ -419,10 +419,14 @@ export type TestStatus = 'unknown' | 'pending' | 'running' | 'failed' | 'approve
|
|
419
419
|
|
420
420
|
export interface TestResult {
|
421
421
|
status: 'failed' | 'success';
|
422
|
+
retries: number;
|
422
423
|
// TODO Remove checks `name == browser` in TestResultsView
|
423
424
|
// images?: Partial<{ [name: string]: Images }> | Images;
|
424
425
|
images?: Partial<Record<string, Images>>;
|
425
426
|
error?: string;
|
427
|
+
// Test metadata for reporting
|
428
|
+
duration?: number;
|
429
|
+
attachments?: string[];
|
426
430
|
}
|
427
431
|
|
428
432
|
export class ImagesError extends Error {
|
@@ -470,6 +474,8 @@ export interface CreeveyTestContext extends BaseCreeveyTestContext {
|
|
470
474
|
export enum TEST_EVENTS {
|
471
475
|
RUN_BEGIN = 'start',
|
472
476
|
RUN_END = 'end',
|
477
|
+
SUITE_BEGIN = 'suite',
|
478
|
+
SUITE_END = 'suite end',
|
473
479
|
TEST_BEGIN = 'test',
|
474
480
|
TEST_END = 'test end',
|
475
481
|
TEST_FAIL = 'fail',
|
@@ -494,7 +500,7 @@ export interface FakeTest {
|
|
494
500
|
title: string;
|
495
501
|
fullTitle: () => string;
|
496
502
|
titlePath: () => string[];
|
497
|
-
currentRetry: () => number;
|
503
|
+
currentRetry: () => number | undefined;
|
498
504
|
retires: () => number;
|
499
505
|
slow: () => number;
|
500
506
|
duration?: number;
|
@@ -504,6 +510,15 @@ export interface FakeTest {
|
|
504
510
|
err?: unknown;
|
505
511
|
// NOTE: image files
|
506
512
|
attachments?: string[];
|
513
|
+
|
514
|
+
// NOTE: Creevey specific fields
|
515
|
+
creevey: {
|
516
|
+
reportDir: string;
|
517
|
+
sessionId: string;
|
518
|
+
browserName: string;
|
519
|
+
willRetry: boolean;
|
520
|
+
images: Partial<Record<string, Partial<Images>>>;
|
521
|
+
};
|
507
522
|
}
|
508
523
|
|
509
524
|
export interface CreeveyStatus {
|