@stanterprise/playwright-reporter 1.0.0 → 1.0.1
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 +261 -11
- package/dist/client/grpcClient.d.ts +16 -0
- package/dist/client/grpcClient.js +172 -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 +6 -7
|
@@ -3,25 +3,54 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
3
3
|
exports.processAttachments = processAttachments;
|
|
4
4
|
exports.extractErrorInfo = extractErrorInfo;
|
|
5
5
|
const protobuf_1 = require("@stanterprise/protobuf");
|
|
6
|
+
const Attachment = protobuf_1.common.v1.common.Attachment;
|
|
7
|
+
/**
|
|
8
|
+
* Constant for converting bytes to megabytes
|
|
9
|
+
*/
|
|
10
|
+
const BYTES_PER_MB = 1048576;
|
|
6
11
|
/**
|
|
7
12
|
* Process Playwright test attachments into protobuf Attachment objects
|
|
13
|
+
* @param result Test result or test step containing attachments
|
|
14
|
+
* @param maxAttachmentSize Maximum size for attachment content (default 1MB, reduced from 10MB)
|
|
8
15
|
*/
|
|
9
|
-
function processAttachments(result) {
|
|
16
|
+
function processAttachments(result, maxAttachmentSize = 1048576) {
|
|
10
17
|
const attachments = [];
|
|
11
18
|
if (!result.attachments || result.attachments.length === 0) {
|
|
12
19
|
return attachments;
|
|
13
20
|
}
|
|
21
|
+
let totalAttachmentSize = 0;
|
|
22
|
+
const MAX_TOTAL_ATTACHMENT_SIZE = 2097152; // 2MB total limit across all attachments
|
|
14
23
|
for (const attachment of result.attachments) {
|
|
15
|
-
const att = new
|
|
24
|
+
const att = new Attachment({
|
|
16
25
|
name: attachment.name,
|
|
17
26
|
mime_type: attachment.contentType,
|
|
18
27
|
});
|
|
19
|
-
//
|
|
28
|
+
// Always prefer path if available to avoid large payloads
|
|
20
29
|
if (attachment.path) {
|
|
21
30
|
att.uri = attachment.path;
|
|
31
|
+
attachments.push(att);
|
|
32
|
+
continue;
|
|
22
33
|
}
|
|
23
|
-
|
|
34
|
+
if (attachment.body) {
|
|
35
|
+
const bodySize = attachment.body.length;
|
|
36
|
+
// Check individual attachment size
|
|
37
|
+
if (bodySize > maxAttachmentSize) {
|
|
38
|
+
console.warn(`Attachment "${attachment.name}" (${(bodySize / BYTES_PER_MB).toFixed(2)}MB) exceeds max size ` +
|
|
39
|
+
`(${(maxAttachmentSize / BYTES_PER_MB).toFixed(2)}MB) and has no path. Skipping content.`);
|
|
40
|
+
// Still add the attachment metadata without content
|
|
41
|
+
attachments.push(att);
|
|
42
|
+
continue;
|
|
43
|
+
}
|
|
44
|
+
// Check total accumulated size
|
|
45
|
+
if (totalAttachmentSize + bodySize > MAX_TOTAL_ATTACHMENT_SIZE) {
|
|
46
|
+
console.warn(`Attachment "${attachment.name}" (${(bodySize / BYTES_PER_MB).toFixed(2)}MB) would exceed total attachment size limit ` +
|
|
47
|
+
`(${(MAX_TOTAL_ATTACHMENT_SIZE / BYTES_PER_MB).toFixed(2)}MB). Skipping content.`);
|
|
48
|
+
attachments.push(att);
|
|
49
|
+
continue;
|
|
50
|
+
}
|
|
51
|
+
// Include content if within limits
|
|
24
52
|
att.content = attachment.body;
|
|
53
|
+
totalAttachmentSize += bodySize;
|
|
25
54
|
}
|
|
26
55
|
attachments.push(att);
|
|
27
56
|
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { StanterpriseReporterOptions } from "../types";
|
|
2
|
+
/**
|
|
3
|
+
* Creates or truncates the debug JSONL file so each test run starts with a clean file.
|
|
4
|
+
* No-op when debug mode is disabled.
|
|
5
|
+
*/
|
|
6
|
+
export declare function initDebugFile(options: StanterpriseReporterOptions): void;
|
|
7
|
+
/**
|
|
8
|
+
* Appends a single JSONL entry for an outgoing gRPC message.
|
|
9
|
+
* Each entry is a JSON object on its own line containing the timestamp,
|
|
10
|
+
* gRPC method path, and the serialized message payload.
|
|
11
|
+
* No-op when debug mode is disabled.
|
|
12
|
+
*/
|
|
13
|
+
export declare function writeDebugEntry(options: StanterpriseReporterOptions, path: string, message: unknown): Promise<void>;
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
+
var ownKeys = function(o) {
|
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
+
var ar = [];
|
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
+
return ar;
|
|
24
|
+
};
|
|
25
|
+
return ownKeys(o);
|
|
26
|
+
};
|
|
27
|
+
return function (mod) {
|
|
28
|
+
if (mod && mod.__esModule) return mod;
|
|
29
|
+
var result = {};
|
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
+
__setModuleDefault(result, mod);
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
})();
|
|
35
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
|
+
exports.initDebugFile = initDebugFile;
|
|
37
|
+
exports.writeDebugEntry = writeDebugEntry;
|
|
38
|
+
const fs = __importStar(require("fs"));
|
|
39
|
+
const nodePath = __importStar(require("path"));
|
|
40
|
+
/**
|
|
41
|
+
* Ensures the parent directory of `filePath` exists, creating it recursively if needed.
|
|
42
|
+
*/
|
|
43
|
+
function ensureParentDir(filePath) {
|
|
44
|
+
const dir = nodePath.dirname(filePath);
|
|
45
|
+
if (dir && dir !== ".") {
|
|
46
|
+
fs.mkdirSync(dir, { recursive: true });
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
/**
|
|
50
|
+
* Creates or truncates the debug JSONL file so each test run starts with a clean file.
|
|
51
|
+
* No-op when debug mode is disabled.
|
|
52
|
+
*/
|
|
53
|
+
function initDebugFile(options) {
|
|
54
|
+
if (!options.debug)
|
|
55
|
+
return;
|
|
56
|
+
const filePath = options.debugFile || "stanterprise-debug.jsonl";
|
|
57
|
+
try {
|
|
58
|
+
ensureParentDir(filePath);
|
|
59
|
+
fs.writeFileSync(filePath, "");
|
|
60
|
+
}
|
|
61
|
+
catch (e) {
|
|
62
|
+
console.error(`Stanterprise Reporter: Failed to initialize debug file "${filePath}":`, e);
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
/**
|
|
66
|
+
* Appends a single JSONL entry for an outgoing gRPC message.
|
|
67
|
+
* Each entry is a JSON object on its own line containing the timestamp,
|
|
68
|
+
* gRPC method path, and the serialized message payload.
|
|
69
|
+
* No-op when debug mode is disabled.
|
|
70
|
+
*/
|
|
71
|
+
async function writeDebugEntry(options, path, message) {
|
|
72
|
+
if (!options.debug)
|
|
73
|
+
return;
|
|
74
|
+
const filePath = options.debugFile || "stanterprise-debug.jsonl";
|
|
75
|
+
const entry = {
|
|
76
|
+
timestamp: new Date().toISOString(),
|
|
77
|
+
path,
|
|
78
|
+
message,
|
|
79
|
+
};
|
|
80
|
+
try {
|
|
81
|
+
ensureParentDir(filePath);
|
|
82
|
+
await fs.promises.appendFile(filePath, JSON.stringify(entry) + "\n");
|
|
83
|
+
}
|
|
84
|
+
catch (e) {
|
|
85
|
+
console.error(`Stanterprise Reporter: Failed to write debug entry to "${filePath}":`, e);
|
|
86
|
+
}
|
|
87
|
+
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Retrieves environment variables with the STANTERPRISE_META_ prefix
|
|
3
|
+
* and returns them as a Map with the prefix stripped from keys.
|
|
4
|
+
*
|
|
5
|
+
* For example, STANTERPRISE_META_BUILD_ID=123 becomes BUILD_ID=123 in the metadata.
|
|
6
|
+
*
|
|
7
|
+
* @returns Map of metadata key-value pairs with prefix removed
|
|
8
|
+
*/
|
|
9
|
+
export declare function getEnvVariables(): Map<string, string>;
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.getEnvVariables = getEnvVariables;
|
|
4
|
+
/**
|
|
5
|
+
* Retrieves environment variables with the STANTERPRISE_META_ prefix
|
|
6
|
+
* and returns them as a Map with the prefix stripped from keys.
|
|
7
|
+
*
|
|
8
|
+
* For example, STANTERPRISE_META_BUILD_ID=123 becomes BUILD_ID=123 in the metadata.
|
|
9
|
+
*
|
|
10
|
+
* @returns Map of metadata key-value pairs with prefix removed
|
|
11
|
+
*/
|
|
12
|
+
function getEnvVariables() {
|
|
13
|
+
const envValues = process.env;
|
|
14
|
+
const prefix = "STANTERPRISE_META_";
|
|
15
|
+
const filteredEntries = Object.entries(envValues)
|
|
16
|
+
.filter(([key]) => key.startsWith(prefix))
|
|
17
|
+
.map(([key, value]) => {
|
|
18
|
+
// Strip the STANTERPRISE_META_ prefix so metadata keys are cleaner
|
|
19
|
+
const strippedKey = key.substring(prefix.length);
|
|
20
|
+
return [strippedKey, value || ""];
|
|
21
|
+
});
|
|
22
|
+
return new Map(filteredEntries);
|
|
23
|
+
}
|
|
@@ -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,7 @@ 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);
|
|
24
|
+
__exportStar(require("./envRetriever"), exports);
|
|
25
|
+
__exportStar(require("./metadataHelpers"), exports);
|
|
26
|
+
__exportStar(require("./debugLogger"), exports);
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Utility functions for building metadata objects
|
|
3
|
+
*/
|
|
4
|
+
import type { TestCase, TestResult, TestStep } from "@playwright/test/reporter";
|
|
5
|
+
/**
|
|
6
|
+
* Truncate a string to a maximum length with ellipsis indicator
|
|
7
|
+
* @param value String value to truncate
|
|
8
|
+
* @param maxLength Maximum length (default 100KB)
|
|
9
|
+
* @returns Truncated string with warning suffix if truncated
|
|
10
|
+
*/
|
|
11
|
+
export declare function truncateValue(value: string, maxLength?: number): string;
|
|
12
|
+
/**
|
|
13
|
+
* Build metadata from annotations array
|
|
14
|
+
* @param annotations Array of annotations with type and optional description
|
|
15
|
+
* @param prefix Prefix for metadata keys (e.g., "annotation", "result_annotation")
|
|
16
|
+
* @returns Record of metadata key-value pairs
|
|
17
|
+
*/
|
|
18
|
+
export declare function buildAnnotationsMetadata(annotations: Array<{
|
|
19
|
+
type: string;
|
|
20
|
+
description?: string;
|
|
21
|
+
}>, prefix?: string): Record<string, string>;
|
|
22
|
+
/**
|
|
23
|
+
* Build metadata from test case and result annotations
|
|
24
|
+
* Combines annotations from both the test case and the result
|
|
25
|
+
* @param test Test case containing annotations
|
|
26
|
+
* @param result Test result containing additional annotations
|
|
27
|
+
* @returns Record of metadata key-value pairs
|
|
28
|
+
*/
|
|
29
|
+
export declare function buildTestMetadata(test: TestCase, result: TestResult): Record<string, string>;
|
|
30
|
+
/**
|
|
31
|
+
* Build metadata from step annotations and category
|
|
32
|
+
* @param step Test step containing annotations and category
|
|
33
|
+
* @returns Record of metadata key-value pairs
|
|
34
|
+
*/
|
|
35
|
+
export declare function buildStepMetadata(step: TestStep): Record<string, string>;
|
|
36
|
+
/**
|
|
37
|
+
* Convert metadata object to Map for protobuf serialization
|
|
38
|
+
* Protobuf requires Map<string, string> for metadata fields
|
|
39
|
+
* @param metadata Record of metadata key-value pairs
|
|
40
|
+
* @returns Map ready for protobuf constructors
|
|
41
|
+
*/
|
|
42
|
+
export declare function toMetadataMap(metadata: Record<string, string>): Map<string, string>;
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.truncateValue = truncateValue;
|
|
4
|
+
exports.buildAnnotationsMetadata = buildAnnotationsMetadata;
|
|
5
|
+
exports.buildTestMetadata = buildTestMetadata;
|
|
6
|
+
exports.buildStepMetadata = buildStepMetadata;
|
|
7
|
+
exports.toMetadataMap = toMetadataMap;
|
|
8
|
+
/**
|
|
9
|
+
* Maximum length for a single metadata value (100KB)
|
|
10
|
+
* Large values are often accidentally embedded test data or debug output
|
|
11
|
+
*/
|
|
12
|
+
const MAX_METADATA_VALUE_LENGTH = 102400;
|
|
13
|
+
/**
|
|
14
|
+
* Maximum total metadata size across all values (1MB)
|
|
15
|
+
*/
|
|
16
|
+
const MAX_TOTAL_METADATA_SIZE = 1048576;
|
|
17
|
+
/**
|
|
18
|
+
* Truncate a string to a maximum length with ellipsis indicator
|
|
19
|
+
* @param value String value to truncate
|
|
20
|
+
* @param maxLength Maximum length (default 100KB)
|
|
21
|
+
* @returns Truncated string with warning suffix if truncated
|
|
22
|
+
*/
|
|
23
|
+
function truncateValue(value, maxLength = MAX_METADATA_VALUE_LENGTH) {
|
|
24
|
+
if (value.length <= maxLength) {
|
|
25
|
+
return value;
|
|
26
|
+
}
|
|
27
|
+
const truncated = value.substring(0, maxLength - 100);
|
|
28
|
+
return `${truncated}... [TRUNCATED: original length ${value.length} bytes, showing first ${maxLength - 100} bytes]`;
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* Build metadata from annotations array
|
|
32
|
+
* @param annotations Array of annotations with type and optional description
|
|
33
|
+
* @param prefix Prefix for metadata keys (e.g., "annotation", "result_annotation")
|
|
34
|
+
* @returns Record of metadata key-value pairs
|
|
35
|
+
*/
|
|
36
|
+
function buildAnnotationsMetadata(annotations, prefix = "annotation") {
|
|
37
|
+
const metadata = {};
|
|
38
|
+
let totalSize = 0;
|
|
39
|
+
for (let index = 0; index < annotations.length; index++) {
|
|
40
|
+
const annotation = annotations[index];
|
|
41
|
+
const typeKey = `${prefix}_${index}_type`;
|
|
42
|
+
const typeValue = annotation.type;
|
|
43
|
+
metadata[typeKey] = typeValue;
|
|
44
|
+
totalSize += typeValue.length;
|
|
45
|
+
if (annotation.description) {
|
|
46
|
+
const descKey = `${prefix}_${index}_description`;
|
|
47
|
+
const descValue = truncateValue(annotation.description);
|
|
48
|
+
// Skip if adding this would exceed total metadata size
|
|
49
|
+
if (totalSize + descValue.length > MAX_TOTAL_METADATA_SIZE) {
|
|
50
|
+
console.warn(`Skipping annotation description for ${typeKey}: would exceed total metadata size limit (${MAX_TOTAL_METADATA_SIZE} bytes)`);
|
|
51
|
+
continue;
|
|
52
|
+
}
|
|
53
|
+
metadata[descKey] = descValue;
|
|
54
|
+
totalSize += descValue.length;
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
return metadata;
|
|
58
|
+
}
|
|
59
|
+
/**
|
|
60
|
+
* Build metadata from test case and result annotations
|
|
61
|
+
* Combines annotations from both the test case and the result
|
|
62
|
+
* @param test Test case containing annotations
|
|
63
|
+
* @param result Test result containing additional annotations
|
|
64
|
+
* @returns Record of metadata key-value pairs
|
|
65
|
+
*/
|
|
66
|
+
function buildTestMetadata(test, result) {
|
|
67
|
+
return {
|
|
68
|
+
...buildAnnotationsMetadata(test.annotations, "annotation"),
|
|
69
|
+
...buildAnnotationsMetadata(result.annotations, "result_annotation"),
|
|
70
|
+
};
|
|
71
|
+
}
|
|
72
|
+
/**
|
|
73
|
+
* Build metadata from step annotations and category
|
|
74
|
+
* @param step Test step containing annotations and category
|
|
75
|
+
* @returns Record of metadata key-value pairs
|
|
76
|
+
*/
|
|
77
|
+
function buildStepMetadata(step) {
|
|
78
|
+
return {
|
|
79
|
+
category: step.category,
|
|
80
|
+
...buildAnnotationsMetadata(step.annotations, "annotation"),
|
|
81
|
+
};
|
|
82
|
+
}
|
|
83
|
+
/**
|
|
84
|
+
* Convert metadata object to Map for protobuf serialization
|
|
85
|
+
* Protobuf requires Map<string, string> for metadata fields
|
|
86
|
+
* @param metadata Record of metadata key-value pairs
|
|
87
|
+
* @returns Map ready for protobuf constructors
|
|
88
|
+
*/
|
|
89
|
+
function toMetadataMap(metadata) {
|
|
90
|
+
return new Map(Object.entries(metadata));
|
|
91
|
+
}
|
|
@@ -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,51 @@
|
|
|
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 = identifyAddress(providedOptions);
|
|
12
|
+
result.grpcEnabled =
|
|
13
|
+
providedOptions.grpcEnabled ??
|
|
14
|
+
(process.env.STANTERPRISE_GRPC_ENABLED || "true").toLowerCase() !== "false";
|
|
15
|
+
result.grpcTimeout = providedOptions.grpcTimeout ?? 1000;
|
|
16
|
+
result.grpcMaxMessageSize = providedOptions.grpcMaxMessageSize ?? 104857600;
|
|
17
|
+
result.maxAttachmentSize = providedOptions.maxAttachmentSize ?? 10485760;
|
|
18
|
+
result.verbose = providedOptions.verbose ?? false;
|
|
19
|
+
result.grpcMaxRetries = providedOptions.grpcMaxRetries ?? 3;
|
|
20
|
+
result.grpcRetryDelay = providedOptions.grpcRetryDelay ?? 100;
|
|
21
|
+
result.debug =
|
|
22
|
+
providedOptions.debug ??
|
|
23
|
+
(process.env.STANTERPRISE_DEBUG || "").toLowerCase() === "true";
|
|
24
|
+
result.debugFile =
|
|
25
|
+
providedOptions.debugFile ||
|
|
26
|
+
process.env.STANTERPRISE_DEBUG_FILE ||
|
|
27
|
+
"stanterprise-debug.jsonl";
|
|
28
|
+
result.tls =
|
|
29
|
+
providedOptions.tls ??
|
|
30
|
+
(process.env.STANTERPRISE_GRPC_TLS || "true").toLowerCase() === "true";
|
|
31
|
+
return result;
|
|
32
|
+
}
|
|
33
|
+
function identifyAddress(providedOptions) {
|
|
34
|
+
if (providedOptions.grpcAddress) {
|
|
35
|
+
return providedOptions.grpcAddress;
|
|
36
|
+
}
|
|
37
|
+
if (process.env.STANTERPRISE_GRPC_ADDRESS) {
|
|
38
|
+
return process.env.STANTERPRISE_GRPC_ADDRESS;
|
|
39
|
+
}
|
|
40
|
+
if (process.env.STANTERPRISE_GRPC_HOST) {
|
|
41
|
+
const host = process.env.STANTERPRISE_GRPC_HOST;
|
|
42
|
+
const port = process.env.STANTERPRISE_GRPC_PORT || "50051";
|
|
43
|
+
return `${host}:${port}`;
|
|
44
|
+
}
|
|
45
|
+
if (providedOptions.grpcHost) {
|
|
46
|
+
const host = providedOptions.grpcHost;
|
|
47
|
+
const port = providedOptions.grpcPort ?? 50051;
|
|
48
|
+
return `${host}:${port}`;
|
|
49
|
+
}
|
|
50
|
+
return "localhost:50051";
|
|
51
|
+
}
|
|
@@ -3,26 +3,27 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
3
3
|
exports.mapTestStatus = mapTestStatus;
|
|
4
4
|
exports.mapStepStatus = mapStepStatus;
|
|
5
5
|
const protobuf_1 = require("@stanterprise/protobuf");
|
|
6
|
+
const TestStatus = protobuf_1.common.v1.common.TestStatus;
|
|
6
7
|
/**
|
|
7
8
|
* Map Playwright test status to protobuf TestStatus
|
|
8
9
|
*/
|
|
9
10
|
function mapTestStatus(status) {
|
|
10
11
|
switch (status) {
|
|
11
12
|
case "passed":
|
|
12
|
-
return
|
|
13
|
+
return TestStatus.PASSED;
|
|
13
14
|
case "failed":
|
|
14
|
-
return
|
|
15
|
+
return TestStatus.FAILED;
|
|
15
16
|
case "skipped":
|
|
16
|
-
return
|
|
17
|
+
return TestStatus.SKIPPED;
|
|
17
18
|
case "timedOut":
|
|
18
|
-
return
|
|
19
|
+
return TestStatus.FAILED; // Treat timeout as failed
|
|
19
20
|
default:
|
|
20
|
-
return
|
|
21
|
+
return TestStatus.UNKNOWN;
|
|
21
22
|
}
|
|
22
23
|
}
|
|
23
24
|
/**
|
|
24
25
|
* Map step error to test status
|
|
25
26
|
*/
|
|
26
27
|
function mapStepStatus(hasError) {
|
|
27
|
-
return hasError ?
|
|
28
|
+
return hasError ? TestStatus.FAILED : TestStatus.PASSED;
|
|
28
29
|
}
|
|
@@ -2,15 +2,15 @@
|
|
|
2
2
|
* Utility functions for creating timestamps and durations
|
|
3
3
|
*/
|
|
4
4
|
import { google } from "@stanterprise/protobuf";
|
|
5
|
-
|
|
5
|
+
declare const Timestamp: typeof google.google.protobuf.Timestamp;
|
|
6
6
|
/**
|
|
7
7
|
* Create a protobuf Timestamp from a JavaScript Date
|
|
8
8
|
*/
|
|
9
|
-
export declare function createTimestamp(date: Date): InstanceType<typeof
|
|
9
|
+
export declare function createTimestamp(date: Date): InstanceType<typeof Timestamp>;
|
|
10
10
|
/**
|
|
11
11
|
* Create a protobuf Timestamp from milliseconds since epoch
|
|
12
12
|
*/
|
|
13
|
-
export declare function createTimestampFromMs(ms: number): InstanceType<typeof
|
|
13
|
+
export declare function createTimestampFromMs(ms: number): InstanceType<typeof Timestamp>;
|
|
14
14
|
/**
|
|
15
15
|
* Create a duration object from milliseconds.
|
|
16
16
|
*
|
|
@@ -18,9 +18,9 @@ export declare function createTimestampFromMs(ms: number): InstanceType<typeof g
|
|
|
18
18
|
* but not exported in the package's main barrel export. Since Duration and Timestamp
|
|
19
19
|
* have identical structure (seconds: number, nanos: number) and the protobuf serialization
|
|
20
20
|
* is wire-compatible, we use Timestamp as a Duration substitute. This works correctly at runtime.
|
|
21
|
-
* The return type is cast to Duration for proper type checking with the schema.
|
|
22
21
|
*
|
|
23
22
|
* @param durationMs Duration in milliseconds
|
|
24
23
|
* @returns A Duration object (implemented as Timestamp due to export limitations)
|
|
25
24
|
*/
|
|
26
|
-
export declare function createDuration(durationMs: number):
|
|
25
|
+
export declare function createDuration(durationMs: number): any;
|
|
26
|
+
export {};
|
|
@@ -7,22 +7,32 @@ exports.createDuration = createDuration;
|
|
|
7
7
|
* Utility functions for creating timestamps and durations
|
|
8
8
|
*/
|
|
9
9
|
const protobuf_1 = require("@stanterprise/protobuf");
|
|
10
|
+
const Timestamp = protobuf_1.google.google.protobuf.Timestamp;
|
|
10
11
|
/**
|
|
11
12
|
* Create a protobuf Timestamp from a JavaScript Date
|
|
12
13
|
*/
|
|
13
14
|
function createTimestamp(date) {
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
15
|
+
const ms = date.getTime();
|
|
16
|
+
// Validate to prevent NaN from causing protobuf serialization failures
|
|
17
|
+
if (isNaN(ms)) {
|
|
18
|
+
throw new Error(`Invalid date provided to createTimestamp: ${date.toString()}`);
|
|
19
|
+
}
|
|
20
|
+
return new Timestamp({
|
|
21
|
+
seconds: Math.floor(ms / 1000),
|
|
22
|
+
nanos: Math.floor((ms % 1000) * 1000000),
|
|
17
23
|
});
|
|
18
24
|
}
|
|
19
25
|
/**
|
|
20
26
|
* Create a protobuf Timestamp from milliseconds since epoch
|
|
21
27
|
*/
|
|
22
28
|
function createTimestampFromMs(ms) {
|
|
23
|
-
|
|
29
|
+
// Validate to prevent NaN from causing protobuf serialization failures
|
|
30
|
+
if (typeof ms !== "number" || isNaN(ms)) {
|
|
31
|
+
throw new Error(`Invalid milliseconds provided to createTimestampFromMs: ${ms}`);
|
|
32
|
+
}
|
|
33
|
+
return new Timestamp({
|
|
24
34
|
seconds: Math.floor(ms / 1000),
|
|
25
|
-
nanos: (ms % 1000) * 1000000,
|
|
35
|
+
nanos: Math.floor((ms % 1000) * 1000000),
|
|
26
36
|
});
|
|
27
37
|
}
|
|
28
38
|
/**
|
|
@@ -32,14 +42,20 @@ function createTimestampFromMs(ms) {
|
|
|
32
42
|
* but not exported in the package's main barrel export. Since Duration and Timestamp
|
|
33
43
|
* have identical structure (seconds: number, nanos: number) and the protobuf serialization
|
|
34
44
|
* is wire-compatible, we use Timestamp as a Duration substitute. This works correctly at runtime.
|
|
35
|
-
* The return type is cast to Duration for proper type checking with the schema.
|
|
36
45
|
*
|
|
37
46
|
* @param durationMs Duration in milliseconds
|
|
38
47
|
* @returns A Duration object (implemented as Timestamp due to export limitations)
|
|
39
48
|
*/
|
|
40
49
|
function createDuration(durationMs) {
|
|
41
|
-
|
|
50
|
+
// Validate to prevent NaN from causing protobuf serialization failures
|
|
51
|
+
if (typeof durationMs !== "number" || isNaN(durationMs)) {
|
|
52
|
+
throw new Error(`Invalid duration provided to createDuration: ${durationMs}`);
|
|
53
|
+
}
|
|
54
|
+
if (durationMs < 0) {
|
|
55
|
+
throw new Error(`Negative duration provided to createDuration: ${durationMs}`);
|
|
56
|
+
}
|
|
57
|
+
return new Timestamp({
|
|
42
58
|
seconds: Math.floor(durationMs / 1000),
|
|
43
|
-
nanos: (durationMs % 1000) * 1000000,
|
|
59
|
+
nanos: Math.floor((durationMs % 1000) * 1000000),
|
|
44
60
|
});
|
|
45
61
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@stanterprise/playwright-reporter",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.1",
|
|
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": {
|
|
@@ -16,8 +16,7 @@
|
|
|
16
16
|
"main": "dist/index.js",
|
|
17
17
|
"types": "dist/index.d.ts",
|
|
18
18
|
"files": [
|
|
19
|
-
"dist/**/*"
|
|
20
|
-
"README.md"
|
|
19
|
+
"dist/**/*"
|
|
21
20
|
],
|
|
22
21
|
"keywords": [
|
|
23
22
|
"playwright",
|
|
@@ -44,9 +43,9 @@
|
|
|
44
43
|
},
|
|
45
44
|
"devDependencies": {
|
|
46
45
|
"@playwright/test": "^1.52.0",
|
|
47
|
-
"@types/jest": "^
|
|
48
|
-
"@types/node": "^22.
|
|
49
|
-
"jest": "^
|
|
46
|
+
"@types/jest": "^29.5.14",
|
|
47
|
+
"@types/node": "^22.19.7",
|
|
48
|
+
"jest": "^29.7.0",
|
|
50
49
|
"ts-jest": "^29.4.5",
|
|
51
50
|
"typescript": "^5.9.3"
|
|
52
51
|
},
|
|
@@ -54,6 +53,6 @@
|
|
|
54
53
|
"@grpc/grpc-js": "^1.14.1",
|
|
55
54
|
"@protobuf-ts/grpc-transport": "^2.11.1",
|
|
56
55
|
"@protobuf-ts/grpcweb-transport": "^2.11.1",
|
|
57
|
-
"@stanterprise/protobuf": "^0.
|
|
56
|
+
"@stanterprise/protobuf": "^0.1.3"
|
|
58
57
|
}
|
|
59
58
|
}
|