creevey 0.10.0-beta.40 → 0.10.0-beta.41

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.
Files changed (34) hide show
  1. package/dist/client/web/assets/{index-B0Xv0lOY.js → index-C47njyZV.js} +2 -2
  2. package/dist/client/web/index.html +1 -1
  3. package/dist/server/config.js +3 -2
  4. package/dist/server/config.js.map +1 -1
  5. package/dist/server/master/runner.js +6 -4
  6. package/dist/server/master/runner.js.map +1 -1
  7. package/dist/server/{reporter.d.ts → reporters/creevey.d.ts} +0 -4
  8. package/dist/server/reporters/creevey.js +63 -0
  9. package/dist/server/reporters/creevey.js.map +1 -0
  10. package/dist/server/reporters/index.d.ts +2 -0
  11. package/dist/server/reporters/index.js +16 -0
  12. package/dist/server/reporters/index.js.map +1 -0
  13. package/dist/server/reporters/junit.d.ts +16 -0
  14. package/dist/server/reporters/junit.js +165 -0
  15. package/dist/server/reporters/junit.js.map +1 -0
  16. package/dist/server/reporters/teamcity.d.ts +7 -0
  17. package/dist/server/reporters/teamcity.js +60 -0
  18. package/dist/server/reporters/teamcity.js.map +1 -0
  19. package/dist/server/worker/start.js +2 -0
  20. package/dist/server/worker/start.js.map +1 -1
  21. package/dist/types.d.ts +7 -3
  22. package/dist/types.js.map +1 -1
  23. package/package.json +1 -1
  24. package/src/server/config.ts +2 -1
  25. package/src/server/master/runner.ts +6 -4
  26. package/src/server/reporters/creevey.ts +71 -0
  27. package/src/server/reporters/index.ts +11 -0
  28. package/src/server/reporters/junit.ts +205 -0
  29. package/src/server/reporters/teamcity.ts +74 -0
  30. package/src/server/worker/start.ts +2 -0
  31. package/src/types.ts +7 -4
  32. package/dist/server/reporter.js +0 -117
  33. package/dist/server/reporter.js.map +0 -1
  34. package/src/server/reporter.ts +0 -141
@@ -0,0 +1,71 @@
1
+ import chalk from 'chalk';
2
+ import Logger from 'loglevel';
3
+ import prefix from 'loglevel-plugin-prefix';
4
+ import { FakeTest, isImageError, TEST_EVENTS } from '../../types.js';
5
+ import EventEmitter from 'events';
6
+
7
+ const testLevels: Record<string, string> = {
8
+ INFO: chalk.green('PASS'),
9
+ WARN: chalk.yellow('START'),
10
+ ERROR: chalk.red('FAIL'),
11
+ };
12
+
13
+ export class CreeveyReporter {
14
+ private logger: Logger.Logger | null = null;
15
+ // TODO Output in better way, like vitest, maybe
16
+ constructor(runner: EventEmitter) {
17
+ runner.on(TEST_EVENTS.TEST_BEGIN, (test: FakeTest) => {
18
+ this.getLogger(test.creevey).warn(chalk.cyan(test.fullTitle()));
19
+ });
20
+ runner.on(TEST_EVENTS.TEST_PASS, (test: FakeTest) => {
21
+ this.getLogger(test.creevey).info(chalk.cyan(test.fullTitle()), chalk.gray(`(${test.duration} ms)`));
22
+ });
23
+ runner.on(TEST_EVENTS.TEST_FAIL, (test: FakeTest, error) => {
24
+ this.getLogger(test.creevey).error(
25
+ chalk.cyan(test.fullTitle()),
26
+ chalk.gray(`(${test.duration} ms)`),
27
+ '\n ',
28
+ this.getErrors(
29
+ error,
30
+ (error, imageName) => `${chalk.bold(imageName ?? test.creevey.browserName)}:${error}`,
31
+ (error) => error.stack ?? error.message,
32
+ ).join('\n '),
33
+ );
34
+ });
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
+
51
+ private getErrors(
52
+ error: unknown,
53
+ imageErrorToString: (error: string, imageName?: string) => string,
54
+ errorToString: (error: Error) => string,
55
+ ): string[] {
56
+ const errors = [];
57
+ if (!(error instanceof Error)) {
58
+ errors.push(error as string);
59
+ } else if (!isImageError(error)) {
60
+ errors.push(errorToString(error));
61
+ } else if (typeof error.images == 'string') {
62
+ errors.push(imageErrorToString(error.images));
63
+ } else {
64
+ const imageErrors = error.images ?? {};
65
+ Object.keys(imageErrors).forEach((imageName) => {
66
+ errors.push(imageErrorToString(imageErrors[imageName] ?? '', imageName));
67
+ });
68
+ }
69
+ return errors;
70
+ }
71
+ }
@@ -0,0 +1,11 @@
1
+ import { BaseReporter } from '../../types.js';
2
+ import { CreeveyReporter } from './creevey.js';
3
+ import { JUnitReporter } from './junit.js';
4
+ import { TeamcityReporter } from './teamcity.js';
5
+
6
+ export function getReporter(reporter: BaseReporter | 'creevey' | 'teamcity' | 'junit'): BaseReporter {
7
+ if (reporter === 'creevey') return CreeveyReporter;
8
+ if (reporter === 'teamcity') return TeamcityReporter;
9
+ if (reporter === 'junit') return JUnitReporter;
10
+ return reporter;
11
+ }
@@ -0,0 +1,205 @@
1
+ import EventEmitter from 'events';
2
+ import { dirname, resolve } from 'path';
3
+ import { closeSync, existsSync, mkdirSync, openSync, writeFileSync } from 'fs';
4
+ import { TEST_EVENTS, FakeTest } from '../../types.js';
5
+ import { logger } from '../logger.js';
6
+
7
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
8
+ class IndentedLogger<T = any> {
9
+ private currentIndent = '';
10
+
11
+ constructor(private baseLog: (text: string) => T) {}
12
+
13
+ indent(): void {
14
+ this.currentIndent += ' ';
15
+ }
16
+
17
+ unindent(): void {
18
+ this.currentIndent = this.currentIndent.substring(0, this.currentIndent.length - 4);
19
+ }
20
+
21
+ log(text: string): T {
22
+ return this.baseLog(this.currentIndent + text);
23
+ }
24
+ }
25
+
26
+ // NOTE: This is a reworked copy of the JUnitReporter class from Vitest.
27
+ export class JUnitReporter {
28
+ private reportFile: string;
29
+ private fileFd?: number;
30
+ private logger: IndentedLogger<void>;
31
+ private suites: Record<string, FakeTest[]> = {};
32
+ // TODO classnameTemplate
33
+ constructor(runner: EventEmitter, options: { reportDir: string; reporterOptions: { outputFile?: string } }) {
34
+ const { reportDir, reporterOptions } = options;
35
+
36
+ this.reportFile = reporterOptions.outputFile ?? resolve(reportDir, 'junit.xml');
37
+
38
+ this.logger = new IndentedLogger((text) => {
39
+ this.fileFd ??= openSync(this.reportFile, 'w+');
40
+
41
+ writeFileSync(this.fileFd, `${text}\n`);
42
+ });
43
+
44
+ runner.on(TEST_EVENTS.RUN_BEGIN, () => {
45
+ this.suites = {};
46
+
47
+ const outputDirectory = dirname(this.reportFile);
48
+ if (!existsSync(outputDirectory)) {
49
+ mkdirSync(outputDirectory, { recursive: true });
50
+ }
51
+
52
+ this.fileFd = openSync(this.reportFile, 'w+');
53
+ });
54
+ runner.on(TEST_EVENTS.TEST_PASS, (test: FakeTest) => {
55
+ const suite = this.suites[test.parent.title] ?? [];
56
+ suite.push(test);
57
+ });
58
+ runner.on(TEST_EVENTS.TEST_FAIL, (test: FakeTest) => {
59
+ const suite = this.suites[test.parent.title] ?? [];
60
+ suite.push(test);
61
+ });
62
+ runner.on(TEST_EVENTS.RUN_END, () => {
63
+ this.onFinished();
64
+ });
65
+ }
66
+
67
+ private writeElement(name: string, attrs: Record<string, string | number | undefined>, children?: () => void): void {
68
+ const pairs: string[] = [];
69
+ for (const key in attrs) {
70
+ const attr = attrs[key];
71
+ if (attr === undefined) {
72
+ continue;
73
+ }
74
+
75
+ pairs.push(`${key}="${escapeXML(attr)}"`);
76
+ }
77
+
78
+ this.logger.log(`<${name}${pairs.length ? ` ${pairs.join(' ')}` : ''}>`);
79
+ this.logger.indent();
80
+ children?.call(this);
81
+ this.logger.unindent();
82
+
83
+ this.logger.log(`</${name}>`);
84
+ }
85
+
86
+ private writeTasks(tests: FakeTest[]): void {
87
+ for (const test of tests) {
88
+ const classname = test.parent.title;
89
+
90
+ this.writeElement(
91
+ 'testcase',
92
+ {
93
+ classname,
94
+ name: test.title,
95
+ time: getDuration(test),
96
+ },
97
+ () => {
98
+ if (test.state === 'failed') {
99
+ const error = test.err;
100
+ this.writeElement('failure', { message: error });
101
+ }
102
+ },
103
+ );
104
+ }
105
+ }
106
+
107
+ private onFinished(): void {
108
+ this.logger.log('<?xml version="1.0" encoding="UTF-8" ?>');
109
+
110
+ const suites = Object.entries(this.suites).map(([name, tests]) => {
111
+ return {
112
+ name,
113
+ tests,
114
+ failures: tests.filter((test) => test.state === 'failed').length,
115
+ time: tests.reduce((acc, test) => acc + (test.duration ?? 0), 0),
116
+ };
117
+ });
118
+ const stats = suites.reduce(
119
+ (s, { tests, failures, time }) => {
120
+ s.tests += tests.length;
121
+ s.failures += failures;
122
+ s.time += time;
123
+ return s;
124
+ },
125
+ { name: 'creevey tests', tests: 0, failures: 0, time: 0 },
126
+ );
127
+
128
+ this.writeElement('testsuites', { ...stats, time: executionTime(stats.time) }, () => {
129
+ suites.forEach(({ name, tests, failures, time }) => {
130
+ this.writeElement(
131
+ 'testsuite',
132
+ {
133
+ name,
134
+ tests: tests.length,
135
+ failures,
136
+ time: executionTime(time),
137
+ },
138
+ () => {
139
+ this.writeTasks(tests);
140
+ },
141
+ );
142
+ });
143
+ });
144
+
145
+ if (this.reportFile) {
146
+ logger().info(`JUNIT report written to ${this.reportFile}`);
147
+ }
148
+
149
+ if (this.fileFd) {
150
+ closeSync(this.fileFd);
151
+ this.fileFd = undefined;
152
+ }
153
+ }
154
+ }
155
+
156
+ // https://gist.github.com/john-doherty/b9195065884cdbfd2017a4756e6409cc
157
+ function removeInvalidXMLCharacters(value: string, removeDiscouragedChars: boolean): string {
158
+ let regex =
159
+ // eslint-disable-next-line no-control-regex
160
+ /([\0-\x08\v\f\x0E-\x1F\uFFFD\uFFFE\uFFFF]|[\uD800-\uDBFF](?![\uDC00-\uDFFF])|(?:[^\uD800-\uDBFF]|^)[\uDC00-\uDFFF])/g;
161
+ value = String(value).replace(regex, '');
162
+
163
+ if (removeDiscouragedChars) {
164
+ // remove everything discouraged by XML 1.0 specifications
165
+ regex = new RegExp(
166
+ '([\\x7F-\\x84]|[\\x86-\\x9F]|[\\uFDD0-\\uFDEF]|\\uD83F[\\uDFFE\\uDFFF]|(?:\\uD87F[\\uDF' +
167
+ 'FE\\uDFFF])|\\uD8BF[\\uDFFE\\uDFFF]|\\uD8FF[\\uDFFE\\uDFFF]|(?:\\uD93F[\\uDFFE\\uD' +
168
+ 'FFF])|\\uD97F[\\uDFFE\\uDFFF]|\\uD9BF[\\uDFFE\\uDFFF]|\\uD9FF[\\uDFFE\\uDFFF]' +
169
+ '|\\uDA3F[\\uDFFE\\uDFFF]|\\uDA7F[\\uDFFE\\uDFFF]|\\uDABF[\\uDFFE\\uDFFF]|(?:\\' +
170
+ 'uDAFF[\\uDFFE\\uDFFF])|\\uDB3F[\\uDFFE\\uDFFF]|\\uDB7F[\\uDFFE\\uDFFF]|(?:\\uDBBF' +
171
+ '[\\uDFFE\\uDFFF])|\\uDBFF[\\uDFFE\\uDFFF](?:[\\0-\\t\\v\\f\\x0E-\\u2027\\u202A-\\uD7FF\\' +
172
+ 'uE000-\\uFFFF]|[\\uD800-\\uDBFF][\\uDC00-\\uDFFF]|[\\uD800-\\uDBFF](?![\\uDC00-\\uDFFF])|' +
173
+ '(?:[^\\uD800-\\uDBFF]|^)[\\uDC00-\\uDFFF]))',
174
+ 'g',
175
+ );
176
+
177
+ value = value.replace(regex, '');
178
+ }
179
+
180
+ return value;
181
+ }
182
+
183
+ function escapeXML(value: string | number): string {
184
+ return removeInvalidXMLCharacters(
185
+ String(value)
186
+ .replace(/&/g, '&amp;')
187
+ .replace(/"/g, '&quot;')
188
+ .replace(/'/g, '&apos;')
189
+ .replace(/</g, '&lt;')
190
+ .replace(/>/g, '&gt;'),
191
+ true,
192
+ );
193
+ }
194
+
195
+ function executionTime(durationMS: number) {
196
+ return (durationMS / 1000).toLocaleString('en-US', {
197
+ useGrouping: false,
198
+ maximumFractionDigits: 10,
199
+ });
200
+ }
201
+
202
+ function getDuration(task: FakeTest): string | undefined {
203
+ const duration = task.duration ?? 0;
204
+ return executionTime(duration);
205
+ }
@@ -0,0 +1,74 @@
1
+ import { FakeTest, Images, isDefined, TEST_EVENTS } from '../../types.js';
2
+ import EventEmitter from 'events';
3
+
4
+ export class TeamcityReporter {
5
+ constructor(runner: EventEmitter, options: { reportDir: string }) {
6
+ const { reportDir } = options;
7
+
8
+ runner.on(TEST_EVENTS.TEST_BEGIN, (test: FakeTest) => {
9
+ console.log(`##teamcity[testStarted name='${this.escape(test.fullTitle())}' flowId='${test.creevey.workerId}']`);
10
+ });
11
+
12
+ runner.on(TEST_EVENTS.TEST_PASS, (test: FakeTest) => {
13
+ console.log(`##teamcity[testFinished name='${this.escape(test.fullTitle())}' flowId='${test.creevey.workerId}']`);
14
+ });
15
+
16
+ runner.on(TEST_EVENTS.TEST_FAIL, (test: FakeTest, error: Error) => {
17
+ const browserName = this.escape(test.creevey.browserName);
18
+ Object.entries(test.creevey.images).forEach(([name, image]) => {
19
+ if (!image) return;
20
+ const filePath = test
21
+ .titlePath()
22
+ .slice(0, -1)
23
+ .concat(name == browserName ? [] : [browserName])
24
+ .map(this.escape)
25
+ .join('/');
26
+
27
+ const { error: _, ...rest } = image;
28
+ Object.values(rest as Partial<Images>)
29
+ .filter(isDefined)
30
+ .forEach((fileName) => {
31
+ console.log(`##teamcity[publishArtifacts '${reportDir}/${filePath}/${fileName} => report/${filePath}']`);
32
+ console.log(
33
+ `##teamcity[testMetadata testName='${this.escape(
34
+ test.fullTitle(),
35
+ )}' type='image' value='report/${filePath}/${fileName}' flowId='${test.creevey.workerId}']`,
36
+ );
37
+ });
38
+ });
39
+
40
+ // Output failed test as passed due TC don't support retry mechanic
41
+ // 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
42
+
43
+ if (test.creevey.willRetry)
44
+ console.log(
45
+ `##teamcity[testFinished name='${this.escape(test.fullTitle())}' flowId='${test.creevey.workerId}']`,
46
+ );
47
+ else
48
+ console.log(
49
+ `##teamcity[testFailed name='${this.escape(test.fullTitle())}' message='${this.escape(
50
+ error.message,
51
+ )}' details='${this.escape(error.stack ?? '')}' flowId='${test.creevey.workerId}']`,
52
+ );
53
+ });
54
+ }
55
+
56
+ private escape = (str: string): string => {
57
+ if (!str) return '';
58
+ return (
59
+ str
60
+ .toString()
61
+ // eslint-disable-next-line no-control-regex
62
+ .replace(/\x1B.*?m/g, '')
63
+ .replace(/\|/g, '||')
64
+ .replace(/\n/g, '|n')
65
+ .replace(/\r/g, '|r')
66
+ .replace(/\[/g, '|[')
67
+ .replace(/\]/g, '|]')
68
+ .replace(/\u0085/g, '|x')
69
+ .replace(/\u2028/g, '|l')
70
+ .replace(/\u2029/g, '|p')
71
+ .replace(/'/g, "|'")
72
+ );
73
+ };
74
+ }
@@ -215,6 +215,8 @@ export async function start(browser: string, gridUrl: string, config: Config, op
215
215
  } else {
216
216
  const result = {
217
217
  sessionId,
218
+ browserName: baseContext.browserName,
219
+ workerId: process.pid,
218
220
  images: imagesContext.images,
219
221
  error: serializeError(error),
220
222
  duration,
package/src/types.ts CHANGED
@@ -189,7 +189,7 @@ export interface DockerAuth {
189
189
  }
190
190
 
191
191
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
192
- export type BaseReporter = new (runner: EventEmitter, options: { reporterOptions: any }) => void;
192
+ export type BaseReporter = new (runner: EventEmitter, options: { reportDir: string; reporterOptions: any }) => void;
193
193
 
194
194
  export interface Config {
195
195
  /**
@@ -224,8 +224,9 @@ export interface Config {
224
224
  /**
225
225
  * Specify a custom reporter for test results. Creevey accepts only mocha-like reporters
226
226
  * @optional
227
+ * @default 'creevey'
227
228
  */
228
- reporter: BaseReporter;
229
+ reporter: BaseReporter | 'creevey' | 'teamcity' | 'junit';
229
230
  /**
230
231
  * Options which are used by reporter
231
232
  */
@@ -428,6 +429,8 @@ export interface TestResult {
428
429
  duration?: number;
429
430
  attachments?: string[];
430
431
  sessionId?: string;
432
+ browserName?: string;
433
+ workerId?: number;
431
434
  }
432
435
 
433
436
  export class ImagesError extends Error {
@@ -508,15 +511,15 @@ export interface FakeTest {
508
511
  state?: 'failed' | 'passed';
509
512
  // NOTE > duration, > duration / 2, > 0
510
513
  speed?: 'slow' | 'medium' | 'fast';
511
- err?: unknown;
514
+ err?: string;
512
515
  // NOTE: image files
513
516
  attachments?: string[];
514
517
 
515
518
  // NOTE: Creevey specific fields
516
519
  creevey: {
517
- reportDir: string;
518
520
  sessionId: string;
519
521
  browserName: string;
522
+ workerId: number;
520
523
  willRetry: boolean;
521
524
  images: Partial<Record<string, Partial<Images>>>;
522
525
  };
@@ -1,117 +0,0 @@
1
- "use strict";
2
- var __importDefault = (this && this.__importDefault) || function (mod) {
3
- return (mod && mod.__esModule) ? mod : { "default": mod };
4
- };
5
- Object.defineProperty(exports, "__esModule", { value: true });
6
- exports.TeamcityReporter = exports.CreeveyReporter = void 0;
7
- const chalk_1 = __importDefault(require("chalk"));
8
- const loglevel_1 = __importDefault(require("loglevel"));
9
- const loglevel_plugin_prefix_1 = __importDefault(require("loglevel-plugin-prefix"));
10
- const types_js_1 = require("../types.js");
11
- const testLevels = {
12
- INFO: chalk_1.default.green('PASS'),
13
- WARN: chalk_1.default.yellow('START'),
14
- ERROR: chalk_1.default.red('FAIL'),
15
- };
16
- class CreeveyReporter {
17
- logger = null;
18
- // TODO Output in better way, like vitest, maybe
19
- constructor(runner) {
20
- runner.on(types_js_1.TEST_EVENTS.TEST_BEGIN, (test) => {
21
- this.getLogger(test.creevey).warn(chalk_1.default.cyan(test.fullTitle()));
22
- });
23
- runner.on(types_js_1.TEST_EVENTS.TEST_PASS, (test) => {
24
- this.getLogger(test.creevey).info(chalk_1.default.cyan(test.fullTitle()), chalk_1.default.gray(`(${test.duration} ms)`));
25
- });
26
- runner.on(types_js_1.TEST_EVENTS.TEST_FAIL, (test, error) => {
27
- this.getLogger(test.creevey).error(chalk_1.default.cyan(test.fullTitle()), chalk_1.default.gray(`(${test.duration} ms)`), '\n ', this.getErrors(error, (error, imageName) => `${chalk_1.default.bold(imageName ?? test.creevey.browserName)}:${error}`, (error) => error.stack ?? error.message).join('\n '));
28
- });
29
- }
30
- getLogger(options) {
31
- if (this.logger)
32
- return this.logger;
33
- const { sessionId, browserName } = options;
34
- const testLogger = loglevel_1.default.getLogger(sessionId);
35
- this.logger = loglevel_plugin_prefix_1.default.apply(testLogger, {
36
- format(level) {
37
- return `[${browserName}:${chalk_1.default.gray(process.pid)}] ${testLevels[level]} => ${chalk_1.default.gray(sessionId)}`;
38
- },
39
- });
40
- return this.logger;
41
- }
42
- getErrors(error, imageErrorToString, errorToString) {
43
- const errors = [];
44
- if (!(error instanceof Error)) {
45
- errors.push(error);
46
- }
47
- else if (!(0, types_js_1.isImageError)(error)) {
48
- errors.push(errorToString(error));
49
- }
50
- else if (typeof error.images == 'string') {
51
- errors.push(imageErrorToString(error.images));
52
- }
53
- else {
54
- const imageErrors = error.images ?? {};
55
- Object.keys(imageErrors).forEach((imageName) => {
56
- errors.push(imageErrorToString(imageErrors[imageName] ?? '', imageName));
57
- });
58
- }
59
- return errors;
60
- }
61
- }
62
- exports.CreeveyReporter = CreeveyReporter;
63
- class TeamcityReporter {
64
- constructor(runner) {
65
- runner.on(types_js_1.TEST_EVENTS.TEST_BEGIN, (test) => {
66
- console.log(`##teamcity[testStarted name='${this.escape(test.fullTitle())}' flowId='${process.pid}']`);
67
- });
68
- runner.on(types_js_1.TEST_EVENTS.TEST_PASS, (test) => {
69
- console.log(`##teamcity[testFinished name='${this.escape(test.fullTitle())}' flowId='${process.pid}']`);
70
- });
71
- runner.on(types_js_1.TEST_EVENTS.TEST_FAIL, (test, error) => {
72
- const browserName = this.escape(test.creevey.browserName);
73
- Object.entries(test.creevey.images).forEach(([name, image]) => {
74
- if (!image)
75
- return;
76
- const filePath = test
77
- .titlePath()
78
- .slice(0, -1)
79
- .concat(name == browserName ? [] : [browserName])
80
- .map(this.escape)
81
- .join('/');
82
- const { error: _, ...rest } = image;
83
- Object.values(rest)
84
- .filter(types_js_1.isDefined)
85
- .forEach((fileName) => {
86
- console.log(`##teamcity[publishArtifacts '${test.creevey.reportDir}/${filePath}/${fileName} => report/${filePath}']`);
87
- console.log(`##teamcity[testMetadata testName='${this.escape(test.fullTitle())}' type='image' value='report/${filePath}/${fileName}' flowId='${process.pid}']`);
88
- });
89
- });
90
- // Output failed test as passed due TC don't support retry mechanic
91
- // 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
92
- if (test.creevey.willRetry)
93
- console.log(`##teamcity[testFinished name='${this.escape(test.fullTitle())}' flowId='${process.pid}']`);
94
- else
95
- console.log(`##teamcity[testFailed name='${this.escape(test.fullTitle())}' message='${this.escape(error.message)}' details='${this.escape(error.stack ?? '')}' flowId='${process.pid}']`);
96
- });
97
- }
98
- escape = (str) => {
99
- if (!str)
100
- return '';
101
- return (str
102
- .toString()
103
- // eslint-disable-next-line no-control-regex
104
- .replace(/\x1B.*?m/g, '')
105
- .replace(/\|/g, '||')
106
- .replace(/\n/g, '|n')
107
- .replace(/\r/g, '|r')
108
- .replace(/\[/g, '|[')
109
- .replace(/\]/g, '|]')
110
- .replace(/\u0085/g, '|x')
111
- .replace(/\u2028/g, '|l')
112
- .replace(/\u2029/g, '|p')
113
- .replace(/'/g, "|'"));
114
- };
115
- }
116
- exports.TeamcityReporter = TeamcityReporter;
117
- //# sourceMappingURL=reporter.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"reporter.js","sourceRoot":"","sources":["../../src/server/reporter.ts"],"names":[],"mappings":"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"}