@stanterprise/playwright-reporter 1.0.0 → 1.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +270 -12
- package/dist/client/grpcClient.d.ts +16 -0
- package/dist/client/grpcClient.js +180 -0
- package/dist/client/messageQueue.d.ts +27 -0
- package/dist/client/messageQueue.js +71 -0
- package/dist/handlers/index.d.ts +9 -0
- package/dist/handlers/index.js +19 -0
- package/dist/handlers/onBeginHandler.d.ts +6 -0
- package/dist/handlers/onBeginHandler.js +98 -0
- package/dist/handlers/onEndHandler.d.ts +5 -0
- package/dist/handlers/onEndHandler.js +53 -0
- package/dist/handlers/onErrorHandler.d.ts +5 -0
- package/dist/handlers/onErrorHandler.js +16 -0
- package/dist/handlers/onStepBeginHandler.d.ts +5 -0
- package/dist/handlers/onStepBeginHandler.js +39 -0
- package/dist/handlers/onStepEndHandler.d.ts +5 -0
- package/dist/handlers/onStepEndHandler.js +62 -0
- package/dist/handlers/onTestBeginHandler.d.ts +5 -0
- package/dist/handlers/onTestBeginHandler.js +33 -0
- package/dist/handlers/onTestEndHandler.d.ts +5 -0
- package/dist/handlers/onTestEndHandler.js +58 -0
- package/dist/handlers/onTestFailHandler.d.ts +5 -0
- package/dist/handlers/onTestFailHandler.js +25 -0
- package/dist/reporter.d.ts +4 -15
- package/dist/reporter.js +97 -386
- package/dist/types.d.ts +56 -0
- package/dist/utils/attachmentProcessor.d.ts +6 -2
- package/dist/utils/attachmentProcessor.js +33 -4
- package/dist/utils/debugLogger.d.ts +13 -0
- package/dist/utils/debugLogger.js +87 -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 +4 -0
- package/dist/utils/index.js +4 -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 +51 -0
- package/dist/utils/statusMapper.js +7 -6
- package/dist/utils/timeHelpers.d.ts +5 -5
- package/dist/utils/timeHelpers.js +24 -8
- package/package.json +9 -10
|
@@ -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;
|
|
@@ -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,6 @@
|
|
|
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
|
+
import { MessageQueue } from "../client/messageQueue";
|
|
6
|
+
export declare function handleOnBeginEvent(config: FullConfig, suite: Suite, name: string, runId: string, executionId: string, client: grpc.Client, options: StanterpriseReporterOptions, queue?: MessageQueue): void;
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.handleOnBeginEvent = handleOnBeginEvent;
|
|
4
|
+
const events_1 = require("@stanterprise/protobuf/testsystem/v1/events");
|
|
5
|
+
const grpcClient_1 = require("../client/grpcClient");
|
|
6
|
+
const entities_1 = require("@stanterprise/protobuf/testsystem/v1/entities");
|
|
7
|
+
const utils_1 = require("../utils");
|
|
8
|
+
const common_1 = require("@stanterprise/protobuf/testsystem/v1/common");
|
|
9
|
+
function handleOnBeginEvent(config, suite, name, runId, executionId, client, options, queue) {
|
|
10
|
+
// Report root suite and all child suites recursively
|
|
11
|
+
// Convert all metadata values to strings for protobuf compatibility
|
|
12
|
+
// Use plain object instead of Map - protobuf expects object literals
|
|
13
|
+
const metadata = {};
|
|
14
|
+
Object.entries(config.metadata || {}).forEach(([key, value]) => {
|
|
15
|
+
metadata[key] = String(value);
|
|
16
|
+
});
|
|
17
|
+
const variables = (0, utils_1.getEnvVariables)();
|
|
18
|
+
// Merge environment variables into metadata
|
|
19
|
+
variables.forEach((value, key) => {
|
|
20
|
+
metadata[key] = value;
|
|
21
|
+
});
|
|
22
|
+
// Add shard information if present
|
|
23
|
+
if (config.shard) {
|
|
24
|
+
metadata["shard.current"] = String(config.shard.current);
|
|
25
|
+
metadata["shard.total"] = String(config.shard.total);
|
|
26
|
+
}
|
|
27
|
+
const request = new events_1.ReportRunStartEventRequest({
|
|
28
|
+
run_id: runId,
|
|
29
|
+
execution_id: executionId,
|
|
30
|
+
name: name,
|
|
31
|
+
test_suites: mapSuites(suite, runId, executionId),
|
|
32
|
+
total_tests: suite.allTests().length,
|
|
33
|
+
metadata: (0, utils_1.toMetadataMap)(metadata),
|
|
34
|
+
});
|
|
35
|
+
(0, grpcClient_1.reportUnary)(options, client, "/testsystem.v1.observer.TestEventCollector/ReportRunStart", request, options.grpcTimeout, queue).catch((e) => {
|
|
36
|
+
const details = e instanceof Error ? `${e.message}` : String(e);
|
|
37
|
+
console.warn(`onBegin. gRPC disabled for the remainder of the run. Address=${options.grpcAddress}. Details: ${details}`);
|
|
38
|
+
});
|
|
39
|
+
}
|
|
40
|
+
function mapSuites(suite, runId, executionId) {
|
|
41
|
+
let allSuites = getAllSuites(suite);
|
|
42
|
+
return allSuites.map((currentSuite) => mapSingleSuite(currentSuite, runId, executionId));
|
|
43
|
+
}
|
|
44
|
+
function getAllSuites(suite) {
|
|
45
|
+
let suites = [];
|
|
46
|
+
suites.push(suite);
|
|
47
|
+
suite.suites.forEach((childSuite) => {
|
|
48
|
+
suites = suites.concat(getAllSuites(childSuite));
|
|
49
|
+
});
|
|
50
|
+
return suites;
|
|
51
|
+
}
|
|
52
|
+
function mapSingleSuite(suite, runId, executionId) {
|
|
53
|
+
const suiteId = (0, utils_1.generateSuiteId)(suite);
|
|
54
|
+
const parentSuiteId = suite.parent
|
|
55
|
+
? (0, utils_1.generateSuiteId)(suite.parent)
|
|
56
|
+
: undefined;
|
|
57
|
+
let type;
|
|
58
|
+
switch (suite.type) {
|
|
59
|
+
case "root":
|
|
60
|
+
type = entities_1.SuiteType.ROOT;
|
|
61
|
+
break;
|
|
62
|
+
case "describe":
|
|
63
|
+
type = entities_1.SuiteType.SUBSUITE;
|
|
64
|
+
break;
|
|
65
|
+
case "file":
|
|
66
|
+
type = entities_1.SuiteType.FILE;
|
|
67
|
+
break;
|
|
68
|
+
case "project":
|
|
69
|
+
type = entities_1.SuiteType.PROJECT;
|
|
70
|
+
break;
|
|
71
|
+
}
|
|
72
|
+
return new entities_1.TestSuiteRun({
|
|
73
|
+
id: suiteId,
|
|
74
|
+
name: suite.title,
|
|
75
|
+
run_id: runId,
|
|
76
|
+
parent_suite_id: parentSuiteId,
|
|
77
|
+
status: common_1.TestStatus.NOT_RUN,
|
|
78
|
+
test_cases: suite.tests.map((test) => new entities_1.TestCaseRun({
|
|
79
|
+
id: test.id,
|
|
80
|
+
name: test.title,
|
|
81
|
+
run_id: runId,
|
|
82
|
+
test_suite_id: suiteId,
|
|
83
|
+
tags: test.tags,
|
|
84
|
+
location: test.location
|
|
85
|
+
? `${test.location.file}:${test.location.line}:${test.location.column}`
|
|
86
|
+
: undefined,
|
|
87
|
+
status: common_1.TestStatus.NOT_RUN,
|
|
88
|
+
retry_count: test.retries,
|
|
89
|
+
retry_index: 0,
|
|
90
|
+
execution_id: executionId,
|
|
91
|
+
})),
|
|
92
|
+
test_case_ids: suite.tests.map((test) => test.id),
|
|
93
|
+
type: type,
|
|
94
|
+
location: suite.location
|
|
95
|
+
? `${suite.location.file}:${suite.location.line}:${suite.location.column}`
|
|
96
|
+
: undefined,
|
|
97
|
+
});
|
|
98
|
+
}
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import { FullResult } from "@playwright/test/reporter";
|
|
2
|
+
import { StanterpriseReporterOptions } from "../types";
|
|
3
|
+
import * as grpc from "@grpc/grpc-js";
|
|
4
|
+
import { MessageQueue } from "../client/messageQueue";
|
|
5
|
+
export declare function handleOnEndEvent(result: FullResult, runId: string, executionId: string, client: grpc.Client, options: StanterpriseReporterOptions, queue?: MessageQueue, metadata?: Record<string, string>): void;
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.handleOnEndEvent = handleOnEndEvent;
|
|
4
|
+
const grpcClient_1 = require("../client/grpcClient");
|
|
5
|
+
const utils_1 = require("../utils");
|
|
6
|
+
const events_1 = require("@stanterprise/protobuf/testsystem/v1/events");
|
|
7
|
+
const common_1 = require("@stanterprise/protobuf/testsystem/v1/common");
|
|
8
|
+
function mapTestStatus(status) {
|
|
9
|
+
switch (status) {
|
|
10
|
+
case "passed":
|
|
11
|
+
return common_1.TestStatus.PASSED;
|
|
12
|
+
case "failed":
|
|
13
|
+
return common_1.TestStatus.FAILED;
|
|
14
|
+
case "timedout":
|
|
15
|
+
return common_1.TestStatus.TIMEDOUT;
|
|
16
|
+
case "interrupted":
|
|
17
|
+
return common_1.TestStatus.INTERRUPTED;
|
|
18
|
+
default:
|
|
19
|
+
return common_1.TestStatus.UNKNOWN;
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
function handleOnEndEvent(result, runId, executionId, client, options, queue, metadata) {
|
|
23
|
+
// Validate and sanitize inputs to prevent serialization failures
|
|
24
|
+
const startTimeMs = result.startTime?.getTime();
|
|
25
|
+
const duration = result.duration;
|
|
26
|
+
// Ensure valid numeric values (protobuf cannot serialize NaN or undefined)
|
|
27
|
+
const validStartTimeMs = typeof startTimeMs === "number" && !isNaN(startTimeMs)
|
|
28
|
+
? startTimeMs
|
|
29
|
+
: Date.now();
|
|
30
|
+
const validDuration = typeof duration === "number" && !isNaN(duration) && duration >= 0
|
|
31
|
+
? duration
|
|
32
|
+
: 0;
|
|
33
|
+
const request = new events_1.TestRunEndEventRequest({
|
|
34
|
+
run_id: runId,
|
|
35
|
+
final_status: mapTestStatus(result.status),
|
|
36
|
+
start_time: (0, utils_1.createTimestampFromMs)(validStartTimeMs),
|
|
37
|
+
duration: (0, utils_1.createDuration)(validDuration),
|
|
38
|
+
metadata: (0, utils_1.toMetadataMap)(metadata || {}),
|
|
39
|
+
execution_id: executionId,
|
|
40
|
+
});
|
|
41
|
+
(0, grpcClient_1.reportUnary)(options, client, "/testsystem.v1.observer.TestEventCollector/ReportRunEnd", request, options.grpcTimeout, queue).catch((error) => {
|
|
42
|
+
if (error instanceof Error) {
|
|
43
|
+
console.error(`Failed to report end event: ${error.message}`, {
|
|
44
|
+
stack: error.stack,
|
|
45
|
+
});
|
|
46
|
+
}
|
|
47
|
+
else {
|
|
48
|
+
console.error("Failed to report end event: Unknown error type", {
|
|
49
|
+
error,
|
|
50
|
+
});
|
|
51
|
+
}
|
|
52
|
+
});
|
|
53
|
+
}
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import { TestError } from "@playwright/test/reporter";
|
|
2
|
+
import { StanterpriseReporterOptions } from "../types";
|
|
3
|
+
import * as grpc from "@grpc/grpc-js";
|
|
4
|
+
import { MessageQueue } from "../client/messageQueue";
|
|
5
|
+
export declare function handleOnErrorEvent(error: TestError, runId: string, executionId: string, grpcClient: grpc.Client, options: StanterpriseReporterOptions, queue?: MessageQueue): void;
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.handleOnErrorEvent = handleOnErrorEvent;
|
|
4
|
+
const events_1 = require("@stanterprise/protobuf/testsystem/v1/events");
|
|
5
|
+
const timeHelpers_1 = require("../utils/timeHelpers");
|
|
6
|
+
const grpcClient_1 = require("../client/grpcClient");
|
|
7
|
+
function handleOnErrorEvent(error, runId, executionId, grpcClient, options, queue) {
|
|
8
|
+
const request = new events_1.StdErrorEventRequest({
|
|
9
|
+
run_id: runId,
|
|
10
|
+
message: error.message,
|
|
11
|
+
timestamp: (0, timeHelpers_1.createTimestampFromMs)(Date.now()),
|
|
12
|
+
execution_id: executionId,
|
|
13
|
+
});
|
|
14
|
+
// Fire-and-forget to avoid slowing tests
|
|
15
|
+
(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));
|
|
16
|
+
}
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import { TestCase, TestResult, TestStep } from "@playwright/test/reporter";
|
|
2
|
+
import { StanterpriseReporterOptions } from "../types";
|
|
3
|
+
import * as grpc from "@grpc/grpc-js";
|
|
4
|
+
import { MessageQueue } from "../client/messageQueue";
|
|
5
|
+
export declare function handleOnStepBeginEvent(test: TestCase, result: TestResult, step: TestStep, runId: string, executionId: string, client: grpc.Client, options: StanterpriseReporterOptions, queue?: MessageQueue): void;
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.handleOnStepBeginEvent = handleOnStepBeginEvent;
|
|
4
|
+
const entities_1 = require("@stanterprise/protobuf/testsystem/v1/entities");
|
|
5
|
+
const events_1 = require("@stanterprise/protobuf/testsystem/v1/events");
|
|
6
|
+
const utils_1 = require("../utils");
|
|
7
|
+
const grpcClient_1 = require("../client/grpcClient");
|
|
8
|
+
const utils_2 = require("../utils");
|
|
9
|
+
const common_1 = require("@stanterprise/protobuf/testsystem/v1/common");
|
|
10
|
+
function handleOnStepBeginEvent(test, result, step, runId, executionId, client, options, queue) {
|
|
11
|
+
// Build metadata from step annotations
|
|
12
|
+
const metadata = (0, utils_1.buildStepMetadata)(step);
|
|
13
|
+
// Get parent step ID if this step has a parent
|
|
14
|
+
const stepId = (0, utils_2.generateStepId)(step, test);
|
|
15
|
+
// Build and send the StepBegin event
|
|
16
|
+
const request = new events_1.StepBeginEventRequest({
|
|
17
|
+
step: new entities_1.StepRun({
|
|
18
|
+
id: stepId,
|
|
19
|
+
run_id: runId,
|
|
20
|
+
test_case_id: test.id,
|
|
21
|
+
title: step.title,
|
|
22
|
+
status: common_1.TestStatus.RUNNING,
|
|
23
|
+
type: step.category,
|
|
24
|
+
start_time: (0, utils_1.createTimestamp)(step.startTime),
|
|
25
|
+
location: step.location
|
|
26
|
+
? `${step.location.file}:${step.location.line}:${step.location.column}`
|
|
27
|
+
: undefined,
|
|
28
|
+
metadata: (0, utils_1.toMetadataMap)(metadata),
|
|
29
|
+
parent_step_id: step.parent
|
|
30
|
+
? (0, utils_2.generateStepId)(step.parent, test)
|
|
31
|
+
: undefined,
|
|
32
|
+
worker_index: result.workerIndex.toString(),
|
|
33
|
+
retry_index: result.retry,
|
|
34
|
+
execution_id: executionId,
|
|
35
|
+
}),
|
|
36
|
+
});
|
|
37
|
+
// Fire-and-forget to avoid slowing tests
|
|
38
|
+
(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));
|
|
39
|
+
}
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import { TestCase, TestResult, TestStep } from "@playwright/test/reporter";
|
|
2
|
+
import { StanterpriseReporterOptions } from "../types";
|
|
3
|
+
import * as grpc from "@grpc/grpc-js";
|
|
4
|
+
import { MessageQueue } from "../client/messageQueue";
|
|
5
|
+
export declare function handleOnStepEndEvent(test: TestCase, result: TestResult, step: TestStep, runId: string, executionId: string, client: grpc.Client, options: StanterpriseReporterOptions, queue?: MessageQueue): void;
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.handleOnStepEndEvent = handleOnStepEndEvent;
|
|
4
|
+
const entities_1 = require("@stanterprise/protobuf/testsystem/v1/entities");
|
|
5
|
+
const events_1 = require("@stanterprise/protobuf/testsystem/v1/events");
|
|
6
|
+
const utils_1 = require("../utils");
|
|
7
|
+
const grpcClient_1 = require("../client/grpcClient");
|
|
8
|
+
const utils_2 = require("../utils");
|
|
9
|
+
function handleOnStepEndEvent(test, result, step, runId, executionId, client, options, queue) {
|
|
10
|
+
// Map step error to status
|
|
11
|
+
const stepStatus = (0, utils_1.mapStepStatus)(!!step.error);
|
|
12
|
+
const attachments = (0, utils_1.processAttachments)(result, options.maxAttachmentSize || 10485760);
|
|
13
|
+
// Build metadata from step annotations
|
|
14
|
+
const metadata = (0, utils_1.buildStepMetadata)(step);
|
|
15
|
+
const stepId = (0, utils_2.generateStepId)(step, test);
|
|
16
|
+
// Extract comprehensive error details if present
|
|
17
|
+
const errorMessage = step.error?.message || "";
|
|
18
|
+
const errorStack = step.error?.stack || "";
|
|
19
|
+
const errorValue = step.error?.value || "";
|
|
20
|
+
const errorSnippet = step.error?.snippet || "";
|
|
21
|
+
const errorLocation = step.error?.location
|
|
22
|
+
? `${step.error.location.file}:${step.error.location.line}:${step.error.location.column}`
|
|
23
|
+
: "";
|
|
24
|
+
// Combine all error information into metadata for comprehensive error tracking
|
|
25
|
+
const errorMetadata = {};
|
|
26
|
+
if (errorStack)
|
|
27
|
+
errorMetadata.error_stack = (0, utils_1.truncateValue)(errorStack);
|
|
28
|
+
if (errorValue)
|
|
29
|
+
errorMetadata.error_value = (0, utils_1.truncateValue)(errorValue);
|
|
30
|
+
if (errorSnippet)
|
|
31
|
+
errorMetadata.error_snippet = (0, utils_1.truncateValue)(errorSnippet);
|
|
32
|
+
if (errorLocation)
|
|
33
|
+
errorMetadata.error_location = (0, utils_1.truncateValue)(errorLocation);
|
|
34
|
+
// Build and send the StepEnd event
|
|
35
|
+
const request = new events_1.StepEndEventRequest({
|
|
36
|
+
step: new entities_1.StepRun({
|
|
37
|
+
id: stepId,
|
|
38
|
+
run_id: runId,
|
|
39
|
+
test_case_id: test.id,
|
|
40
|
+
title: step.title,
|
|
41
|
+
type: step.category,
|
|
42
|
+
start_time: (0, utils_1.createTimestamp)(step.startTime),
|
|
43
|
+
duration: (0, utils_1.createDuration)(step.duration),
|
|
44
|
+
status: stepStatus,
|
|
45
|
+
error: errorMessage,
|
|
46
|
+
errors: errorMessage ? [errorMessage] : [],
|
|
47
|
+
location: step.location
|
|
48
|
+
? `${step.location.file}:${step.location.line}:${step.location.column}`
|
|
49
|
+
: "",
|
|
50
|
+
metadata: (0, utils_1.toMetadataMap)({ ...metadata, ...errorMetadata }),
|
|
51
|
+
parent_step_id: step.parent
|
|
52
|
+
? (0, utils_2.generateStepId)(step.parent, test)
|
|
53
|
+
: undefined,
|
|
54
|
+
worker_index: result.workerIndex.toString(),
|
|
55
|
+
retry_index: result.retry,
|
|
56
|
+
attachments: attachments,
|
|
57
|
+
execution_id: executionId,
|
|
58
|
+
}),
|
|
59
|
+
});
|
|
60
|
+
// Fire-and-forget to avoid slowing tests
|
|
61
|
+
(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));
|
|
62
|
+
}
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import { TestCase, TestResult } from "@playwright/test/reporter";
|
|
2
|
+
import { StanterpriseReporterOptions } from "../types";
|
|
3
|
+
import * as grpc from "@grpc/grpc-js";
|
|
4
|
+
import { MessageQueue } from "../client/messageQueue";
|
|
5
|
+
export declare function handleOnTestBeginEvent(test: TestCase, result: TestResult, runId: string, executionId: string, client: grpc.Client, options: StanterpriseReporterOptions, queue?: MessageQueue): void;
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.handleOnTestBeginEvent = handleOnTestBeginEvent;
|
|
4
|
+
const entities_1 = require("@stanterprise/protobuf/testsystem/v1/entities");
|
|
5
|
+
const events_1 = require("@stanterprise/protobuf/testsystem/v1/events");
|
|
6
|
+
const utils_1 = require("../utils");
|
|
7
|
+
const grpcClient_1 = require("../client/grpcClient");
|
|
8
|
+
const common_1 = require("@stanterprise/protobuf/testsystem/v1/common");
|
|
9
|
+
function handleOnTestBeginEvent(test, result, runId, executionId, client, options, queue) {
|
|
10
|
+
// Build and send the TestBegin event via generic unary call.
|
|
11
|
+
const request = new events_1.TestBeginEventRequest({
|
|
12
|
+
test_case: new entities_1.TestCaseRun({
|
|
13
|
+
id: test.id,
|
|
14
|
+
name: test.title,
|
|
15
|
+
run_id: runId,
|
|
16
|
+
retry_index: result.retry,
|
|
17
|
+
retry_count: test.retries,
|
|
18
|
+
location: test.location
|
|
19
|
+
? `${test.location.file}:${test.location.line}:${test.location.column}`
|
|
20
|
+
: undefined,
|
|
21
|
+
status: common_1.TestStatus.RUNNING,
|
|
22
|
+
test_suite_id: (0, utils_1.generateSuiteId)(test.parent),
|
|
23
|
+
start_time: (0, utils_1.createTimestamp)(result.startTime),
|
|
24
|
+
tags: test.tags,
|
|
25
|
+
execution_id: executionId,
|
|
26
|
+
}),
|
|
27
|
+
});
|
|
28
|
+
// Fire-and-forget to avoid slowing tests; log errors once.
|
|
29
|
+
(0, grpcClient_1.reportUnary)(options, client, "/testsystem.v1.observer.TestEventCollector/ReportTestBegin", request, options.grpcTimeout, queue).catch((e) => {
|
|
30
|
+
const details = e instanceof Error ? `${e.message}` : String(e);
|
|
31
|
+
console.warn(`Failed to report test begin. Details: ${details}`);
|
|
32
|
+
});
|
|
33
|
+
}
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import { TestCase, TestResult } from "@playwright/test/reporter";
|
|
2
|
+
import { StanterpriseReporterOptions } from "../types";
|
|
3
|
+
import * as grpc from "@grpc/grpc-js";
|
|
4
|
+
import { MessageQueue } from "../client/messageQueue";
|
|
5
|
+
export declare function handleOnTestEndEvent(test: TestCase, result: TestResult, runId: string, executionId: string, client: grpc.Client, options: StanterpriseReporterOptions, queue?: MessageQueue): void;
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.handleOnTestEndEvent = handleOnTestEndEvent;
|
|
4
|
+
const entities_1 = require("@stanterprise/protobuf/testsystem/v1/entities");
|
|
5
|
+
const events_1 = require("@stanterprise/protobuf/testsystem/v1/events");
|
|
6
|
+
const utils_1 = require("../utils");
|
|
7
|
+
const grpcClient_1 = require("../client/grpcClient");
|
|
8
|
+
const utils_2 = require("../utils");
|
|
9
|
+
/**
|
|
10
|
+
* Constant for converting bytes to megabytes
|
|
11
|
+
*/
|
|
12
|
+
const BYTES_PER_MB = 1048576;
|
|
13
|
+
function handleOnTestEndEvent(test, result, runId, executionId, client, options, queue) {
|
|
14
|
+
// Map Playwright test status to protobuf TestStatus
|
|
15
|
+
const testStatus = (0, utils_1.mapTestStatus)(result.status);
|
|
16
|
+
// Process attachments (screenshots, videos, etc.)
|
|
17
|
+
const attachments = (0, utils_1.processAttachments)(result, options.maxAttachmentSize || 10485760);
|
|
18
|
+
// Extract error information if the test failed
|
|
19
|
+
const { errorMessage, stackTrace, errors } = (0, utils_1.extractErrorInfo)(result);
|
|
20
|
+
// Get test suite run ID from parent suite if available, ensuring suite is reported
|
|
21
|
+
const suiteId = (0, utils_2.generateSuiteId)(test.parent);
|
|
22
|
+
// Build metadata from test annotations and result metadata
|
|
23
|
+
const metadata = (0, utils_1.buildTestMetadata)(test, result);
|
|
24
|
+
// Build and send the TestEnd event
|
|
25
|
+
const request = new events_1.TestEndEventRequest({
|
|
26
|
+
test_case: new entities_1.TestCaseRun({
|
|
27
|
+
id: test.id,
|
|
28
|
+
name: test.title,
|
|
29
|
+
run_id: runId,
|
|
30
|
+
test_suite_id: suiteId,
|
|
31
|
+
status: testStatus,
|
|
32
|
+
start_time: (0, utils_1.createTimestamp)(result.startTime),
|
|
33
|
+
duration: (0, utils_1.createDuration)(result.duration),
|
|
34
|
+
attachments: attachments,
|
|
35
|
+
error_message: errorMessage,
|
|
36
|
+
stack_trace: stackTrace,
|
|
37
|
+
errors: errors,
|
|
38
|
+
metadata: (0, utils_1.toMetadataMap)(metadata),
|
|
39
|
+
tags: test.tags,
|
|
40
|
+
retry_index: result.retry,
|
|
41
|
+
retry_count: test.retries,
|
|
42
|
+
execution_id: executionId,
|
|
43
|
+
}),
|
|
44
|
+
});
|
|
45
|
+
// Serialize once to capture payload size for potential error logging
|
|
46
|
+
const serializedPayload = request.serializeBinary();
|
|
47
|
+
const payloadSize = serializedPayload.length;
|
|
48
|
+
// Warn if payload is unusually large (>1MB)
|
|
49
|
+
if (payloadSize > 1048576) {
|
|
50
|
+
console.warn(`Large payload detected for test "${test.title}": ${(payloadSize / BYTES_PER_MB).toFixed(2)}MB. ` +
|
|
51
|
+
`Attachments: ${attachments.length}, Metadata keys: ${Object.keys(metadata).length}, ` +
|
|
52
|
+
`Error length: ${errorMessage.length + stackTrace.length} bytes`);
|
|
53
|
+
}
|
|
54
|
+
// Fire-and-forget to avoid slowing tests
|
|
55
|
+
(0, grpcClient_1.reportUnary)(options, client, "/testsystem.v1.observer.TestEventCollector/ReportTestEnd", request, options.grpcTimeout, queue).catch((e) => {
|
|
56
|
+
console.error(`Failed to report test end for "${test.title}" (payload size: ${(payloadSize / BYTES_PER_MB).toFixed(2)}MB)`, e);
|
|
57
|
+
});
|
|
58
|
+
}
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import { TestCase, TestResult } from "@playwright/test/reporter";
|
|
2
|
+
import { StanterpriseReporterOptions } from "../types";
|
|
3
|
+
import * as grpc from "@grpc/grpc-js";
|
|
4
|
+
import { MessageQueue } from "../client/messageQueue";
|
|
5
|
+
export declare function handleOnTestFailEvent(test: TestCase, result: TestResult, runId: string, executionId: string, grpcClient: grpc.Client, options: StanterpriseReporterOptions, queue?: MessageQueue): void;
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.handleOnTestFailEvent = handleOnTestFailEvent;
|
|
4
|
+
const utils_1 = require("../utils");
|
|
5
|
+
const events_1 = require("@stanterprise/protobuf/testsystem/v1/events");
|
|
6
|
+
const grpcClient_1 = require("../client/grpcClient");
|
|
7
|
+
function handleOnTestFailEvent(test, result, runId, executionId, grpcClient, options, queue) {
|
|
8
|
+
// Extract failure details
|
|
9
|
+
const { errorMessage: failureMessage, stackTrace } = (0, utils_1.extractErrorInfo)(result);
|
|
10
|
+
// Process attachments for failed tests
|
|
11
|
+
const attachments = (0, utils_1.processAttachments)(result);
|
|
12
|
+
// Build and send the TestFailure event
|
|
13
|
+
const request = new events_1.TestFailureEventRequest({
|
|
14
|
+
test_id: test.id,
|
|
15
|
+
run_id: runId,
|
|
16
|
+
failure_message: failureMessage,
|
|
17
|
+
stack_trace: stackTrace,
|
|
18
|
+
timestamp: (0, utils_1.createTimestampFromMs)(Date.now()),
|
|
19
|
+
attachments: attachments,
|
|
20
|
+
retry_index: result.retry,
|
|
21
|
+
execution_id: executionId,
|
|
22
|
+
});
|
|
23
|
+
// Fire-and-forget to avoid slowing tests
|
|
24
|
+
(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));
|
|
25
|
+
}
|
package/dist/reporter.d.ts
CHANGED
|
@@ -2,20 +2,15 @@ import type { Reporter, FullConfig, Suite, TestCase, TestResult, FullResult, Tes
|
|
|
2
2
|
import { StanterpriseReporterOptions } from "./types";
|
|
3
3
|
export default class StanterpriseReporter implements Reporter {
|
|
4
4
|
private grpcClient;
|
|
5
|
-
private
|
|
6
|
-
private grpcEnabled;
|
|
7
|
-
private grpcTimeout;
|
|
8
|
-
private grpcFirstErrorLogged;
|
|
9
|
-
private verbose;
|
|
5
|
+
private options;
|
|
10
6
|
private runId;
|
|
7
|
+
private executionId;
|
|
11
8
|
private runStartTime;
|
|
12
|
-
private
|
|
13
|
-
private
|
|
9
|
+
private messageQueue;
|
|
10
|
+
private metadata;
|
|
14
11
|
constructor(options?: StanterpriseReporterOptions);
|
|
15
12
|
onBegin(config: FullConfig, suite: Suite): void;
|
|
16
13
|
onExit(): Promise<void>;
|
|
17
|
-
getRunId(): string;
|
|
18
|
-
getRunStartTime(): Date;
|
|
19
14
|
onEnd(result: FullResult): Promise<{
|
|
20
15
|
status?: FullResult["status"];
|
|
21
16
|
} | undefined | void> | void;
|
|
@@ -27,10 +22,4 @@ export default class StanterpriseReporter implements Reporter {
|
|
|
27
22
|
onError(error: TestError): void;
|
|
28
23
|
onStdErr(chunk: string | Buffer, test: void | TestCase, result: void | TestResult): void;
|
|
29
24
|
onStdOut(chunk: string | Buffer, test: void | TestCase, result: void | TestResult): void;
|
|
30
|
-
private getSuiteId;
|
|
31
|
-
private reportSuiteBegin;
|
|
32
|
-
private reportSuiteEnd;
|
|
33
|
-
private mapSuiteStatus;
|
|
34
|
-
private logGrpcErrorOnce;
|
|
35
|
-
private reportUnary;
|
|
36
25
|
}
|