@testomatio/reporter 2.9.3-beta-vitestmeta → 2.9.3-beta.2-allure-improv
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/README.md +4 -2
- package/lib/adapter/codecept.js +0 -1
- package/lib/adapter/playwright.js +8 -19
- package/lib/adapter/vitest.js +1 -4
- package/lib/allureReader.d.ts +178 -0
- package/lib/allureReader.js +781 -0
- package/lib/bin/cli.js +34 -87
- package/lib/junit-adapter/index.js +4 -0
- package/lib/junit-adapter/kotlin.d.ts +5 -0
- package/lib/junit-adapter/kotlin.js +46 -0
- package/lib/pipe/debug.d.ts +0 -6
- package/lib/pipe/debug.js +0 -11
- package/lib/replay.js +0 -21
- package/lib/reporter-functions.js +0 -12
- package/lib/template/testomatio.hbs +11 -77
- package/lib/utils/pipe_utils.d.ts +17 -0
- package/lib/utils/pipe_utils.js +45 -0
- package/lib/utils/utils.js +9 -0
- package/lib/xmlReader.d.ts +0 -1
- package/lib/xmlReader.js +2 -38
- package/package.json +1 -1
- package/src/adapter/codecept.js +0 -2
- package/src/adapter/playwright.js +9 -22
- package/src/adapter/vitest.js +1 -4
- package/src/allureReader.js +875 -0
- package/src/bin/cli.js +43 -95
- package/src/junit-adapter/index.js +4 -0
- package/src/junit-adapter/kotlin.js +48 -0
- package/src/pipe/debug.js +0 -11
- package/src/replay.js +0 -21
- package/src/reporter-functions.js +0 -13
- package/src/template/testomatio.hbs +11 -77
- package/src/utils/pipe_utils.js +49 -0
- package/src/utils/utils.js +5 -0
- package/src/xmlReader.js +2 -47
package/README.md
CHANGED
|
@@ -76,6 +76,7 @@ npx testomatio-reporter <command> [options]
|
|
|
76
76
|
| [TestCafe](./docs/frameworks.md#testcafe) | [Detox](./docs/frameworks.md#detox) | [Codeception](https://github.com/testomatio/php-reporter) |
|
|
77
77
|
| [Newman (Postman)](./docs/frameworks.md#newman) | [JUnit](./docs/junit.md#junit) | [NUnit](./docs/junit.md#nunit) |
|
|
78
78
|
| [PyTest](./docs/junit.md#pytest) | [PHPUnit](./docs/junit.md#phpunit) | [Protractor](./docs/frameworks.md#protractor) |
|
|
79
|
+
| [Allure](./docs/allure.md) | | |
|
|
79
80
|
|
|
80
81
|
or **any [other via JUnit](./docs/junit.md)** report....
|
|
81
82
|
|
|
@@ -137,9 +138,10 @@ Bring this reporter on CI and never lose test results again!
|
|
|
137
138
|
- [HTML report](./docs/pipes/html.md)
|
|
138
139
|
- [Markdown report](./docs/pipes/markdown.md)
|
|
139
140
|
- [Bitbucket](./docs/pipes/bitbucket.md)
|
|
140
|
-
-
|
|
141
|
-
- 📓 [JUnit](./docs/junit.md)
|
|
141
|
+
- 📓 [JUnit Reports](./docs/junit.md)
|
|
142
142
|
- 🗄️ [Artifacts](./docs/artifacts.md)
|
|
143
|
+
- 🔬 [Allure Reports](./docs/allure.md)
|
|
144
|
+
- 🔗 [Linking Tests](./docs/linking-tests.md)
|
|
143
145
|
- 🔂 [Workflows](./docs/workflows.md)
|
|
144
146
|
- 🖊️ [Logger](./docs/logger.md)
|
|
145
147
|
- 🪲 [Debug File Format](./docs/debug-file-format.md)
|
package/lib/adapter/codecept.js
CHANGED
|
@@ -237,7 +237,6 @@ async function uploadAttachments(client, attachments, messagePrefix, attachmentT
|
|
|
237
237
|
if (client.uploader.isEnabled) {
|
|
238
238
|
log_js_1.log.info(`Attachments: ${messagePrefix} ${attachments.length} ${attachmentType} ...`);
|
|
239
239
|
}
|
|
240
|
-
await Promise.all(client.pipes?.map(pipe => pipe.addArtifacts?.(attachments)) || []);
|
|
241
240
|
const promises = attachments.map(async (attachment) => {
|
|
242
241
|
const { rid, title, path, type } = attachment;
|
|
243
242
|
const file = { path, type, title };
|
|
@@ -51,8 +51,13 @@ class PlaywrightReporter {
|
|
|
51
51
|
const { title } = test;
|
|
52
52
|
const { error, duration } = result;
|
|
53
53
|
const pwAttachments = (result.attachments || []).filter(a => a.body || a.path);
|
|
54
|
-
const files =
|
|
55
|
-
|
|
54
|
+
const files = pwAttachments
|
|
55
|
+
.map(att => ({
|
|
56
|
+
path: this.#getArtifactPath(att),
|
|
57
|
+
title: att.name || title,
|
|
58
|
+
type: att.contentType,
|
|
59
|
+
}))
|
|
60
|
+
.filter(f => f.path);
|
|
56
61
|
const suite_title = test.parent ? test.parent?.title : path_1.default.basename(test?.location?.file);
|
|
57
62
|
const rid = test.id || test.testId || (0, uuid_1.v4)();
|
|
58
63
|
/**
|
|
@@ -139,7 +144,7 @@ class PlaywrightReporter {
|
|
|
139
144
|
this.uploads.push({
|
|
140
145
|
rid: `${rid}-${project.name}`,
|
|
141
146
|
title: test.title,
|
|
142
|
-
files:
|
|
147
|
+
files: pwAttachments,
|
|
143
148
|
file: test.location?.file,
|
|
144
149
|
});
|
|
145
150
|
// remove empty uploads
|
|
@@ -205,22 +210,6 @@ function checkStatus(status) {
|
|
|
205
210
|
passed: constants_js_1.STATUS.PASSED,
|
|
206
211
|
}[status] || constants_js_1.STATUS.FAILED);
|
|
207
212
|
}
|
|
208
|
-
function buildArtifactFiles(attachments, getArtifactPath, title) {
|
|
209
|
-
return attachments
|
|
210
|
-
.filter(isScreenshotArtifact)
|
|
211
|
-
.map(attachment => ({
|
|
212
|
-
path: getArtifactPath(attachment),
|
|
213
|
-
title: attachment.name || title,
|
|
214
|
-
type: attachment.contentType,
|
|
215
|
-
}))
|
|
216
|
-
.filter(file => file.path);
|
|
217
|
-
}
|
|
218
|
-
function processArtifactsForUpload(attachments) {
|
|
219
|
-
return attachments.filter(attachment => !isScreenshotArtifact(attachment));
|
|
220
|
-
}
|
|
221
|
-
function isScreenshotArtifact(attachment) {
|
|
222
|
-
return attachment?.contentType === 'image/png' && attachment?.name === 'screenshot';
|
|
223
|
-
}
|
|
224
213
|
function appendStep(step, shift = 0) {
|
|
225
214
|
// nesting too deep, ignore those steps
|
|
226
215
|
if (shift >= 10)
|
package/lib/adapter/vitest.js
CHANGED
|
@@ -393,10 +393,7 @@ function normalizeVitestTest(test) {
|
|
|
393
393
|
file,
|
|
394
394
|
suiteTitle,
|
|
395
395
|
logs: '',
|
|
396
|
-
meta: {
|
|
397
|
-
...(test.task?.meta || {}),
|
|
398
|
-
...(typeof test.meta === 'function' ? test.meta() : test.meta || {}),
|
|
399
|
-
},
|
|
396
|
+
meta: typeof test.meta === 'function' ? test.meta() : {},
|
|
400
397
|
};
|
|
401
398
|
}
|
|
402
399
|
return {
|
|
@@ -0,0 +1,178 @@
|
|
|
1
|
+
export default AllureReader;
|
|
2
|
+
declare class AllureReader {
|
|
3
|
+
constructor(opts?: {});
|
|
4
|
+
requestParams: {
|
|
5
|
+
apiKey: any;
|
|
6
|
+
url: any;
|
|
7
|
+
title: string;
|
|
8
|
+
env: string;
|
|
9
|
+
group_title: string;
|
|
10
|
+
batchMode: "manual";
|
|
11
|
+
};
|
|
12
|
+
runId: any;
|
|
13
|
+
opts: {};
|
|
14
|
+
withPackage: any;
|
|
15
|
+
store: {};
|
|
16
|
+
pipesPromise: Promise<any[]>;
|
|
17
|
+
_tests: any[];
|
|
18
|
+
stats: {};
|
|
19
|
+
suites: {};
|
|
20
|
+
uploader: S3Uploader;
|
|
21
|
+
version: any;
|
|
22
|
+
set tests(value: any[]);
|
|
23
|
+
get tests(): any[];
|
|
24
|
+
createRun(): Promise<any[]>;
|
|
25
|
+
pipes: any;
|
|
26
|
+
parse(resultsPattern: any): {};
|
|
27
|
+
parseContainerFiles(containerFiles: any): void;
|
|
28
|
+
processAllureResult(result: any, resultsDir: any): {
|
|
29
|
+
rid: any;
|
|
30
|
+
title: any;
|
|
31
|
+
status: any;
|
|
32
|
+
suite_title: any;
|
|
33
|
+
file: string;
|
|
34
|
+
run_time: number;
|
|
35
|
+
steps: any;
|
|
36
|
+
message: any;
|
|
37
|
+
stack: any;
|
|
38
|
+
meta: {};
|
|
39
|
+
links: {
|
|
40
|
+
label: string;
|
|
41
|
+
}[];
|
|
42
|
+
artifacts: any[];
|
|
43
|
+
create: boolean;
|
|
44
|
+
overwrite: boolean;
|
|
45
|
+
};
|
|
46
|
+
mapStatus(status: any): any;
|
|
47
|
+
/**
|
|
48
|
+
* Map an Allure step status to the Testomat.io Step status enum
|
|
49
|
+
* (`passed | failed | none | custom`, see testomat-api-definition.yml).
|
|
50
|
+
*
|
|
51
|
+
* Allure marks a step `broken` when it threw an unexpected error — that is a
|
|
52
|
+
* failure for reporting purposes, matching how `mapStatus` treats tests.
|
|
53
|
+
* `skipped` and anything unknown/absent become `none` (the neutral value),
|
|
54
|
+
* since the step enum has no `skipped`.
|
|
55
|
+
*
|
|
56
|
+
* @param {string} status - Allure step status
|
|
57
|
+
* @returns {'passed'|'failed'|'none'} Testomat.io step status
|
|
58
|
+
*/
|
|
59
|
+
mapStepStatus(status: string): "passed" | "failed" | "none";
|
|
60
|
+
extractSuiteTitle(result: any): any;
|
|
61
|
+
stripNamespace(suiteName: any): any;
|
|
62
|
+
extractFile(result: any): string;
|
|
63
|
+
getFileExtension(result: any): any;
|
|
64
|
+
extractMeta(result: any): {};
|
|
65
|
+
extractLinks(result: any): {
|
|
66
|
+
label: string;
|
|
67
|
+
}[];
|
|
68
|
+
/**
|
|
69
|
+
* Extract a Testomat.io test id from Allure links so reported tests match
|
|
70
|
+
* existing cases instead of creating duplicates.
|
|
71
|
+
*
|
|
72
|
+
* Allure's `@TmsLink("T1a2b3c4d")` produces a link with `type: "tms"`. Some exporters
|
|
73
|
+
* omit the type but still point the link URL at a Testomat.io test page; both are
|
|
74
|
+
* accepted. The link `name` is used as the id (falling back to the last URL segment).
|
|
75
|
+
*
|
|
76
|
+
* @param {object} result - Parsed Allure result JSON
|
|
77
|
+
* @returns {string|null} Normalized test id, or null when no usable link exists
|
|
78
|
+
*/
|
|
79
|
+
extractTestId(result: object): string | null;
|
|
80
|
+
/**
|
|
81
|
+
* Normalize a value into a Testomat.io test id.
|
|
82
|
+
*
|
|
83
|
+
* Testomat.io test ids are exactly **8 word characters**. The value may arrive bare
|
|
84
|
+
* (`1a2b3c4d`), or carrying the `T` / `@T` markers Testomat uses in code and titles
|
|
85
|
+
* (`T1a2b3c4d`, `@T1a2b3c4d`). The markers are removed only when doing so still leaves
|
|
86
|
+
* a valid 8-char id, so a real id that happens to start with `T` is preserved.
|
|
87
|
+
*
|
|
88
|
+
* Anything that does not resolve to a valid 8-char id — a numeric Allure TestOps id
|
|
89
|
+
* like `12345`, a JIRA key, a 6-digit TMS number — is rejected (returns null) so we
|
|
90
|
+
* never send an unmatchable id that would create duplicates.
|
|
91
|
+
*
|
|
92
|
+
* @param {string|number|null|undefined} value
|
|
93
|
+
* @returns {string|null} The bare 8-char id, or null when the value is not a valid id
|
|
94
|
+
*/
|
|
95
|
+
normalizeTestId(value: string | number | null | undefined): string | null;
|
|
96
|
+
/**
|
|
97
|
+
* Recover a Testomat.io test id from the `@TmsLink("…")` annotation in test source.
|
|
98
|
+
*
|
|
99
|
+
* Allure does not emit link annotations for skipped (`@Ignore` / `@Disabled`) tests,
|
|
100
|
+
* so their results carry no `tms` link and `extractTestId` returns null — which makes
|
|
101
|
+
* the server create a duplicate case. The id still lives in the source, on the test
|
|
102
|
+
* method, so we read it from there as a fallback.
|
|
103
|
+
*
|
|
104
|
+
* The lookup is method-scoped: we locate the test method declaration by name, then
|
|
105
|
+
* scan upward across its annotation/comment block (the lines directly above it) for
|
|
106
|
+
* `@TmsLink`. Scanning stops at the first real code line so an unrelated method's
|
|
107
|
+
* annotation can never be picked up.
|
|
108
|
+
*
|
|
109
|
+
* @param {string} contents - full source file
|
|
110
|
+
* @param {object} test - converted test (uses `title`)
|
|
111
|
+
* @returns {string|null} normalized 8-char id, or null
|
|
112
|
+
*/
|
|
113
|
+
extractTmsIdFromSource(contents: string, test: object): string | null;
|
|
114
|
+
convertSteps(steps: any, depth?: number): any;
|
|
115
|
+
/**
|
|
116
|
+
* Check whether any step in the given (already converted) subtree already
|
|
117
|
+
* carries an `error`. Used to keep the failure message on the deepest failed
|
|
118
|
+
* step only, instead of repeating it on every ancestor in the failure chain.
|
|
119
|
+
*
|
|
120
|
+
* @param {Array<object>|undefined} steps - converted child steps
|
|
121
|
+
* @returns {boolean}
|
|
122
|
+
*/
|
|
123
|
+
subtreeHasError(steps: Array<object> | undefined): boolean;
|
|
124
|
+
/**
|
|
125
|
+
* Build the `error` payload for a failed step from its Allure `statusDetails`.
|
|
126
|
+
*
|
|
127
|
+
* Allure stores the failure message and stack trace (which includes the failing
|
|
128
|
+
* source line) in `statusDetails` on the step itself. We only surface it for
|
|
129
|
+
* failed/broken steps — passing or skipped steps carry no error. Returns null
|
|
130
|
+
* when there is no usable failure information so the field is omitted entirely.
|
|
131
|
+
*
|
|
132
|
+
* @param {object} step - Allure step
|
|
133
|
+
* @returns {{message: string, stack: string}|null}
|
|
134
|
+
*/
|
|
135
|
+
extractStepError(step: object): {
|
|
136
|
+
message: string;
|
|
137
|
+
stack: string;
|
|
138
|
+
} | null;
|
|
139
|
+
calculateRunTime(item: any): number;
|
|
140
|
+
convertParameters(parameters: any): {};
|
|
141
|
+
combineRetryAttempts(attempts: any): any;
|
|
142
|
+
calculateStats(): {};
|
|
143
|
+
fetchSourceCode(): void;
|
|
144
|
+
getLanguage(): any;
|
|
145
|
+
/**
|
|
146
|
+
* Fill in `test_id` for tests that have none by reading the `@TmsLink` annotation
|
|
147
|
+
* from their source. This rescues skipped (`@Ignore` / `@Disabled`) tests, whose
|
|
148
|
+
* Allure results drop the `@TmsLink` link and would otherwise create duplicate cases.
|
|
149
|
+
*
|
|
150
|
+
* Opt-in: only runs when `--java-tests` (a source root) is provided. The source
|
|
151
|
+
* files are indexed once by basename; for each id-less test we read the candidate
|
|
152
|
+
* file(s) matching its `testFile` / class name and parse the method's `@TmsLink`.
|
|
153
|
+
*/
|
|
154
|
+
recoverTestIdsFromSource(): void;
|
|
155
|
+
/**
|
|
156
|
+
* Build (and cache) a basename -> [absolute paths] index of source files under `root`.
|
|
157
|
+
* Walks synchronously, skipping common build/dependency directories.
|
|
158
|
+
*
|
|
159
|
+
* @param {string} root
|
|
160
|
+
* @returns {Map<string, string[]>}
|
|
161
|
+
*/
|
|
162
|
+
indexSourceFiles(root: string): Map<string, string[]>;
|
|
163
|
+
_sourceIndex: Map<any, any>;
|
|
164
|
+
/**
|
|
165
|
+
* Candidate source file paths for a test, looked up in the basename index.
|
|
166
|
+
* Uses the `testFile` meta label (e.g. `Foo.kt`) and the class name from `file`,
|
|
167
|
+
* trying both `.kt` and `.java` extensions. Ambiguity (same basename in several
|
|
168
|
+
* dirs) is harmless: only the file that actually declares the method will match.
|
|
169
|
+
*
|
|
170
|
+
* @param {object} t
|
|
171
|
+
* @param {Map<string, string[]>} index
|
|
172
|
+
* @returns {string[]}
|
|
173
|
+
*/
|
|
174
|
+
sourceCandidatesForTest(t: object, index: Map<string, string[]>): string[];
|
|
175
|
+
uploadArtifacts(): Promise<void>;
|
|
176
|
+
uploadData(): Promise<any[]>;
|
|
177
|
+
}
|
|
178
|
+
import { S3Uploader } from './uploader.js';
|