@e2edev/github 0.1.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/LICENSE +21 -0
- package/README.md +32 -0
- package/dist/actions.d.ts +47 -0
- package/dist/actions.d.ts.map +1 -0
- package/dist/actions.js +90 -0
- package/dist/actions.js.map +1 -0
- package/dist/comment.d.ts +29 -0
- package/dist/comment.d.ts.map +1 -0
- package/dist/comment.js +209 -0
- package/dist/comment.js.map +1 -0
- package/dist/index.d.ts +11 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +9 -0
- package/dist/index.js.map +1 -0
- package/dist/json.d.ts +3 -0
- package/dist/json.d.ts.map +1 -0
- package/dist/json.js +5 -0
- package/dist/json.js.map +1 -0
- package/dist/post.d.ts +25 -0
- package/dist/post.d.ts.map +1 -0
- package/dist/post.js +69 -0
- package/dist/post.js.map +1 -0
- package/dist/reporter.d.ts +33 -0
- package/dist/reporter.d.ts.map +1 -0
- package/dist/reporter.js +107 -0
- package/dist/reporter.js.map +1 -0
- package/package.json +64 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 TesterArmy, Inc.
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
# @e2edev/github
|
|
2
|
+
|
|
3
|
+
The GitHub reporter for [`@e2edev/e2e`](https://www.npmjs.com/package/@e2edev/e2e).
|
|
4
|
+
From GitHub Actions, every run becomes one pull request comment, edited in
|
|
5
|
+
place on reruns, and the same text lands in the job summary.
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
npm install --save-dev @e2edev/github
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
```ts title="e2e.config.ts"
|
|
12
|
+
import type { E2EConfig } from '@e2edev/e2e';
|
|
13
|
+
import { playwright } from '@e2edev/playwright';
|
|
14
|
+
import { github } from '@e2edev/github';
|
|
15
|
+
|
|
16
|
+
export default {
|
|
17
|
+
targets: [{ engine: playwright({ url: 'http://localhost:3000' }) }],
|
|
18
|
+
reporters: ['list', github()],
|
|
19
|
+
} satisfies E2EConfig;
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
The job needs `permissions: pull-requests: write` and the step that runs
|
|
23
|
+
`e2e` needs `GITHUB_TOKEN: ${{ github.token }}` in its `env`. Matrix replicas
|
|
24
|
+
of one job pass a `key` so their comments stay apart. When the reporter cannot
|
|
25
|
+
post (another CI, a push, a fork's read-only token) it says why in one summary
|
|
26
|
+
row and never changes the run's exit code.
|
|
27
|
+
|
|
28
|
+
Full documentation lives at [e2e-docs.vercel.app/github](https://e2e-docs.vercel.app/github).
|
|
29
|
+
|
|
30
|
+
## License
|
|
31
|
+
|
|
32
|
+
MIT
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* What GitHub Actions tells a job about itself, read from the environment
|
|
3
|
+
* the runner sets: the repository, the pull request the job runs for, the
|
|
4
|
+
* commit to link sources at, the token, and where the job summary is written.
|
|
5
|
+
*
|
|
6
|
+
* The pull request number comes from the event payload first, since
|
|
7
|
+
* `pull_request`, `pull_request_target`, and a PR's `issue_comment` all carry
|
|
8
|
+
* it there, and from `GITHUB_REF` (`refs/pull/N/merge`) when the payload has
|
|
9
|
+
* none. Nothing here is read until the run has finished.
|
|
10
|
+
*/
|
|
11
|
+
export interface ActionsContext {
|
|
12
|
+
/** `owner/repo`. */
|
|
13
|
+
readonly repository: string;
|
|
14
|
+
/** `https://github.com`, or the GitHub Enterprise Server host. */
|
|
15
|
+
readonly serverUrl: string;
|
|
16
|
+
/** `https://api.github.com`, or the Enterprise Server API root. */
|
|
17
|
+
readonly apiUrl: string;
|
|
18
|
+
/** The workflow run page, where uploaded artifacts live. */
|
|
19
|
+
readonly runUrl: string;
|
|
20
|
+
readonly eventName: string;
|
|
21
|
+
readonly workflow: string;
|
|
22
|
+
readonly job: string;
|
|
23
|
+
/**
|
|
24
|
+
* The commit sources are linked at: the pull request's head when the event
|
|
25
|
+
* payload names it, the checked-out SHA when the event is not about a pull
|
|
26
|
+
* request, and nothing on an `issue_comment`, whose SHA is the default
|
|
27
|
+
* branch and would link every source line to the wrong revision.
|
|
28
|
+
*/
|
|
29
|
+
readonly sha: string | undefined;
|
|
30
|
+
readonly pullRequest: number | undefined;
|
|
31
|
+
/** `GITHUB_TOKEN`, or `GH_TOKEN` the way the `gh` CLI reads it. */
|
|
32
|
+
readonly token: string | undefined;
|
|
33
|
+
/** The file `GITHUB_STEP_SUMMARY` names, when the runner provides one. */
|
|
34
|
+
readonly stepSummaryPath: string | undefined;
|
|
35
|
+
}
|
|
36
|
+
export interface ActionsDeps {
|
|
37
|
+
readonly env: NodeJS.ProcessEnv;
|
|
38
|
+
readonly readFile: (file: string) => Promise<string>;
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* The Actions context of the current job, or undefined outside GitHub
|
|
42
|
+
* Actions. On Actions the runner always sets the repository, run id, event
|
|
43
|
+
* name, workflow, and job; a job missing one is broken, not "not Actions",
|
|
44
|
+
* and says so.
|
|
45
|
+
*/
|
|
46
|
+
export declare function detectActions(deps: ActionsDeps): Promise<ActionsContext | undefined>;
|
|
47
|
+
//# sourceMappingURL=actions.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"actions.d.ts","sourceRoot":"","sources":["../src/actions.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAIH,MAAM,WAAW,cAAc;IAC7B,oBAAoB;IACpB,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;IAC5B,kEAAkE;IAClE,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,mEAAmE;IACnE,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,4DAA4D;IAC5D,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC;IACrB;;;;;OAKG;IACH,QAAQ,CAAC,GAAG,EAAE,MAAM,GAAG,SAAS,CAAC;IACjC,QAAQ,CAAC,WAAW,EAAE,MAAM,GAAG,SAAS,CAAC;IACzC,mEAAmE;IACnE,QAAQ,CAAC,KAAK,EAAE,MAAM,GAAG,SAAS,CAAC;IACnC,0EAA0E;IAC1E,QAAQ,CAAC,eAAe,EAAE,MAAM,GAAG,SAAS,CAAC;CAC9C;AAED,MAAM,WAAW,WAAW;IAC1B,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC,UAAU,CAAC;IAChC,QAAQ,CAAC,QAAQ,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,OAAO,CAAC,MAAM,CAAC,CAAC;CACtD;AAsDD;;;;;GAKG;AACH,wBAAsB,aAAa,CAAC,IAAI,EAAE,WAAW,GAAG,OAAO,CAAC,cAAc,GAAG,SAAS,CAAC,CA0B1F"}
|
package/dist/actions.js
ADDED
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* What GitHub Actions tells a job about itself, read from the environment
|
|
3
|
+
* the runner sets: the repository, the pull request the job runs for, the
|
|
4
|
+
* commit to link sources at, the token, and where the job summary is written.
|
|
5
|
+
*
|
|
6
|
+
* The pull request number comes from the event payload first, since
|
|
7
|
+
* `pull_request`, `pull_request_target`, and a PR's `issue_comment` all carry
|
|
8
|
+
* it there, and from `GITHUB_REF` (`refs/pull/N/merge`) when the payload has
|
|
9
|
+
* none. Nothing here is read until the run has finished.
|
|
10
|
+
*/
|
|
11
|
+
import { isRecord } from './json.js';
|
|
12
|
+
/** A variable's trimmed value; unset and blank both read as undefined. */
|
|
13
|
+
function envValue(env, name) {
|
|
14
|
+
const raw = env[name];
|
|
15
|
+
return raw === undefined || raw.trim() === '' ? undefined : raw.trim();
|
|
16
|
+
}
|
|
17
|
+
function pullRequestNumber(value) {
|
|
18
|
+
return typeof value === 'number' && Number.isInteger(value) && value > 0 ? value : undefined;
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* Reads the pull request out of the webhook payload, or nothing when the
|
|
22
|
+
* payload is missing, unreadable, or not about a pull request.
|
|
23
|
+
*/
|
|
24
|
+
async function readEvent(path, deps) {
|
|
25
|
+
if (path === undefined)
|
|
26
|
+
return undefined;
|
|
27
|
+
let payload;
|
|
28
|
+
try {
|
|
29
|
+
payload = JSON.parse(await deps.readFile(path));
|
|
30
|
+
}
|
|
31
|
+
catch {
|
|
32
|
+
return undefined;
|
|
33
|
+
}
|
|
34
|
+
if (!isRecord(payload))
|
|
35
|
+
return undefined;
|
|
36
|
+
const pull = payload['pull_request'];
|
|
37
|
+
if (isRecord(pull)) {
|
|
38
|
+
const head = pull['head'];
|
|
39
|
+
return {
|
|
40
|
+
number: pullRequestNumber(pull['number']),
|
|
41
|
+
headSha: isRecord(head) && typeof head['sha'] === 'string' ? head['sha'] : undefined,
|
|
42
|
+
};
|
|
43
|
+
}
|
|
44
|
+
// An `issue_comment` on a pull request: the issue carries a `pull_request`
|
|
45
|
+
// link but not the head commit, and GITHUB_SHA is the default branch here.
|
|
46
|
+
const issue = payload['issue'];
|
|
47
|
+
if (isRecord(issue) && isRecord(issue['pull_request'])) {
|
|
48
|
+
return { number: pullRequestNumber(issue['number']), headSha: undefined };
|
|
49
|
+
}
|
|
50
|
+
return undefined;
|
|
51
|
+
}
|
|
52
|
+
/** The number in `refs/pull/N/merge` or `refs/pull/N/head`. */
|
|
53
|
+
function pullFromRef(ref) {
|
|
54
|
+
const match = ref === undefined ? null : /^refs\/pull\/(\d+)\//.exec(ref);
|
|
55
|
+
return match === null ? undefined : pullRequestNumber(Number(match[1]));
|
|
56
|
+
}
|
|
57
|
+
/**
|
|
58
|
+
* The Actions context of the current job, or undefined outside GitHub
|
|
59
|
+
* Actions. On Actions the runner always sets the repository, run id, event
|
|
60
|
+
* name, workflow, and job; a job missing one is broken, not "not Actions",
|
|
61
|
+
* and says so.
|
|
62
|
+
*/
|
|
63
|
+
export async function detectActions(deps) {
|
|
64
|
+
const value = (name) => envValue(deps.env, name);
|
|
65
|
+
if (value('GITHUB_ACTIONS') === undefined)
|
|
66
|
+
return undefined;
|
|
67
|
+
const required = (name) => {
|
|
68
|
+
const found = value(name);
|
|
69
|
+
if (found === undefined)
|
|
70
|
+
throw new Error(`GITHUB_ACTIONS is set but ${name} is not`);
|
|
71
|
+
return found;
|
|
72
|
+
};
|
|
73
|
+
const repository = required('GITHUB_REPOSITORY');
|
|
74
|
+
const serverUrl = (value('GITHUB_SERVER_URL') ?? 'https://github.com').replace(/\/+$/, '');
|
|
75
|
+
const event = await readEvent(value('GITHUB_EVENT_PATH'), deps);
|
|
76
|
+
return {
|
|
77
|
+
repository,
|
|
78
|
+
serverUrl,
|
|
79
|
+
apiUrl: (value('GITHUB_API_URL') ?? 'https://api.github.com').replace(/\/+$/, ''),
|
|
80
|
+
runUrl: `${serverUrl}/${repository}/actions/runs/${required('GITHUB_RUN_ID')}`,
|
|
81
|
+
eventName: required('GITHUB_EVENT_NAME'),
|
|
82
|
+
workflow: required('GITHUB_WORKFLOW'),
|
|
83
|
+
job: required('GITHUB_JOB'),
|
|
84
|
+
sha: event === undefined ? value('GITHUB_SHA') : event.headSha,
|
|
85
|
+
pullRequest: event?.number ?? pullFromRef(value('GITHUB_REF')),
|
|
86
|
+
token: value('GITHUB_TOKEN') ?? value('GH_TOKEN'),
|
|
87
|
+
stepSummaryPath: value('GITHUB_STEP_SUMMARY'),
|
|
88
|
+
};
|
|
89
|
+
}
|
|
90
|
+
//# sourceMappingURL=actions.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"actions.js","sourceRoot":"","sources":["../src/actions.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The report-1 document as one pull request comment: a headline with the
|
|
3
|
+
* counts, the run-level errors, a table of every test that did not simply
|
|
4
|
+
* pass, the passed tests folded away, and where the evidence is.
|
|
5
|
+
*
|
|
6
|
+
* Everything a test wrote (titles, error messages) is untrusted text on its
|
|
7
|
+
* way into markdown GitHub renders, so cells are escaped and clipped. The
|
|
8
|
+
* builder is exported so a hosted run page can render the same comment.
|
|
9
|
+
*/
|
|
10
|
+
import type { Report } from '@e2edev/e2e';
|
|
11
|
+
export interface CommentOptions {
|
|
12
|
+
/** A hidden HTML comment the reporter finds its own comment by; left out of a job summary. */
|
|
13
|
+
readonly marker?: string | undefined;
|
|
14
|
+
/** Where the run's artifacts can be fetched: the workflow run page, or a hosted run page. */
|
|
15
|
+
readonly artifactsUrl?: string | undefined;
|
|
16
|
+
/** A link to a source line, when the commit is known. */
|
|
17
|
+
readonly sourceUrl?: ((file: string, line: number) => string) | undefined;
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* A marker is what a rerun finds the comment by, so it can never be cut. With
|
|
21
|
+
* it bounded, the head and footer together stay far under the budget and the
|
|
22
|
+
* body is the only part that ever gives way.
|
|
23
|
+
*/
|
|
24
|
+
export declare const MAX_MARKER_CHARS = 1024;
|
|
25
|
+
/** The artifacts link sits in the footer beside the marker, so it is bounded the same way. */
|
|
26
|
+
export declare const MAX_URL_CHARS = 2048;
|
|
27
|
+
/** Renders the run as a pull request comment body. */
|
|
28
|
+
export declare function renderComment(report: Report, options?: CommentOptions): string;
|
|
29
|
+
//# sourceMappingURL=comment.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"comment.d.ts","sourceRoot":"","sources":["../src/comment.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAGH,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AAQ1C,MAAM,WAAW,cAAc;IAC7B,8FAA8F;IAC9F,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IACrC,6FAA6F;IAC7F,QAAQ,CAAC,YAAY,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAC3C,yDAAyD;IACzD,QAAQ,CAAC,SAAS,CAAC,EAAE,CAAC,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,KAAK,MAAM,CAAC,GAAG,SAAS,CAAC;CAC3E;AAWD;;;;GAIG;AACH,eAAO,MAAM,gBAAgB,OAAQ,CAAC;AACtC,8FAA8F;AAC9F,eAAO,MAAM,aAAa,OAAQ,CAAC;AA2InC,sDAAsD;AACtD,wBAAgB,aAAa,CAAC,MAAM,EAAE,MAAM,EAAE,OAAO,GAAE,cAAmB,GAAG,MAAM,CA2ElF"}
|
package/dist/comment.js
ADDED
|
@@ -0,0 +1,209 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The report-1 document as one pull request comment: a headline with the
|
|
3
|
+
* counts, the run-level errors, a table of every test that did not simply
|
|
4
|
+
* pass, the passed tests folded away, and where the evidence is.
|
|
5
|
+
*
|
|
6
|
+
* Everything a test wrote (titles, error messages) is untrusted text on its
|
|
7
|
+
* way into markdown GitHub renders, so cells are escaped and clipped. The
|
|
8
|
+
* builder is exported so a hosted run page can render the same comment.
|
|
9
|
+
*/
|
|
10
|
+
import { stripVTControlCharacters } from 'node:util';
|
|
11
|
+
/** GitHub rejects a comment body past 65536 characters; stay clear of it. */
|
|
12
|
+
const MAX_BODY_CHARS = 60_000;
|
|
13
|
+
/** Reader caps, not size guards: past these the comment says how many more there are. */
|
|
14
|
+
const MAX_TABLE_ROWS = 50;
|
|
15
|
+
const MAX_RUN_ERRORS = 20;
|
|
16
|
+
const MAX_PASSED_LINES = 200;
|
|
17
|
+
const MAX_FOOTER_TARGETS = 8;
|
|
18
|
+
const MAX_CELL_CHARS = 240;
|
|
19
|
+
const TRUNCATED_NOTE = "_Comment truncated to fit GitHub's size limit; the full report is in the run artifacts._";
|
|
20
|
+
/**
|
|
21
|
+
* A marker is what a rerun finds the comment by, so it can never be cut. With
|
|
22
|
+
* it bounded, the head and footer together stay far under the budget and the
|
|
23
|
+
* body is the only part that ever gives way.
|
|
24
|
+
*/
|
|
25
|
+
export const MAX_MARKER_CHARS = 1_024;
|
|
26
|
+
/** The artifacts link sits in the footer beside the marker, so it is bounded the same way. */
|
|
27
|
+
export const MAX_URL_CHARS = 2_048;
|
|
28
|
+
/** C0/C1 control characters except tab and newline; ANSI sequences are stripped first. */
|
|
29
|
+
// oxlint-disable-next-line no-control-regex -- the control range is the point
|
|
30
|
+
const CONTROL_PATTERN = /[\x00-\x08\x0b-\x1f\x7f-\x9f]/g;
|
|
31
|
+
/** One line of plain text: no color codes, no control characters, no line breaks. */
|
|
32
|
+
function plain(text) {
|
|
33
|
+
return stripVTControlCharacters(text).replace(CONTROL_PATTERN, '').replace(/\s+/g, ' ').trim();
|
|
34
|
+
}
|
|
35
|
+
/** Clips by code point, so an emoji at the cut never becomes a lone surrogate. */
|
|
36
|
+
function clip(text, max) {
|
|
37
|
+
const points = [...text];
|
|
38
|
+
return points.length <= max ? text : `${points.slice(0, max - 1).join('')}…`;
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* Text safe inside a table cell: markdown that could open a construct is
|
|
42
|
+
* escaped, angle brackets become entities so no HTML gets through, and the
|
|
43
|
+
* cell separator is escaped. An underscore stays: inside a word GitHub
|
|
44
|
+
* never reads it as emphasis, and error codes are full of them.
|
|
45
|
+
*/
|
|
46
|
+
function cell(text, max = MAX_CELL_CHARS) {
|
|
47
|
+
return clip(plain(text), max)
|
|
48
|
+
.replace(/[\\`*[\]~|]/g, (char) => `\\${char}`)
|
|
49
|
+
.replaceAll('<', '<')
|
|
50
|
+
.replaceAll('>', '>');
|
|
51
|
+
}
|
|
52
|
+
function formatDuration(ms) {
|
|
53
|
+
if (ms < 1_000)
|
|
54
|
+
return `${Math.round(ms)}ms`;
|
|
55
|
+
if (ms < 60_000)
|
|
56
|
+
return `${(ms / 1_000).toFixed(1)}s`;
|
|
57
|
+
// Round to whole seconds first, so 119.5 s is 2m 0s and never 1m 60s.
|
|
58
|
+
const total = Math.round(ms / 1_000);
|
|
59
|
+
return `${Math.floor(total / 60)}m ${total % 60}s`;
|
|
60
|
+
}
|
|
61
|
+
function plural(count, noun) {
|
|
62
|
+
return `${count} ${noun}${count === 1 ? '' : 's'}`;
|
|
63
|
+
}
|
|
64
|
+
function outcome(result, groups) {
|
|
65
|
+
const attempts = result.serialGroupId === undefined ? result.attempts : (groups.get(result.serialGroupId)?.attempts ?? []);
|
|
66
|
+
const last = attempts.at(-1);
|
|
67
|
+
const member = result.serialGroupId === undefined || last === undefined || !('members' in last)
|
|
68
|
+
? undefined
|
|
69
|
+
: last.members.find((candidate) => candidate.testId === result.testId);
|
|
70
|
+
return {
|
|
71
|
+
durationMs: member?.durationMs ?? last?.durationMs ?? 0,
|
|
72
|
+
error: member?.error ?? last?.error,
|
|
73
|
+
failedAttempts: attempts.slice(0, -1).filter((attempt) => attempt.status !== 'passed').length,
|
|
74
|
+
artifactKinds: kinds(attempts.flatMap((attempt) => attempt.artifacts)),
|
|
75
|
+
};
|
|
76
|
+
}
|
|
77
|
+
/** Screenshots first, then the recording, then the trace; the compiler fails when a kind is missing here. */
|
|
78
|
+
const KIND_RANK = { screenshot: 0, video: 1, trace: 2, download: 3, log: 4 };
|
|
79
|
+
function kinds(artifacts) {
|
|
80
|
+
return [...new Set(artifacts.map((artifact) => artifact.kind))].toSorted((a, b) => KIND_RANK[a] - KIND_RANK[b]);
|
|
81
|
+
}
|
|
82
|
+
function bucket(status) {
|
|
83
|
+
return status === 'passed' || status === 'flaky' || status === 'skipped' ? status : 'failed';
|
|
84
|
+
}
|
|
85
|
+
const ICON = { failed: '🔴', flaky: '⚠️', skipped: '⏭️', passed: '🟢' };
|
|
86
|
+
/** The order the table lists what did not pass; passed tests fold away below it. */
|
|
87
|
+
const LISTED = ['failed', 'flaky', 'skipped'];
|
|
88
|
+
function groupByBucket(results) {
|
|
89
|
+
const groups = new Map();
|
|
90
|
+
for (const result of results) {
|
|
91
|
+
const key = bucket(result.status);
|
|
92
|
+
groups.set(key, [...(groups.get(key) ?? []), result]);
|
|
93
|
+
}
|
|
94
|
+
return groups;
|
|
95
|
+
}
|
|
96
|
+
function headline(run, groups) {
|
|
97
|
+
const parts = ['failed', 'flaky', 'passed', 'skipped']
|
|
98
|
+
.filter((key) => (groups.get(key)?.length ?? 0) > 0)
|
|
99
|
+
.map((key) => `${groups.get(key)?.length} ${key}`);
|
|
100
|
+
const summary = parts.length > 0 ? parts.join(', ') : run.errors.length > 0 ? 'no tests ran' : 'no tests selected';
|
|
101
|
+
return `### ${run.status === 'passed' ? ICON.passed : ICON.failed} e2e: ${summary}`;
|
|
102
|
+
}
|
|
103
|
+
function errorText(error) {
|
|
104
|
+
const phase = error.phase === undefined ? '' : ` (${plain(error.phase)})`;
|
|
105
|
+
return `**${cell(error.code, 128)}**${phase} ${cell(error.message)}`.trim();
|
|
106
|
+
}
|
|
107
|
+
function outcomeCell(result, final) {
|
|
108
|
+
if (result.status === 'flaky' && final.failedAttempts > 0) {
|
|
109
|
+
return `flaky: passed after ${plural(final.failedAttempts, 'failed attempt')}`;
|
|
110
|
+
}
|
|
111
|
+
if (result.status === 'skipped')
|
|
112
|
+
return `skipped: ${cell(result.skip?.reason ?? 'skipped')}`;
|
|
113
|
+
return final.error === undefined ? cell(result.status) : errorText(final.error);
|
|
114
|
+
}
|
|
115
|
+
function evidenceCell(final, options) {
|
|
116
|
+
if (final.artifactKinds.length === 0)
|
|
117
|
+
return '';
|
|
118
|
+
const text = final.artifactKinds.join(', ');
|
|
119
|
+
return options.artifactsUrl === undefined ? text : `[${text}](${options.artifactsUrl})`;
|
|
120
|
+
}
|
|
121
|
+
function footer(run, options) {
|
|
122
|
+
const duration = formatDuration(Date.parse(run.finishedAt) - Date.parse(run.startedAt));
|
|
123
|
+
const shown = run.targets.slice(0, MAX_FOOTER_TARGETS).map((target) => cell(target.id, 64));
|
|
124
|
+
if (run.targets.length > shown.length)
|
|
125
|
+
shown.push(`and ${run.targets.length - shown.length} more`);
|
|
126
|
+
const targets = shown.length === 0 ? '' : ` (${shown.join(', ')})`;
|
|
127
|
+
return [
|
|
128
|
+
...(options.artifactsUrl === undefined
|
|
129
|
+
? []
|
|
130
|
+
: [`Screenshots, traces, and recordings: [run artifacts](${options.artifactsUrl}).`]),
|
|
131
|
+
`<sub>e2e ${cell(run.runner.version, 64)} · ${duration} · ${plural(run.targets.length, 'target')}${targets}</sub>`,
|
|
132
|
+
];
|
|
133
|
+
}
|
|
134
|
+
/** Renders the run as a pull request comment body. */
|
|
135
|
+
export function renderComment(report, options = {}) {
|
|
136
|
+
if (options.marker !== undefined && options.marker.length > MAX_MARKER_CHARS) {
|
|
137
|
+
throw new Error(`renderComment: marker must be at most ${MAX_MARKER_CHARS} characters, got ${options.marker.length}`);
|
|
138
|
+
}
|
|
139
|
+
if (options.artifactsUrl !== undefined && options.artifactsUrl.length > MAX_URL_CHARS) {
|
|
140
|
+
throw new Error(`renderComment: artifactsUrl must be at most ${MAX_URL_CHARS} characters, got ${options.artifactsUrl.length}`);
|
|
141
|
+
}
|
|
142
|
+
const run = report.run;
|
|
143
|
+
const serialGroups = new Map(run.serialGroups.map((group) => [group.id, group]));
|
|
144
|
+
const groups = groupByBucket(run.results);
|
|
145
|
+
const manyTargets = run.targets.length > 1;
|
|
146
|
+
/** `file:line › suite › title (target)`, the location linked when the commit is known. */
|
|
147
|
+
const name = (result, location) => {
|
|
148
|
+
const title = result.titlePath.map((part) => cell(part, 120)).join(' › ');
|
|
149
|
+
return `${location} › ${title}${manyTargets ? ` (${cell(result.targetId, 64)})` : ''}`;
|
|
150
|
+
};
|
|
151
|
+
const linked = (result) => {
|
|
152
|
+
const location = `${result.source.file}:${result.source.line}`;
|
|
153
|
+
return options.sourceUrl === undefined
|
|
154
|
+
? `\`${plain(location).replaceAll('`', '')}\``
|
|
155
|
+
: `[${cell(location)}](${options.sourceUrl(result.source.file, result.source.line)})`;
|
|
156
|
+
};
|
|
157
|
+
// Body parts in priority order; each is one line, except the passed list,
|
|
158
|
+
// which is one block since a `<details>` cannot be cut halfway.
|
|
159
|
+
const errors = run.errors.slice(0, MAX_RUN_ERRORS).map((error) => `> ${errorText(error)}`);
|
|
160
|
+
if (run.errors.length > errors.length)
|
|
161
|
+
errors.push(`> and ${run.errors.length - errors.length} more`);
|
|
162
|
+
const listed = LISTED.flatMap((key) => groups.get(key) ?? []);
|
|
163
|
+
const rows = listed.slice(0, MAX_TABLE_ROWS).map((result) => {
|
|
164
|
+
const final = outcome(result, serialGroups);
|
|
165
|
+
return `| ${ICON[bucket(result.status)]} | ${name(result, linked(result))} | ${outcomeCell(result, final)} | ${evidenceCell(final, options)} |`;
|
|
166
|
+
});
|
|
167
|
+
const table = rows.length === 0 ? [] : ['| | Test | Outcome | Evidence |', '| --- | --- | --- | --- |', ...rows];
|
|
168
|
+
if (listed.length > rows.length)
|
|
169
|
+
table.push(`| | and ${listed.length - rows.length} more | | |`);
|
|
170
|
+
const passed = groups.get('passed') ?? [];
|
|
171
|
+
const passedBlock = passed.length === 0
|
|
172
|
+
? []
|
|
173
|
+
: [
|
|
174
|
+
[
|
|
175
|
+
'<details>',
|
|
176
|
+
`<summary>${plural(passed.length, 'passed test')}</summary>`,
|
|
177
|
+
'',
|
|
178
|
+
...passed
|
|
179
|
+
.slice(0, MAX_PASSED_LINES)
|
|
180
|
+
.map((result) => `- ${name(result, cell(result.file, 200))} (${formatDuration(outcome(result, serialGroups).durationMs)})`),
|
|
181
|
+
...(passed.length > MAX_PASSED_LINES ? [`- and ${passed.length - MAX_PASSED_LINES} more`] : []),
|
|
182
|
+
'</details>',
|
|
183
|
+
].join('\n'),
|
|
184
|
+
];
|
|
185
|
+
const sections = [errors, table, passedBlock].filter((section) => section.length > 0);
|
|
186
|
+
const head = [...(options.marker === undefined ? [] : [options.marker]), headline(run, groups), ''];
|
|
187
|
+
const tail = footer(run, options);
|
|
188
|
+
const render = (body) => `${[...head, ...body, ...tail].join('\n')}\n`;
|
|
189
|
+
// Greedy fit: keep whole parts in order while they fit, then say what was cut.
|
|
190
|
+
let size = render([TRUNCATED_NOTE]).length;
|
|
191
|
+
const body = [];
|
|
192
|
+
let cut = false;
|
|
193
|
+
for (const section of sections) {
|
|
194
|
+
for (const part of section) {
|
|
195
|
+
if (size + part.length + 1 > MAX_BODY_CHARS) {
|
|
196
|
+
cut = true;
|
|
197
|
+
break;
|
|
198
|
+
}
|
|
199
|
+
body.push(part);
|
|
200
|
+
size += part.length + 1;
|
|
201
|
+
}
|
|
202
|
+
if (cut)
|
|
203
|
+
break;
|
|
204
|
+
body.push('');
|
|
205
|
+
size += 1;
|
|
206
|
+
}
|
|
207
|
+
return render(cut ? [...body, TRUNCATED_NOTE] : body);
|
|
208
|
+
}
|
|
209
|
+
//# sourceMappingURL=comment.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"comment.js","sourceRoot":"","sources":["../src/comment.ts"],"names":[],"mappings":"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"}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `@e2edev/github` public surface: the `github()` reporter factory and the
|
|
3
|
+
* comment builder it uses. The reporter posts one pull request comment per
|
|
4
|
+
* run from GitHub Actions, through the `reporters` seam of `@e2edev/e2e`,
|
|
5
|
+
* and writes the same text to the job summary.
|
|
6
|
+
*/
|
|
7
|
+
export { github } from './reporter.ts';
|
|
8
|
+
export type { GitHubOptions } from './reporter.ts';
|
|
9
|
+
export { renderComment, MAX_MARKER_CHARS, MAX_URL_CHARS } from './comment.ts';
|
|
10
|
+
export type { CommentOptions } from './comment.ts';
|
|
11
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,EAAE,MAAM,EAAE,MAAM,eAAe,CAAC;AACvC,YAAY,EAAE,aAAa,EAAE,MAAM,eAAe,CAAC;AACnD,OAAO,EAAE,aAAa,EAAE,gBAAgB,EAAE,aAAa,EAAE,MAAM,cAAc,CAAC;AAC9E,YAAY,EAAE,cAAc,EAAE,MAAM,cAAc,CAAC"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `@e2edev/github` public surface: the `github()` reporter factory and the
|
|
3
|
+
* comment builder it uses. The reporter posts one pull request comment per
|
|
4
|
+
* run from GitHub Actions, through the `reporters` seam of `@e2edev/e2e`,
|
|
5
|
+
* and writes the same text to the job summary.
|
|
6
|
+
*/
|
|
7
|
+
export { github } from './reporter.js';
|
|
8
|
+
export { renderComment, MAX_MARKER_CHARS, MAX_URL_CHARS } from './comment.js';
|
|
9
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,EAAE,MAAM,EAAE,MAAM,eAAe,CAAC;AAEvC,OAAO,EAAE,aAAa,EAAE,gBAAgB,EAAE,aAAa,EAAE,MAAM,cAAc,CAAC"}
|
package/dist/json.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"json.d.ts","sourceRoot":"","sources":["../src/json.ts"],"names":[],"mappings":"AAAA,0EAA0E;AAC1E,wBAAgB,QAAQ,CAAC,KAAK,EAAE,OAAO,GAAG,KAAK,IAAI,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAEzE"}
|
package/dist/json.js
ADDED
package/dist/json.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"json.js","sourceRoot":"","sources":["../src/json.ts"],"names":[],"mappings":"AAAA,0EAA0E;AAC1E,MAAM,UAAU,QAAQ,CAAC,KAAc;IACrC,OAAO,OAAO,KAAK,KAAK,QAAQ,IAAI,KAAK,KAAK,IAAI,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC;AAC9E,CAAC"}
|
package/dist/post.d.ts
ADDED
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* One comment per pull request, kept current: the reporter looks for the
|
|
3
|
+
* comment carrying its marker and edits it, and creates one only when none
|
|
4
|
+
* exists. The REST calls are the three GitHub documents them as: list the
|
|
5
|
+
* issue's comments, update a comment, create a comment.
|
|
6
|
+
*
|
|
7
|
+
* Every request carries the reporter's abort signal. Anything GitHub answers
|
|
8
|
+
* outside the protocol is an error naming itself; the runner prints one line.
|
|
9
|
+
*/
|
|
10
|
+
export interface PostParams {
|
|
11
|
+
readonly fetch: typeof fetch;
|
|
12
|
+
readonly signal: AbortSignal;
|
|
13
|
+
/** `https://api.github.com`, or an Enterprise Server API root. */
|
|
14
|
+
readonly apiUrl: string;
|
|
15
|
+
readonly token: string;
|
|
16
|
+
/** `owner/repo`. */
|
|
17
|
+
readonly repository: string;
|
|
18
|
+
readonly pullRequest: number;
|
|
19
|
+
/** The hidden HTML comment that identifies the reporter's own comment. */
|
|
20
|
+
readonly marker: string;
|
|
21
|
+
readonly body: string;
|
|
22
|
+
}
|
|
23
|
+
/** Creates or updates the reporter's comment and resolves with its URL. */
|
|
24
|
+
export declare function upsertComment(params: PostParams): Promise<string>;
|
|
25
|
+
//# sourceMappingURL=post.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"post.d.ts","sourceRoot":"","sources":["../src/post.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAIH,MAAM,WAAW,UAAU;IACzB,QAAQ,CAAC,KAAK,EAAE,OAAO,KAAK,CAAC;IAC7B,QAAQ,CAAC,MAAM,EAAE,WAAW,CAAC;IAC7B,kEAAkE;IAClE,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,oBAAoB;IACpB,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;IAC5B,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;IAC7B,0EAA0E;IAC1E,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;CACvB;AAqDD,2EAA2E;AAC3E,wBAAsB,aAAa,CAAC,MAAM,EAAE,UAAU,GAAG,OAAO,CAAC,MAAM,CAAC,CAWvE"}
|
package/dist/post.js
ADDED
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* One comment per pull request, kept current: the reporter looks for the
|
|
3
|
+
* comment carrying its marker and edits it, and creates one only when none
|
|
4
|
+
* exists. The REST calls are the three GitHub documents them as: list the
|
|
5
|
+
* issue's comments, update a comment, create a comment.
|
|
6
|
+
*
|
|
7
|
+
* Every request carries the reporter's abort signal. Anything GitHub answers
|
|
8
|
+
* outside the protocol is an error naming itself; the runner prints one line.
|
|
9
|
+
*/
|
|
10
|
+
import { isRecord } from './json.js';
|
|
11
|
+
const PER_PAGE = 100;
|
|
12
|
+
/**
|
|
13
|
+
* The list is followed until a short page says it is exhausted. The cap is a
|
|
14
|
+
* guard against a server that never sends one, not a limit any pull request
|
|
15
|
+
* reaches: ten thousand comments.
|
|
16
|
+
*/
|
|
17
|
+
const MAX_PAGES = 100;
|
|
18
|
+
const USER_AGENT = '@e2edev/github';
|
|
19
|
+
/** One GitHub REST call; anything but success is an error the runner can print. */
|
|
20
|
+
async function request(params, method, route, body) {
|
|
21
|
+
const response = await params.fetch(`${params.apiUrl}${route}`, {
|
|
22
|
+
method,
|
|
23
|
+
headers: {
|
|
24
|
+
authorization: `Bearer ${params.token}`,
|
|
25
|
+
accept: 'application/vnd.github+json',
|
|
26
|
+
'x-github-api-version': '2022-11-28',
|
|
27
|
+
'user-agent': USER_AGENT,
|
|
28
|
+
...(body === undefined ? {} : { 'content-type': 'application/json' }),
|
|
29
|
+
},
|
|
30
|
+
...(body === undefined ? {} : { body: JSON.stringify(body) }),
|
|
31
|
+
signal: params.signal,
|
|
32
|
+
});
|
|
33
|
+
if (response.ok)
|
|
34
|
+
return response.json().catch(() => undefined);
|
|
35
|
+
const detail = (await response.text().catch(() => '')).slice(0, 200);
|
|
36
|
+
if (response.status === 401)
|
|
37
|
+
throw new Error('GitHub rejected the token (GITHUB_TOKEN or GH_TOKEN)');
|
|
38
|
+
if (response.status === 403 || response.status === 404) {
|
|
39
|
+
throw new Error(`the token cannot comment on ${params.repository}#${params.pullRequest}: a pull request from a fork runs with a read-only token, and the job needs \`permissions: pull-requests: write\``);
|
|
40
|
+
}
|
|
41
|
+
throw new Error(`GitHub responded ${response.status} to ${method} ${route}${detail === '' ? '' : `: ${detail}`}`);
|
|
42
|
+
}
|
|
43
|
+
/** The id of the comment carrying the marker, or undefined when no page has one. */
|
|
44
|
+
async function findComment(params, issue) {
|
|
45
|
+
for (let page = 1; page <= MAX_PAGES; page += 1) {
|
|
46
|
+
const comments = await request(params, 'GET', `${issue}/comments?per_page=${PER_PAGE}&page=${page}`);
|
|
47
|
+
if (!Array.isArray(comments))
|
|
48
|
+
throw new Error('GitHub answered the comment list with a body that is not a list');
|
|
49
|
+
const found = comments.find((comment) => isRecord(comment) && typeof comment['body'] === 'string' && comment['body'].includes(params.marker));
|
|
50
|
+
if (isRecord(found) && typeof found['id'] === 'number')
|
|
51
|
+
return found['id'];
|
|
52
|
+
if (comments.length < PER_PAGE)
|
|
53
|
+
return undefined;
|
|
54
|
+
}
|
|
55
|
+
return undefined;
|
|
56
|
+
}
|
|
57
|
+
/** Creates or updates the reporter's comment and resolves with its URL. */
|
|
58
|
+
export async function upsertComment(params) {
|
|
59
|
+
const issue = `/repos/${params.repository}/issues/${params.pullRequest}`;
|
|
60
|
+
const existing = await findComment(params, issue);
|
|
61
|
+
const created = existing === undefined
|
|
62
|
+
? await request(params, 'POST', `${issue}/comments`, { body: params.body })
|
|
63
|
+
: await request(params, 'PATCH', `/repos/${params.repository}/issues/comments/${existing}`, { body: params.body });
|
|
64
|
+
if (!isRecord(created) || typeof created['html_url'] !== 'string') {
|
|
65
|
+
throw new Error('GitHub answered the comment without its html_url');
|
|
66
|
+
}
|
|
67
|
+
return created['html_url'];
|
|
68
|
+
}
|
|
69
|
+
//# sourceMappingURL=post.js.map
|
package/dist/post.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"post.js","sourceRoot":"","sources":["../src/post.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import type { FinishedRun, Reporter, ReporterSummary } from '@e2edev/e2e';
|
|
2
|
+
import { type ActionsDeps } from './actions.ts';
|
|
3
|
+
export interface GitHubOptions {
|
|
4
|
+
/**
|
|
5
|
+
* Tells one job's comment apart from another's when the workflow, job, and
|
|
6
|
+
* project are the same, as in a matrix: `key: process.env.MATRIX_BROWSER`.
|
|
7
|
+
* Without it, matrix replicas of one job share a comment and the last one
|
|
8
|
+
* to finish wins. Never the run id: a rerun must find the old comment.
|
|
9
|
+
*/
|
|
10
|
+
key?: string;
|
|
11
|
+
}
|
|
12
|
+
/** What the reporter touches outside itself, so tests run it against fakes. */
|
|
13
|
+
export interface ReportDeps extends ActionsDeps {
|
|
14
|
+
readonly fetch: typeof fetch;
|
|
15
|
+
readonly appendFile: (file: string, text: string) => Promise<void>;
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* The GitHub reporter. Add it to `reporters` beside the built-in ids:
|
|
19
|
+
*
|
|
20
|
+
* ```ts
|
|
21
|
+
* import { github } from '@e2edev/github';
|
|
22
|
+
* export default { targets, reporters: ['list', github()] } satisfies E2EConfig;
|
|
23
|
+
* ```
|
|
24
|
+
*
|
|
25
|
+
* On GitHub Actions it writes the run to the job summary and, for a pull
|
|
26
|
+
* request, posts or updates one comment. Everything it reads (the token, the
|
|
27
|
+
* event, the repository) is read when the run finishes, never when the config
|
|
28
|
+
* loads, so constructing it has no side effects.
|
|
29
|
+
*/
|
|
30
|
+
export declare function github(options?: GitHubOptions): Reporter;
|
|
31
|
+
/** Reports one finished run and resolves with the summary rows describing what happened. */
|
|
32
|
+
export declare function reportRun(run: FinishedRun, signal: AbortSignal, options: GitHubOptions, deps: ReportDeps): Promise<ReporterSummary>;
|
|
33
|
+
//# sourceMappingURL=reporter.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"reporter.d.ts","sourceRoot":"","sources":["../src/reporter.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,WAAW,EAAE,QAAQ,EAAE,eAAe,EAAE,MAAM,aAAa,CAAC;AAC1E,OAAO,EAAsC,KAAK,WAAW,EAAE,MAAM,cAAc,CAAC;AAMpF,MAAM,WAAW,aAAa;IAC5B;;;;;OAKG;IACH,GAAG,CAAC,EAAE,MAAM,CAAC;CACd;AAED,+EAA+E;AAC/E,MAAM,WAAW,UAAW,SAAQ,WAAW;IAC7C,QAAQ,CAAC,KAAK,EAAE,OAAO,KAAK,CAAC;IAC7B,QAAQ,CAAC,UAAU,EAAE,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;CACpE;AAED;;;;;;;;;;;;GAYG;AACH,wBAAgB,MAAM,CAAC,OAAO,GAAE,aAAkB,GAAG,QAAQ,CAW5D;AA+DD,4FAA4F;AAC5F,wBAAsB,SAAS,CAC7B,GAAG,EAAE,WAAW,EAChB,MAAM,EAAE,WAAW,EACnB,OAAO,EAAE,aAAa,EACtB,IAAI,EAAE,UAAU,GACf,OAAO,CAAC,eAAe,CAAC,CAyB1B"}
|
package/dist/reporter.js
ADDED
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
import { appendFile, readFile } from 'node:fs/promises';
|
|
2
|
+
import { detectActions } from './actions.js';
|
|
3
|
+
import { renderComment } from './comment.js';
|
|
4
|
+
import { upsertComment } from './post.js';
|
|
5
|
+
const SUMMARY_LABEL = 'GitHub';
|
|
6
|
+
/**
|
|
7
|
+
* The GitHub reporter. Add it to `reporters` beside the built-in ids:
|
|
8
|
+
*
|
|
9
|
+
* ```ts
|
|
10
|
+
* import { github } from '@e2edev/github';
|
|
11
|
+
* export default { targets, reporters: ['list', github()] } satisfies E2EConfig;
|
|
12
|
+
* ```
|
|
13
|
+
*
|
|
14
|
+
* On GitHub Actions it writes the run to the job summary and, for a pull
|
|
15
|
+
* request, posts or updates one comment. Everything it reads (the token, the
|
|
16
|
+
* event, the repository) is read when the run finishes, never when the config
|
|
17
|
+
* loads, so constructing it has no side effects.
|
|
18
|
+
*/
|
|
19
|
+
export function github(options = {}) {
|
|
20
|
+
return {
|
|
21
|
+
name: 'github',
|
|
22
|
+
onRunFinished: (run, signal) => reportRun(run, signal, options, {
|
|
23
|
+
fetch: globalThis.fetch,
|
|
24
|
+
env: process.env,
|
|
25
|
+
readFile: (file) => readFile(file, 'utf8'),
|
|
26
|
+
appendFile: (file, text) => appendFile(file, text, 'utf8'),
|
|
27
|
+
}),
|
|
28
|
+
};
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* Each encoded marker field is cut to this many characters, so four fields
|
|
32
|
+
* and their names stay under `MAX_MARKER_CHARS` whatever the input. The cut
|
|
33
|
+
* never lands inside a percent escape, and it is the same on every run.
|
|
34
|
+
*/
|
|
35
|
+
const MAX_MARKER_FIELD_CHARS = 200;
|
|
36
|
+
function markerField(value) {
|
|
37
|
+
const encoded = encodeURIComponent(value);
|
|
38
|
+
if (encoded.length <= MAX_MARKER_FIELD_CHARS)
|
|
39
|
+
return encoded;
|
|
40
|
+
return encoded.slice(0, MAX_MARKER_FIELD_CHARS).replace(/%[0-9A-F]?$/, '');
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* The marker one job's comment carries, so two workflows or jobs each keep
|
|
44
|
+
* their own. Every field is cut the same way on every run, so a rerun
|
|
45
|
+
* derives the same marker and finds its comment.
|
|
46
|
+
*/
|
|
47
|
+
function commentMarker(run, context, key) {
|
|
48
|
+
const fields = {
|
|
49
|
+
project: run.report.run.project.id,
|
|
50
|
+
workflow: context.workflow,
|
|
51
|
+
job: context.job,
|
|
52
|
+
key,
|
|
53
|
+
};
|
|
54
|
+
const parts = Object.entries(fields).flatMap(([name, value]) => value === undefined || value === '' ? [] : [`${name}=${markerField(value)}`]);
|
|
55
|
+
return `<!-- e2e-github ${parts.join(' ')} -->`;
|
|
56
|
+
}
|
|
57
|
+
function sourceUrl(context) {
|
|
58
|
+
const sha = context.sha;
|
|
59
|
+
if (sha === undefined)
|
|
60
|
+
return undefined;
|
|
61
|
+
return (file, line) => `${context.serverUrl}/${context.repository}/blob/${sha}/${file.split('/').map(encodeURIComponent).join('/')}#L${line}`;
|
|
62
|
+
}
|
|
63
|
+
function posting(context) {
|
|
64
|
+
if (context.pullRequest === undefined)
|
|
65
|
+
return { skipped: `${context.eventName} is not a pull request` };
|
|
66
|
+
if (context.token === undefined) {
|
|
67
|
+
return { skipped: "set GITHUB_TOKEN in the step's env (GITHUB_TOKEN: ${{ github.token }})" };
|
|
68
|
+
}
|
|
69
|
+
return { token: context.token, pullRequest: context.pullRequest };
|
|
70
|
+
}
|
|
71
|
+
async function writeSummary(context, text, deps) {
|
|
72
|
+
if (context.stepSummaryPath === undefined)
|
|
73
|
+
return 'none';
|
|
74
|
+
try {
|
|
75
|
+
await deps.appendFile(context.stepSummaryPath, `${text}\n`);
|
|
76
|
+
return 'written';
|
|
77
|
+
}
|
|
78
|
+
catch (error) {
|
|
79
|
+
return { failed: error instanceof Error ? error.message : String(error) };
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
/** Reports one finished run and resolves with the summary rows describing what happened. */
|
|
83
|
+
export async function reportRun(run, signal, options, deps) {
|
|
84
|
+
const context = await detectActions(deps);
|
|
85
|
+
if (context === undefined)
|
|
86
|
+
return [{ label: SUMMARY_LABEL, text: 'not posted: not running on GitHub Actions' }];
|
|
87
|
+
const links = { artifactsUrl: context.runUrl, sourceUrl: sourceUrl(context) };
|
|
88
|
+
const summary = await writeSummary(context, renderComment(run.report, links), deps);
|
|
89
|
+
const post = posting(context);
|
|
90
|
+
const marker = commentMarker(run, context, options.key);
|
|
91
|
+
const lead = 'skipped' in post
|
|
92
|
+
? `not posted: ${post.skipped}${summary === 'written' ? '; written to the job summary' : ''}`
|
|
93
|
+
: await upsertComment({
|
|
94
|
+
fetch: deps.fetch,
|
|
95
|
+
signal,
|
|
96
|
+
apiUrl: context.apiUrl,
|
|
97
|
+
repository: context.repository,
|
|
98
|
+
...post,
|
|
99
|
+
marker,
|
|
100
|
+
body: renderComment(run.report, { ...links, marker }),
|
|
101
|
+
});
|
|
102
|
+
return [
|
|
103
|
+
{ label: SUMMARY_LABEL, text: lead },
|
|
104
|
+
...(typeof summary === 'object' ? [{ label: SUMMARY_LABEL, text: `job summary not written: ${summary.failed}` }] : []),
|
|
105
|
+
];
|
|
106
|
+
}
|
|
107
|
+
//# sourceMappingURL=reporter.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"reporter.js","sourceRoot":"","sources":["../src/reporter.ts"],"names":[],"mappings":"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"}
|
package/package.json
ADDED
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@e2edev/github",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "GitHub reporter for e2e: one pull request comment per run, from GitHub Actions",
|
|
5
|
+
"keywords": [
|
|
6
|
+
"e2e",
|
|
7
|
+
"testing",
|
|
8
|
+
"agentic",
|
|
9
|
+
"end-to-end",
|
|
10
|
+
"reporter",
|
|
11
|
+
"github",
|
|
12
|
+
"github-actions",
|
|
13
|
+
"pull-request"
|
|
14
|
+
],
|
|
15
|
+
"license": "MIT",
|
|
16
|
+
"contributors": [
|
|
17
|
+
"Oskar Kwaśniewski <oskar@tester.army>",
|
|
18
|
+
"Szymon Rybczak <szymon@tester.army>"
|
|
19
|
+
],
|
|
20
|
+
"repository": {
|
|
21
|
+
"type": "git",
|
|
22
|
+
"url": "git+https://github.com/tester-army/e2e.git",
|
|
23
|
+
"directory": "packages/github"
|
|
24
|
+
},
|
|
25
|
+
"bugs": {
|
|
26
|
+
"url": "https://github.com/tester-army/e2e/issues"
|
|
27
|
+
},
|
|
28
|
+
"homepage": "https://github.com/tester-army/e2e/tree/main/packages/github#readme",
|
|
29
|
+
"publishConfig": {
|
|
30
|
+
"registry": "https://registry.npmjs.org/",
|
|
31
|
+
"access": "restricted",
|
|
32
|
+
"provenance": false,
|
|
33
|
+
"tag": "beta"
|
|
34
|
+
},
|
|
35
|
+
"type": "module",
|
|
36
|
+
"exports": {
|
|
37
|
+
".": {
|
|
38
|
+
"types": "./dist/index.d.ts",
|
|
39
|
+
"default": "./dist/index.js"
|
|
40
|
+
}
|
|
41
|
+
},
|
|
42
|
+
"files": [
|
|
43
|
+
"dist"
|
|
44
|
+
],
|
|
45
|
+
"peerDependencies": {
|
|
46
|
+
"@e2edev/e2e": ">=0.9.0 <1"
|
|
47
|
+
},
|
|
48
|
+
"devDependencies": {
|
|
49
|
+
"@types/node": "26.4.1",
|
|
50
|
+
"typescript": "7.0.2",
|
|
51
|
+
"vitest": "5.0.0",
|
|
52
|
+
"@e2edev/e2e": "0.10.0"
|
|
53
|
+
},
|
|
54
|
+
"engines": {
|
|
55
|
+
"node": ">=22.12.0"
|
|
56
|
+
},
|
|
57
|
+
"scripts": {
|
|
58
|
+
"build": "node -e \"require('node:fs').rmSync('dist',{recursive:true,force:true})\" && tsc --project tsconfig.build.json",
|
|
59
|
+
"typecheck": "tsc --noEmit",
|
|
60
|
+
"test": "vitest run",
|
|
61
|
+
"test:watch": "vitest",
|
|
62
|
+
"test:unit": "vitest run tests/unit"
|
|
63
|
+
}
|
|
64
|
+
}
|