@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.
Files changed (52) hide show
  1. package/README.md +52 -20
  2. package/dist/index.cjs +57 -0
  3. package/dist/index.d.cts +18 -0
  4. package/dist/index.d.ts +18 -2
  5. package/dist/index.js +36 -1
  6. package/package.json +22 -44
  7. package/dist/fixtures/createTestPulseTest.d.ts +0 -14
  8. package/dist/fixtures/createTestPulseTest.js +0 -33
  9. package/dist/fixtures/options.d.ts +0 -2
  10. package/dist/fixtures/options.js +0 -36
  11. package/dist/fixtures/runtime.d.ts +0 -3
  12. package/dist/fixtures/runtime.js +0 -6
  13. package/dist/fixtures/types.d.ts +0 -8
  14. package/dist/fixtures/types.js +0 -1
  15. package/dist/fixtures.d.ts +0 -2
  16. package/dist/fixtures.js +0 -1
  17. package/dist/live/CaptureScheduler.d.ts +0 -22
  18. package/dist/live/CaptureScheduler.js +0 -87
  19. package/dist/live/EncoderProcess.d.ts +0 -5
  20. package/dist/live/EncoderProcess.js +0 -61
  21. package/dist/live/LiveCaptureService.d.ts +0 -2
  22. package/dist/live/LiveCaptureService.js +0 -103
  23. package/dist/live/LiveSessionResolver.d.ts +0 -2
  24. package/dist/live/LiveSessionResolver.js +0 -86
  25. package/dist/live/LiveSessionState.d.ts +0 -6
  26. package/dist/live/LiveSessionState.js +0 -20
  27. package/dist/live/Mp4FragmentParser.d.ts +0 -4
  28. package/dist/live/Mp4FragmentParser.js +0 -32
  29. package/dist/live/SegmentQueue.d.ts +0 -9
  30. package/dist/live/SegmentQueue.js +0 -32
  31. package/dist/live/StreamUploader.d.ts +0 -21
  32. package/dist/live/StreamUploader.js +0 -78
  33. package/dist/live/telemetry.d.ts +0 -3
  34. package/dist/live/telemetry.js +0 -15
  35. package/dist/live/types.d.ts +0 -42
  36. package/dist/live/types.js +0 -1
  37. package/dist/reporter/ArtifactUploader.d.ts +0 -13
  38. package/dist/reporter/ArtifactUploader.js +0 -48
  39. package/dist/reporter/EventQueue.d.ts +0 -7
  40. package/dist/reporter/EventQueue.js +0 -15
  41. package/dist/reporter/GitRuntimeMetadataResolver.d.ts +0 -11
  42. package/dist/reporter/GitRuntimeMetadataResolver.js +0 -91
  43. package/dist/reporter/PlaywrightReporter.d.ts +0 -22
  44. package/dist/reporter/PlaywrightReporter.js +0 -142
  45. package/dist/reporter/RunClient.d.ts +0 -19
  46. package/dist/reporter/RunClient.js +0 -42
  47. package/dist/reporter/types.d.ts +0 -25
  48. package/dist/reporter/types.js +0 -1
  49. package/dist/shared/endpoint.d.ts +0 -1
  50. package/dist/shared/endpoint.js +0 -15
  51. package/dist/shared/http.d.ts +0 -2
  52. 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
- }
@@ -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
- }
@@ -1 +0,0 @@
1
- export {};
@@ -1 +0,0 @@
1
- export declare function resolveTestPulseEndpoint(endpoint: string | undefined): string;
@@ -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
- }
@@ -1,2 +0,0 @@
1
- export declare function postJson(url: string, body: unknown, headers?: HeadersInit): Promise<Response>;
2
- export declare function postJsonOrThrow(url: string, body: unknown, errorPrefix: string, headers?: HeadersInit): Promise<Response>;
@@ -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
- }