@stanterprise/playwright-reporter 0.0.19 → 0.0.21
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 +56 -3
- package/dist/client/grpcClient.d.ts +9 -3
- package/dist/client/grpcClient.js +17 -4
- package/dist/client/messageQueue.d.ts +27 -0
- package/dist/client/messageQueue.js +71 -0
- package/dist/handlers/onBeginHandler.d.ts +2 -1
- package/dist/handlers/onBeginHandler.js +2 -2
- package/dist/handlers/onEndHandler.d.ts +2 -1
- package/dist/handlers/onEndHandler.js +2 -2
- package/dist/handlers/onErrorHandler.d.ts +2 -1
- package/dist/handlers/onErrorHandler.js +2 -2
- package/dist/handlers/onStepBeginHandler.d.ts +2 -1
- package/dist/handlers/onStepBeginHandler.js +2 -2
- package/dist/handlers/onStepEndHandler.d.ts +2 -1
- package/dist/handlers/onStepEndHandler.js +2 -2
- package/dist/handlers/onTestBeginHandler.d.ts +2 -1
- package/dist/handlers/onTestBeginHandler.js +2 -2
- package/dist/handlers/onTestEndHandler.d.ts +2 -1
- package/dist/handlers/onTestEndHandler.js +2 -2
- package/dist/handlers/onTestFailHandler.d.ts +2 -1
- package/dist/handlers/onTestFailHandler.js +2 -2
- package/dist/reporter.d.ts +1 -0
- package/dist/reporter.js +16 -8
- package/dist/types.d.ts +16 -0
- package/dist/utils/debugLogger.d.ts +13 -0
- package/dist/utils/debugLogger.js +87 -0
- package/dist/utils/index.d.ts +1 -0
- package/dist/utils/index.js +1 -0
- package/dist/utils/optionsMapper.js +7 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -9,12 +9,13 @@ Custom Playwright test reporter that sends test results to Stanterprise via gRPC
|
|
|
9
9
|
- 📎 **Attachment Support**: Handles screenshots, videos, and other test attachments
|
|
10
10
|
- 🔌 **gRPC Integration**: Communicates with Stanterprise backend via gRPC protocol
|
|
11
11
|
- ⚙️ **Configurable**: Supports environment variables and configuration options
|
|
12
|
+
- 🐛 **Debug Mode**: Captures outgoing gRPC messages to a JSONL file for troubleshooting
|
|
12
13
|
- 🛡️ **Error Resilient**: Graceful error handling that doesn't interrupt test execution
|
|
13
14
|
|
|
14
15
|
## Installation
|
|
15
16
|
|
|
16
17
|
```bash
|
|
17
|
-
npm install stanterprise
|
|
18
|
+
npm install @stanterprise/playwright-reporter --save-dev
|
|
18
19
|
```
|
|
19
20
|
|
|
20
21
|
## Configuration
|
|
@@ -27,7 +28,7 @@ Add the reporter to your `playwright.config.ts`:
|
|
|
27
28
|
import { defineConfig } from "@playwright/test";
|
|
28
29
|
|
|
29
30
|
export default defineConfig({
|
|
30
|
-
reporter: [["stanterprise
|
|
31
|
+
reporter: [["@stanterprise/playwright-reporter"]],
|
|
31
32
|
// ... other config
|
|
32
33
|
});
|
|
33
34
|
```
|
|
@@ -40,7 +41,7 @@ import { defineConfig } from "@playwright/test";
|
|
|
40
41
|
export default defineConfig({
|
|
41
42
|
reporter: [
|
|
42
43
|
[
|
|
43
|
-
"stanterprise
|
|
44
|
+
"@stanterprise/playwright-reporter",
|
|
44
45
|
{
|
|
45
46
|
grpcAddress: "localhost:50051", // gRPC server address
|
|
46
47
|
grpcEnabled: true, // Enable/disable gRPC reporting
|
|
@@ -48,6 +49,8 @@ export default defineConfig({
|
|
|
48
49
|
grpcMaxMessageSize: 104857600, // Max message size (100MB default)
|
|
49
50
|
maxAttachmentSize: 10485760, // Max attachment size (10MB default)
|
|
50
51
|
verbose: false, // Enable verbose logging
|
|
52
|
+
debug: false, // Enable debug mode (writes gRPC messages to JSONL file)
|
|
53
|
+
debugFile: "stanterprise-debug.jsonl", // Path for debug JSONL output
|
|
51
54
|
},
|
|
52
55
|
],
|
|
53
56
|
],
|
|
@@ -62,6 +65,8 @@ You can also configure the reporter using environment variables:
|
|
|
62
65
|
- `STANTERPRISE_GRPC_ADDRESS`: gRPC server address (default: `localhost:50051`)
|
|
63
66
|
- `STANTERPRISE_GRPC_ENABLED`: Enable/disable gRPC reporting (default: `true`)
|
|
64
67
|
- `STANTERPRISE_META_*`: Custom metadata fields for test runs (prefix will be stripped)
|
|
68
|
+
- `STANTERPRISE_DEBUG`: Enable debug mode — captures outgoing gRPC messages to a JSONL file (default: `false`; case-insensitive: `"true"`, `"TRUE"`, `"True"` all work)
|
|
69
|
+
- `STANTERPRISE_DEBUG_FILE`: File path for debug JSONL output (default: `stanterprise-debug.jsonl`)
|
|
65
70
|
|
|
66
71
|
#### Custom Metadata
|
|
67
72
|
|
|
@@ -94,6 +99,8 @@ npx playwright test
|
|
|
94
99
|
| `verbose` | boolean | `false` | Enable verbose logging |
|
|
95
100
|
| `grpcMaxRetries` | number | `3` | Maximum number of retry attempts for failed gRPC calls |
|
|
96
101
|
| `grpcRetryDelay` | number | `100` | Initial delay for retries in ms (uses exponential backoff) |
|
|
102
|
+
| `debug` | boolean | `false` | Enable debug mode — writes all outgoing gRPC messages to a JSONL file. **Warning:** output may include sensitive request payloads and grow very large. Use only in controlled environments. |
|
|
103
|
+
| `debugFile` | string | `stanterprise-debug.jsonl` | File path for debug JSONL output. Parent directories are created automatically if they do not exist. |
|
|
97
104
|
|
|
98
105
|
### Retry Configuration
|
|
99
106
|
|
|
@@ -133,6 +140,51 @@ To disable retries:
|
|
|
133
140
|
}
|
|
134
141
|
```
|
|
135
142
|
|
|
143
|
+
## Debug Mode
|
|
144
|
+
|
|
145
|
+
The reporter includes an opt-in debug mode that captures every outgoing gRPC message to a JSONL file. This is useful for troubleshooting, auditing what is sent to the backend, or diagnosing unexpected behavior.
|
|
146
|
+
|
|
147
|
+
> **Warning:** Debug output may include full request payloads — including attachment content and metadata — which can contain sensitive values. Debug files may also grow very large. Enable this only in controlled environments and handle the output file securely.
|
|
148
|
+
|
|
149
|
+
### Enabling Debug Mode
|
|
150
|
+
|
|
151
|
+
Via configuration:
|
|
152
|
+
|
|
153
|
+
```typescript
|
|
154
|
+
export default defineConfig({
|
|
155
|
+
reporter: [
|
|
156
|
+
[
|
|
157
|
+
"@stanterprise/playwright-reporter",
|
|
158
|
+
{
|
|
159
|
+
debug: true,
|
|
160
|
+
debugFile: "debug-output.jsonl", // optional, defaults to stanterprise-debug.jsonl
|
|
161
|
+
},
|
|
162
|
+
],
|
|
163
|
+
],
|
|
164
|
+
});
|
|
165
|
+
```
|
|
166
|
+
|
|
167
|
+
Via environment variables:
|
|
168
|
+
|
|
169
|
+
```bash
|
|
170
|
+
STANTERPRISE_DEBUG=true STANTERPRISE_DEBUG_FILE=debug-output.jsonl npx playwright test
|
|
171
|
+
```
|
|
172
|
+
|
|
173
|
+
### Debug File Format
|
|
174
|
+
|
|
175
|
+
Each line is a JSON object with three fields:
|
|
176
|
+
|
|
177
|
+
```json
|
|
178
|
+
{"timestamp":"2026-03-23T04:10:00.000Z","path":"/testsystem.v1.observer.TestEventCollector/ReportRunStart","message":{"run_id":"abc-123","name":"My Run"}}
|
|
179
|
+
{"timestamp":"2026-03-23T04:10:01.000Z","path":"/testsystem.v1.observer.TestEventCollector/ReportTestBegin","message":{"test_case":{"id":"t1","name":"login test"}}}
|
|
180
|
+
```
|
|
181
|
+
|
|
182
|
+
- `timestamp` — ISO 8601 timestamp when the message was sent
|
|
183
|
+
- `path` — gRPC method path
|
|
184
|
+
- `message` — full serialized request payload
|
|
185
|
+
|
|
186
|
+
The debug file is created/truncated at reporter initialization, so each test run starts with a clean file. Entries are only written for messages that are actually dispatched (i.e., when gRPC is enabled). Writes are performed asynchronously to minimize overhead.
|
|
187
|
+
|
|
136
188
|
## Sharding Support
|
|
137
189
|
|
|
138
190
|
The reporter automatically detects and reports when tests are running in sharded mode. When sharding is configured, the reporter includes the following metadata in test run events:
|
|
@@ -319,6 +371,7 @@ The reporter is organized into several modules:
|
|
|
319
371
|
- **utils/statusMapper.ts**: Maps Playwright statuses to protobuf enums
|
|
320
372
|
- **utils/attachmentProcessor.ts**: Processes test attachments
|
|
321
373
|
- **utils/timeHelpers.ts**: Handles timestamp and duration conversions
|
|
374
|
+
- **utils/debugLogger.ts**: Writes outgoing gRPC messages to a JSONL file when debug mode is enabled
|
|
322
375
|
|
|
323
376
|
## Troubleshooting
|
|
324
377
|
|
|
@@ -1,10 +1,16 @@
|
|
|
1
1
|
import { StanterpriseReporterOptions } from "../types";
|
|
2
2
|
import * as grpc from "@grpc/grpc-js";
|
|
3
|
+
import { MessageQueue } from "./messageQueue";
|
|
3
4
|
export default function getClient(options: StanterpriseReporterOptions): grpc.Client | null;
|
|
4
5
|
/**
|
|
5
|
-
* Helper: generic unary call using raw method path with retry support
|
|
6
|
+
* Helper: generic unary call using raw method path with retry support.
|
|
7
|
+
*
|
|
8
|
+
* When a `queue` is provided the call is enqueued and executed only after all
|
|
9
|
+
* previously enqueued calls have completed, guaranteeing sequential delivery to
|
|
10
|
+
* the server. Without a queue the call is executed immediately (legacy / test
|
|
11
|
+
* behaviour).
|
|
6
12
|
*/
|
|
7
|
-
export declare function reportUnary(options: StanterpriseReporterOptions, grpcClient: grpc.Client,
|
|
13
|
+
export declare function reportUnary(options: StanterpriseReporterOptions, grpcClient: grpc.Client, grpcPath: string, message: {
|
|
8
14
|
serialize?: (w?: any) => Uint8Array;
|
|
9
15
|
serializeBinary?: () => Uint8Array;
|
|
10
|
-
}, deadlineMs?: number): Promise<Buffer>;
|
|
16
|
+
}, deadlineMs?: number, queue?: MessageQueue): Promise<Buffer>;
|
|
@@ -36,6 +36,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
36
36
|
exports.default = getClient;
|
|
37
37
|
exports.reportUnary = reportUnary;
|
|
38
38
|
const grpc = __importStar(require("@grpc/grpc-js"));
|
|
39
|
+
const debugLogger_1 = require("../utils/debugLogger");
|
|
39
40
|
function getClient(options) {
|
|
40
41
|
try {
|
|
41
42
|
const maxMessageSize = options.grpcMaxMessageSize || 104857600; // 100MB default
|
|
@@ -145,12 +146,24 @@ async function attemptWithRetries(options, grpcClient, path, message, deadlineMs
|
|
|
145
146
|
throw lastError || new Error("Retry loop exhausted without result");
|
|
146
147
|
}
|
|
147
148
|
/**
|
|
148
|
-
* Helper: generic unary call using raw method path with retry support
|
|
149
|
+
* Helper: generic unary call using raw method path with retry support.
|
|
150
|
+
*
|
|
151
|
+
* When a `queue` is provided the call is enqueued and executed only after all
|
|
152
|
+
* previously enqueued calls have completed, guaranteeing sequential delivery to
|
|
153
|
+
* the server. Without a queue the call is executed immediately (legacy / test
|
|
154
|
+
* behaviour).
|
|
149
155
|
*/
|
|
150
|
-
async function reportUnary(options, grpcClient,
|
|
156
|
+
async function reportUnary(options, grpcClient, grpcPath, message, deadlineMs = 1000, queue) {
|
|
151
157
|
if (!options.grpcEnabled || !grpcClient) {
|
|
152
158
|
return Buffer.alloc(0);
|
|
153
159
|
}
|
|
154
|
-
const
|
|
155
|
-
|
|
160
|
+
const execute = async () => {
|
|
161
|
+
await (0, debugLogger_1.writeDebugEntry)(options, grpcPath, message);
|
|
162
|
+
const maxRetries = options.grpcMaxRetries ?? 3;
|
|
163
|
+
return attemptWithRetries(options, grpcClient, grpcPath, message, deadlineMs, maxRetries);
|
|
164
|
+
};
|
|
165
|
+
if (queue) {
|
|
166
|
+
return queue.enqueue(execute);
|
|
167
|
+
}
|
|
168
|
+
return execute();
|
|
156
169
|
}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* A sequential async message queue that processes enqueued tasks one at a time,
|
|
3
|
+
* guaranteeing that each task completes before the next one begins.
|
|
4
|
+
*
|
|
5
|
+
* This ensures that gRPC messages are delivered to the server in the exact order
|
|
6
|
+
* they were enqueued, regardless of how quickly each individual call completes.
|
|
7
|
+
* It also serializes debug file writes as a side effect, since those are performed
|
|
8
|
+
* inside each task.
|
|
9
|
+
*/
|
|
10
|
+
export declare class MessageQueue {
|
|
11
|
+
private tasks;
|
|
12
|
+
private processing;
|
|
13
|
+
private drainResolvers;
|
|
14
|
+
/**
|
|
15
|
+
* Adds a task to the queue and returns a Promise that resolves (or rejects)
|
|
16
|
+
* when the task has been executed. If the queue is idle the task starts
|
|
17
|
+
* immediately; otherwise it waits for all preceding tasks to finish first.
|
|
18
|
+
*/
|
|
19
|
+
enqueue<T>(fn: () => Promise<T>): Promise<T>;
|
|
20
|
+
/**
|
|
21
|
+
* Returns a Promise that resolves once the queue has been fully drained
|
|
22
|
+
* (i.e. all currently enqueued tasks have finished). Resolves immediately
|
|
23
|
+
* if the queue is already empty.
|
|
24
|
+
*/
|
|
25
|
+
drain(): Promise<void>;
|
|
26
|
+
private processNext;
|
|
27
|
+
}
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.MessageQueue = void 0;
|
|
4
|
+
/**
|
|
5
|
+
* A sequential async message queue that processes enqueued tasks one at a time,
|
|
6
|
+
* guaranteeing that each task completes before the next one begins.
|
|
7
|
+
*
|
|
8
|
+
* This ensures that gRPC messages are delivered to the server in the exact order
|
|
9
|
+
* they were enqueued, regardless of how quickly each individual call completes.
|
|
10
|
+
* It also serializes debug file writes as a side effect, since those are performed
|
|
11
|
+
* inside each task.
|
|
12
|
+
*/
|
|
13
|
+
class MessageQueue {
|
|
14
|
+
constructor() {
|
|
15
|
+
this.tasks = [];
|
|
16
|
+
this.processing = false;
|
|
17
|
+
this.drainResolvers = [];
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* Adds a task to the queue and returns a Promise that resolves (or rejects)
|
|
21
|
+
* when the task has been executed. If the queue is idle the task starts
|
|
22
|
+
* immediately; otherwise it waits for all preceding tasks to finish first.
|
|
23
|
+
*/
|
|
24
|
+
enqueue(fn) {
|
|
25
|
+
return new Promise((resolve, reject) => {
|
|
26
|
+
this.tasks.push(async () => {
|
|
27
|
+
try {
|
|
28
|
+
const result = await fn();
|
|
29
|
+
resolve(result);
|
|
30
|
+
}
|
|
31
|
+
catch (e) {
|
|
32
|
+
reject(e);
|
|
33
|
+
}
|
|
34
|
+
});
|
|
35
|
+
if (!this.processing) {
|
|
36
|
+
this.processNext();
|
|
37
|
+
}
|
|
38
|
+
});
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* Returns a Promise that resolves once the queue has been fully drained
|
|
42
|
+
* (i.e. all currently enqueued tasks have finished). Resolves immediately
|
|
43
|
+
* if the queue is already empty.
|
|
44
|
+
*/
|
|
45
|
+
drain() {
|
|
46
|
+
if (!this.processing && this.tasks.length === 0) {
|
|
47
|
+
return Promise.resolve();
|
|
48
|
+
}
|
|
49
|
+
return new Promise((resolve) => {
|
|
50
|
+
this.drainResolvers.push(resolve);
|
|
51
|
+
});
|
|
52
|
+
}
|
|
53
|
+
async processNext() {
|
|
54
|
+
this.processing = true;
|
|
55
|
+
while (this.tasks.length > 0) {
|
|
56
|
+
const task = this.tasks.shift();
|
|
57
|
+
try {
|
|
58
|
+
await task();
|
|
59
|
+
}
|
|
60
|
+
catch {
|
|
61
|
+
// Each task's promise already carries the error to its caller.
|
|
62
|
+
// Errors must not stop the queue from processing subsequent tasks.
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
this.processing = false;
|
|
66
|
+
const resolvers = this.drainResolvers;
|
|
67
|
+
this.drainResolvers = [];
|
|
68
|
+
resolvers.forEach((r) => r());
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
exports.MessageQueue = MessageQueue;
|
|
@@ -2,4 +2,5 @@ import { FullConfig } from "@playwright/test";
|
|
|
2
2
|
import { Suite } from "@playwright/test/reporter";
|
|
3
3
|
import { StanterpriseReporterOptions } from "../types";
|
|
4
4
|
import * as grpc from "@grpc/grpc-js";
|
|
5
|
-
|
|
5
|
+
import { MessageQueue } from "../client/messageQueue";
|
|
6
|
+
export declare function handleOnBeginEvent(config: FullConfig, suite: Suite, name: string, runId: string, client: grpc.Client, options: StanterpriseReporterOptions, queue?: MessageQueue): void;
|
|
@@ -6,7 +6,7 @@ const grpcClient_1 = require("../client/grpcClient");
|
|
|
6
6
|
const entities_1 = require("@stanterprise/protobuf/testsystem/v1/entities");
|
|
7
7
|
const utils_1 = require("../utils");
|
|
8
8
|
const common_1 = require("@stanterprise/protobuf/testsystem/v1/common");
|
|
9
|
-
function handleOnBeginEvent(config, suite, name, runId, client, options) {
|
|
9
|
+
function handleOnBeginEvent(config, suite, name, runId, client, options, queue) {
|
|
10
10
|
// Report root suite and all child suites recursively
|
|
11
11
|
// Convert all metadata values to strings for protobuf compatibility
|
|
12
12
|
// Use plain object instead of Map - protobuf expects object literals
|
|
@@ -31,7 +31,7 @@ function handleOnBeginEvent(config, suite, name, runId, client, options) {
|
|
|
31
31
|
total_tests: suite.allTests().length,
|
|
32
32
|
metadata: (0, utils_1.toMetadataMap)(metadata),
|
|
33
33
|
});
|
|
34
|
-
(0, grpcClient_1.reportUnary)(options, client, "/testsystem.v1.observer.TestEventCollector/ReportRunStart", request, options.grpcTimeout).catch((e) => {
|
|
34
|
+
(0, grpcClient_1.reportUnary)(options, client, "/testsystem.v1.observer.TestEventCollector/ReportRunStart", request, options.grpcTimeout, queue).catch((e) => {
|
|
35
35
|
const details = e instanceof Error ? `${e.message}` : String(e);
|
|
36
36
|
console.warn(`onBegin. gRPC disabled for the remainder of the run. Address=${options.grpcAddress}. Details: ${details}`);
|
|
37
37
|
});
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { FullResult } from "@playwright/test/reporter";
|
|
2
2
|
import { StanterpriseReporterOptions } from "../types";
|
|
3
3
|
import * as grpc from "@grpc/grpc-js";
|
|
4
|
-
|
|
4
|
+
import { MessageQueue } from "../client/messageQueue";
|
|
5
|
+
export declare function handleOnEndEvent(result: FullResult, runId: string, client: grpc.Client, options: StanterpriseReporterOptions, queue?: MessageQueue): void;
|
|
@@ -19,7 +19,7 @@ function mapTestStatus(status) {
|
|
|
19
19
|
return common_1.TestStatus.UNKNOWN;
|
|
20
20
|
}
|
|
21
21
|
}
|
|
22
|
-
function handleOnEndEvent(result, runId, client, options) {
|
|
22
|
+
function handleOnEndEvent(result, runId, client, options, queue) {
|
|
23
23
|
// Validate and sanitize inputs to prevent serialization failures
|
|
24
24
|
const startTimeMs = result.startTime?.getTime();
|
|
25
25
|
const duration = result.duration;
|
|
@@ -36,7 +36,7 @@ function handleOnEndEvent(result, runId, client, options) {
|
|
|
36
36
|
start_time: (0, utils_1.createTimestampFromMs)(validStartTimeMs),
|
|
37
37
|
duration: (0, utils_1.createDuration)(validDuration),
|
|
38
38
|
});
|
|
39
|
-
(0, grpcClient_1.reportUnary)(options, client, "/testsystem.v1.observer.TestEventCollector/ReportRunEnd", request, options.grpcTimeout).catch((error) => {
|
|
39
|
+
(0, grpcClient_1.reportUnary)(options, client, "/testsystem.v1.observer.TestEventCollector/ReportRunEnd", request, options.grpcTimeout, queue).catch((error) => {
|
|
40
40
|
if (error instanceof Error) {
|
|
41
41
|
console.error(`Failed to report end event: ${error.message}`, {
|
|
42
42
|
stack: error.stack,
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { TestError } from "@playwright/test/reporter";
|
|
2
2
|
import { StanterpriseReporterOptions } from "../types";
|
|
3
3
|
import * as grpc from "@grpc/grpc-js";
|
|
4
|
-
|
|
4
|
+
import { MessageQueue } from "../client/messageQueue";
|
|
5
|
+
export declare function handleOnErrorEvent(error: TestError, runId: string, grpcClient: grpc.Client, options: StanterpriseReporterOptions, queue?: MessageQueue): void;
|
|
@@ -4,12 +4,12 @@ exports.handleOnErrorEvent = handleOnErrorEvent;
|
|
|
4
4
|
const events_1 = require("@stanterprise/protobuf/testsystem/v1/events");
|
|
5
5
|
const timeHelpers_1 = require("../utils/timeHelpers");
|
|
6
6
|
const grpcClient_1 = require("../client/grpcClient");
|
|
7
|
-
function handleOnErrorEvent(error, runId, grpcClient, options) {
|
|
7
|
+
function handleOnErrorEvent(error, runId, grpcClient, options, queue) {
|
|
8
8
|
const request = new events_1.StdErrorEventRequest({
|
|
9
9
|
run_id: runId,
|
|
10
10
|
message: error.message,
|
|
11
11
|
timestamp: (0, timeHelpers_1.createTimestampFromMs)(Date.now()),
|
|
12
12
|
});
|
|
13
13
|
// Fire-and-forget to avoid slowing tests
|
|
14
|
-
(0, grpcClient_1.reportUnary)(options, grpcClient, "/testsystem.v1.observer.TestEventCollector/ReportStdError", request, options.grpcTimeout).catch((e) => console.error("Failed to report standard error", e));
|
|
14
|
+
(0, grpcClient_1.reportUnary)(options, grpcClient, "/testsystem.v1.observer.TestEventCollector/ReportStdError", request, options.grpcTimeout, queue).catch((e) => console.error("Failed to report standard error", e));
|
|
15
15
|
}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { TestCase, TestResult, TestStep } from "@playwright/test/reporter";
|
|
2
2
|
import { StanterpriseReporterOptions } from "../types";
|
|
3
3
|
import * as grpc from "@grpc/grpc-js";
|
|
4
|
-
|
|
4
|
+
import { MessageQueue } from "../client/messageQueue";
|
|
5
|
+
export declare function handleOnStepBeginEvent(test: TestCase, result: TestResult, step: TestStep, runId: string, client: grpc.Client, options: StanterpriseReporterOptions, queue?: MessageQueue): void;
|
|
@@ -7,7 +7,7 @@ const utils_1 = require("../utils");
|
|
|
7
7
|
const grpcClient_1 = require("../client/grpcClient");
|
|
8
8
|
const utils_2 = require("../utils");
|
|
9
9
|
const common_1 = require("@stanterprise/protobuf/testsystem/v1/common");
|
|
10
|
-
function handleOnStepBeginEvent(test, result, step, runId, client, options) {
|
|
10
|
+
function handleOnStepBeginEvent(test, result, step, runId, client, options, queue) {
|
|
11
11
|
// Build metadata from step annotations
|
|
12
12
|
const metadata = (0, utils_1.buildStepMetadata)(step);
|
|
13
13
|
// Get parent step ID if this step has a parent
|
|
@@ -34,5 +34,5 @@ function handleOnStepBeginEvent(test, result, step, runId, client, options) {
|
|
|
34
34
|
}),
|
|
35
35
|
});
|
|
36
36
|
// Fire-and-forget to avoid slowing tests
|
|
37
|
-
(0, grpcClient_1.reportUnary)(options, client, "/testsystem.v1.observer.TestEventCollector/ReportStepBegin", request, options.grpcTimeout).catch((e) => console.error("Failed to report step begin", e));
|
|
37
|
+
(0, grpcClient_1.reportUnary)(options, client, "/testsystem.v1.observer.TestEventCollector/ReportStepBegin", request, options.grpcTimeout, queue).catch((e) => console.error("Failed to report step begin", e));
|
|
38
38
|
}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { TestCase, TestResult, TestStep } from "@playwright/test/reporter";
|
|
2
2
|
import { StanterpriseReporterOptions } from "../types";
|
|
3
3
|
import * as grpc from "@grpc/grpc-js";
|
|
4
|
-
|
|
4
|
+
import { MessageQueue } from "../client/messageQueue";
|
|
5
|
+
export declare function handleOnStepEndEvent(test: TestCase, result: TestResult, step: TestStep, runId: string, client: grpc.Client, options: StanterpriseReporterOptions, queue?: MessageQueue): void;
|
|
@@ -6,7 +6,7 @@ const events_1 = require("@stanterprise/protobuf/testsystem/v1/events");
|
|
|
6
6
|
const utils_1 = require("../utils");
|
|
7
7
|
const grpcClient_1 = require("../client/grpcClient");
|
|
8
8
|
const utils_2 = require("../utils");
|
|
9
|
-
function handleOnStepEndEvent(test, result, step, runId, client, options) {
|
|
9
|
+
function handleOnStepEndEvent(test, result, step, runId, client, options, queue) {
|
|
10
10
|
// Map step error to status
|
|
11
11
|
const stepStatus = (0, utils_1.mapStepStatus)(!!step.error);
|
|
12
12
|
// Build metadata from step annotations
|
|
@@ -55,5 +55,5 @@ function handleOnStepEndEvent(test, result, step, runId, client, options) {
|
|
|
55
55
|
}),
|
|
56
56
|
});
|
|
57
57
|
// Fire-and-forget to avoid slowing tests
|
|
58
|
-
(0, grpcClient_1.reportUnary)(options, client, "/testsystem.v1.observer.TestEventCollector/ReportStepEnd", request, options.grpcTimeout).catch((e) => console.error("Failed to report step end", e));
|
|
58
|
+
(0, grpcClient_1.reportUnary)(options, client, "/testsystem.v1.observer.TestEventCollector/ReportStepEnd", request, options.grpcTimeout, queue).catch((e) => console.error("Failed to report step end", e));
|
|
59
59
|
}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { TestCase, TestResult } from "@playwright/test/reporter";
|
|
2
2
|
import { StanterpriseReporterOptions } from "../types";
|
|
3
3
|
import * as grpc from "@grpc/grpc-js";
|
|
4
|
-
|
|
4
|
+
import { MessageQueue } from "../client/messageQueue";
|
|
5
|
+
export declare function handleOnTestBeginEvent(test: TestCase, result: TestResult, runId: string, client: grpc.Client, options: StanterpriseReporterOptions, queue?: MessageQueue): void;
|
|
@@ -6,7 +6,7 @@ const events_1 = require("@stanterprise/protobuf/testsystem/v1/events");
|
|
|
6
6
|
const utils_1 = require("../utils");
|
|
7
7
|
const grpcClient_1 = require("../client/grpcClient");
|
|
8
8
|
const common_1 = require("@stanterprise/protobuf/testsystem/v1/common");
|
|
9
|
-
function handleOnTestBeginEvent(test, result, runId, client, options) {
|
|
9
|
+
function handleOnTestBeginEvent(test, result, runId, client, options, queue) {
|
|
10
10
|
// Build and send the TestBegin event via generic unary call.
|
|
11
11
|
const request = new events_1.TestBeginEventRequest({
|
|
12
12
|
test_case: new entities_1.TestCaseRun({
|
|
@@ -25,7 +25,7 @@ function handleOnTestBeginEvent(test, result, runId, client, options) {
|
|
|
25
25
|
}),
|
|
26
26
|
});
|
|
27
27
|
// Fire-and-forget to avoid slowing tests; log errors once.
|
|
28
|
-
(0, grpcClient_1.reportUnary)(options, client, "/testsystem.v1.observer.TestEventCollector/ReportTestBegin", request, options.grpcTimeout).catch((e) => {
|
|
28
|
+
(0, grpcClient_1.reportUnary)(options, client, "/testsystem.v1.observer.TestEventCollector/ReportTestBegin", request, options.grpcTimeout, queue).catch((e) => {
|
|
29
29
|
const details = e instanceof Error ? `${e.message}` : String(e);
|
|
30
30
|
console.warn(`Failed to report test begin. Details: ${details}`);
|
|
31
31
|
});
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { TestCase, TestResult } from "@playwright/test/reporter";
|
|
2
2
|
import { StanterpriseReporterOptions } from "../types";
|
|
3
3
|
import * as grpc from "@grpc/grpc-js";
|
|
4
|
-
|
|
4
|
+
import { MessageQueue } from "../client/messageQueue";
|
|
5
|
+
export declare function handleOnTestEndEvent(test: TestCase, result: TestResult, runId: string, client: grpc.Client, options: StanterpriseReporterOptions, queue?: MessageQueue): void;
|
|
@@ -10,7 +10,7 @@ const utils_2 = require("../utils");
|
|
|
10
10
|
* Constant for converting bytes to megabytes
|
|
11
11
|
*/
|
|
12
12
|
const BYTES_PER_MB = 1048576;
|
|
13
|
-
function handleOnTestEndEvent(test, result, runId, client, options) {
|
|
13
|
+
function handleOnTestEndEvent(test, result, runId, client, options, queue) {
|
|
14
14
|
// Map Playwright test status to protobuf TestStatus
|
|
15
15
|
const testStatus = (0, utils_1.mapTestStatus)(result.status);
|
|
16
16
|
// Process attachments (screenshots, videos, etc.)
|
|
@@ -51,7 +51,7 @@ function handleOnTestEndEvent(test, result, runId, client, options) {
|
|
|
51
51
|
`Error length: ${errorMessage.length + stackTrace.length} bytes`);
|
|
52
52
|
}
|
|
53
53
|
// Fire-and-forget to avoid slowing tests
|
|
54
|
-
(0, grpcClient_1.reportUnary)(options, client, "/testsystem.v1.observer.TestEventCollector/ReportTestEnd", request, options.grpcTimeout).catch((e) => {
|
|
54
|
+
(0, grpcClient_1.reportUnary)(options, client, "/testsystem.v1.observer.TestEventCollector/ReportTestEnd", request, options.grpcTimeout, queue).catch((e) => {
|
|
55
55
|
console.error(`Failed to report test end for "${test.title}" (payload size: ${(payloadSize / BYTES_PER_MB).toFixed(2)}MB)`, e);
|
|
56
56
|
});
|
|
57
57
|
}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { TestCase, TestResult } from "@playwright/test/reporter";
|
|
2
2
|
import { StanterpriseReporterOptions } from "../types";
|
|
3
3
|
import * as grpc from "@grpc/grpc-js";
|
|
4
|
-
|
|
4
|
+
import { MessageQueue } from "../client/messageQueue";
|
|
5
|
+
export declare function handleOnTestFailEvent(test: TestCase, result: TestResult, runId: string, grpcClient: grpc.Client, options: StanterpriseReporterOptions, queue?: MessageQueue): void;
|
|
@@ -4,7 +4,7 @@ exports.handleOnTestFailEvent = handleOnTestFailEvent;
|
|
|
4
4
|
const utils_1 = require("../utils");
|
|
5
5
|
const events_1 = require("@stanterprise/protobuf/testsystem/v1/events");
|
|
6
6
|
const grpcClient_1 = require("../client/grpcClient");
|
|
7
|
-
function handleOnTestFailEvent(test, result, runId, grpcClient, options) {
|
|
7
|
+
function handleOnTestFailEvent(test, result, runId, grpcClient, options, queue) {
|
|
8
8
|
// Extract failure details
|
|
9
9
|
const { errorMessage: failureMessage, stackTrace } = (0, utils_1.extractErrorInfo)(result);
|
|
10
10
|
// Process attachments for failed tests
|
|
@@ -20,5 +20,5 @@ function handleOnTestFailEvent(test, result, runId, grpcClient, options) {
|
|
|
20
20
|
retry_index: result.retry,
|
|
21
21
|
});
|
|
22
22
|
// Fire-and-forget to avoid slowing tests
|
|
23
|
-
(0, grpcClient_1.reportUnary)(options, grpcClient, "/testsystem.v1.observer.TestEventCollector/ReportTestFailure", request, options.grpcTimeout).catch((e) => console.error("Failed to report test failure", e));
|
|
23
|
+
(0, grpcClient_1.reportUnary)(options, grpcClient, "/testsystem.v1.observer.TestEventCollector/ReportTestFailure", request, options.grpcTimeout, queue).catch((e) => console.error("Failed to report test failure", e));
|
|
24
24
|
}
|
package/dist/reporter.d.ts
CHANGED
|
@@ -5,6 +5,7 @@ export default class StanterpriseReporter implements Reporter {
|
|
|
5
5
|
private options;
|
|
6
6
|
private runId;
|
|
7
7
|
private runStartTime;
|
|
8
|
+
private messageQueue;
|
|
8
9
|
constructor(options?: StanterpriseReporterOptions);
|
|
9
10
|
onBegin(config: FullConfig, suite: Suite): void;
|
|
10
11
|
onExit(): Promise<void>;
|
package/dist/reporter.js
CHANGED
|
@@ -5,7 +5,9 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
5
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
6
|
const crypto_1 = require("crypto");
|
|
7
7
|
const optionsMapper_1 = __importDefault(require("./utils/optionsMapper"));
|
|
8
|
+
const debugLogger_1 = require("./utils/debugLogger");
|
|
8
9
|
const grpcClient_1 = __importDefault(require("./client/grpcClient"));
|
|
10
|
+
const messageQueue_1 = require("./client/messageQueue");
|
|
9
11
|
const handlers_1 = require("./handlers");
|
|
10
12
|
class StanterpriseReporter {
|
|
11
13
|
constructor(options = {}) {
|
|
@@ -15,6 +17,8 @@ class StanterpriseReporter {
|
|
|
15
17
|
// Generate a unique run ID for this test run
|
|
16
18
|
this.runId = "";
|
|
17
19
|
this.runStartTime = new Date();
|
|
20
|
+
// Sequential message queue – guarantees ordered delivery to the server
|
|
21
|
+
this.messageQueue = new messageQueue_1.MessageQueue();
|
|
18
22
|
this.options = (0, optionsMapper_1.default)(options);
|
|
19
23
|
if (this.options.verbose) {
|
|
20
24
|
console.log("Stanterprise Reporter: Initialized with options:", {
|
|
@@ -26,6 +30,8 @@ class StanterpriseReporter {
|
|
|
26
30
|
}
|
|
27
31
|
// Generate a UUID for runId
|
|
28
32
|
this.runId = process.env.STANTERPRISE_RUN_ID || (0, crypto_1.randomUUID)();
|
|
33
|
+
// Initialize debug file (creates/truncates it) when debug mode is enabled
|
|
34
|
+
(0, debugLogger_1.initDebugFile)(this.options);
|
|
29
35
|
}
|
|
30
36
|
onBegin(config, suite) {
|
|
31
37
|
// Lazily create the client if enabled.
|
|
@@ -50,10 +56,12 @@ class StanterpriseReporter {
|
|
|
50
56
|
}
|
|
51
57
|
}
|
|
52
58
|
if (this.options.grpcEnabled) {
|
|
53
|
-
(0, handlers_1.handleOnBeginEvent)(config, suite, process.env.STANTERPRISE_TEST_RUN_NAME || "Playwright Test Run", this.runId, this.grpcClient, this.options);
|
|
59
|
+
(0, handlers_1.handleOnBeginEvent)(config, suite, process.env.STANTERPRISE_TEST_RUN_NAME || "Playwright Test Run", this.runId, this.grpcClient, this.options, this.messageQueue);
|
|
54
60
|
}
|
|
55
61
|
}
|
|
56
62
|
async onExit() {
|
|
63
|
+
// Wait for all enqueued messages to be sent before closing the connection.
|
|
64
|
+
await this.messageQueue.drain();
|
|
57
65
|
console.log(`Stanterprise Reporter: Test run completed - Run ID: ${this.runId}`);
|
|
58
66
|
// Cleanup gRPC client
|
|
59
67
|
try {
|
|
@@ -73,7 +81,7 @@ class StanterpriseReporter {
|
|
|
73
81
|
console.log(`Playwright start time: ${result.startTime.toISOString()}`);
|
|
74
82
|
}
|
|
75
83
|
if (this.options.grpcEnabled) {
|
|
76
|
-
(0, handlers_1.handleOnEndEvent)(result, this.runId, this.grpcClient, this.options);
|
|
84
|
+
(0, handlers_1.handleOnEndEvent)(result, this.runId, this.grpcClient, this.options, this.messageQueue);
|
|
77
85
|
}
|
|
78
86
|
return Promise.resolve();
|
|
79
87
|
}
|
|
@@ -85,7 +93,7 @@ class StanterpriseReporter {
|
|
|
85
93
|
console.log(` Test ID: ${test.id}`);
|
|
86
94
|
}
|
|
87
95
|
if (this.options.grpcEnabled) {
|
|
88
|
-
(0, handlers_1.handleOnTestBeginEvent)(test, result, this.runId, this.grpcClient, this.options);
|
|
96
|
+
(0, handlers_1.handleOnTestBeginEvent)(test, result, this.runId, this.grpcClient, this.options, this.messageQueue);
|
|
89
97
|
}
|
|
90
98
|
}
|
|
91
99
|
async onStepBegin(test, result, step) {
|
|
@@ -94,7 +102,7 @@ class StanterpriseReporter {
|
|
|
94
102
|
console.log(` Category: ${step.category}`);
|
|
95
103
|
}
|
|
96
104
|
if (this.options.grpcEnabled) {
|
|
97
|
-
(0, handlers_1.handleOnStepBeginEvent)(test, result, step, this.runId, this.grpcClient, this.options);
|
|
105
|
+
(0, handlers_1.handleOnStepBeginEvent)(test, result, step, this.runId, this.grpcClient, this.options, this.messageQueue);
|
|
98
106
|
}
|
|
99
107
|
}
|
|
100
108
|
onStepEnd(test, result, step) {
|
|
@@ -103,7 +111,7 @@ class StanterpriseReporter {
|
|
|
103
111
|
console.log(` Duration: ${step.duration}ms`);
|
|
104
112
|
}
|
|
105
113
|
if (this.options.grpcEnabled) {
|
|
106
|
-
(0, handlers_1.handleOnStepEndEvent)(test, result, step, this.runId, this.grpcClient, this.options);
|
|
114
|
+
(0, handlers_1.handleOnStepEndEvent)(test, result, step, this.runId, this.grpcClient, this.options, this.messageQueue);
|
|
107
115
|
}
|
|
108
116
|
}
|
|
109
117
|
onTestEnd(test, result) {
|
|
@@ -113,7 +121,7 @@ class StanterpriseReporter {
|
|
|
113
121
|
console.log(` Duration: ${result.duration}ms`);
|
|
114
122
|
}
|
|
115
123
|
if (this.options.grpcEnabled) {
|
|
116
|
-
(0, handlers_1.handleOnTestEndEvent)(test, result, this.runId, this.grpcClient, this.options);
|
|
124
|
+
(0, handlers_1.handleOnTestEndEvent)(test, result, this.runId, this.grpcClient, this.options, this.messageQueue);
|
|
117
125
|
}
|
|
118
126
|
}
|
|
119
127
|
onTestFail(test, result) {
|
|
@@ -121,7 +129,7 @@ class StanterpriseReporter {
|
|
|
121
129
|
console.log(`Stanterprise Reporter: Test failed - ${test.title}`);
|
|
122
130
|
}
|
|
123
131
|
if (this.options.grpcEnabled) {
|
|
124
|
-
(0, handlers_1.handleOnTestFailEvent)(test, result, this.runId, this.grpcClient, this.options);
|
|
132
|
+
(0, handlers_1.handleOnTestFailEvent)(test, result, this.runId, this.grpcClient, this.options, this.messageQueue);
|
|
125
133
|
}
|
|
126
134
|
}
|
|
127
135
|
onError(error) {
|
|
@@ -131,7 +139,7 @@ class StanterpriseReporter {
|
|
|
131
139
|
console.error(`Stack trace: ${error.stack}`);
|
|
132
140
|
}
|
|
133
141
|
if (this.options.grpcEnabled) {
|
|
134
|
-
(0, handlers_1.handleOnErrorEvent)(error, this.runId, this.grpcClient, this.options);
|
|
142
|
+
(0, handlers_1.handleOnErrorEvent)(error, this.runId, this.grpcClient, this.options, this.messageQueue);
|
|
135
143
|
}
|
|
136
144
|
}
|
|
137
145
|
onStdErr(chunk, test, result) {
|
package/dist/types.d.ts
CHANGED
|
@@ -47,6 +47,22 @@ export interface StanterpriseReporterOptions {
|
|
|
47
47
|
* @default 100
|
|
48
48
|
*/
|
|
49
49
|
grpcRetryDelay?: number;
|
|
50
|
+
/**
|
|
51
|
+
* Whether to enable debug mode, which writes all outgoing gRPC messages to a JSONL file.
|
|
52
|
+
* Warning: debug output may include full request payloads, including attachment content
|
|
53
|
+
* and metadata, which can contain sensitive values. Debug files may also grow very large.
|
|
54
|
+
* Enable this only for troubleshooting in controlled environments.
|
|
55
|
+
* @default false or process.env.STANTERPRISE_DEBUG === "true"
|
|
56
|
+
*/
|
|
57
|
+
debug?: boolean;
|
|
58
|
+
/**
|
|
59
|
+
* File path for debug JSONL output when debug mode is enabled.
|
|
60
|
+
* Warning: the file may contain sensitive request data and can become very large,
|
|
61
|
+
* especially when attachments are included. Use only in controlled environments and
|
|
62
|
+
* handle the output file securely.
|
|
63
|
+
* @default "stanterprise-debug.jsonl" or process.env.STANTERPRISE_DEBUG_FILE
|
|
64
|
+
*/
|
|
65
|
+
debugFile?: string;
|
|
50
66
|
}
|
|
51
67
|
/**
|
|
52
68
|
* Internal type for managing test execution state
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { StanterpriseReporterOptions } from "../types";
|
|
2
|
+
/**
|
|
3
|
+
* Creates or truncates the debug JSONL file so each test run starts with a clean file.
|
|
4
|
+
* No-op when debug mode is disabled.
|
|
5
|
+
*/
|
|
6
|
+
export declare function initDebugFile(options: StanterpriseReporterOptions): void;
|
|
7
|
+
/**
|
|
8
|
+
* Appends a single JSONL entry for an outgoing gRPC message.
|
|
9
|
+
* Each entry is a JSON object on its own line containing the timestamp,
|
|
10
|
+
* gRPC method path, and the serialized message payload.
|
|
11
|
+
* No-op when debug mode is disabled.
|
|
12
|
+
*/
|
|
13
|
+
export declare function writeDebugEntry(options: StanterpriseReporterOptions, path: string, message: unknown): Promise<void>;
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
+
var ownKeys = function(o) {
|
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
+
var ar = [];
|
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
+
return ar;
|
|
24
|
+
};
|
|
25
|
+
return ownKeys(o);
|
|
26
|
+
};
|
|
27
|
+
return function (mod) {
|
|
28
|
+
if (mod && mod.__esModule) return mod;
|
|
29
|
+
var result = {};
|
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
+
__setModuleDefault(result, mod);
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
})();
|
|
35
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
|
+
exports.initDebugFile = initDebugFile;
|
|
37
|
+
exports.writeDebugEntry = writeDebugEntry;
|
|
38
|
+
const fs = __importStar(require("fs"));
|
|
39
|
+
const nodePath = __importStar(require("path"));
|
|
40
|
+
/**
|
|
41
|
+
* Ensures the parent directory of `filePath` exists, creating it recursively if needed.
|
|
42
|
+
*/
|
|
43
|
+
function ensureParentDir(filePath) {
|
|
44
|
+
const dir = nodePath.dirname(filePath);
|
|
45
|
+
if (dir && dir !== ".") {
|
|
46
|
+
fs.mkdirSync(dir, { recursive: true });
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
/**
|
|
50
|
+
* Creates or truncates the debug JSONL file so each test run starts with a clean file.
|
|
51
|
+
* No-op when debug mode is disabled.
|
|
52
|
+
*/
|
|
53
|
+
function initDebugFile(options) {
|
|
54
|
+
if (!options.debug)
|
|
55
|
+
return;
|
|
56
|
+
const filePath = options.debugFile || "stanterprise-debug.jsonl";
|
|
57
|
+
try {
|
|
58
|
+
ensureParentDir(filePath);
|
|
59
|
+
fs.writeFileSync(filePath, "");
|
|
60
|
+
}
|
|
61
|
+
catch (e) {
|
|
62
|
+
console.error(`Stanterprise Reporter: Failed to initialize debug file "${filePath}":`, e);
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
/**
|
|
66
|
+
* Appends a single JSONL entry for an outgoing gRPC message.
|
|
67
|
+
* Each entry is a JSON object on its own line containing the timestamp,
|
|
68
|
+
* gRPC method path, and the serialized message payload.
|
|
69
|
+
* No-op when debug mode is disabled.
|
|
70
|
+
*/
|
|
71
|
+
async function writeDebugEntry(options, path, message) {
|
|
72
|
+
if (!options.debug)
|
|
73
|
+
return;
|
|
74
|
+
const filePath = options.debugFile || "stanterprise-debug.jsonl";
|
|
75
|
+
const entry = {
|
|
76
|
+
timestamp: new Date().toISOString(),
|
|
77
|
+
path,
|
|
78
|
+
message,
|
|
79
|
+
};
|
|
80
|
+
try {
|
|
81
|
+
ensureParentDir(filePath);
|
|
82
|
+
await fs.promises.appendFile(filePath, JSON.stringify(entry) + "\n");
|
|
83
|
+
}
|
|
84
|
+
catch (e) {
|
|
85
|
+
console.error(`Stanterprise Reporter: Failed to write debug entry to "${filePath}":`, e);
|
|
86
|
+
}
|
|
87
|
+
}
|
package/dist/utils/index.d.ts
CHANGED
package/dist/utils/index.js
CHANGED
|
@@ -21,5 +21,12 @@ function defineOptions(providedOptions) {
|
|
|
21
21
|
result.verbose = providedOptions.verbose || false;
|
|
22
22
|
result.grpcMaxRetries = providedOptions.grpcMaxRetries ?? 3;
|
|
23
23
|
result.grpcRetryDelay = providedOptions.grpcRetryDelay || 100;
|
|
24
|
+
result.debug =
|
|
25
|
+
providedOptions.debug ??
|
|
26
|
+
(process.env.STANTERPRISE_DEBUG || "").toLowerCase() === "true";
|
|
27
|
+
result.debugFile =
|
|
28
|
+
providedOptions.debugFile ||
|
|
29
|
+
process.env.STANTERPRISE_DEBUG_FILE ||
|
|
30
|
+
"stanterprise-debug.jsonl";
|
|
24
31
|
return result;
|
|
25
32
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@stanterprise/playwright-reporter",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.21",
|
|
4
4
|
"description": "Custom Playwright test reporter that sends test results to Stanterprise via gRPC",
|
|
5
5
|
"homepage": "https://github.com/stanterprise/stanterprise-playwright-reporter#readme",
|
|
6
6
|
"bugs": {
|