@stanterprise/playwright-reporter 0.0.1 → 0.0.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/dist/client/grpcClient.d.ts +7 -0
- package/dist/client/grpcClient.js +79 -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 +66 -0
- package/dist/handlers/onEndHandler.d.ts +1 -0
- package/dist/handlers/onEndHandler.js +4 -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 +42 -0
- package/dist/handlers/onStepEndHandler.d.ts +4 -0
- package/dist/handlers/onStepEndHandler.js +47 -0
- package/dist/handlers/onTestBeginHandler.d.ts +4 -0
- package/dist/handlers/onTestBeginHandler.js +25 -0
- package/dist/handlers/onTestEndHandler.d.ts +4 -0
- package/dist/handlers/onTestEndHandler.js +51 -0
- package/dist/handlers/onTestFailHandler.d.ts +4 -0
- package/dist/handlers/onTestFailHandler.js +23 -0
- package/dist/reporter.d.ts +1 -15
- package/dist/reporter.js +68 -386
- package/dist/utils/idGenerators.d.ts +20 -0
- package/dist/utils/idGenerators.js +40 -0
- package/dist/utils/index.d.ts +1 -0
- package/dist/utils/index.js +1 -0
- package/dist/utils/optionsMapper.d.ts +7 -0
- package/dist/utils/optionsMapper.js +21 -0
- package/package.json +2 -2
- package/dist/utils/suiteMapper.d.ts +0 -1
- package/dist/utils/suiteMapper.js +0 -4
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import { StanterpriseReporterOptions } from "../types";
|
|
2
|
+
import * as grpc from "@grpc/grpc-js";
|
|
3
|
+
export default function getClient(options: StanterpriseReporterOptions): grpc.Client | null;
|
|
4
|
+
export declare function reportUnary(options: StanterpriseReporterOptions, grpcClient: grpc.Client, path: string, message: {
|
|
5
|
+
serialize?: (w?: any) => Uint8Array;
|
|
6
|
+
serializeBinary?: () => Uint8Array;
|
|
7
|
+
}, deadlineMs?: number): Promise<Buffer>;
|
|
@@ -0,0 +1,79 @@
|
|
|
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
|
+
function getClient(options) {
|
|
40
|
+
try {
|
|
41
|
+
return new grpc.Client(options.grpcAddress, grpc.credentials.createInsecure());
|
|
42
|
+
}
|
|
43
|
+
catch (e) {
|
|
44
|
+
console.error("Failed to create gRPC client", e);
|
|
45
|
+
}
|
|
46
|
+
return null;
|
|
47
|
+
}
|
|
48
|
+
// Helper: generic unary call using raw method path
|
|
49
|
+
async function reportUnary(options, grpcClient, path, message, deadlineMs = 1000) {
|
|
50
|
+
if (!options.grpcEnabled || !grpcClient) {
|
|
51
|
+
return Buffer.alloc(0);
|
|
52
|
+
}
|
|
53
|
+
const reqSerialize = (arg) => {
|
|
54
|
+
const m = arg;
|
|
55
|
+
const bytes = m?.serializeBinary
|
|
56
|
+
? m.serializeBinary()
|
|
57
|
+
: m?.serialize
|
|
58
|
+
? m.serialize()
|
|
59
|
+
: new Uint8Array(0);
|
|
60
|
+
return Buffer.from(bytes);
|
|
61
|
+
};
|
|
62
|
+
const resDeserialize = (bytes) => bytes;
|
|
63
|
+
const metadata = new grpc.Metadata();
|
|
64
|
+
const callOptions = {
|
|
65
|
+
deadline: new Date(Date.now() + deadlineMs),
|
|
66
|
+
};
|
|
67
|
+
return new Promise((resolve, reject) => {
|
|
68
|
+
try {
|
|
69
|
+
grpcClient.makeUnaryRequest(path, reqSerialize, resDeserialize, message, metadata, callOptions, (err, response) => {
|
|
70
|
+
if (err)
|
|
71
|
+
return reject(err);
|
|
72
|
+
resolve(response);
|
|
73
|
+
});
|
|
74
|
+
}
|
|
75
|
+
catch (e) {
|
|
76
|
+
reject(e);
|
|
77
|
+
}
|
|
78
|
+
});
|
|
79
|
+
}
|
|
@@ -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, runId: string, client: grpc.Client, options: StanterpriseReporterOptions): void;
|
|
@@ -0,0 +1,66 @@
|
|
|
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
|
+
function handleOnBeginEvent(config, suite, runId, client, options) {
|
|
9
|
+
// Report root suite and all child suites recursively
|
|
10
|
+
const request = new events_1.MapTestRunEventRequest({
|
|
11
|
+
run_id: runId,
|
|
12
|
+
test_suites: mapSuites(suite, runId),
|
|
13
|
+
});
|
|
14
|
+
(0, grpcClient_1.reportUnary)(options, client, "/testsystem.v1.observer.TestEventCollector/MapTestRun", request, options.grpcTimeout).catch((e) => {
|
|
15
|
+
const details = e instanceof Error ? `${e.message}` : String(e);
|
|
16
|
+
console.warn(`onBegin. gRPC disabled for the remainder of the run. Address=${options.grpcAddress}. Details: ${details}`);
|
|
17
|
+
});
|
|
18
|
+
}
|
|
19
|
+
function mapSuites(suite, runId) {
|
|
20
|
+
let allSuites = getAllSuites(suite);
|
|
21
|
+
return allSuites.map((currentSuite) => mapSingleSuite(currentSuite, runId));
|
|
22
|
+
}
|
|
23
|
+
function getAllSuites(suite) {
|
|
24
|
+
let suites = [];
|
|
25
|
+
suites.push(suite);
|
|
26
|
+
suite.suites.forEach((childSuite) => {
|
|
27
|
+
suites = suites.concat(getAllSuites(childSuite));
|
|
28
|
+
});
|
|
29
|
+
return suites;
|
|
30
|
+
}
|
|
31
|
+
function mapSingleSuite(suite, runId) {
|
|
32
|
+
const suiteId = (0, utils_1.generateSuiteId)(suite);
|
|
33
|
+
const parentSuiteId = suite.parent
|
|
34
|
+
? (0, utils_1.generateSuiteId)(suite.parent)
|
|
35
|
+
: undefined;
|
|
36
|
+
let type;
|
|
37
|
+
switch (suite.type) {
|
|
38
|
+
case "root":
|
|
39
|
+
type = entities_1.SuiteType.ROOT;
|
|
40
|
+
break;
|
|
41
|
+
case "describe":
|
|
42
|
+
type = entities_1.SuiteType.SUBSUITE;
|
|
43
|
+
break;
|
|
44
|
+
case "file":
|
|
45
|
+
type = entities_1.SuiteType.SUBSUITE;
|
|
46
|
+
break;
|
|
47
|
+
case "project":
|
|
48
|
+
type = entities_1.SuiteType.PROJECT;
|
|
49
|
+
break;
|
|
50
|
+
}
|
|
51
|
+
return new entities_1.TestSuiteRun({
|
|
52
|
+
id: suiteId,
|
|
53
|
+
name: suite.title,
|
|
54
|
+
run_id: runId,
|
|
55
|
+
parent_suite_id: parentSuiteId,
|
|
56
|
+
test_cases: suite.tests.map((test) => new entities_1.TestCaseRun({
|
|
57
|
+
id: test.id,
|
|
58
|
+
name: test.title,
|
|
59
|
+
run_id: runId,
|
|
60
|
+
})),
|
|
61
|
+
test_case_ids: suite.tests.map((test) => test.id),
|
|
62
|
+
type: type,
|
|
63
|
+
// start_time: createTimestamp(startTime),
|
|
64
|
+
// metadata: metadata,
|
|
65
|
+
});
|
|
66
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function handleOnEndEvent(): void;
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import { TestError } from "@playwright/test/reporter";
|
|
2
|
+
import { StanterpriseReporterOptions } from "../types";
|
|
3
|
+
import * as grpc from "@grpc/grpc-js";
|
|
4
|
+
export declare function handleOnErrorEvent(error: TestError, runId: string, grpcClient: grpc.Client, options: StanterpriseReporterOptions): void;
|
|
@@ -0,0 +1,15 @@
|
|
|
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, grpcClient, options) {
|
|
8
|
+
const request = new events_1.StdErrorEventRequest({
|
|
9
|
+
run_id: runId,
|
|
10
|
+
message: error.message,
|
|
11
|
+
timestamp: (0, timeHelpers_1.createTimestampFromMs)(Date.now()),
|
|
12
|
+
});
|
|
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));
|
|
15
|
+
}
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import { TestCase, TestResult, TestStep } from "@playwright/test/reporter";
|
|
2
|
+
import { StanterpriseReporterOptions } from "../types";
|
|
3
|
+
import * as grpc from "@grpc/grpc-js";
|
|
4
|
+
export declare function handleOnStepBeginEvent(test: TestCase, result: TestResult, step: TestStep, runId: string, client: grpc.Client, options: StanterpriseReporterOptions): void;
|
|
@@ -0,0 +1,42 @@
|
|
|
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
|
+
function handleOnStepBeginEvent(test, result, step, runId, client, options) {
|
|
10
|
+
// Build metadata from step annotations
|
|
11
|
+
const metadata = new Map();
|
|
12
|
+
metadata.set("category", step.category);
|
|
13
|
+
step.annotations.forEach((annotation, index) => {
|
|
14
|
+
metadata.set(`annotation_${index}_type`, annotation.type);
|
|
15
|
+
if (annotation.description) {
|
|
16
|
+
metadata.set(`annotation_${index}_description`, annotation.description);
|
|
17
|
+
}
|
|
18
|
+
});
|
|
19
|
+
// Get parent step ID if this step has a parent
|
|
20
|
+
const stepId = (0, utils_2.generateStepId)(step, test);
|
|
21
|
+
// Build and send the StepBegin event
|
|
22
|
+
const request = new events_1.StepBeginEventRequest({
|
|
23
|
+
step: new entities_1.StepRun({
|
|
24
|
+
id: stepId,
|
|
25
|
+
run_id: runId,
|
|
26
|
+
test_case_run_id: test.id,
|
|
27
|
+
title: step.title,
|
|
28
|
+
type: step.category,
|
|
29
|
+
start_time: (0, utils_1.createTimestamp)(step.startTime),
|
|
30
|
+
location: step.location
|
|
31
|
+
? `${step.location.file}:${step.location.line}:${step.location.column}`
|
|
32
|
+
: "",
|
|
33
|
+
metadata: metadata,
|
|
34
|
+
parent_step_id: step.parent
|
|
35
|
+
? (0, utils_2.generateStepId)(step.parent, test)
|
|
36
|
+
: undefined,
|
|
37
|
+
worker_index: result.workerIndex.toString(),
|
|
38
|
+
}),
|
|
39
|
+
});
|
|
40
|
+
// Fire-and-forget to avoid slowing tests
|
|
41
|
+
(0, grpcClient_1.reportUnary)(options, client, "/testsystem.v1.observer.TestEventCollector/ReportStepBegin", request, options.grpcTimeout).catch((e) => console.error("Failed to report step begin", e));
|
|
42
|
+
}
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import { TestCase, TestResult, TestStep } from "@playwright/test/reporter";
|
|
2
|
+
import { StanterpriseReporterOptions } from "../types";
|
|
3
|
+
import * as grpc from "@grpc/grpc-js";
|
|
4
|
+
export declare function handleOnStepEndEvent(test: TestCase, result: TestResult, step: TestStep, runId: string, client: grpc.Client, options: StanterpriseReporterOptions): void;
|
|
@@ -0,0 +1,47 @@
|
|
|
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, client, options) {
|
|
10
|
+
// Map step error to status
|
|
11
|
+
const stepStatus = (0, utils_1.mapStepStatus)(!!step.error);
|
|
12
|
+
// Build metadata from step annotations
|
|
13
|
+
const metadata = new Map();
|
|
14
|
+
metadata.set("category", step.category);
|
|
15
|
+
step.annotations.forEach((annotation, index) => {
|
|
16
|
+
metadata.set(`annotation_${index}_type`, annotation.type);
|
|
17
|
+
if (annotation.description) {
|
|
18
|
+
metadata.set(`annotation_${index}_description`, annotation.description);
|
|
19
|
+
}
|
|
20
|
+
});
|
|
21
|
+
const stepId = (0, utils_2.generateStepId)(step, test);
|
|
22
|
+
// Build and send the StepEnd event
|
|
23
|
+
const request = new events_1.StepEndEventRequest({
|
|
24
|
+
step: new entities_1.StepRun({
|
|
25
|
+
id: stepId,
|
|
26
|
+
run_id: runId,
|
|
27
|
+
test_case_run_id: test.id,
|
|
28
|
+
title: step.title,
|
|
29
|
+
type: step.category,
|
|
30
|
+
start_time: (0, utils_1.createTimestamp)(step.startTime),
|
|
31
|
+
duration: (0, utils_1.createDuration)(step.duration),
|
|
32
|
+
status: stepStatus,
|
|
33
|
+
error: step.error ? step.error.message || "" : "",
|
|
34
|
+
errors: step.error ? [step.error.message || ""] : [],
|
|
35
|
+
location: step.location
|
|
36
|
+
? `${step.location.file}:${step.location.line}:${step.location.column}`
|
|
37
|
+
: "",
|
|
38
|
+
metadata: metadata,
|
|
39
|
+
parent_step_id: step.parent
|
|
40
|
+
? (0, utils_2.generateStepId)(step.parent, test)
|
|
41
|
+
: undefined,
|
|
42
|
+
worker_index: result.workerIndex.toString(),
|
|
43
|
+
}),
|
|
44
|
+
});
|
|
45
|
+
// Fire-and-forget to avoid slowing tests
|
|
46
|
+
(0, grpcClient_1.reportUnary)(options, client, "/testsystem.v1.observer.TestEventCollector/ReportStepEnd", request, options.grpcTimeout).catch((e) => console.error("Failed to report step end", e));
|
|
47
|
+
}
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import { TestCase, TestResult } from "@playwright/test/reporter";
|
|
2
|
+
import { StanterpriseReporterOptions } from "../types";
|
|
3
|
+
import * as grpc from "@grpc/grpc-js";
|
|
4
|
+
export declare function handleOnTestBeginEvent(test: TestCase, result: TestResult, runId: string, client: grpc.Client, options: StanterpriseReporterOptions): void;
|
|
@@ -0,0 +1,25 @@
|
|
|
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
|
+
function handleOnTestBeginEvent(test, result, runId, client, options) {
|
|
9
|
+
// Build and send the TestBegin event via generic unary call.
|
|
10
|
+
const request = new events_1.TestBeginEventRequest({
|
|
11
|
+
test_case: new entities_1.TestCaseRun({
|
|
12
|
+
id: test.id,
|
|
13
|
+
name: test.title,
|
|
14
|
+
run_id: runId,
|
|
15
|
+
test_suite_run_id: (0, utils_1.generateSuiteId)(test.parent),
|
|
16
|
+
start_time: (0, utils_1.createTimestamp)(result.startTime),
|
|
17
|
+
tags: test.tags,
|
|
18
|
+
}),
|
|
19
|
+
});
|
|
20
|
+
// Fire-and-forget to avoid slowing tests; log errors once.
|
|
21
|
+
(0, grpcClient_1.reportUnary)(options, client, "/testsystem.v1.observer.TestEventCollector/ReportTestBegin", request, options.grpcTimeout).catch((e) => {
|
|
22
|
+
const details = e instanceof Error ? `${e.message}` : String(e);
|
|
23
|
+
console.warn(`Failed to report test begin. Details: ${details}`);
|
|
24
|
+
});
|
|
25
|
+
}
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import { TestCase, TestResult } from "@playwright/test/reporter";
|
|
2
|
+
import { StanterpriseReporterOptions } from "../types";
|
|
3
|
+
import * as grpc from "@grpc/grpc-js";
|
|
4
|
+
export declare function handleOnTestEndEvent(test: TestCase, result: TestResult, runId: string, client: grpc.Client, options: StanterpriseReporterOptions): void;
|
|
@@ -0,0 +1,51 @@
|
|
|
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
|
+
function handleOnTestEndEvent(test, result, runId, client, options) {
|
|
10
|
+
// Map Playwright test status to protobuf TestStatus
|
|
11
|
+
const testStatus = (0, utils_1.mapTestStatus)(result.status);
|
|
12
|
+
// Process attachments (screenshots, videos, etc.)
|
|
13
|
+
const attachments = (0, utils_1.processAttachments)(result);
|
|
14
|
+
// Extract error information if the test failed
|
|
15
|
+
const { errorMessage, stackTrace, errors } = (0, utils_1.extractErrorInfo)(result);
|
|
16
|
+
// Get test suite run ID from parent suite if available, ensuring suite is reported
|
|
17
|
+
const suiteId = (0, utils_2.generateSuiteId)(test.parent);
|
|
18
|
+
// Build metadata from test annotations and result metadata
|
|
19
|
+
const metadata = new Map();
|
|
20
|
+
test.annotations.forEach((annotation, index) => {
|
|
21
|
+
metadata.set(`annotation_${index}_type`, annotation.type);
|
|
22
|
+
if (annotation.description) {
|
|
23
|
+
metadata.set(`annotation_${index}_description`, annotation.description);
|
|
24
|
+
}
|
|
25
|
+
});
|
|
26
|
+
result.annotations.forEach((annotation, index) => {
|
|
27
|
+
metadata.set(`result_annotation_${index}_type`, annotation.type);
|
|
28
|
+
if (annotation.description) {
|
|
29
|
+
metadata.set(`result_annotation_${index}_description`, annotation.description);
|
|
30
|
+
}
|
|
31
|
+
});
|
|
32
|
+
// Build and send the TestEnd event
|
|
33
|
+
const request = new events_1.TestEndEventRequest({
|
|
34
|
+
test_case: new entities_1.TestCaseRun({
|
|
35
|
+
id: test.id,
|
|
36
|
+
name: test.title,
|
|
37
|
+
run_id: runId,
|
|
38
|
+
test_suite_run_id: suiteId,
|
|
39
|
+
status: testStatus,
|
|
40
|
+
start_time: (0, utils_1.createTimestamp)(result.startTime),
|
|
41
|
+
attachments: attachments,
|
|
42
|
+
error_message: errorMessage,
|
|
43
|
+
stack_trace: stackTrace,
|
|
44
|
+
errors: errors,
|
|
45
|
+
metadata: metadata,
|
|
46
|
+
tags: test.tags,
|
|
47
|
+
}),
|
|
48
|
+
});
|
|
49
|
+
// Fire-and-forget to avoid slowing tests
|
|
50
|
+
(0, grpcClient_1.reportUnary)(options, client, "/testsystem.v1.observer.TestEventCollector/ReportTestEnd", request, options.grpcTimeout).catch((e) => console.error("Failed to report test end", e));
|
|
51
|
+
}
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import { TestCase, TestResult } from "@playwright/test/reporter";
|
|
2
|
+
import { StanterpriseReporterOptions } from "../types";
|
|
3
|
+
import * as grpc from "@grpc/grpc-js";
|
|
4
|
+
export declare function handleOnTestFailEvent(test: TestCase, result: TestResult, runId: string, grpcClient: grpc.Client, options: StanterpriseReporterOptions): void;
|
|
@@ -0,0 +1,23 @@
|
|
|
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, grpcClient, options) {
|
|
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
|
+
});
|
|
21
|
+
// Fire-and-forget to avoid slowing tests
|
|
22
|
+
(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
|
+
}
|
package/dist/reporter.d.ts
CHANGED
|
@@ -2,20 +2,12 @@ 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;
|
|
11
7
|
private runStartTime;
|
|
12
|
-
private rootSuite;
|
|
13
|
-
private reportedSuites;
|
|
14
8
|
constructor(options?: StanterpriseReporterOptions);
|
|
15
9
|
onBegin(config: FullConfig, suite: Suite): void;
|
|
16
10
|
onExit(): Promise<void>;
|
|
17
|
-
getRunId(): string;
|
|
18
|
-
getRunStartTime(): Date;
|
|
19
11
|
onEnd(result: FullResult): Promise<{
|
|
20
12
|
status?: FullResult["status"];
|
|
21
13
|
} | undefined | void> | void;
|
|
@@ -27,10 +19,4 @@ export default class StanterpriseReporter implements Reporter {
|
|
|
27
19
|
onError(error: TestError): void;
|
|
28
20
|
onStdErr(chunk: string | Buffer, test: void | TestCase, result: void | TestResult): void;
|
|
29
21
|
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
22
|
}
|
package/dist/reporter.js
CHANGED
|
@@ -1,104 +1,54 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
-
var
|
|
3
|
-
|
|
4
|
-
|
|
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
|
-
})();
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
35
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
|
-
const protobuf_1 = require("@stanterprise/protobuf");
|
|
37
|
-
const grpc = __importStar(require("@grpc/grpc-js"));
|
|
38
6
|
const crypto_1 = require("crypto");
|
|
39
|
-
const
|
|
40
|
-
|
|
41
|
-
const
|
|
42
|
-
const TestCaseEntities = protobuf_1.testCase.v1.entities;
|
|
43
|
-
const TestSuiteEntities = protobuf_1.testSuite.v1.entities;
|
|
44
|
-
const TestStatus = protobuf_1.common.v1.common.TestStatus;
|
|
7
|
+
const optionsMapper_1 = __importDefault(require("./utils/optionsMapper"));
|
|
8
|
+
const grpcClient_1 = __importDefault(require("./client/grpcClient"));
|
|
9
|
+
const handlers_1 = require("./handlers");
|
|
45
10
|
class StanterpriseReporter {
|
|
46
11
|
constructor(options = {}) {
|
|
47
12
|
// Generic gRPC client (we call unary methods by path directly).
|
|
48
13
|
this.grpcClient = null;
|
|
49
|
-
this.
|
|
14
|
+
this.options = {};
|
|
50
15
|
// Generate a unique run ID for this test run
|
|
51
16
|
this.runId = "";
|
|
52
17
|
this.runStartTime = new Date();
|
|
53
|
-
this.
|
|
54
|
-
|
|
55
|
-
this.reportedSuites = new Map();
|
|
56
|
-
this.grpcAddress =
|
|
57
|
-
options.grpcAddress ||
|
|
58
|
-
process.env.STANTERPRISE_GRPC_ADDRESS ||
|
|
59
|
-
"localhost:50051";
|
|
60
|
-
this.grpcEnabled =
|
|
61
|
-
options.grpcEnabled ??
|
|
62
|
-
(process.env.STANTERPRISE_GRPC_ENABLED || "true").toLowerCase() !==
|
|
63
|
-
"false";
|
|
64
|
-
this.grpcTimeout = options.grpcTimeout || 1000;
|
|
65
|
-
this.verbose = options.verbose || false;
|
|
66
|
-
if (this.verbose) {
|
|
18
|
+
this.options = (0, optionsMapper_1.default)(options);
|
|
19
|
+
if (this.options.verbose) {
|
|
67
20
|
console.log("Stanterprise Reporter: Initialized with options:", {
|
|
68
|
-
grpcAddress: this.grpcAddress,
|
|
69
|
-
grpcEnabled: this.grpcEnabled,
|
|
70
|
-
grpcTimeout: this.grpcTimeout,
|
|
71
|
-
verbose: this.verbose,
|
|
21
|
+
grpcAddress: this.options.grpcAddress,
|
|
22
|
+
grpcEnabled: this.options.grpcEnabled,
|
|
23
|
+
grpcTimeout: this.options.grpcTimeout,
|
|
24
|
+
verbose: this.options.verbose,
|
|
72
25
|
});
|
|
73
26
|
}
|
|
27
|
+
// Generate a UUID for runId
|
|
28
|
+
this.runId = process.env.STANTERPRISE_RUN_ID || (0, crypto_1.randomUUID)();
|
|
74
29
|
}
|
|
75
30
|
onBegin(config, suite) {
|
|
76
|
-
// Generate a UUID for runId
|
|
77
|
-
this.runId = (0, crypto_1.randomUUID)();
|
|
78
|
-
this.rootSuite = suite;
|
|
79
31
|
// Lazily create the client if enabled.
|
|
80
|
-
if (this.grpcEnabled) {
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
this.logGrpcErrorOnce("Failed to create gRPC client", e);
|
|
32
|
+
if (this.options.grpcEnabled) {
|
|
33
|
+
this.grpcClient = (0, grpcClient_1.default)(this.options);
|
|
34
|
+
if (!this.grpcClient) {
|
|
35
|
+
console.warn(`Stanterprise Reporter: Failed to create gRPC client. Disabling gRPC reporting.`);
|
|
36
|
+
this.options.grpcEnabled = false;
|
|
86
37
|
}
|
|
87
38
|
}
|
|
88
39
|
else {
|
|
89
|
-
if (this.verbose) {
|
|
40
|
+
if (this.options.verbose) {
|
|
90
41
|
console.log("Stanterprise Reporter: gRPC disabled via STANTERPRISE_GRPC_ENABLED=false");
|
|
91
42
|
}
|
|
92
43
|
}
|
|
93
|
-
if (this.verbose) {
|
|
44
|
+
if (this.options.verbose) {
|
|
94
45
|
console.log(`Stanterprise Reporter: Test run started with ID: ${this.runId}`);
|
|
95
46
|
console.log(`Number of tests: ${suite.allTests().length}`);
|
|
96
47
|
console.log(`Run started at: ${this.runStartTime.toISOString()}`);
|
|
97
48
|
}
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
this.reportSuiteBegin(suite, rootSuiteId);
|
|
49
|
+
if (this.options.grpcEnabled) {
|
|
50
|
+
(0, handlers_1.handleOnBeginEvent)(config, suite, this.runId, this.grpcClient, this.options);
|
|
51
|
+
}
|
|
102
52
|
}
|
|
103
53
|
async onExit() {
|
|
104
54
|
console.log(`Stanterprise Reporter: Test run completed - Run ID: ${this.runId}`);
|
|
@@ -110,207 +60,63 @@ class StanterpriseReporter {
|
|
|
110
60
|
console.error("Stanterprise Reporter: Error during gRPC client cleanup in onExit:", e);
|
|
111
61
|
}
|
|
112
62
|
}
|
|
113
|
-
// Getter to access the current run ID
|
|
114
|
-
getRunId() {
|
|
115
|
-
return this.runId;
|
|
116
|
-
}
|
|
117
|
-
// Getter to access the run start time
|
|
118
|
-
getRunStartTime() {
|
|
119
|
-
return this.runStartTime;
|
|
120
|
-
}
|
|
121
63
|
onEnd(result) {
|
|
122
64
|
const runDuration = Date.now() - this.runStartTime.getTime();
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
if (this.rootSuite) {
|
|
130
|
-
this.reportSuiteEnd(this.rootSuite, result);
|
|
65
|
+
if (this.options.verbose) {
|
|
66
|
+
console.log(`Stanterprise Reporter: Test run ended - Run ID: ${this.runId}`);
|
|
67
|
+
console.log(`Final result: ${result.status}`);
|
|
68
|
+
console.log(`Run duration: ${runDuration}ms (Playwright duration: ${result.duration}ms)`);
|
|
69
|
+
console.log(`Run start time: ${this.runStartTime.toISOString()}`);
|
|
70
|
+
console.log(`Playwright start time: ${result.startTime.toISOString()}`);
|
|
131
71
|
}
|
|
132
72
|
return Promise.resolve();
|
|
133
73
|
}
|
|
134
74
|
onTestBegin(test, result) {
|
|
135
75
|
// Create unique test execution ID combining run ID and test ID
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
const metadata = new Map();
|
|
145
|
-
test.annotations.forEach((annotation, index) => {
|
|
146
|
-
metadata.set(`annotation_${index}_type`, annotation.type);
|
|
147
|
-
if (annotation.description) {
|
|
148
|
-
metadata.set(`annotation_${index}_description`, annotation.description);
|
|
149
|
-
}
|
|
150
|
-
});
|
|
151
|
-
// Build and send the TestBegin event via generic unary call.
|
|
152
|
-
const request = new EventsNS.TestBeginEventRequest({
|
|
153
|
-
test_case: new TestCaseEntities.TestCaseRun({
|
|
154
|
-
id: test.id,
|
|
155
|
-
name: test.title,
|
|
156
|
-
run_id: this.runId,
|
|
157
|
-
test_suite_run_id: testSuiteRunId,
|
|
158
|
-
start_time: (0, utils_1.createTimestamp)(result.startTime),
|
|
159
|
-
metadata: metadata,
|
|
160
|
-
tags: test.tags,
|
|
161
|
-
}),
|
|
162
|
-
});
|
|
163
|
-
// Fire-and-forget to avoid slowing tests; log errors once.
|
|
164
|
-
this.reportUnary("/testsystem.v1.observer.TestEventCollector/ReportTestBegin", request, this.grpcTimeout).catch((e) => this.logGrpcErrorOnce("Failed to report test begin", e));
|
|
76
|
+
if (this.options.verbose) {
|
|
77
|
+
console.log(`Stanterprise Reporter: Test started - ${test.title}`);
|
|
78
|
+
console.log(` Run ID: ${this.runId}`);
|
|
79
|
+
console.log(` Test ID: ${test.id}`);
|
|
80
|
+
}
|
|
81
|
+
if (this.options.grpcEnabled) {
|
|
82
|
+
(0, handlers_1.handleOnTestBeginEvent)(test, result, this.runId, this.grpcClient, this.options);
|
|
83
|
+
}
|
|
165
84
|
}
|
|
166
85
|
async onStepBegin(test, result, step) {
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
step.annotations.forEach((annotation, index) => {
|
|
175
|
-
metadata.set(`annotation_${index}_type`, annotation.type);
|
|
176
|
-
if (annotation.description) {
|
|
177
|
-
metadata.set(`annotation_${index}_description`, annotation.description);
|
|
178
|
-
}
|
|
179
|
-
});
|
|
180
|
-
// Get parent step ID if this step has a parent
|
|
181
|
-
const parentStepId = step.parent
|
|
182
|
-
? `${uniqueTestExecutionId}-${step.parent.title}-${step.parent.startTime.getTime()}`
|
|
183
|
-
: "";
|
|
184
|
-
// Build and send the StepBegin event
|
|
185
|
-
const request = new EventsNS.StepBeginEventRequest({
|
|
186
|
-
step: new TestCaseEntities.StepRun({
|
|
187
|
-
id: uniqueStepId,
|
|
188
|
-
run_id: this.runId,
|
|
189
|
-
test_case_run_id: uniqueTestExecutionId,
|
|
190
|
-
title: step.title,
|
|
191
|
-
type: step.category,
|
|
192
|
-
start_time: (0, utils_1.createTimestamp)(step.startTime),
|
|
193
|
-
location: step.location
|
|
194
|
-
? `${step.location.file}:${step.location.line}:${step.location.column}`
|
|
195
|
-
: "",
|
|
196
|
-
metadata: metadata,
|
|
197
|
-
parent_step_id: parentStepId,
|
|
198
|
-
worker_index: result.workerIndex.toString(),
|
|
199
|
-
}),
|
|
200
|
-
});
|
|
201
|
-
// Fire-and-forget to avoid slowing tests
|
|
202
|
-
this.reportUnary("/testsystem.v1.observer.TestEventCollector/ReportStepBegin", request, this.grpcTimeout).catch((e) => this.logGrpcErrorOnce("Failed to report step begin", e));
|
|
86
|
+
if (this.options.verbose) {
|
|
87
|
+
console.log(`Stanterprise Reporter: Step started - ${step.title}`);
|
|
88
|
+
console.log(` Category: ${step.category}`);
|
|
89
|
+
}
|
|
90
|
+
if (this.options.grpcEnabled) {
|
|
91
|
+
(0, handlers_1.handleOnStepBeginEvent)(test, result, step, this.runId, this.grpcClient, this.options);
|
|
92
|
+
}
|
|
203
93
|
}
|
|
204
94
|
onStepEnd(test, result, step) {
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
const metadata = new Map();
|
|
213
|
-
metadata.set("category", step.category);
|
|
214
|
-
step.annotations.forEach((annotation, index) => {
|
|
215
|
-
metadata.set(`annotation_${index}_type`, annotation.type);
|
|
216
|
-
if (annotation.description) {
|
|
217
|
-
metadata.set(`annotation_${index}_description`, annotation.description);
|
|
218
|
-
}
|
|
219
|
-
});
|
|
220
|
-
// Get parent step ID if this step has a parent
|
|
221
|
-
const parentStepId = step.parent
|
|
222
|
-
? `${uniqueTestExecutionId}-${step.parent.title}-${step.parent.startTime.getTime()}`
|
|
223
|
-
: "";
|
|
224
|
-
// Build and send the StepEnd event
|
|
225
|
-
const request = new EventsNS.StepEndEventRequest({
|
|
226
|
-
step: new TestCaseEntities.StepRun({
|
|
227
|
-
id: uniqueStepId,
|
|
228
|
-
run_id: this.runId,
|
|
229
|
-
test_case_run_id: uniqueTestExecutionId,
|
|
230
|
-
title: step.title,
|
|
231
|
-
type: step.category,
|
|
232
|
-
start_time: (0, utils_1.createTimestamp)(step.startTime),
|
|
233
|
-
duration: (0, utils_1.createDuration)(step.duration),
|
|
234
|
-
status: stepStatus,
|
|
235
|
-
error: step.error ? step.error.message || "" : "",
|
|
236
|
-
errors: step.error ? [step.error.message || ""] : [],
|
|
237
|
-
location: step.location
|
|
238
|
-
? `${step.location.file}:${step.location.line}:${step.location.column}`
|
|
239
|
-
: "",
|
|
240
|
-
metadata: metadata,
|
|
241
|
-
parent_step_id: parentStepId,
|
|
242
|
-
worker_index: result.workerIndex.toString(),
|
|
243
|
-
}),
|
|
244
|
-
});
|
|
245
|
-
// Fire-and-forget to avoid slowing tests
|
|
246
|
-
this.reportUnary("/testsystem.v1.observer.TestEventCollector/ReportStepEnd", request, this.grpcTimeout).catch((e) => this.logGrpcErrorOnce("Failed to report step end", e));
|
|
95
|
+
if (this.options.verbose) {
|
|
96
|
+
console.log(`Stanterprise Reporter: Step ended - ${step.title}`);
|
|
97
|
+
console.log(` Duration: ${step.duration}ms`);
|
|
98
|
+
}
|
|
99
|
+
if (this.options.grpcEnabled) {
|
|
100
|
+
(0, handlers_1.handleOnStepEndEvent)(test, result, step, this.runId, this.grpcClient, this.options);
|
|
101
|
+
}
|
|
247
102
|
}
|
|
248
103
|
onTestEnd(test, result) {
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
// Extract error information if the test failed
|
|
258
|
-
const { errorMessage, stackTrace, errors } = (0, utils_1.extractErrorInfo)(result);
|
|
259
|
-
// Get test suite run ID from parent suite if available, ensuring suite is reported
|
|
260
|
-
const testSuiteRunId = test.parent ? this.getSuiteId(test.parent) : "";
|
|
261
|
-
// Build metadata from test annotations and result metadata
|
|
262
|
-
const metadata = new Map();
|
|
263
|
-
test.annotations.forEach((annotation, index) => {
|
|
264
|
-
metadata.set(`annotation_${index}_type`, annotation.type);
|
|
265
|
-
if (annotation.description) {
|
|
266
|
-
metadata.set(`annotation_${index}_description`, annotation.description);
|
|
267
|
-
}
|
|
268
|
-
});
|
|
269
|
-
result.annotations.forEach((annotation, index) => {
|
|
270
|
-
metadata.set(`result_annotation_${index}_type`, annotation.type);
|
|
271
|
-
if (annotation.description) {
|
|
272
|
-
metadata.set(`result_annotation_${index}_description`, annotation.description);
|
|
273
|
-
}
|
|
274
|
-
});
|
|
275
|
-
// Build and send the TestEnd event
|
|
276
|
-
const request = new EventsNS.TestEndEventRequest({
|
|
277
|
-
test_case: new TestCaseEntities.TestCaseRun({
|
|
278
|
-
id: test.id,
|
|
279
|
-
name: test.title,
|
|
280
|
-
run_id: this.runId,
|
|
281
|
-
test_suite_run_id: testSuiteRunId,
|
|
282
|
-
status: testStatus,
|
|
283
|
-
start_time: (0, utils_1.createTimestamp)(result.startTime),
|
|
284
|
-
attachments: attachments,
|
|
285
|
-
error_message: errorMessage,
|
|
286
|
-
stack_trace: stackTrace,
|
|
287
|
-
errors: errors,
|
|
288
|
-
metadata: metadata,
|
|
289
|
-
tags: test.tags,
|
|
290
|
-
}),
|
|
291
|
-
});
|
|
292
|
-
// Fire-and-forget to avoid slowing tests
|
|
293
|
-
this.reportUnary("/testsystem.v1.observer.TestEventCollector/ReportTestEnd", request, this.grpcTimeout).catch((e) => this.logGrpcErrorOnce("Failed to report test end", e));
|
|
104
|
+
if (this.options.verbose) {
|
|
105
|
+
console.log(`Stanterprise Reporter: Test ended - ${test.title}`);
|
|
106
|
+
console.log(` Status: ${result.status}`);
|
|
107
|
+
console.log(` Duration: ${result.duration}ms`);
|
|
108
|
+
}
|
|
109
|
+
if (this.options.grpcEnabled) {
|
|
110
|
+
(0, handlers_1.handleOnTestEndEvent)(test, result, this.runId, this.grpcClient, this.options);
|
|
111
|
+
}
|
|
294
112
|
}
|
|
295
113
|
onTestFail(test, result) {
|
|
296
|
-
|
|
297
|
-
if (this.verbose) {
|
|
114
|
+
if (this.options.verbose) {
|
|
298
115
|
console.log(`Stanterprise Reporter: Test failed - ${test.title}`);
|
|
299
116
|
}
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
const attachments = (0, utils_1.processAttachments)(result);
|
|
304
|
-
// Build and send the TestFailure event
|
|
305
|
-
const request = new EventsNS.TestFailureEventRequest({
|
|
306
|
-
test_id: uniqueTestExecutionId,
|
|
307
|
-
failure_message: failureMessage,
|
|
308
|
-
stack_trace: stackTrace,
|
|
309
|
-
timestamp: (0, utils_1.createTimestampFromMs)(Date.now()),
|
|
310
|
-
attachments: attachments,
|
|
311
|
-
});
|
|
312
|
-
// Fire-and-forget to avoid slowing tests
|
|
313
|
-
this.reportUnary("/testsystem.v1.observer.TestEventCollector/ReportTestFailure", request, this.grpcTimeout).catch((e) => this.logGrpcErrorOnce("Failed to report test failure", e));
|
|
117
|
+
if (this.options.grpcEnabled) {
|
|
118
|
+
(0, handlers_1.handleOnTestFailEvent)(test, result, this.runId, this.grpcClient, this.options);
|
|
119
|
+
}
|
|
314
120
|
}
|
|
315
121
|
onError(error) {
|
|
316
122
|
console.error("Stanterprise Reporter: An error occurred during the test run");
|
|
@@ -318,6 +124,9 @@ class StanterpriseReporter {
|
|
|
318
124
|
if (error.stack) {
|
|
319
125
|
console.error(`Stack trace: ${error.stack}`);
|
|
320
126
|
}
|
|
127
|
+
if (this.options.grpcEnabled) {
|
|
128
|
+
(0, handlers_1.handleOnErrorEvent)(error, this.runId, this.grpcClient, this.options);
|
|
129
|
+
}
|
|
321
130
|
}
|
|
322
131
|
onStdErr(chunk, test, result) {
|
|
323
132
|
console.error(`Stanterprise Reporter: Standard error output - ${chunk.toString()}`);
|
|
@@ -325,132 +134,5 @@ class StanterpriseReporter {
|
|
|
325
134
|
onStdOut(chunk, test, result) {
|
|
326
135
|
// console.log(`Stanterprise Reporter: Standard output - ${chunk.toString()}`);
|
|
327
136
|
}
|
|
328
|
-
// Helper: Get suite ID for a suite, ensuring it's been reported
|
|
329
|
-
getSuiteId(suite) {
|
|
330
|
-
// Check if we've already reported this suite
|
|
331
|
-
if (this.reportedSuites.has(suite)) {
|
|
332
|
-
return this.reportedSuites.get(suite);
|
|
333
|
-
}
|
|
334
|
-
// Generate a unique suite ID based on suite hierarchy
|
|
335
|
-
const titlePath = suite.titlePath().join("/") || "root";
|
|
336
|
-
const suiteId = `${this.runId}-suite-${titlePath}`;
|
|
337
|
-
// Track that we've reported this suite
|
|
338
|
-
this.reportedSuites.set(suite, suiteId);
|
|
339
|
-
return suiteId;
|
|
340
|
-
}
|
|
341
|
-
// Helper: Report suite begin event
|
|
342
|
-
reportSuiteBegin(suite, suiteId) {
|
|
343
|
-
const id = suiteId || `${this.runId}-suite-${suite.title || "root"}`;
|
|
344
|
-
console.log(`Stanterprise Reporter: Suite started - ${suite.title || "root"}`);
|
|
345
|
-
console.log(` Suite type: ${suite.type}`);
|
|
346
|
-
// Build metadata from suite location
|
|
347
|
-
const metadata = new Map();
|
|
348
|
-
metadata.set("suite_type", suite.type);
|
|
349
|
-
if (suite.location) {
|
|
350
|
-
metadata.set("location_file", suite.location.file);
|
|
351
|
-
metadata.set("location_line", suite.location.line.toString());
|
|
352
|
-
metadata.set("location_column", suite.location.column.toString());
|
|
353
|
-
}
|
|
354
|
-
// Build and send the SuiteBegin event
|
|
355
|
-
const request = new EventsNS.SuiteBeginEventRequest({
|
|
356
|
-
suite: new TestSuiteEntities.TestSuiteRun({
|
|
357
|
-
id: id,
|
|
358
|
-
name: suite.title || "root",
|
|
359
|
-
start_time: (0, utils_1.createTimestamp)(this.runStartTime),
|
|
360
|
-
metadata: metadata,
|
|
361
|
-
}),
|
|
362
|
-
});
|
|
363
|
-
// Fire-and-forget to avoid slowing tests
|
|
364
|
-
this.reportUnary("/testsystem.v1.observer.TestEventCollector/ReportSuiteBegin", request, this.grpcTimeout).catch((e) => this.logGrpcErrorOnce("Failed to report suite begin", e));
|
|
365
|
-
}
|
|
366
|
-
// Helper: Report suite end event
|
|
367
|
-
reportSuiteEnd(suite, result) {
|
|
368
|
-
const suiteId = `${this.runId}-suite-${suite.title || "root"}`;
|
|
369
|
-
const endTime = new Date();
|
|
370
|
-
const duration = endTime.getTime() - this.runStartTime.getTime();
|
|
371
|
-
console.log(`Stanterprise Reporter: Suite ended - ${suite.title || "root"}`);
|
|
372
|
-
console.log(` Duration: ${duration}ms`);
|
|
373
|
-
// Map result status to protobuf TestStatus
|
|
374
|
-
const suiteStatus = this.mapSuiteStatus(result.status);
|
|
375
|
-
// Build metadata from suite location
|
|
376
|
-
const metadata = new Map();
|
|
377
|
-
metadata.set("suite_type", suite.type);
|
|
378
|
-
metadata.set("final_result", result.status);
|
|
379
|
-
if (suite.location) {
|
|
380
|
-
metadata.set("location_file", suite.location.file);
|
|
381
|
-
metadata.set("location_line", suite.location.line.toString());
|
|
382
|
-
metadata.set("location_column", suite.location.column.toString());
|
|
383
|
-
}
|
|
384
|
-
// Build and send the SuiteEnd event
|
|
385
|
-
const request = new EventsNS.SuiteEndEventRequest({
|
|
386
|
-
suite: new TestSuiteEntities.TestSuiteRun({
|
|
387
|
-
id: suiteId,
|
|
388
|
-
name: suite.title || "root",
|
|
389
|
-
start_time: (0, utils_1.createTimestamp)(this.runStartTime),
|
|
390
|
-
end_time: (0, utils_1.createTimestamp)(endTime),
|
|
391
|
-
duration: (0, utils_1.createDuration)(duration),
|
|
392
|
-
status: suiteStatus,
|
|
393
|
-
metadata: metadata,
|
|
394
|
-
}),
|
|
395
|
-
});
|
|
396
|
-
// Fire-and-forget to avoid slowing tests
|
|
397
|
-
this.reportUnary("/testsystem.v1.observer.TestEventCollector/ReportSuiteEnd", request, this.grpcTimeout).catch((e) => this.logGrpcErrorOnce("Failed to report suite end", e));
|
|
398
|
-
}
|
|
399
|
-
// Helper: Map FullResult status to protobuf TestStatus
|
|
400
|
-
mapSuiteStatus(status) {
|
|
401
|
-
switch (status) {
|
|
402
|
-
case "passed":
|
|
403
|
-
return TestStatus.PASSED;
|
|
404
|
-
case "failed":
|
|
405
|
-
return TestStatus.FAILED;
|
|
406
|
-
case "timedout":
|
|
407
|
-
return TestStatus.FAILED;
|
|
408
|
-
case "interrupted":
|
|
409
|
-
return TestStatus.BROKEN;
|
|
410
|
-
default:
|
|
411
|
-
return TestStatus.UNKNOWN;
|
|
412
|
-
}
|
|
413
|
-
}
|
|
414
|
-
// Helper: log first gRPC error once and disable further attempts
|
|
415
|
-
logGrpcErrorOnce(prefix, err) {
|
|
416
|
-
if (this.grpcFirstErrorLogged)
|
|
417
|
-
return;
|
|
418
|
-
this.grpcFirstErrorLogged = true;
|
|
419
|
-
const details = err instanceof Error ? `${err.message}` : String(err);
|
|
420
|
-
console.warn(`${prefix}. gRPC disabled for the remainder of the run. Address=${this.grpcAddress}. Details: ${details}`);
|
|
421
|
-
this.grpcEnabled = false;
|
|
422
|
-
}
|
|
423
|
-
// Helper: generic unary call using raw method path
|
|
424
|
-
async reportUnary(path, message, deadlineMs = 1000) {
|
|
425
|
-
if (!this.grpcEnabled || !this.grpcClient) {
|
|
426
|
-
return Buffer.alloc(0);
|
|
427
|
-
}
|
|
428
|
-
const reqSerialize = (arg) => {
|
|
429
|
-
const m = arg;
|
|
430
|
-
const bytes = m?.serializeBinary
|
|
431
|
-
? m.serializeBinary()
|
|
432
|
-
: m?.serialize
|
|
433
|
-
? m.serialize()
|
|
434
|
-
: new Uint8Array(0);
|
|
435
|
-
return Buffer.from(bytes);
|
|
436
|
-
};
|
|
437
|
-
const resDeserialize = (bytes) => bytes;
|
|
438
|
-
const metadata = new grpc.Metadata();
|
|
439
|
-
const callOptions = {
|
|
440
|
-
deadline: new Date(Date.now() + deadlineMs),
|
|
441
|
-
};
|
|
442
|
-
return new Promise((resolve, reject) => {
|
|
443
|
-
try {
|
|
444
|
-
this.grpcClient.makeUnaryRequest(path, reqSerialize, resDeserialize, message, metadata, callOptions, (err, response) => {
|
|
445
|
-
if (err)
|
|
446
|
-
return reject(err);
|
|
447
|
-
resolve(response);
|
|
448
|
-
});
|
|
449
|
-
}
|
|
450
|
-
catch (e) {
|
|
451
|
-
reject(e);
|
|
452
|
-
}
|
|
453
|
-
});
|
|
454
|
-
}
|
|
455
137
|
}
|
|
456
138
|
exports.default = StanterpriseReporter;
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { Suite, TestCase, TestStep } from "@playwright/test/reporter";
|
|
2
|
+
/**
|
|
3
|
+
* Generates a unique suite ID based on the suite's title path.
|
|
4
|
+
* @param suite The Playwright Suite object.
|
|
5
|
+
* @returns A unique string ID for the suite.
|
|
6
|
+
*/
|
|
7
|
+
export declare function generateSuiteId(suite: Suite): string;
|
|
8
|
+
/**
|
|
9
|
+
* Generates a unique test ID based on the test case. Currently uses the test's existing ID.
|
|
10
|
+
* @param test The Playwright TestCase object.
|
|
11
|
+
* @returns A unique string ID for the test case.
|
|
12
|
+
*/
|
|
13
|
+
export declare function generateTestId(test: TestCase): string;
|
|
14
|
+
/**
|
|
15
|
+
* Generates a unique step ID based on the step and its parent test case.
|
|
16
|
+
* @param step The Playwright TestStep object.
|
|
17
|
+
* @param test The Playwright TestCase object that the step belongs to.
|
|
18
|
+
* @returns A unique string ID for the step.
|
|
19
|
+
*/
|
|
20
|
+
export declare function generateStepId(step: TestStep, test: TestCase): string;
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.generateSuiteId = generateSuiteId;
|
|
7
|
+
exports.generateTestId = generateTestId;
|
|
8
|
+
exports.generateStepId = generateStepId;
|
|
9
|
+
const crypto_1 = __importDefault(require("crypto"));
|
|
10
|
+
/**
|
|
11
|
+
* Generates a unique suite ID based on the suite's title path.
|
|
12
|
+
* @param suite The Playwright Suite object.
|
|
13
|
+
* @returns A unique string ID for the suite.
|
|
14
|
+
*/
|
|
15
|
+
function generateSuiteId(suite) {
|
|
16
|
+
return crypto_1.default
|
|
17
|
+
.createHash("sha256")
|
|
18
|
+
.update(suite.titlePath().join(":"))
|
|
19
|
+
.digest("hex");
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* Generates a unique test ID based on the test case. Currently uses the test's existing ID.
|
|
23
|
+
* @param test The Playwright TestCase object.
|
|
24
|
+
* @returns A unique string ID for the test case.
|
|
25
|
+
*/
|
|
26
|
+
function generateTestId(test) {
|
|
27
|
+
return test.id;
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* Generates a unique step ID based on the step and its parent test case.
|
|
31
|
+
* @param step The Playwright TestStep object.
|
|
32
|
+
* @param test The Playwright TestCase object that the step belongs to.
|
|
33
|
+
* @returns A unique string ID for the step.
|
|
34
|
+
*/
|
|
35
|
+
function generateStepId(step, test) {
|
|
36
|
+
return crypto_1.default
|
|
37
|
+
.createHash("sha256")
|
|
38
|
+
.update(`${test.id}:${step.titlePath().join(":")}`)
|
|
39
|
+
.digest("hex");
|
|
40
|
+
}
|
package/dist/utils/index.d.ts
CHANGED
package/dist/utils/index.js
CHANGED
|
@@ -20,3 +20,4 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
20
20
|
__exportStar(require("./statusMapper"), exports);
|
|
21
21
|
__exportStar(require("./attachmentProcessor"), exports);
|
|
22
22
|
__exportStar(require("./timeHelpers"), exports);
|
|
23
|
+
__exportStar(require("./idGenerators"), exports);
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import { StanterpriseReporterOptions } from "../types";
|
|
2
|
+
/**
|
|
3
|
+
* Define and map reporter options from provided options and environment variables. Apply defaults where necessary.
|
|
4
|
+
* @param providedOptions Options provided during reporter initialization
|
|
5
|
+
* @returns Mapped and completed StanterpriseReporterOptions
|
|
6
|
+
*/
|
|
7
|
+
export default function defineOptions(providedOptions: StanterpriseReporterOptions): StanterpriseReporterOptions;
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.default = defineOptions;
|
|
4
|
+
/**
|
|
5
|
+
* Define and map reporter options from provided options and environment variables. Apply defaults where necessary.
|
|
6
|
+
* @param providedOptions Options provided during reporter initialization
|
|
7
|
+
* @returns Mapped and completed StanterpriseReporterOptions
|
|
8
|
+
*/
|
|
9
|
+
function defineOptions(providedOptions) {
|
|
10
|
+
const result = {};
|
|
11
|
+
result.grpcAddress =
|
|
12
|
+
providedOptions.grpcAddress ||
|
|
13
|
+
process.env.STANTERPRISE_GRPC_ADDRESS ||
|
|
14
|
+
"localhost:50051";
|
|
15
|
+
result.grpcEnabled =
|
|
16
|
+
providedOptions.grpcEnabled ??
|
|
17
|
+
(process.env.STANTERPRISE_GRPC_ENABLED || "true").toLowerCase() !== "false";
|
|
18
|
+
result.grpcTimeout = providedOptions.grpcTimeout || 1000;
|
|
19
|
+
result.verbose = providedOptions.verbose || false;
|
|
20
|
+
return result;
|
|
21
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@stanterprise/playwright-reporter",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.2",
|
|
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": {
|
|
@@ -54,6 +54,6 @@
|
|
|
54
54
|
"@grpc/grpc-js": "^1.14.1",
|
|
55
55
|
"@protobuf-ts/grpc-transport": "^2.11.1",
|
|
56
56
|
"@protobuf-ts/grpcweb-transport": "^2.11.1",
|
|
57
|
-
"@stanterprise/protobuf": "^0.0.
|
|
57
|
+
"@stanterprise/protobuf": "^0.0.15"
|
|
58
58
|
}
|
|
59
59
|
}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export {};
|