@stanterprise/playwright-reporter 0.0.0-debug-flag-2
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 +15 -0
- package/README.md +472 -0
- package/dist/client/grpcClient.d.ts +10 -0
- package/dist/client/grpcClient.js +158 -0
- package/dist/handlers/index.d.ts +9 -0
- package/dist/handlers/index.js +19 -0
- package/dist/handlers/onBeginHandler.d.ts +5 -0
- package/dist/handlers/onBeginHandler.js +96 -0
- package/dist/handlers/onEndHandler.d.ts +4 -0
- package/dist/handlers/onEndHandler.js +51 -0
- package/dist/handlers/onErrorHandler.d.ts +4 -0
- package/dist/handlers/onErrorHandler.js +15 -0
- package/dist/handlers/onStepBeginHandler.d.ts +4 -0
- package/dist/handlers/onStepBeginHandler.js +38 -0
- package/dist/handlers/onStepEndHandler.d.ts +4 -0
- package/dist/handlers/onStepEndHandler.js +59 -0
- package/dist/handlers/onTestBeginHandler.d.ts +4 -0
- package/dist/handlers/onTestBeginHandler.js +32 -0
- package/dist/handlers/onTestEndHandler.d.ts +4 -0
- package/dist/handlers/onTestEndHandler.js +57 -0
- package/dist/handlers/onTestFailHandler.d.ts +4 -0
- package/dist/handlers/onTestFailHandler.js +24 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.js +10 -0
- package/dist/reporter.d.ts +22 -0
- package/dist/reporter.js +149 -0
- package/dist/types.d.ts +94 -0
- package/dist/types.js +5 -0
- package/dist/utils/attachmentProcessor.d.ts +21 -0
- package/dist/utils/attachmentProcessor.js +73 -0
- package/dist/utils/debugLogger.d.ts +13 -0
- package/dist/utils/debugLogger.js +75 -0
- package/dist/utils/envRetriever.d.ts +9 -0
- package/dist/utils/envRetriever.js +23 -0
- package/dist/utils/idGenerators.d.ts +20 -0
- package/dist/utils/idGenerators.js +40 -0
- package/dist/utils/index.d.ts +10 -0
- package/dist/utils/index.js +26 -0
- package/dist/utils/metadataHelpers.d.ts +42 -0
- package/dist/utils/metadataHelpers.js +91 -0
- package/dist/utils/optionsMapper.d.ts +7 -0
- package/dist/utils/optionsMapper.js +31 -0
- package/dist/utils/statusMapper.d.ts +12 -0
- package/dist/utils/statusMapper.js +29 -0
- package/dist/utils/timeHelpers.d.ts +26 -0
- package/dist/utils/timeHelpers.js +61 -0
- package/package.json +58 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
ISC License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2025 Stanterprise Team
|
|
4
|
+
|
|
5
|
+
Permission to use, copy, modify, and/or distribute this software for any
|
|
6
|
+
purpose with or without fee is hereby granted, provided that the above
|
|
7
|
+
copyright notice and this permission notice appear in all copies.
|
|
8
|
+
|
|
9
|
+
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
|
10
|
+
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
|
11
|
+
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
|
12
|
+
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
|
13
|
+
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
|
14
|
+
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
|
15
|
+
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,472 @@
|
|
|
1
|
+
# stanterprise-playwright-reporter
|
|
2
|
+
|
|
3
|
+
Custom Playwright test reporter that sends test results to Stanterprise via gRPC.
|
|
4
|
+
|
|
5
|
+
## Features
|
|
6
|
+
|
|
7
|
+
- 📊 **Comprehensive Test Reporting**: Reports test lifecycle events including begin, end, and failures
|
|
8
|
+
- 🔄 **Step Tracking**: Tracks and reports individual test steps with timing and status
|
|
9
|
+
- 📎 **Attachment Support**: Handles screenshots, videos, and other test attachments
|
|
10
|
+
- 🔌 **gRPC Integration**: Communicates with Stanterprise backend via gRPC protocol
|
|
11
|
+
- ⚙️ **Configurable**: Supports environment variables and configuration options
|
|
12
|
+
- 🛡️ **Error Resilient**: Graceful error handling that doesn't interrupt test execution
|
|
13
|
+
|
|
14
|
+
## Installation
|
|
15
|
+
|
|
16
|
+
```bash
|
|
17
|
+
npm install stanterprise-playwright-reporter --save-dev
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
## Configuration
|
|
21
|
+
|
|
22
|
+
### Basic Setup
|
|
23
|
+
|
|
24
|
+
Add the reporter to your `playwright.config.ts`:
|
|
25
|
+
|
|
26
|
+
```typescript
|
|
27
|
+
import { defineConfig } from "@playwright/test";
|
|
28
|
+
|
|
29
|
+
export default defineConfig({
|
|
30
|
+
reporter: [["stanterprise-playwright-reporter"]],
|
|
31
|
+
// ... other config
|
|
32
|
+
});
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
### Advanced Configuration
|
|
36
|
+
|
|
37
|
+
```typescript
|
|
38
|
+
import { defineConfig } from "@playwright/test";
|
|
39
|
+
|
|
40
|
+
export default defineConfig({
|
|
41
|
+
reporter: [
|
|
42
|
+
[
|
|
43
|
+
"stanterprise-playwright-reporter",
|
|
44
|
+
{
|
|
45
|
+
grpcAddress: "localhost:50051", // gRPC server address
|
|
46
|
+
grpcEnabled: true, // Enable/disable gRPC reporting
|
|
47
|
+
grpcTimeout: 1000, // Timeout for gRPC calls in milliseconds
|
|
48
|
+
grpcMaxMessageSize: 104857600, // Max message size (100MB default)
|
|
49
|
+
maxAttachmentSize: 10485760, // Max attachment size (10MB default)
|
|
50
|
+
verbose: false, // Enable verbose logging
|
|
51
|
+
},
|
|
52
|
+
],
|
|
53
|
+
],
|
|
54
|
+
// ... other config
|
|
55
|
+
});
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
### Environment Variables
|
|
59
|
+
|
|
60
|
+
You can also configure the reporter using environment variables:
|
|
61
|
+
|
|
62
|
+
- `STANTERPRISE_GRPC_ADDRESS`: gRPC server address (default: `localhost:50051`)
|
|
63
|
+
- `STANTERPRISE_GRPC_ENABLED`: Enable/disable gRPC reporting (default: `true`)
|
|
64
|
+
- `STANTERPRISE_META_*`: Custom metadata fields for test runs (prefix will be stripped)
|
|
65
|
+
|
|
66
|
+
#### Custom Metadata
|
|
67
|
+
|
|
68
|
+
Any environment variable with the `STANTERPRISE_META_` prefix will be automatically included in the test run metadata. The prefix is stripped from the key name.
|
|
69
|
+
|
|
70
|
+
For example:
|
|
71
|
+
|
|
72
|
+
- `STANTERPRISE_META_BUILD_ID=12345` becomes `BUILD_ID: 12345` in metadata
|
|
73
|
+
- `STANTERPRISE_META_BRANCH=main` becomes `BRANCH: main` in metadata
|
|
74
|
+
- `STANTERPRISE_META_COMMIT_SHA=abc123` becomes `COMMIT_SHA: abc123` in metadata
|
|
75
|
+
|
|
76
|
+
Example:
|
|
77
|
+
|
|
78
|
+
```bash
|
|
79
|
+
STANTERPRISE_GRPC_ADDRESS=myserver.com:50051 \
|
|
80
|
+
STANTERPRISE_META_BUILD_ID=12345 \
|
|
81
|
+
STANTERPRISE_META_BRANCH=main \
|
|
82
|
+
npx playwright test
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
## Configuration Options
|
|
86
|
+
|
|
87
|
+
| Option | Type | Default | Description |
|
|
88
|
+
| -------------------- | ------- | ----------------- | --------------------------------------------------- |
|
|
89
|
+
| `grpcAddress` | string | `localhost:50051` | gRPC server address |
|
|
90
|
+
| `grpcEnabled` | boolean | `true` | Enable/disable gRPC reporting |
|
|
91
|
+
| `grpcTimeout` | number | `1000` | Timeout for gRPC calls in milliseconds |
|
|
92
|
+
| `grpcMaxMessageSize` | number | `104857600` | Max message size in bytes (100MB default) |
|
|
93
|
+
| `maxAttachmentSize` | number | `10485760` | Max attachment content size in bytes (10MB default) |
|
|
94
|
+
| `verbose` | boolean | `false` | Enable verbose logging |
|
|
95
|
+
| `grpcMaxRetries` | number | `3` | Maximum number of retry attempts for failed gRPC calls |
|
|
96
|
+
| `grpcRetryDelay` | number | `100` | Initial delay for retries in ms (uses exponential backoff) |
|
|
97
|
+
|
|
98
|
+
### Retry Configuration
|
|
99
|
+
|
|
100
|
+
The reporter automatically retries failed gRPC calls for transient errors using exponential backoff:
|
|
101
|
+
|
|
102
|
+
- **Retryable errors**: `UNAVAILABLE`, `DEADLINE_EXCEEDED`, `INTERNAL`, `UNKNOWN`
|
|
103
|
+
- **Non-retryable errors**: `INVALID_ARGUMENT`, `NOT_FOUND`, `PERMISSION_DENIED`, `UNAUTHENTICATED`, `ALREADY_EXISTS`
|
|
104
|
+
|
|
105
|
+
**Exponential backoff formula**: `grpcRetryDelay * (2 ^ attemptNumber)`
|
|
106
|
+
|
|
107
|
+
Example with default settings (`grpcRetryDelay: 100`, `grpcMaxRetries: 3`):
|
|
108
|
+
- Initial attempt: Immediate
|
|
109
|
+
- Retry 1: Wait 100ms (100 * 2^0)
|
|
110
|
+
- Retry 2: Wait 200ms (100 * 2^1)
|
|
111
|
+
- Retry 3: Wait 400ms (100 * 2^2)
|
|
112
|
+
|
|
113
|
+
```typescript
|
|
114
|
+
export default defineConfig({
|
|
115
|
+
reporter: [
|
|
116
|
+
[
|
|
117
|
+
"stanterprise-playwright-reporter",
|
|
118
|
+
{
|
|
119
|
+
grpcMaxRetries: 5, // Try up to 5 times
|
|
120
|
+
grpcRetryDelay: 200, // Start with 200ms delay
|
|
121
|
+
verbose: true, // Log retry attempts
|
|
122
|
+
},
|
|
123
|
+
],
|
|
124
|
+
],
|
|
125
|
+
});
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
To disable retries:
|
|
129
|
+
|
|
130
|
+
```typescript
|
|
131
|
+
{
|
|
132
|
+
grpcMaxRetries: 0 // No retries, fail fast
|
|
133
|
+
}
|
|
134
|
+
```
|
|
135
|
+
|
|
136
|
+
## Sharding Support
|
|
137
|
+
|
|
138
|
+
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:
|
|
139
|
+
|
|
140
|
+
- `shard.current`: The current shard number (1-based)
|
|
141
|
+
- `shard.total`: The total number of shards
|
|
142
|
+
|
|
143
|
+
### Configuring Sharding
|
|
144
|
+
|
|
145
|
+
In your `playwright.config.ts`:
|
|
146
|
+
|
|
147
|
+
```typescript
|
|
148
|
+
import { defineConfig } from "@playwright/test";
|
|
149
|
+
|
|
150
|
+
export default defineConfig({
|
|
151
|
+
shard: { total: 5, current: 1 },
|
|
152
|
+
reporter: [["stanterprise-playwright-reporter"]],
|
|
153
|
+
});
|
|
154
|
+
```
|
|
155
|
+
|
|
156
|
+
Or via CLI:
|
|
157
|
+
|
|
158
|
+
```bash
|
|
159
|
+
npx playwright test --shard=1/5
|
|
160
|
+
npx playwright test --shard=2/5
|
|
161
|
+
# ... etc
|
|
162
|
+
```
|
|
163
|
+
|
|
164
|
+
### Aggregating Results Across Shards
|
|
165
|
+
|
|
166
|
+
**Important:** To have a complete set of tests accumulated in the observability server, you must specify the same `STANTERPRISE_RUN_ID` environment variable for all shards. This ensures that results from all shards are associated with the same test run.
|
|
167
|
+
|
|
168
|
+
```bash
|
|
169
|
+
# Set the same run ID for all shards
|
|
170
|
+
export STANTERPRISE_RUN_ID="my-test-run-123"
|
|
171
|
+
|
|
172
|
+
# Run all shards with the same run ID
|
|
173
|
+
npx playwright test --shard=1/5
|
|
174
|
+
npx playwright test --shard=2/5
|
|
175
|
+
npx playwright test --shard=3/5
|
|
176
|
+
npx playwright test --shard=4/5
|
|
177
|
+
npx playwright test --shard=5/5
|
|
178
|
+
```
|
|
179
|
+
|
|
180
|
+
In CI/CD environments, you can use a unique identifier from your CI system:
|
|
181
|
+
|
|
182
|
+
```bash
|
|
183
|
+
# GitHub Actions example
|
|
184
|
+
STANTERPRISE_RUN_ID="${GITHUB_RUN_ID}" npx playwright test --shard=1/5
|
|
185
|
+
|
|
186
|
+
# GitLab CI example
|
|
187
|
+
STANTERPRISE_RUN_ID="${CI_PIPELINE_ID}" npx playwright test --shard=1/5
|
|
188
|
+
|
|
189
|
+
# Jenkins example
|
|
190
|
+
STANTERPRISE_RUN_ID="${BUILD_ID}" npx playwright test --shard=1/5
|
|
191
|
+
```
|
|
192
|
+
|
|
193
|
+
Without a shared `STANTERPRISE_RUN_ID`, each shard will generate its own unique run ID, and the results will be reported as separate test runs rather than a single aggregated run.
|
|
194
|
+
|
|
195
|
+
The shard information is automatically included in the metadata sent to the server, allowing you to:
|
|
196
|
+
|
|
197
|
+
- Track which tests ran on which shard
|
|
198
|
+
- Aggregate results from multiple shards
|
|
199
|
+
- Debug shard-specific issues
|
|
200
|
+
|
|
201
|
+
When verbose logging is enabled, you'll see shard information in the console output:
|
|
202
|
+
|
|
203
|
+
```
|
|
204
|
+
Stanterprise Reporter: Test run started with ID: 123e4567-e89b-12d3-a456-426614174000
|
|
205
|
+
Number of tests: 42
|
|
206
|
+
Run started at: 2026-01-25T10:30:00.000Z
|
|
207
|
+
Shard: 1 of 5
|
|
208
|
+
```
|
|
209
|
+
|
|
210
|
+
## What Gets Reported
|
|
211
|
+
|
|
212
|
+
### Test Events
|
|
213
|
+
|
|
214
|
+
- **Test Begin**: When a test starts
|
|
215
|
+
- **Test End**: When a test completes with status (passed/failed/skipped/timedOut)
|
|
216
|
+
- **Test Failure**: Detailed failure information including error messages and stack traces
|
|
217
|
+
|
|
218
|
+
### Step Events
|
|
219
|
+
|
|
220
|
+
- **Step Begin**: When a test step starts
|
|
221
|
+
- **Step End**: When a test step completes with duration and status
|
|
222
|
+
|
|
223
|
+
### Attachments
|
|
224
|
+
|
|
225
|
+
The reporter automatically processes and sends:
|
|
226
|
+
|
|
227
|
+
- Screenshots
|
|
228
|
+
- Videos
|
|
229
|
+
- Trace files
|
|
230
|
+
- Any other attachments captured during test execution
|
|
231
|
+
|
|
232
|
+
## Usage Example
|
|
233
|
+
|
|
234
|
+
```typescript
|
|
235
|
+
// playwright.config.ts
|
|
236
|
+
import { defineConfig } from "@playwright/test";
|
|
237
|
+
|
|
238
|
+
export default defineConfig({
|
|
239
|
+
use: {
|
|
240
|
+
screenshot: "only-on-failure",
|
|
241
|
+
video: "retain-on-failure",
|
|
242
|
+
},
|
|
243
|
+
reporter: [
|
|
244
|
+
["list"], // Also show in console
|
|
245
|
+
[
|
|
246
|
+
"stanterprise-playwright-reporter",
|
|
247
|
+
{
|
|
248
|
+
grpcAddress: process.env.STANTERPRISE_GRPC_ADDRESS || "localhost:50051",
|
|
249
|
+
verbose: process.env.CI === "true",
|
|
250
|
+
},
|
|
251
|
+
],
|
|
252
|
+
],
|
|
253
|
+
});
|
|
254
|
+
```
|
|
255
|
+
|
|
256
|
+
## API
|
|
257
|
+
|
|
258
|
+
### StanterpriseReporter
|
|
259
|
+
|
|
260
|
+
Main reporter class implementing Playwright's `Reporter` interface.
|
|
261
|
+
|
|
262
|
+
```typescript
|
|
263
|
+
import { StanterpriseReporter } from "stanterprise-playwright-reporter";
|
|
264
|
+
|
|
265
|
+
const reporter = new StanterpriseReporter({
|
|
266
|
+
grpcAddress: "localhost:50051",
|
|
267
|
+
grpcEnabled: true,
|
|
268
|
+
grpcTimeout: 1000,
|
|
269
|
+
grpcMaxMessageSize: 104857600, // 100MB
|
|
270
|
+
maxAttachmentSize: 10485760, // 10MB
|
|
271
|
+
verbose: false,
|
|
272
|
+
});
|
|
273
|
+
```
|
|
274
|
+
|
|
275
|
+
### Types
|
|
276
|
+
|
|
277
|
+
```typescript
|
|
278
|
+
import type {
|
|
279
|
+
StanterpriseReporterOptions,
|
|
280
|
+
TestExecutionContext,
|
|
281
|
+
StepExecutionContext,
|
|
282
|
+
} from "stanterprise-playwright-reporter";
|
|
283
|
+
```
|
|
284
|
+
|
|
285
|
+
## Development
|
|
286
|
+
|
|
287
|
+
### Building
|
|
288
|
+
|
|
289
|
+
```bash
|
|
290
|
+
npm run build
|
|
291
|
+
```
|
|
292
|
+
|
|
293
|
+
### Testing
|
|
294
|
+
|
|
295
|
+
```bash
|
|
296
|
+
npm test
|
|
297
|
+
```
|
|
298
|
+
|
|
299
|
+
### Cleaning
|
|
300
|
+
|
|
301
|
+
```bash
|
|
302
|
+
npm run clean
|
|
303
|
+
```
|
|
304
|
+
|
|
305
|
+
## How It Works
|
|
306
|
+
|
|
307
|
+
1. **Test Run Initialization**: When tests start, the reporter generates a unique run ID and establishes a gRPC connection
|
|
308
|
+
2. **Event Reporting**: As tests execute, the reporter sends events to the Stanterprise backend via gRPC
|
|
309
|
+
3. **Fire-and-Forget**: Events are sent asynchronously to avoid slowing down test execution
|
|
310
|
+
4. **Error Handling**: Connection errors are logged once and further attempts are disabled for that run
|
|
311
|
+
5. **Cleanup**: When tests complete, the gRPC connection is properly closed
|
|
312
|
+
|
|
313
|
+
## Architecture
|
|
314
|
+
|
|
315
|
+
The reporter is organized into several modules:
|
|
316
|
+
|
|
317
|
+
- **reporter.ts**: Main reporter implementation
|
|
318
|
+
- **types.ts**: TypeScript type definitions and interfaces
|
|
319
|
+
- **utils/statusMapper.ts**: Maps Playwright statuses to protobuf enums
|
|
320
|
+
- **utils/attachmentProcessor.ts**: Processes test attachments
|
|
321
|
+
- **utils/timeHelpers.ts**: Handles timestamp and duration conversions
|
|
322
|
+
|
|
323
|
+
## Troubleshooting
|
|
324
|
+
|
|
325
|
+
### gRPC Error 13 INTERNAL: failed to publish event
|
|
326
|
+
|
|
327
|
+
This error typically occurs due to one of two issues:
|
|
328
|
+
|
|
329
|
+
**1. Metadata Serialization (Most Common)**
|
|
330
|
+
|
|
331
|
+
If you see this error with the stack trace mentioning "metadata is not iterable" or serialization issues, it was a bug in v0.0.13 and earlier versions. **Solution: Upgrade to v0.0.14 or later.**
|
|
332
|
+
|
|
333
|
+
**2. Large Attachments**
|
|
334
|
+
|
|
335
|
+
This error can also occur when test event payloads exceed the gRPC message size limit. Common causes:
|
|
336
|
+
|
|
337
|
+
1. **Large attachments**: Tests with large screenshots, videos, or trace files
|
|
338
|
+
2. **Default size limits**: gRPC has a default 4MB message size limit
|
|
339
|
+
|
|
340
|
+
**Solutions:**
|
|
341
|
+
|
|
342
|
+
1. **Increase message size limits** (recommended):
|
|
343
|
+
|
|
344
|
+
```typescript
|
|
345
|
+
reporter: [
|
|
346
|
+
[
|
|
347
|
+
"stanterprise-playwright-reporter",
|
|
348
|
+
{
|
|
349
|
+
grpcMaxMessageSize: 104857600, // 100MB (default)
|
|
350
|
+
maxAttachmentSize: 10485760, // 10MB (default)
|
|
351
|
+
},
|
|
352
|
+
],
|
|
353
|
+
],
|
|
354
|
+
```
|
|
355
|
+
|
|
356
|
+
2. **Reduce attachment sizes**:
|
|
357
|
+
- Configure Playwright to save attachments to disk instead of embedding them
|
|
358
|
+
- The reporter automatically uses file paths when available instead of content
|
|
359
|
+
|
|
360
|
+
```typescript
|
|
361
|
+
use: {
|
|
362
|
+
screenshot: "only-on-failure",
|
|
363
|
+
video: "retain-on-failure",
|
|
364
|
+
trace: "retain-on-failure",
|
|
365
|
+
},
|
|
366
|
+
```
|
|
367
|
+
|
|
368
|
+
3. **Monitor payload sizes**: Enable verbose logging to see actual payload sizes:
|
|
369
|
+
```typescript
|
|
370
|
+
reporter: [
|
|
371
|
+
["stanterprise-playwright-reporter", { verbose: true }],
|
|
372
|
+
],
|
|
373
|
+
```
|
|
374
|
+
|
|
375
|
+
The reporter will log warnings when attachments exceed the size limit and automatically skip their content while preserving metadata.
|
|
376
|
+
|
|
377
|
+
### Connection Issues
|
|
378
|
+
|
|
379
|
+
If you see gRPC connection errors:
|
|
380
|
+
|
|
381
|
+
1. Verify the gRPC server is running and accessible
|
|
382
|
+
2. Check the `grpcAddress` configuration
|
|
383
|
+
3. Ensure firewall rules allow the connection
|
|
384
|
+
4. Enable verbose logging to see detailed error messages
|
|
385
|
+
|
|
386
|
+
### Reporter Not Working
|
|
387
|
+
|
|
388
|
+
1. Verify the reporter is properly configured in `playwright.config.ts`
|
|
389
|
+
2. Check that `grpcEnabled` is not set to `false`
|
|
390
|
+
3. Look for error messages in the test output
|
|
391
|
+
4. Try with `verbose: true` to see detailed logs
|
|
392
|
+
|
|
393
|
+
## Contributing
|
|
394
|
+
|
|
395
|
+
Contributions are welcome! Please feel free to submit a Pull Request.
|
|
396
|
+
|
|
397
|
+
## For Maintainers
|
|
398
|
+
|
|
399
|
+
### Publishing to NPM
|
|
400
|
+
|
|
401
|
+
#### Automated Publishing (Recommended)
|
|
402
|
+
|
|
403
|
+
The repository uses GitHub Actions to automatically publish to NPM when a version tag is pushed:
|
|
404
|
+
|
|
405
|
+
1. Update the version following [semantic versioning](https://semver.org/):
|
|
406
|
+
|
|
407
|
+
```bash
|
|
408
|
+
npm version patch # for bug fixes (1.0.0 → 1.0.1)
|
|
409
|
+
npm version minor # for new features (1.0.0 → 1.1.0)
|
|
410
|
+
npm version major # for breaking changes (1.0.0 → 2.0.0)
|
|
411
|
+
```
|
|
412
|
+
|
|
413
|
+
2. Push the version tag to GitHub:
|
|
414
|
+
|
|
415
|
+
```bash
|
|
416
|
+
git push --follow-tags
|
|
417
|
+
```
|
|
418
|
+
|
|
419
|
+
3. The GitHub Actions workflow will automatically:
|
|
420
|
+
- Run tests
|
|
421
|
+
- Build the package
|
|
422
|
+
- Publish to NPM with provenance
|
|
423
|
+
|
|
424
|
+
**Note**: Requires `NPM_TOKEN` secret to be configured in GitHub repository settings with a valid NPM access token.
|
|
425
|
+
|
|
426
|
+
#### Manual Publishing
|
|
427
|
+
|
|
428
|
+
Alternatively, you can publish manually:
|
|
429
|
+
|
|
430
|
+
1. Ensure all tests pass:
|
|
431
|
+
|
|
432
|
+
```bash
|
|
433
|
+
npm test
|
|
434
|
+
```
|
|
435
|
+
|
|
436
|
+
2. Update the version:
|
|
437
|
+
|
|
438
|
+
```bash
|
|
439
|
+
npm version patch|minor|major
|
|
440
|
+
```
|
|
441
|
+
|
|
442
|
+
3. Publish to NPM (prepublishOnly script will build automatically):
|
|
443
|
+
|
|
444
|
+
```bash
|
|
445
|
+
npm publish
|
|
446
|
+
```
|
|
447
|
+
|
|
448
|
+
4. Push the tag to GitHub:
|
|
449
|
+
```bash
|
|
450
|
+
git push --follow-tags
|
|
451
|
+
```
|
|
452
|
+
|
|
453
|
+
### What Gets Published
|
|
454
|
+
|
|
455
|
+
The package includes:
|
|
456
|
+
|
|
457
|
+
- `dist/` - Compiled JavaScript and TypeScript declarations
|
|
458
|
+
- `README.md` - Documentation
|
|
459
|
+
|
|
460
|
+
The following are excluded via `.npmignore`:
|
|
461
|
+
|
|
462
|
+
- Source files (`src/`, `tests/`, `examples/`)
|
|
463
|
+
- Development configuration files
|
|
464
|
+
- Build artifacts and logs
|
|
465
|
+
|
|
466
|
+
## License
|
|
467
|
+
|
|
468
|
+
ISC - See [LICENSE](LICENSE) file for details
|
|
469
|
+
|
|
470
|
+
## Support
|
|
471
|
+
|
|
472
|
+
For issues and questions, please visit the [GitHub repository](https://github.com/stanterprise/stanterprise-playwright-reporter).
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { StanterpriseReporterOptions } from "../types";
|
|
2
|
+
import * as grpc from "@grpc/grpc-js";
|
|
3
|
+
export default function getClient(options: StanterpriseReporterOptions): grpc.Client | null;
|
|
4
|
+
/**
|
|
5
|
+
* Helper: generic unary call using raw method path with retry support
|
|
6
|
+
*/
|
|
7
|
+
export declare function reportUnary(options: StanterpriseReporterOptions, grpcClient: grpc.Client, path: string, message: {
|
|
8
|
+
serialize?: (w?: any) => Uint8Array;
|
|
9
|
+
serializeBinary?: () => Uint8Array;
|
|
10
|
+
}, deadlineMs?: number): Promise<Buffer>;
|
|
@@ -0,0 +1,158 @@
|
|
|
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.default = getClient;
|
|
37
|
+
exports.reportUnary = reportUnary;
|
|
38
|
+
const grpc = __importStar(require("@grpc/grpc-js"));
|
|
39
|
+
const debugLogger_1 = require("../utils/debugLogger");
|
|
40
|
+
function getClient(options) {
|
|
41
|
+
try {
|
|
42
|
+
const maxMessageSize = options.grpcMaxMessageSize || 104857600; // 100MB default
|
|
43
|
+
return new grpc.Client(options.grpcAddress, grpc.credentials.createInsecure(), {
|
|
44
|
+
"grpc.max_send_message_length": maxMessageSize,
|
|
45
|
+
"grpc.max_receive_message_length": maxMessageSize,
|
|
46
|
+
});
|
|
47
|
+
}
|
|
48
|
+
catch (e) {
|
|
49
|
+
console.error("Failed to create gRPC client", e);
|
|
50
|
+
}
|
|
51
|
+
return null;
|
|
52
|
+
}
|
|
53
|
+
/**
|
|
54
|
+
* Determines if a gRPC error code represents a transient failure worth retrying
|
|
55
|
+
*/
|
|
56
|
+
function shouldAttemptRetryForCode(errorCode) {
|
|
57
|
+
const transientCodes = new Set([
|
|
58
|
+
grpc.status.UNAVAILABLE,
|
|
59
|
+
grpc.status.DEADLINE_EXCEEDED,
|
|
60
|
+
grpc.status.INTERNAL,
|
|
61
|
+
grpc.status.UNKNOWN,
|
|
62
|
+
]);
|
|
63
|
+
return transientCodes.has(errorCode);
|
|
64
|
+
}
|
|
65
|
+
/**
|
|
66
|
+
* Calculates wait time using power-of-two exponential backoff
|
|
67
|
+
*/
|
|
68
|
+
function calculateWaitDuration(baseDelayMs, attemptsSoFar) {
|
|
69
|
+
return baseDelayMs * Math.pow(2, attemptsSoFar);
|
|
70
|
+
}
|
|
71
|
+
/**
|
|
72
|
+
* Performs a single gRPC unary request attempt
|
|
73
|
+
*/
|
|
74
|
+
function executeSingleAttempt(grpcClient, path, message, deadlineMs) {
|
|
75
|
+
const reqSerialize = (arg) => {
|
|
76
|
+
const m = arg;
|
|
77
|
+
const bytes = m?.serializeBinary
|
|
78
|
+
? m.serializeBinary()
|
|
79
|
+
: m?.serialize
|
|
80
|
+
? m.serialize()
|
|
81
|
+
: new Uint8Array(0);
|
|
82
|
+
return Buffer.from(bytes);
|
|
83
|
+
};
|
|
84
|
+
const resDeserialize = (bytes) => bytes;
|
|
85
|
+
const metadata = new grpc.Metadata();
|
|
86
|
+
const callOptions = {
|
|
87
|
+
deadline: new Date(Date.now() + deadlineMs),
|
|
88
|
+
};
|
|
89
|
+
return new Promise((resolve, reject) => {
|
|
90
|
+
try {
|
|
91
|
+
grpcClient.makeUnaryRequest(path, reqSerialize, resDeserialize, message, metadata, callOptions, (err, response) => {
|
|
92
|
+
if (err)
|
|
93
|
+
return reject(err);
|
|
94
|
+
resolve(response);
|
|
95
|
+
});
|
|
96
|
+
}
|
|
97
|
+
catch (e) {
|
|
98
|
+
reject(e);
|
|
99
|
+
}
|
|
100
|
+
});
|
|
101
|
+
}
|
|
102
|
+
/**
|
|
103
|
+
* Pauses execution for specified milliseconds with unref'd timer to avoid blocking shutdown
|
|
104
|
+
*/
|
|
105
|
+
function pauseExecution(durationMs) {
|
|
106
|
+
return new Promise((resolve) => {
|
|
107
|
+
const timer = setTimeout(resolve, durationMs);
|
|
108
|
+
// Unref the timer so it doesn't keep the process alive
|
|
109
|
+
timer.unref();
|
|
110
|
+
});
|
|
111
|
+
}
|
|
112
|
+
/**
|
|
113
|
+
* Attempts gRPC call with exponential backoff retry logic using iterative approach
|
|
114
|
+
*/
|
|
115
|
+
async function attemptWithRetries(options, grpcClient, path, message, deadlineMs, maxRetries) {
|
|
116
|
+
let attemptNumber = 0;
|
|
117
|
+
let lastError = null;
|
|
118
|
+
// Try initial attempt + retries
|
|
119
|
+
for (let retriesRemaining = maxRetries; retriesRemaining >= 0; retriesRemaining--) {
|
|
120
|
+
try {
|
|
121
|
+
return await executeSingleAttempt(grpcClient, path, message, deadlineMs);
|
|
122
|
+
}
|
|
123
|
+
catch (err) {
|
|
124
|
+
lastError = err;
|
|
125
|
+
const grpcErr = err;
|
|
126
|
+
const statusCode = grpcErr?.code ?? grpc.status.UNKNOWN;
|
|
127
|
+
// Check if we should retry this error
|
|
128
|
+
const isRetryable = shouldAttemptRetryForCode(statusCode);
|
|
129
|
+
const hasRetriesLeft = retriesRemaining > 0;
|
|
130
|
+
if (!isRetryable || !hasRetriesLeft) {
|
|
131
|
+
throw err;
|
|
132
|
+
}
|
|
133
|
+
// Log retry attempt if verbose
|
|
134
|
+
if (options.verbose) {
|
|
135
|
+
console.log(`gRPC call failed (code: ${statusCode}), retrying... ` +
|
|
136
|
+
`(attempt ${attemptNumber + 1}, ${retriesRemaining} retries left)`);
|
|
137
|
+
}
|
|
138
|
+
// Calculate and wait for backoff duration
|
|
139
|
+
const baseDelay = options.grpcRetryDelay ?? 100;
|
|
140
|
+
const waitTime = calculateWaitDuration(baseDelay, attemptNumber);
|
|
141
|
+
await pauseExecution(waitTime);
|
|
142
|
+
attemptNumber++;
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
// This should never be reached due to the throw in the loop, but TypeScript needs it
|
|
146
|
+
throw lastError || new Error("Retry loop exhausted without result");
|
|
147
|
+
}
|
|
148
|
+
/**
|
|
149
|
+
* Helper: generic unary call using raw method path with retry support
|
|
150
|
+
*/
|
|
151
|
+
async function reportUnary(options, grpcClient, path, message, deadlineMs = 1000) {
|
|
152
|
+
(0, debugLogger_1.writeDebugEntry)(options, path, message);
|
|
153
|
+
if (!options.grpcEnabled || !grpcClient) {
|
|
154
|
+
return Buffer.alloc(0);
|
|
155
|
+
}
|
|
156
|
+
const maxRetries = options.grpcMaxRetries ?? 3;
|
|
157
|
+
return attemptWithRetries(options, grpcClient, path, message, deadlineMs, maxRetries);
|
|
158
|
+
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { handleOnBeginEvent } from "./onBeginHandler";
|
|
2
|
+
import { handleOnEndEvent } from "./onEndHandler";
|
|
3
|
+
import { handleOnStepBeginEvent } from "./onStepBeginHandler";
|
|
4
|
+
import { handleOnStepEndEvent } from "./onStepEndHandler";
|
|
5
|
+
import { handleOnTestBeginEvent } from "./onTestBeginHandler";
|
|
6
|
+
import { handleOnTestEndEvent } from "./onTestEndHandler";
|
|
7
|
+
import { handleOnTestFailEvent } from "./onTestFailHandler";
|
|
8
|
+
import { handleOnErrorEvent } from "./onErrorHandler";
|
|
9
|
+
export { handleOnBeginEvent, handleOnEndEvent, handleOnStepBeginEvent, handleOnStepEndEvent, handleOnTestBeginEvent, handleOnTestEndEvent, handleOnTestFailEvent, handleOnErrorEvent, };
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.handleOnErrorEvent = exports.handleOnTestFailEvent = exports.handleOnTestEndEvent = exports.handleOnTestBeginEvent = exports.handleOnStepEndEvent = exports.handleOnStepBeginEvent = exports.handleOnEndEvent = exports.handleOnBeginEvent = void 0;
|
|
4
|
+
const onBeginHandler_1 = require("./onBeginHandler");
|
|
5
|
+
Object.defineProperty(exports, "handleOnBeginEvent", { enumerable: true, get: function () { return onBeginHandler_1.handleOnBeginEvent; } });
|
|
6
|
+
const onEndHandler_1 = require("./onEndHandler");
|
|
7
|
+
Object.defineProperty(exports, "handleOnEndEvent", { enumerable: true, get: function () { return onEndHandler_1.handleOnEndEvent; } });
|
|
8
|
+
const onStepBeginHandler_1 = require("./onStepBeginHandler");
|
|
9
|
+
Object.defineProperty(exports, "handleOnStepBeginEvent", { enumerable: true, get: function () { return onStepBeginHandler_1.handleOnStepBeginEvent; } });
|
|
10
|
+
const onStepEndHandler_1 = require("./onStepEndHandler");
|
|
11
|
+
Object.defineProperty(exports, "handleOnStepEndEvent", { enumerable: true, get: function () { return onStepEndHandler_1.handleOnStepEndEvent; } });
|
|
12
|
+
const onTestBeginHandler_1 = require("./onTestBeginHandler");
|
|
13
|
+
Object.defineProperty(exports, "handleOnTestBeginEvent", { enumerable: true, get: function () { return onTestBeginHandler_1.handleOnTestBeginEvent; } });
|
|
14
|
+
const onTestEndHandler_1 = require("./onTestEndHandler");
|
|
15
|
+
Object.defineProperty(exports, "handleOnTestEndEvent", { enumerable: true, get: function () { return onTestEndHandler_1.handleOnTestEndEvent; } });
|
|
16
|
+
const onTestFailHandler_1 = require("./onTestFailHandler");
|
|
17
|
+
Object.defineProperty(exports, "handleOnTestFailEvent", { enumerable: true, get: function () { return onTestFailHandler_1.handleOnTestFailEvent; } });
|
|
18
|
+
const onErrorHandler_1 = require("./onErrorHandler");
|
|
19
|
+
Object.defineProperty(exports, "handleOnErrorEvent", { enumerable: true, get: function () { return onErrorHandler_1.handleOnErrorEvent; } });
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import { FullConfig } from "@playwright/test";
|
|
2
|
+
import { Suite } from "@playwright/test/reporter";
|
|
3
|
+
import { StanterpriseReporterOptions } from "../types";
|
|
4
|
+
import * as grpc from "@grpc/grpc-js";
|
|
5
|
+
export declare function handleOnBeginEvent(config: FullConfig, suite: Suite, name: string, runId: string, client: grpc.Client, options: StanterpriseReporterOptions): void;
|