@kinora/reporter 0.1.3 → 0.3.0
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 +16 -30
- package/dist/index.d.mts +16 -1
- package/dist/index.mjs +44 -7
- package/package.json +10 -7
package/README.md
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
# @kinora/reporter
|
|
2
2
|
|
|
3
|
-
Playwright reporter that uploads your test results to a [kinora](https://github.com/
|
|
3
|
+
Playwright reporter that uploads your test results to a [kinora](https://github.com/Kinora-dev/kinora) server: pass rates, trends, flaky tests, and the full Playwright trace for failures, across projects and over time.
|
|
4
4
|
|
|
5
|
-
It runs on `onEnd`, posts the normalized run, then uploads the trace.zip for each
|
|
5
|
+
It runs on `onEnd`, posts the normalized run, then uploads the trace.zip for each test that produced one (videos and screenshots too, see below). Upload never fails your test run.
|
|
6
6
|
|
|
7
7
|
## Install
|
|
8
8
|
|
|
@@ -33,42 +33,28 @@ The token comes from the environment (keep it out of the config file); the serve
|
|
|
33
33
|
KINORA_TOKEN=<token> npx playwright test
|
|
34
34
|
```
|
|
35
35
|
|
|
36
|
-
Create an API token in the kinora dashboard (Settings → Workspace). Self-hosting? Point at your server with `KINORA_URL
|
|
36
|
+
Create an API token in the kinora dashboard (Settings → Workspace). Self-hosting? Point at your server with `KINORA_URL`.
|
|
37
37
|
|
|
38
|
-
##
|
|
38
|
+
## Videos and screenshots without tracing
|
|
39
|
+
|
|
40
|
+
Traces are uploaded by default, and Playwright already embeds a test's screenshots and video
|
|
41
|
+
inside its trace.zip. If you run without tracing, upload them on their own:
|
|
39
42
|
|
|
40
43
|
```ts
|
|
41
|
-
[
|
|
42
|
-
'
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
},
|
|
46
|
-
]
|
|
44
|
+
reporter: [['@kinora/reporter', {
|
|
45
|
+
project: { slug: 'web-app' },
|
|
46
|
+
uploadAttachments: ['trace', 'video', 'screenshot'],
|
|
47
|
+
}]]
|
|
47
48
|
```
|
|
48
49
|
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
| `project` | `{ slug: string, name?: string }` | required | Target project. `name` defaults to `slug`. |
|
|
52
|
-
| `url` | `string` | env `KINORA_URL`, then cloud | kinora server base URL. Set for self-host. |
|
|
53
|
-
| `token` | `string` | env `KINORA_TOKEN` | Project API token. Prefer the env var. |
|
|
54
|
-
| `git` | `{ sha?, branch?, repoUrl? }` | auto on GitHub Actions | Git metadata. `repoUrl` links a sha to its commit. |
|
|
55
|
-
| `ci` | `{ provider?, runUrl?, runNumber? }` | auto on GitHub Actions | CI metadata for the run. |
|
|
56
|
-
|
|
57
|
-
On GitHub Actions, `git` and `ci` are filled from the standard `GITHUB_*` env vars (including the repo URL, so shas link to their commit in the dashboard). Pass them explicitly on other CI providers.
|
|
50
|
+
Kinora uploads whatever Playwright attached, so `screenshot: 'only-on-failure'` and
|
|
51
|
+
`video: 'retain-on-failure'` keep deciding what exists in the first place.
|
|
58
52
|
|
|
59
|
-
##
|
|
60
|
-
|
|
61
|
-
```yaml
|
|
62
|
-
- run: npx playwright test
|
|
63
|
-
env:
|
|
64
|
-
KINORA_TOKEN: ${{ secrets.KINORA_TOKEN }}
|
|
65
|
-
```
|
|
53
|
+
## Documentation
|
|
66
54
|
|
|
67
|
-
|
|
55
|
+
Full reporter options, GitHub PR comments, CI examples, and self-hosting are in the docs:
|
|
68
56
|
|
|
69
|
-
|
|
70
|
-
- Traces are uploaded only for tests that produced one, so enable `trace` in your Playwright config (`on-first-retry`, `retain-on-failure`, etc.).
|
|
71
|
-
- Cross-run test identity is the file + title path + Playwright project name, so history stays stable as long as those don't change.
|
|
57
|
+
**https://docs.kinora.dev/guides/reporter/**
|
|
72
58
|
|
|
73
59
|
## License
|
|
74
60
|
|
package/dist/index.d.mts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { CiMeta, GitMeta } from "@kinora/core";
|
|
1
|
+
import { AttachmentKind, CiMeta, GitMeta } from "@kinora/core";
|
|
2
2
|
import { FullConfig, FullResult, Reporter, Suite } from "@playwright/test/reporter";
|
|
3
3
|
|
|
4
4
|
//#region src/index.d.ts
|
|
@@ -14,6 +14,20 @@ interface KinoraReporterOptions {
|
|
|
14
14
|
};
|
|
15
15
|
git?: GitMeta;
|
|
16
16
|
ci?: CiMeta;
|
|
17
|
+
/**
|
|
18
|
+
* Post/update a summary comment on the GitHub PR (uses the job's GITHUB_TOKEN; needs
|
|
19
|
+
* `permissions: pull-requests: write`). `label` distinguishes matrix legs sharing one PR.
|
|
20
|
+
*/
|
|
21
|
+
prComment?: boolean | {
|
|
22
|
+
label?: string;
|
|
23
|
+
policy?: 'always' | 'on-failure';
|
|
24
|
+
};
|
|
25
|
+
/**
|
|
26
|
+
* Which attachment kinds to upload. Defaults to `['trace']`. Add `'video'` / `'screenshot'`
|
|
27
|
+
* to host them on their own, which is what you want when `trace` is off (with tracing on,
|
|
28
|
+
* Playwright already embeds them in the trace.zip).
|
|
29
|
+
*/
|
|
30
|
+
uploadAttachments?: AttachmentKind[];
|
|
17
31
|
}
|
|
18
32
|
declare class KinoraReporter implements Reporter {
|
|
19
33
|
private readonly options;
|
|
@@ -22,6 +36,7 @@ declare class KinoraReporter implements Reporter {
|
|
|
22
36
|
constructor(options: KinoraReporterOptions);
|
|
23
37
|
onBegin(config: FullConfig, suite: Suite): void;
|
|
24
38
|
onEnd(result: FullResult): Promise<void>;
|
|
39
|
+
private maybePostPrComment;
|
|
25
40
|
}
|
|
26
41
|
//#endregion
|
|
27
42
|
export { KinoraReporterOptions, KinoraReporter as default };
|
package/dist/index.mjs
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
|
+
import { readFileSync } from "node:fs";
|
|
1
2
|
import { readFile } from "node:fs/promises";
|
|
2
3
|
import process from "node:process";
|
|
3
|
-
import { DEFAULT_KINORA_URL, IngestError, createIngestClient, effectiveAttachments,
|
|
4
|
+
import { DEFAULT_KINORA_URL, DEFAULT_UPLOAD_ATTACHMENTS, IngestError, createIngestClient, effectiveAttachments, isUploadableAttachment, makeTestKey, postPrComment, resolvePrContext } from "@kinora/core";
|
|
4
5
|
//#region src/index.ts
|
|
5
6
|
function identity(test) {
|
|
6
7
|
let file = "";
|
|
@@ -70,12 +71,14 @@ function countsOf(tests) {
|
|
|
70
71
|
function detectGit() {
|
|
71
72
|
const sha = process.env.GITHUB_SHA;
|
|
72
73
|
const branch = process.env.GITHUB_REF_NAME;
|
|
74
|
+
const baseBranch = process.env.GITHUB_BASE_REF || void 0;
|
|
73
75
|
const { GITHUB_SERVER_URL, GITHUB_REPOSITORY } = process.env;
|
|
74
76
|
const repoUrl = GITHUB_SERVER_URL && GITHUB_REPOSITORY ? `${GITHUB_SERVER_URL}/${GITHUB_REPOSITORY}` : void 0;
|
|
75
77
|
if (!sha && !branch && !repoUrl) return void 0;
|
|
76
78
|
return {
|
|
77
79
|
sha,
|
|
78
80
|
branch,
|
|
81
|
+
baseBranch,
|
|
79
82
|
repoUrl
|
|
80
83
|
};
|
|
81
84
|
}
|
|
@@ -125,12 +128,14 @@ var KinoraReporter = class {
|
|
|
125
128
|
try {
|
|
126
129
|
const client = createIngestClient({
|
|
127
130
|
baseUrl: url,
|
|
128
|
-
token
|
|
131
|
+
token,
|
|
132
|
+
regression: !!this.options.prComment
|
|
129
133
|
});
|
|
130
134
|
const res = await client.uploadRun(payload);
|
|
131
|
-
|
|
135
|
+
const kinds = this.options.uploadAttachments ?? DEFAULT_UPLOAD_ATTACHMENTS;
|
|
136
|
+
let artifacts = 0;
|
|
132
137
|
for (const t of tests) for (const a of t.attachments) {
|
|
133
|
-
if (!a.path || !
|
|
138
|
+
if (!a.path || !isUploadableAttachment(a, kinds)) continue;
|
|
134
139
|
try {
|
|
135
140
|
await client.uploadArtifact({
|
|
136
141
|
runId: res.runId,
|
|
@@ -139,17 +144,49 @@ var KinoraReporter = class {
|
|
|
139
144
|
contentType: a.contentType,
|
|
140
145
|
body: await readFile(a.path)
|
|
141
146
|
});
|
|
142
|
-
|
|
147
|
+
artifacts++;
|
|
143
148
|
} catch (err) {
|
|
144
|
-
console.warn(`[kinora]
|
|
149
|
+
console.warn(`[kinora] ${a.name} upload failed for ${t.testKey}:`, err instanceof Error ? err.message : err);
|
|
145
150
|
}
|
|
146
151
|
}
|
|
147
|
-
console.log(`[kinora] uploaded ${res.tests} tests + ${
|
|
152
|
+
console.log(`[kinora] uploaded ${res.tests} tests + ${artifacts} artifacts (run ${res.runId})`);
|
|
153
|
+
await this.maybePostPrComment(payload, res);
|
|
148
154
|
} catch (err) {
|
|
149
155
|
if (err instanceof IngestError && err.status === 402) console.warn(`[kinora] ${err.message}`);
|
|
150
156
|
else console.error(`[kinora] upload failed:`, err instanceof Error ? err.message : err);
|
|
151
157
|
}
|
|
152
158
|
}
|
|
159
|
+
async maybePostPrComment(payload, res) {
|
|
160
|
+
if (!this.options.prComment) return;
|
|
161
|
+
const opt = this.options.prComment;
|
|
162
|
+
const label = typeof opt === "object" ? opt.label : void 0;
|
|
163
|
+
const policy = typeof opt === "object" ? opt.policy : void 0;
|
|
164
|
+
try {
|
|
165
|
+
const ctx = resolvePrContext(this.config?.shard ? {
|
|
166
|
+
...process.env,
|
|
167
|
+
__KINORA_IS_SHARD: "1"
|
|
168
|
+
} : process.env, (p) => {
|
|
169
|
+
try {
|
|
170
|
+
return readFileSync(p, "utf8");
|
|
171
|
+
} catch {
|
|
172
|
+
return;
|
|
173
|
+
}
|
|
174
|
+
});
|
|
175
|
+
if (!ctx) return;
|
|
176
|
+
const outcome = await postPrComment(ctx, {
|
|
177
|
+
projectSlug: payload.project.slug,
|
|
178
|
+
projectName: payload.project.name,
|
|
179
|
+
label,
|
|
180
|
+
runUrl: res.runUrl,
|
|
181
|
+
ciRunUrl: payload.run.ci?.runUrl,
|
|
182
|
+
counts: payload.run.counts,
|
|
183
|
+
regression: res.regression
|
|
184
|
+
}, policy ?? "always");
|
|
185
|
+
if (outcome !== "skipped") console.warn(`[kinora] PR comment ${outcome}`);
|
|
186
|
+
} catch (err) {
|
|
187
|
+
console.warn("[kinora] PR comment failed:", err instanceof Error ? err.message : err);
|
|
188
|
+
}
|
|
189
|
+
}
|
|
153
190
|
};
|
|
154
191
|
//#endregion
|
|
155
192
|
export { KinoraReporter as default };
|
package/package.json
CHANGED
|
@@ -1,17 +1,17 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@kinora/reporter",
|
|
3
3
|
"type": "module",
|
|
4
|
-
"version": "0.
|
|
4
|
+
"version": "0.3.0",
|
|
5
5
|
"description": "Playwright reporter that uploads test results to a kinora server",
|
|
6
6
|
"author": "Joris Gallot",
|
|
7
7
|
"license": "MIT",
|
|
8
|
-
"homepage": "https://github.com/
|
|
8
|
+
"homepage": "https://github.com/Kinora-dev/kinora#readme",
|
|
9
9
|
"repository": {
|
|
10
10
|
"type": "git",
|
|
11
|
-
"url": "git+https://github.com/
|
|
11
|
+
"url": "git+https://github.com/Kinora-dev/kinora.git",
|
|
12
12
|
"directory": "packages/reporter"
|
|
13
13
|
},
|
|
14
|
-
"bugs": "https://github.com/
|
|
14
|
+
"bugs": "https://github.com/Kinora-dev/kinora/issues",
|
|
15
15
|
"keywords": [
|
|
16
16
|
"playwright",
|
|
17
17
|
"reporter",
|
|
@@ -36,16 +36,19 @@
|
|
|
36
36
|
"@playwright/test": ">=1.40.0"
|
|
37
37
|
},
|
|
38
38
|
"dependencies": {
|
|
39
|
-
"@kinora/core": "0.
|
|
39
|
+
"@kinora/core": "0.3.0"
|
|
40
40
|
},
|
|
41
41
|
"devDependencies": {
|
|
42
42
|
"@playwright/test": "^1.61.0",
|
|
43
43
|
"@types/node": "^26.0.0",
|
|
44
|
+
"@vitest/coverage-v8": "^4.1.9",
|
|
44
45
|
"tsdown": "^0.22.3",
|
|
45
|
-
"typescript": "~6.0.3"
|
|
46
|
+
"typescript": "~6.0.3",
|
|
47
|
+
"vitest": "^4.1.9"
|
|
46
48
|
},
|
|
47
49
|
"scripts": {
|
|
48
50
|
"build": "tsdown",
|
|
49
|
-
"typecheck": "tsc --noEmit"
|
|
51
|
+
"typecheck": "tsc --noEmit",
|
|
52
|
+
"test": "vitest run --coverage"
|
|
50
53
|
}
|
|
51
54
|
}
|