@testomatio/reporter 2.9.3-beta-vitestmeta-2 → 2.9.3-beta.3-allure-links
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 +208 -0
- package/lib/allureReader.js +840 -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 -39
- 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 +934 -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 -41
- 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,208 @@
|
|
|
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 all Testomat.io test ids from Allure links so reported results match
|
|
70
|
+
* existing cases instead of creating duplicates.
|
|
71
|
+
*
|
|
72
|
+
* A test may carry several `@TmsLink`s (e.g. `@TmsLinks(@TmsLink("…"), @TmsLink("…"))`),
|
|
73
|
+
* each producing a link with `type: "tms"`. Some exporters omit the type but still point
|
|
74
|
+
* the link URL at a Testomat.io test page; both are accepted. The link `name` is used as
|
|
75
|
+
* the id (falling back to the id segment of a Testomat.io URL). Ids are normalized and
|
|
76
|
+
* de-duplicated, preserving order so the first one stays the primary `test_id`.
|
|
77
|
+
*
|
|
78
|
+
* @param {object} result - Parsed Allure result JSON
|
|
79
|
+
* @returns {string[]} Normalized test ids (possibly empty)
|
|
80
|
+
*/
|
|
81
|
+
extractTmsIds(result: object): string[];
|
|
82
|
+
/**
|
|
83
|
+
* The primary Testomat.io test id (the first `@TmsLink`), or null when there is none.
|
|
84
|
+
*
|
|
85
|
+
* @param {object} result - Parsed Allure result JSON
|
|
86
|
+
* @returns {string|null}
|
|
87
|
+
*/
|
|
88
|
+
extractTestId(result: object): string | null;
|
|
89
|
+
/**
|
|
90
|
+
* Attach additional linked test cases to a reported test as `{ test: id }` link entries
|
|
91
|
+
* (the same shape `linkTest()` uses), so a single result updates every case it is linked
|
|
92
|
+
* to — not just the primary `test_id`. Existing links are preserved; duplicates skipped.
|
|
93
|
+
*
|
|
94
|
+
* @param {object} test - converted test
|
|
95
|
+
* @param {string[]} ids - additional test ids to link
|
|
96
|
+
*/
|
|
97
|
+
addLinkedTestIds(test: object, ids: string[]): void;
|
|
98
|
+
/**
|
|
99
|
+
* Normalize a value into a Testomat.io test id.
|
|
100
|
+
*
|
|
101
|
+
* Testomat.io test ids are exactly **8 word characters**. The value may arrive bare
|
|
102
|
+
* (`1a2b3c4d`), or carrying the `T` / `@T` markers Testomat uses in code and titles
|
|
103
|
+
* (`T1a2b3c4d`, `@T1a2b3c4d`). The markers are removed only when doing so still leaves
|
|
104
|
+
* a valid 8-char id, so a real id that happens to start with `T` is preserved.
|
|
105
|
+
*
|
|
106
|
+
* Anything that does not resolve to a valid 8-char id — a numeric Allure TestOps id
|
|
107
|
+
* like `12345`, a JIRA key, a 6-digit TMS number — is rejected (returns null) so we
|
|
108
|
+
* never send an unmatchable id that would create duplicates.
|
|
109
|
+
*
|
|
110
|
+
* @param {string|number|null|undefined} value
|
|
111
|
+
* @returns {string|null} The bare 8-char id, or null when the value is not a valid id
|
|
112
|
+
*/
|
|
113
|
+
normalizeTestId(value: string | number | null | undefined): string | null;
|
|
114
|
+
/**
|
|
115
|
+
* Recover a Testomat.io test id from the `@TmsLink("…")` annotation in test source.
|
|
116
|
+
*
|
|
117
|
+
* Allure does not emit link annotations for skipped (`@Ignore` / `@Disabled`) tests,
|
|
118
|
+
* so their results carry no `tms` link and `extractTestId` returns null — which makes
|
|
119
|
+
* the server create a duplicate case. The id still lives in the source, on the test
|
|
120
|
+
* method, so we read it from there as a fallback.
|
|
121
|
+
*
|
|
122
|
+
* The lookup is method-scoped: we locate the test method declaration by name, collect
|
|
123
|
+
* its annotation/comment block (the lines directly above it, up to the previous code
|
|
124
|
+
* construct) and read every `@TmsLink` from it — so an unrelated method's annotation can
|
|
125
|
+
* never be picked up. Both `@TmsLink("…")` and the container forms
|
|
126
|
+
* `@TmsLinks(@TmsLink("…"), @TmsLink("…"))` (single- or multi-line) are supported.
|
|
127
|
+
*
|
|
128
|
+
* @param {string} contents - full source file
|
|
129
|
+
* @param {object} test - converted test (uses `title`)
|
|
130
|
+
* @returns {string[]} normalized 8-char ids in source order (possibly empty)
|
|
131
|
+
*/
|
|
132
|
+
extractTmsIdsFromSource(contents: string, test: object): string[];
|
|
133
|
+
/**
|
|
134
|
+
* The primary `@TmsLink` id for a test method in source, or null. Convenience wrapper
|
|
135
|
+
* around {@link extractTmsIdsFromSource}.
|
|
136
|
+
*
|
|
137
|
+
* @param {string} contents - full source file
|
|
138
|
+
* @param {object} test - converted test (uses `title`)
|
|
139
|
+
* @returns {string|null}
|
|
140
|
+
*/
|
|
141
|
+
extractTmsIdFromSource(contents: string, test: object): string | null;
|
|
142
|
+
convertSteps(steps: any, depth?: number): any;
|
|
143
|
+
/**
|
|
144
|
+
* Check whether any step in the given (already converted) subtree already
|
|
145
|
+
* carries an `error`. Used to keep the failure message on the deepest failed
|
|
146
|
+
* step only, instead of repeating it on every ancestor in the failure chain.
|
|
147
|
+
*
|
|
148
|
+
* @param {Array<object>|undefined} steps - converted child steps
|
|
149
|
+
* @returns {boolean}
|
|
150
|
+
*/
|
|
151
|
+
subtreeHasError(steps: Array<object> | undefined): boolean;
|
|
152
|
+
/**
|
|
153
|
+
* Build the `error` payload for a failed step from its Allure `statusDetails`.
|
|
154
|
+
*
|
|
155
|
+
* Allure stores the failure message and stack trace (which includes the failing
|
|
156
|
+
* source line) in `statusDetails` on the step itself. We only surface it for
|
|
157
|
+
* failed/broken steps — passing or skipped steps carry no error. Returns null
|
|
158
|
+
* when there is no usable failure information so the field is omitted entirely.
|
|
159
|
+
*
|
|
160
|
+
* @param {object} step - Allure step
|
|
161
|
+
* @returns {{message: string, stack: string}|null}
|
|
162
|
+
*/
|
|
163
|
+
extractStepError(step: object): {
|
|
164
|
+
message: string;
|
|
165
|
+
stack: string;
|
|
166
|
+
} | null;
|
|
167
|
+
calculateRunTime(item: any): number;
|
|
168
|
+
convertParameters(parameters: any): {};
|
|
169
|
+
combineRetryAttempts(attempts: any): any;
|
|
170
|
+
calculateStats(): {};
|
|
171
|
+
fetchSourceCode(): void;
|
|
172
|
+
getLanguage(): any;
|
|
173
|
+
/**
|
|
174
|
+
* Link tests to their Testomat.io cases by reading `@TmsLink` from source for tests that
|
|
175
|
+
* carry no `{ test: … }` link yet. This rescues skipped (`@Ignore` / `@Disabled`) tests,
|
|
176
|
+
* whose Allure results drop the `@TmsLink` link and would otherwise create duplicate
|
|
177
|
+
* cases. Every `@TmsLink` on the method is linked (consistent with executed tests); the
|
|
178
|
+
* `test_id` is left untouched — links and test_id are separate concerns.
|
|
179
|
+
*
|
|
180
|
+
* Opt-in: only runs when `--java-tests` (a source root) is provided. The source files are
|
|
181
|
+
* indexed once by basename; for each not-yet-linked test we read the candidate file(s)
|
|
182
|
+
* matching its `testFile` / class name and parse the method's `@TmsLink`(s).
|
|
183
|
+
*/
|
|
184
|
+
recoverTmsLinksFromSource(): void;
|
|
185
|
+
/**
|
|
186
|
+
* Build (and cache) a basename -> [absolute paths] index of source files under `root`.
|
|
187
|
+
* Walks synchronously, skipping common build/dependency directories.
|
|
188
|
+
*
|
|
189
|
+
* @param {string} root
|
|
190
|
+
* @returns {Map<string, string[]>}
|
|
191
|
+
*/
|
|
192
|
+
indexSourceFiles(root: string): Map<string, string[]>;
|
|
193
|
+
_sourceIndex: Map<any, any>;
|
|
194
|
+
/**
|
|
195
|
+
* Candidate source file paths for a test, looked up in the basename index.
|
|
196
|
+
* Uses the `testFile` meta label (e.g. `Foo.kt`) and the class name from `file`,
|
|
197
|
+
* trying both `.kt` and `.java` extensions. Ambiguity (same basename in several
|
|
198
|
+
* dirs) is harmless: only the file that actually declares the method will match.
|
|
199
|
+
*
|
|
200
|
+
* @param {object} t
|
|
201
|
+
* @param {Map<string, string[]>} index
|
|
202
|
+
* @returns {string[]}
|
|
203
|
+
*/
|
|
204
|
+
sourceCandidatesForTest(t: object, index: Map<string, string[]>): string[];
|
|
205
|
+
uploadArtifacts(): Promise<void>;
|
|
206
|
+
uploadData(): Promise<any[]>;
|
|
207
|
+
}
|
|
208
|
+
import { S3Uploader } from './uploader.js';
|