@kinora/reporter 0.2.0 → 0.4.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 +15 -55
- package/dist/index.d.mts +7 -2
- package/dist/index.mjs +13 -33
- package/package.json +10 -10
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,68 +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
39
|
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
'@kinora/reporter',
|
|
43
|
-
{
|
|
44
|
-
project: { slug: 'web-app', name: 'Web App' },
|
|
45
|
-
},
|
|
46
|
-
]
|
|
47
|
-
```
|
|
48
|
-
|
|
49
|
-
| Option | Type | Default | Description |
|
|
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?, baseBranch?, repoUrl? }` | auto on GitHub Actions | Git metadata. `repoUrl` links a sha to its commit; `baseBranch` powers "regression vs base" in the PR comment. |
|
|
55
|
-
| `ci` | `{ provider?, runUrl?, runNumber? }` | auto on GitHub Actions | CI metadata for the run. |
|
|
56
|
-
| `prComment` | `boolean \| { label?, policy? }` | off | Post/update a summary comment on the GitHub PR (see below). |
|
|
57
|
-
|
|
58
|
-
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.
|
|
59
|
-
|
|
60
|
-
## GitHub PR comment
|
|
61
|
-
|
|
62
|
-
On a `pull_request` run, the reporter can post (and keep updating) a summary comment on the PR: pass/fail counts, tests newly failing vs the base branch, and a link to the run. It uses the job's ambient `GITHUB_TOKEN`, so no credentials are stored in kinora.
|
|
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:
|
|
63
42
|
|
|
64
43
|
```ts
|
|
65
|
-
reporter: [['@kinora/reporter', {
|
|
44
|
+
reporter: [['@kinora/reporter', {
|
|
45
|
+
project: { slug: 'web-app' },
|
|
46
|
+
uploadAttachments: ['trace', 'video', 'screenshot'],
|
|
47
|
+
}]]
|
|
66
48
|
```
|
|
67
49
|
|
|
68
|
-
|
|
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.
|
|
69
52
|
|
|
70
|
-
|
|
71
|
-
# in your workflow job:
|
|
72
|
-
permissions:
|
|
73
|
-
pull-requests: write # required for the PR comment
|
|
74
|
-
steps:
|
|
75
|
-
- run: npx playwright test
|
|
76
|
-
env:
|
|
77
|
-
KINORA_TOKEN: ${{ secrets.KINORA_TOKEN }}
|
|
78
|
-
```
|
|
79
|
-
|
|
80
|
-
- Same-repo PRs only: `GITHUB_TOKEN` is read-only on fork PRs, so the comment is skipped there.
|
|
81
|
-
- Shard your run with `merge-reports` (blob report) so one merged run posts one comment; per-shard runs are skipped.
|
|
82
|
-
- Matrix builds sharing a PR: set `prComment: { label: 'node20' }` so each leg keeps its own comment.
|
|
83
|
-
- `policy: 'on-failure'` skips the comment on green runs (default posts always).
|
|
84
|
-
|
|
85
|
-
## CI example (GitHub Actions)
|
|
86
|
-
|
|
87
|
-
```yaml
|
|
88
|
-
- run: npx playwright test
|
|
89
|
-
env:
|
|
90
|
-
KINORA_TOKEN: ${{ secrets.KINORA_TOKEN }}
|
|
91
|
-
```
|
|
53
|
+
## Documentation
|
|
92
54
|
|
|
93
|
-
|
|
55
|
+
Full reporter options, GitHub PR comments, CI examples, and self-hosting are in the docs:
|
|
94
56
|
|
|
95
|
-
|
|
96
|
-
- Traces are uploaded only for tests that produced one, so enable `trace` in your Playwright config (`on-first-retry`, `retain-on-failure`, etc.).
|
|
97
|
-
- 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/**
|
|
98
58
|
|
|
99
59
|
## License
|
|
100
60
|
|
package/dist/index.d.mts
CHANGED
|
@@ -1,6 +1,5 @@
|
|
|
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
|
-
|
|
4
3
|
//#region src/index.d.ts
|
|
5
4
|
interface KinoraReporterOptions {
|
|
6
5
|
/** kinora server base URL. Defaults to env KINORA_URL, then the hosted cloud. Set for self-host. */
|
|
@@ -22,6 +21,12 @@ interface KinoraReporterOptions {
|
|
|
22
21
|
label?: string;
|
|
23
22
|
policy?: 'always' | 'on-failure';
|
|
24
23
|
};
|
|
24
|
+
/**
|
|
25
|
+
* Which attachment kinds to upload. Defaults to `['trace']`. Add `'video'` / `'screenshot'`
|
|
26
|
+
* to host them on their own, which is what you want when `trace` is off (with tracing on,
|
|
27
|
+
* Playwright already embeds them in the trace.zip).
|
|
28
|
+
*/
|
|
29
|
+
uploadAttachments?: AttachmentKind[];
|
|
25
30
|
}
|
|
26
31
|
declare class KinoraReporter implements Reporter {
|
|
27
32
|
private readonly options;
|
package/dist/index.mjs
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { readFileSync } from "node:fs";
|
|
2
2
|
import { readFile } from "node:fs/promises";
|
|
3
3
|
import process from "node:process";
|
|
4
|
-
import { DEFAULT_KINORA_URL, IngestError, createIngestClient, effectiveAttachments,
|
|
4
|
+
import { DEFAULT_KINORA_URL, DEFAULT_UPLOAD_ATTACHMENTS, IngestError, createIngestClient, detectCiEnv, effectiveAttachments, isUploadableAttachment, makeTestKey, postPrComment, resolvePrContext } from "@kinora/core";
|
|
5
5
|
//#region src/index.ts
|
|
6
6
|
function identity(test) {
|
|
7
7
|
let file = "";
|
|
@@ -68,29 +68,6 @@ function countsOf(tests) {
|
|
|
68
68
|
for (const t of tests) counts[t.status]++;
|
|
69
69
|
return counts;
|
|
70
70
|
}
|
|
71
|
-
function detectGit() {
|
|
72
|
-
const sha = process.env.GITHUB_SHA;
|
|
73
|
-
const branch = process.env.GITHUB_REF_NAME;
|
|
74
|
-
const baseBranch = process.env.GITHUB_BASE_REF || void 0;
|
|
75
|
-
const { GITHUB_SERVER_URL, GITHUB_REPOSITORY } = process.env;
|
|
76
|
-
const repoUrl = GITHUB_SERVER_URL && GITHUB_REPOSITORY ? `${GITHUB_SERVER_URL}/${GITHUB_REPOSITORY}` : void 0;
|
|
77
|
-
if (!sha && !branch && !repoUrl) return void 0;
|
|
78
|
-
return {
|
|
79
|
-
sha,
|
|
80
|
-
branch,
|
|
81
|
-
baseBranch,
|
|
82
|
-
repoUrl
|
|
83
|
-
};
|
|
84
|
-
}
|
|
85
|
-
function detectCi() {
|
|
86
|
-
if (!process.env.GITHUB_ACTIONS) return void 0;
|
|
87
|
-
const { GITHUB_SERVER_URL, GITHUB_REPOSITORY, GITHUB_RUN_ID, GITHUB_RUN_NUMBER } = process.env;
|
|
88
|
-
return {
|
|
89
|
-
provider: "github",
|
|
90
|
-
runUrl: GITHUB_SERVER_URL && GITHUB_REPOSITORY && GITHUB_RUN_ID ? `${GITHUB_SERVER_URL}/${GITHUB_REPOSITORY}/actions/runs/${GITHUB_RUN_ID}` : void 0,
|
|
91
|
-
runNumber: GITHUB_RUN_NUMBER
|
|
92
|
-
};
|
|
93
|
-
}
|
|
94
71
|
var KinoraReporter = class {
|
|
95
72
|
options;
|
|
96
73
|
suite;
|
|
@@ -110,6 +87,7 @@ var KinoraReporter = class {
|
|
|
110
87
|
return;
|
|
111
88
|
}
|
|
112
89
|
const tests = (this.suite?.allTests() ?? []).map(toNormTest);
|
|
90
|
+
const detected = detectCiEnv(process.env);
|
|
113
91
|
const payload = {
|
|
114
92
|
project: {
|
|
115
93
|
slug: this.options.project.slug,
|
|
@@ -120,8 +98,8 @@ var KinoraReporter = class {
|
|
|
120
98
|
duration: result.duration,
|
|
121
99
|
counts: countsOf(tests),
|
|
122
100
|
playwrightVersion: this.config?.version,
|
|
123
|
-
git: this.options.git ??
|
|
124
|
-
ci: this.options.ci ??
|
|
101
|
+
git: this.options.git ?? detected.git,
|
|
102
|
+
ci: this.options.ci ?? detected.ci
|
|
125
103
|
},
|
|
126
104
|
tests
|
|
127
105
|
};
|
|
@@ -132,9 +110,10 @@ var KinoraReporter = class {
|
|
|
132
110
|
regression: !!this.options.prComment
|
|
133
111
|
});
|
|
134
112
|
const res = await client.uploadRun(payload);
|
|
135
|
-
|
|
113
|
+
const kinds = this.options.uploadAttachments ?? DEFAULT_UPLOAD_ATTACHMENTS;
|
|
114
|
+
let artifacts = 0;
|
|
136
115
|
for (const t of tests) for (const a of t.attachments) {
|
|
137
|
-
if (!a.path || !
|
|
116
|
+
if (!a.path || !isUploadableAttachment(a, kinds)) continue;
|
|
138
117
|
try {
|
|
139
118
|
await client.uploadArtifact({
|
|
140
119
|
runId: res.runId,
|
|
@@ -143,12 +122,12 @@ var KinoraReporter = class {
|
|
|
143
122
|
contentType: a.contentType,
|
|
144
123
|
body: await readFile(a.path)
|
|
145
124
|
});
|
|
146
|
-
|
|
125
|
+
artifacts++;
|
|
147
126
|
} catch (err) {
|
|
148
|
-
console.warn(`[kinora]
|
|
127
|
+
console.warn(`[kinora] ${a.name} upload failed for ${t.testKey}:`, err instanceof Error ? err.message : err);
|
|
149
128
|
}
|
|
150
129
|
}
|
|
151
|
-
console.log(`[kinora] uploaded ${res.tests} tests + ${
|
|
130
|
+
console.log(`[kinora] uploaded ${res.tests} tests + ${artifacts} artifacts (run ${res.runId})`);
|
|
152
131
|
await this.maybePostPrComment(payload, res);
|
|
153
132
|
} catch (err) {
|
|
154
133
|
if (err instanceof IngestError && err.status === 402) console.warn(`[kinora] ${err.message}`);
|
|
@@ -161,10 +140,11 @@ var KinoraReporter = class {
|
|
|
161
140
|
const label = typeof opt === "object" ? opt.label : void 0;
|
|
162
141
|
const policy = typeof opt === "object" ? opt.policy : void 0;
|
|
163
142
|
try {
|
|
164
|
-
const
|
|
143
|
+
const env = this.config?.shard ? {
|
|
165
144
|
...process.env,
|
|
166
145
|
__KINORA_IS_SHARD: "1"
|
|
167
|
-
} : process.env
|
|
146
|
+
} : process.env;
|
|
147
|
+
const ctx = resolvePrContext(env, (p) => {
|
|
168
148
|
try {
|
|
169
149
|
return readFileSync(p, "utf8");
|
|
170
150
|
} catch {
|
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.4.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,15 +36,15 @@
|
|
|
36
36
|
"@playwright/test": ">=1.40.0"
|
|
37
37
|
},
|
|
38
38
|
"dependencies": {
|
|
39
|
-
"@kinora/core": "0.
|
|
39
|
+
"@kinora/core": "0.4.0"
|
|
40
40
|
},
|
|
41
41
|
"devDependencies": {
|
|
42
|
-
"@playwright/test": "^1.
|
|
43
|
-
"@types/node": "^26.
|
|
44
|
-
"@vitest/coverage-v8": "^4.1.
|
|
45
|
-
"tsdown": "^0.22.
|
|
42
|
+
"@playwright/test": "^1.63.0",
|
|
43
|
+
"@types/node": "^26.5.1",
|
|
44
|
+
"@vitest/coverage-v8": "^4.1.11",
|
|
45
|
+
"tsdown": "^0.22.14",
|
|
46
46
|
"typescript": "~6.0.3",
|
|
47
|
-
"vitest": "^4.1.
|
|
47
|
+
"vitest": "^4.1.11"
|
|
48
48
|
},
|
|
49
49
|
"scripts": {
|
|
50
50
|
"build": "tsdown",
|