@testpulse.run/reporter 0.1.6 → 0.2.4
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 +52 -20
- package/dist/index.cjs +57 -0
- package/dist/index.d.cts +18 -0
- package/dist/index.d.ts +18 -2
- package/dist/index.js +36 -1
- package/package.json +22 -44
- package/dist/fixtures/createTestPulseTest.d.ts +0 -14
- package/dist/fixtures/createTestPulseTest.js +0 -33
- package/dist/fixtures/options.d.ts +0 -2
- package/dist/fixtures/options.js +0 -36
- package/dist/fixtures/runtime.d.ts +0 -3
- package/dist/fixtures/runtime.js +0 -6
- package/dist/fixtures/types.d.ts +0 -8
- package/dist/fixtures/types.js +0 -1
- package/dist/fixtures.d.ts +0 -2
- package/dist/fixtures.js +0 -1
- package/dist/live/CaptureScheduler.d.ts +0 -22
- package/dist/live/CaptureScheduler.js +0 -87
- package/dist/live/EncoderProcess.d.ts +0 -5
- package/dist/live/EncoderProcess.js +0 -61
- package/dist/live/LiveCaptureService.d.ts +0 -2
- package/dist/live/LiveCaptureService.js +0 -103
- package/dist/live/LiveSessionResolver.d.ts +0 -2
- package/dist/live/LiveSessionResolver.js +0 -86
- package/dist/live/LiveSessionState.d.ts +0 -6
- package/dist/live/LiveSessionState.js +0 -20
- package/dist/live/Mp4FragmentParser.d.ts +0 -4
- package/dist/live/Mp4FragmentParser.js +0 -32
- package/dist/live/SegmentQueue.d.ts +0 -9
- package/dist/live/SegmentQueue.js +0 -32
- package/dist/live/StreamUploader.d.ts +0 -21
- package/dist/live/StreamUploader.js +0 -78
- package/dist/live/telemetry.d.ts +0 -3
- package/dist/live/telemetry.js +0 -15
- package/dist/live/types.d.ts +0 -42
- package/dist/live/types.js +0 -1
- package/dist/reporter/ArtifactUploader.d.ts +0 -13
- package/dist/reporter/ArtifactUploader.js +0 -48
- package/dist/reporter/EventQueue.d.ts +0 -7
- package/dist/reporter/EventQueue.js +0 -15
- package/dist/reporter/GitRuntimeMetadataResolver.d.ts +0 -11
- package/dist/reporter/GitRuntimeMetadataResolver.js +0 -91
- package/dist/reporter/PlaywrightReporter.d.ts +0 -22
- package/dist/reporter/PlaywrightReporter.js +0 -142
- package/dist/reporter/RunClient.d.ts +0 -19
- package/dist/reporter/RunClient.js +0 -42
- package/dist/reporter/types.d.ts +0 -25
- package/dist/reporter/types.js +0 -1
- package/dist/shared/endpoint.d.ts +0 -1
- package/dist/shared/endpoint.js +0 -15
- package/dist/shared/http.d.ts +0 -2
- package/dist/shared/http.js +0 -17
|
@@ -1,22 +0,0 @@
|
|
|
1
|
-
import type { FullConfig, FullResult, Reporter, Suite, TestCase, TestResult } from "@playwright/test/reporter";
|
|
2
|
-
import type { TestPulseReporterOptions } from "./types.js";
|
|
3
|
-
export default class TestPulseReporter implements Reporter {
|
|
4
|
-
private readonly options;
|
|
5
|
-
private runId;
|
|
6
|
-
private isListOnly;
|
|
7
|
-
private metadataPromise;
|
|
8
|
-
private readonly eventQueue;
|
|
9
|
-
private readonly runClient;
|
|
10
|
-
private readonly artifactUploader;
|
|
11
|
-
private readonly resolvedOptions;
|
|
12
|
-
constructor(options: TestPulseReporterOptions);
|
|
13
|
-
onBegin(_config: FullConfig, _suite: Suite): Promise<void>;
|
|
14
|
-
onTestBegin(test: TestCase): void;
|
|
15
|
-
onTestEnd(test: TestCase, result: TestResult): void;
|
|
16
|
-
onStdOut(chunk: string | Buffer, test?: TestCase): void;
|
|
17
|
-
onStdErr(chunk: string | Buffer, test?: TestCase): void;
|
|
18
|
-
onEnd(result: FullResult): Promise<void>;
|
|
19
|
-
printsToStdio(): boolean;
|
|
20
|
-
private enqueueEvent;
|
|
21
|
-
private ensureRunStarted;
|
|
22
|
-
}
|
|
@@ -1,142 +0,0 @@
|
|
|
1
|
-
import { deleteLiveCaptureState, writeLiveCaptureState } from "../live/LiveSessionState.js";
|
|
2
|
-
import { resolveTestPulseEndpoint } from "../shared/endpoint.js";
|
|
3
|
-
import { ArtifactUploader } from "./ArtifactUploader.js";
|
|
4
|
-
import { EventQueue } from "./EventQueue.js";
|
|
5
|
-
import { resolveGitRuntimeMetadata } from "./GitRuntimeMetadataResolver.js";
|
|
6
|
-
import { RunClient } from "./RunClient.js";
|
|
7
|
-
export default class TestPulseReporter {
|
|
8
|
-
options;
|
|
9
|
-
runId = null;
|
|
10
|
-
isListOnly = false;
|
|
11
|
-
metadataPromise = null;
|
|
12
|
-
eventQueue = new EventQueue();
|
|
13
|
-
runClient;
|
|
14
|
-
artifactUploader;
|
|
15
|
-
resolvedOptions;
|
|
16
|
-
constructor(options) {
|
|
17
|
-
this.options = options;
|
|
18
|
-
this.resolvedOptions = {
|
|
19
|
-
...options,
|
|
20
|
-
endpoint: resolveTestPulseEndpoint(options.endpoint)
|
|
21
|
-
};
|
|
22
|
-
this.runClient = new RunClient(this.resolvedOptions);
|
|
23
|
-
this.artifactUploader = new ArtifactUploader(this.eventQueue, () => this.ensureRunStarted(), this.runClient);
|
|
24
|
-
}
|
|
25
|
-
async onBegin(_config, _suite) {
|
|
26
|
-
this.isListOnly = Boolean(_config.cliListOnly);
|
|
27
|
-
if (this.isListOnly) {
|
|
28
|
-
return;
|
|
29
|
-
}
|
|
30
|
-
await this.ensureRunStarted();
|
|
31
|
-
}
|
|
32
|
-
onTestBegin(test) {
|
|
33
|
-
if (this.isListOnly) {
|
|
34
|
-
return;
|
|
35
|
-
}
|
|
36
|
-
this.enqueueEvent("test.started", {
|
|
37
|
-
testId: test.id,
|
|
38
|
-
testName: test.title,
|
|
39
|
-
location: test.location?.file
|
|
40
|
-
});
|
|
41
|
-
}
|
|
42
|
-
onTestEnd(test, result) {
|
|
43
|
-
if (this.isListOnly) {
|
|
44
|
-
return;
|
|
45
|
-
}
|
|
46
|
-
const eventType = result.status === "passed"
|
|
47
|
-
? "test.passed"
|
|
48
|
-
: result.status === "skipped"
|
|
49
|
-
? "test.skipped"
|
|
50
|
-
: "test.failed";
|
|
51
|
-
const eventSequence = this.enqueueEvent(eventType, {
|
|
52
|
-
testId: test.id,
|
|
53
|
-
testName: test.title,
|
|
54
|
-
durationMs: result.duration,
|
|
55
|
-
retry: result.retry,
|
|
56
|
-
message: result.error?.message ?? null
|
|
57
|
-
});
|
|
58
|
-
if (result.retry > 0) {
|
|
59
|
-
this.enqueueEvent("test.retried", {
|
|
60
|
-
testId: test.id,
|
|
61
|
-
testName: test.title,
|
|
62
|
-
retry: result.retry
|
|
63
|
-
});
|
|
64
|
-
}
|
|
65
|
-
this.artifactUploader.enqueueArtifacts(test, result, eventSequence);
|
|
66
|
-
}
|
|
67
|
-
onStdOut(chunk, test) {
|
|
68
|
-
if (this.isListOnly) {
|
|
69
|
-
return;
|
|
70
|
-
}
|
|
71
|
-
this.enqueueEvent("log", {
|
|
72
|
-
level: "info",
|
|
73
|
-
message: chunk.toString(),
|
|
74
|
-
testId: test?.id ?? null
|
|
75
|
-
});
|
|
76
|
-
}
|
|
77
|
-
onStdErr(chunk, test) {
|
|
78
|
-
if (this.isListOnly) {
|
|
79
|
-
return;
|
|
80
|
-
}
|
|
81
|
-
this.enqueueEvent("log", {
|
|
82
|
-
level: "error",
|
|
83
|
-
message: chunk.toString(),
|
|
84
|
-
testId: test?.id ?? null
|
|
85
|
-
});
|
|
86
|
-
}
|
|
87
|
-
async onEnd(result) {
|
|
88
|
-
if (this.isListOnly || !this.runId) {
|
|
89
|
-
return;
|
|
90
|
-
}
|
|
91
|
-
const runId = await this.ensureRunStarted();
|
|
92
|
-
await this.eventQueue.flush();
|
|
93
|
-
try {
|
|
94
|
-
await this.runClient.completeRun(runId, result.status === "failed" ? "Failed" : "Completed");
|
|
95
|
-
}
|
|
96
|
-
finally {
|
|
97
|
-
await deleteLiveCaptureState(this.options.liveStreamSessionKey, this.options.liveCaptureStateDirectory);
|
|
98
|
-
}
|
|
99
|
-
}
|
|
100
|
-
printsToStdio() {
|
|
101
|
-
return false;
|
|
102
|
-
}
|
|
103
|
-
enqueueEvent(eventType, payload) {
|
|
104
|
-
return this.eventQueue.nextSequence(async (sequence) => {
|
|
105
|
-
const runId = await this.ensureRunStarted();
|
|
106
|
-
const envelope = {
|
|
107
|
-
sequence,
|
|
108
|
-
timestamp: new Date().toISOString(),
|
|
109
|
-
eventType,
|
|
110
|
-
payload
|
|
111
|
-
};
|
|
112
|
-
await this.runClient.sendEvents(runId, [envelope]);
|
|
113
|
-
});
|
|
114
|
-
}
|
|
115
|
-
async ensureRunStarted() {
|
|
116
|
-
if (this.runId) {
|
|
117
|
-
return this.runId;
|
|
118
|
-
}
|
|
119
|
-
if (!this.metadataPromise) {
|
|
120
|
-
this.metadataPromise = resolveGitRuntimeMetadata({
|
|
121
|
-
branch: this.options.branch,
|
|
122
|
-
commitSha: this.options.commitSha
|
|
123
|
-
});
|
|
124
|
-
}
|
|
125
|
-
const metadata = await this.metadataPromise;
|
|
126
|
-
const payload = await this.runClient.startRun(metadata);
|
|
127
|
-
this.runId = payload.runId;
|
|
128
|
-
if (!payload.streamPublisherToken) {
|
|
129
|
-
throw new Error("TestPulse live streaming token was not returned by the backend.");
|
|
130
|
-
}
|
|
131
|
-
await writeLiveCaptureState({
|
|
132
|
-
endpoint: this.resolvedOptions.endpoint,
|
|
133
|
-
projectId: this.options.projectId,
|
|
134
|
-
apiKey: this.options.apiKey,
|
|
135
|
-
runId: this.runId,
|
|
136
|
-
streamPublisherToken: payload.streamPublisherToken,
|
|
137
|
-
createdAt: new Date().toISOString(),
|
|
138
|
-
intervalMs: this.options.liveCaptureIntervalMs
|
|
139
|
-
}, this.options.liveStreamSessionKey, this.options.liveCaptureStateDirectory);
|
|
140
|
-
return this.runId;
|
|
141
|
-
}
|
|
142
|
-
}
|
|
@@ -1,19 +0,0 @@
|
|
|
1
|
-
import type { ResolvedTestPulseReporterOptions, RunEventEnvelope, StartRunResponse } from "./types.js";
|
|
2
|
-
export declare class RunClient {
|
|
3
|
-
private readonly options;
|
|
4
|
-
constructor(options: ResolvedTestPulseReporterOptions);
|
|
5
|
-
startRun(metadata: {
|
|
6
|
-
branch?: string;
|
|
7
|
-
commitSha?: string;
|
|
8
|
-
}): Promise<StartRunResponse>;
|
|
9
|
-
sendEvents(runId: string, events: RunEventEnvelope[]): Promise<void>;
|
|
10
|
-
completeRun(runId: string, status: "Failed" | "Completed"): Promise<void>;
|
|
11
|
-
uploadArtifact(runId: string, artifact: {
|
|
12
|
-
artifactType: "image" | "video";
|
|
13
|
-
filePath: string;
|
|
14
|
-
contentType: string;
|
|
15
|
-
testId: string;
|
|
16
|
-
eventSequence?: number;
|
|
17
|
-
}): Promise<void>;
|
|
18
|
-
private buildHeaders;
|
|
19
|
-
}
|
|
@@ -1,42 +0,0 @@
|
|
|
1
|
-
import { readFile } from "node:fs/promises";
|
|
2
|
-
import { postJson, postJsonOrThrow } from "../shared/http.js";
|
|
3
|
-
export class RunClient {
|
|
4
|
-
options;
|
|
5
|
-
constructor(options) {
|
|
6
|
-
this.options = options;
|
|
7
|
-
}
|
|
8
|
-
async startRun(metadata) {
|
|
9
|
-
const response = await postJsonOrThrow(`${this.options.endpoint}/ingest/v1/projects/${encodeURIComponent(this.options.projectId)}/runs/start`, {
|
|
10
|
-
runName: this.options.runName,
|
|
11
|
-
branch: metadata.branch,
|
|
12
|
-
commitSha: metadata.commitSha
|
|
13
|
-
}, "TestPulse run start failed with", this.buildHeaders());
|
|
14
|
-
return response.json();
|
|
15
|
-
}
|
|
16
|
-
async sendEvents(runId, events) {
|
|
17
|
-
await postJson(`${this.options.endpoint}/ingest/v1/projects/${encodeURIComponent(this.options.projectId)}/runs/${runId}/events`, { events }, this.buildHeaders());
|
|
18
|
-
}
|
|
19
|
-
async completeRun(runId, status) {
|
|
20
|
-
await postJson(`${this.options.endpoint}/ingest/v1/projects/${encodeURIComponent(this.options.projectId)}/runs/${runId}/complete`, { status }, this.buildHeaders());
|
|
21
|
-
}
|
|
22
|
-
async uploadArtifact(runId, artifact) {
|
|
23
|
-
const bytes = await readFile(artifact.filePath);
|
|
24
|
-
const formData = new FormData();
|
|
25
|
-
formData.append("artifactType", artifact.artifactType);
|
|
26
|
-
formData.append("testId", artifact.testId);
|
|
27
|
-
if (artifact.eventSequence !== undefined) {
|
|
28
|
-
formData.append("eventSequence", String(artifact.eventSequence));
|
|
29
|
-
}
|
|
30
|
-
formData.append("file", new Blob([bytes], { type: artifact.contentType }), artifact.filePath.split(/[\\/]/).pop() ?? `${artifact.artifactType}.bin`);
|
|
31
|
-
await fetch(`${this.options.endpoint}/ingest/v1/projects/${encodeURIComponent(this.options.projectId)}/runs/${runId}/artifacts`, {
|
|
32
|
-
method: "POST",
|
|
33
|
-
headers: this.buildHeaders(),
|
|
34
|
-
body: formData
|
|
35
|
-
});
|
|
36
|
-
}
|
|
37
|
-
buildHeaders() {
|
|
38
|
-
return {
|
|
39
|
-
"X-Project-Api-Key": this.options.apiKey
|
|
40
|
-
};
|
|
41
|
-
}
|
|
42
|
-
}
|
package/dist/reporter/types.d.ts
DELETED
|
@@ -1,25 +0,0 @@
|
|
|
1
|
-
export interface TestPulseReporterOptions {
|
|
2
|
-
endpoint?: string;
|
|
3
|
-
projectId: string;
|
|
4
|
-
apiKey: string;
|
|
5
|
-
runName?: string;
|
|
6
|
-
branch?: string;
|
|
7
|
-
commitSha?: string;
|
|
8
|
-
liveCaptureIntervalMs?: number;
|
|
9
|
-
liveStreamSessionKey?: string;
|
|
10
|
-
liveCaptureStateDirectory?: string;
|
|
11
|
-
}
|
|
12
|
-
export type EventPayload = Record<string, unknown>;
|
|
13
|
-
export type RunEventEnvelope = {
|
|
14
|
-
sequence: number;
|
|
15
|
-
timestamp: string;
|
|
16
|
-
eventType: string;
|
|
17
|
-
payload: EventPayload;
|
|
18
|
-
};
|
|
19
|
-
export type StartRunResponse = {
|
|
20
|
-
runId: string;
|
|
21
|
-
streamPublisherToken?: string;
|
|
22
|
-
};
|
|
23
|
-
export interface ResolvedTestPulseReporterOptions extends Omit<TestPulseReporterOptions, "endpoint"> {
|
|
24
|
-
endpoint: string;
|
|
25
|
-
}
|
package/dist/reporter/types.js
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export {};
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export declare function resolveTestPulseEndpoint(endpoint: string | undefined): string;
|
package/dist/shared/endpoint.js
DELETED
|
@@ -1,15 +0,0 @@
|
|
|
1
|
-
const productionEndpoint = "https://app.testpulse.run";
|
|
2
|
-
export function resolveTestPulseEndpoint(endpoint) {
|
|
3
|
-
const trimmed = trim(endpoint);
|
|
4
|
-
if (!trimmed) {
|
|
5
|
-
return productionEndpoint;
|
|
6
|
-
}
|
|
7
|
-
return trimTrailingSlash(trimmed);
|
|
8
|
-
}
|
|
9
|
-
function trim(value) {
|
|
10
|
-
const trimmed = value?.trim();
|
|
11
|
-
return trimmed ? trimmed : undefined;
|
|
12
|
-
}
|
|
13
|
-
function trimTrailingSlash(value) {
|
|
14
|
-
return value.replace(/\/+$/, "");
|
|
15
|
-
}
|
package/dist/shared/http.d.ts
DELETED
package/dist/shared/http.js
DELETED
|
@@ -1,17 +0,0 @@
|
|
|
1
|
-
export async function postJson(url, body, headers = {}) {
|
|
2
|
-
return fetch(url, {
|
|
3
|
-
method: "POST",
|
|
4
|
-
headers: {
|
|
5
|
-
"content-type": "application/json",
|
|
6
|
-
...headers
|
|
7
|
-
},
|
|
8
|
-
body: JSON.stringify(body)
|
|
9
|
-
});
|
|
10
|
-
}
|
|
11
|
-
export async function postJsonOrThrow(url, body, errorPrefix, headers = {}) {
|
|
12
|
-
const response = await postJson(url, body, headers);
|
|
13
|
-
if (!response.ok) {
|
|
14
|
-
throw new Error(`${errorPrefix} ${response.status}.`);
|
|
15
|
-
}
|
|
16
|
-
return response;
|
|
17
|
-
}
|