@testrelic/maestro-analytics 1.2.0 → 1.2.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/dist/cli.cjs +76 -16
- package/dist/index.cjs +49 -49
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +100 -75
- package/dist/index.d.ts +100 -75
- package/dist/index.js +49 -49
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/cli.cjs
CHANGED
|
@@ -1,5 +1,27 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
"use strict";
|
|
3
|
+
var __create = Object.create;
|
|
4
|
+
var __defProp = Object.defineProperty;
|
|
5
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
6
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
7
|
+
var __getProtoOf = Object.getPrototypeOf;
|
|
8
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
9
|
+
var __copyProps = (to, from, except, desc) => {
|
|
10
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
11
|
+
for (let key of __getOwnPropNames(from))
|
|
12
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
13
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
14
|
+
}
|
|
15
|
+
return to;
|
|
16
|
+
};
|
|
17
|
+
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
|
|
18
|
+
// If the importer is in node compatibility mode or this is not an ESM
|
|
19
|
+
// file that has been converted to a CommonJS file using a Babel-
|
|
20
|
+
// compatible transform (i.e. "__esModule" has not been set), then set
|
|
21
|
+
// "default" to the CommonJS "module.exports" for node compatibility.
|
|
22
|
+
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
|
|
23
|
+
mod
|
|
24
|
+
));
|
|
3
25
|
|
|
4
26
|
// src/config.ts
|
|
5
27
|
var import_core2 = require("@testrelic/core");
|
|
@@ -1321,8 +1343,8 @@ function discoverFlowFiles(dir) {
|
|
|
1321
1343
|
var import_node_fs8 = require("fs");
|
|
1322
1344
|
var import_node_path7 = require("path");
|
|
1323
1345
|
var import_node_fs9 = require("fs");
|
|
1324
|
-
var LOG_LINE_REGEX = /^(\d{4}-\d{2}-\d{2}[T ]\d{2}:\d{2}:\d{2}(?:\.\d+)?(?:Z|[+-]\d{2}:?\d{2})?)\s
|
|
1325
|
-
var TIMESTAMP_ONLY_REGEX = /^\[?(\d{2}:\d{2}:\d{2}(?:\.\d+)?)\]?\s
|
|
1346
|
+
var LOG_LINE_REGEX = /^(\d{4}-\d{2}-\d{2}[T ]\d{2}:\d{2}:\d{2}(?:\.\d+)?(?:Z|[+-]\d{2}:?\d{2})?)\s+\[?\s*(\w+)\s*\]?\s+(.+)$/;
|
|
1347
|
+
var TIMESTAMP_ONLY_REGEX = /^\[?(\d{2}:\d{2}:\d{2}(?:\.\d+)?)\]?\s+\[?\s*(\w+)\s*\]?\s+(.+)$/;
|
|
1326
1348
|
function parseLevel(raw) {
|
|
1327
1349
|
const upper = raw.toUpperCase();
|
|
1328
1350
|
if (upper === "DEBUG" || upper === "TRACE" || upper === "VERBOSE") return "DEBUG";
|
|
@@ -1331,10 +1353,31 @@ function parseLevel(raw) {
|
|
|
1331
1353
|
if (upper === "ERROR" || upper === "FATAL" || upper === "SEVERE") return "ERROR";
|
|
1332
1354
|
return "INFO";
|
|
1333
1355
|
}
|
|
1334
|
-
function
|
|
1356
|
+
function buildEntryTimestamp(dateAnchor, hhmmss) {
|
|
1357
|
+
const m = /^(\d{2}):(\d{2}):(\d{2})(?:\.(\d{1,3}))?$/.exec(hhmmss);
|
|
1358
|
+
if (!m) {
|
|
1359
|
+
return (/* @__PURE__ */ new Date()).toISOString();
|
|
1360
|
+
}
|
|
1361
|
+
const h = Number(m[1]);
|
|
1362
|
+
const mi = Number(m[2]);
|
|
1363
|
+
const s = Number(m[3]);
|
|
1364
|
+
const ms = m[4] ? Number(m[4].padEnd(3, "0")) : 0;
|
|
1365
|
+
const local = new Date(
|
|
1366
|
+
dateAnchor.getFullYear(),
|
|
1367
|
+
dateAnchor.getMonth(),
|
|
1368
|
+
dateAnchor.getDate(),
|
|
1369
|
+
h,
|
|
1370
|
+
mi,
|
|
1371
|
+
s,
|
|
1372
|
+
ms
|
|
1373
|
+
);
|
|
1374
|
+
return local.toISOString();
|
|
1375
|
+
}
|
|
1376
|
+
function parseLogContent(content, defaultDate) {
|
|
1335
1377
|
const entries = [];
|
|
1336
1378
|
const lines = content.split("\n");
|
|
1337
|
-
const
|
|
1379
|
+
const anchorDate = defaultDate instanceof Date ? defaultDate : null;
|
|
1380
|
+
const dateAnchorStr = typeof defaultDate === "string" ? defaultDate : defaultDate instanceof Date ? `${defaultDate.getFullYear()}-${String(defaultDate.getMonth() + 1).padStart(2, "0")}-${String(defaultDate.getDate()).padStart(2, "0")}` : (/* @__PURE__ */ new Date()).toISOString().split("T")[0];
|
|
1338
1381
|
for (const line2 of lines) {
|
|
1339
1382
|
const trimmed = line2.trim();
|
|
1340
1383
|
if (!trimmed) continue;
|
|
@@ -1350,8 +1393,9 @@ function parseLogContent(content) {
|
|
|
1350
1393
|
}
|
|
1351
1394
|
match = TIMESTAMP_ONLY_REGEX.exec(trimmed);
|
|
1352
1395
|
if (match) {
|
|
1396
|
+
const ts = anchorDate ? buildEntryTimestamp(anchorDate, match[1]) : `${dateAnchorStr}T${match[1]}`;
|
|
1353
1397
|
entries.push({
|
|
1354
|
-
timestamp:
|
|
1398
|
+
timestamp: ts,
|
|
1355
1399
|
level: parseLevel(match[2]),
|
|
1356
1400
|
message: match[3],
|
|
1357
1401
|
source: null
|
|
@@ -1368,10 +1412,10 @@ function parseLogContent(content) {
|
|
|
1368
1412
|
}
|
|
1369
1413
|
return entries;
|
|
1370
1414
|
}
|
|
1371
|
-
function parseLogFile(filePath) {
|
|
1415
|
+
function parseLogFile(filePath, defaultDate) {
|
|
1372
1416
|
try {
|
|
1373
1417
|
const content = (0, import_node_fs8.readFileSync)(filePath, "utf-8");
|
|
1374
|
-
return parseLogContent(content);
|
|
1418
|
+
return parseLogContent(content, defaultDate);
|
|
1375
1419
|
} catch {
|
|
1376
1420
|
return [];
|
|
1377
1421
|
}
|
|
@@ -4563,10 +4607,13 @@ function extractRecordingPath(commands) {
|
|
|
4563
4607
|
}
|
|
4564
4608
|
return null;
|
|
4565
4609
|
}
|
|
4610
|
+
function normaliseCommandFileName(filePath) {
|
|
4611
|
+
return (0, import_node_path16.basename)(filePath).replace(/^commands[-_]?/i, "").replace(/\.json$/i, "").replace(/^\(/, "").replace(/\)$/, "").toLowerCase();
|
|
4612
|
+
}
|
|
4566
4613
|
function buildFlowRecordingMap(commandSteps) {
|
|
4567
4614
|
const map = /* @__PURE__ */ new Map();
|
|
4568
4615
|
for (const [filePath, commands] of commandSteps) {
|
|
4569
|
-
const name = (
|
|
4616
|
+
const name = normaliseCommandFileName(filePath);
|
|
4570
4617
|
const recPath = extractRecordingPath(commands);
|
|
4571
4618
|
if (recPath && name) map.set(name, recPath);
|
|
4572
4619
|
}
|
|
@@ -4577,7 +4624,7 @@ function commandsForFlow(commandSteps, flowFile) {
|
|
|
4577
4624
|
if (!flowBase || commandSteps.size === 0) {
|
|
4578
4625
|
return { commands: [], matchedAnyFile: false };
|
|
4579
4626
|
}
|
|
4580
|
-
const stripCmdPrefix = (p) => (
|
|
4627
|
+
const stripCmdPrefix = (p) => normaliseCommandFileName(p);
|
|
4581
4628
|
for (const [cmdFile, cmds] of commandSteps) {
|
|
4582
4629
|
if (stripCmdPrefix(cmdFile) === flowBase) {
|
|
4583
4630
|
return { commands: cmds, matchedAnyFile: true };
|
|
@@ -4609,7 +4656,7 @@ function platformToOs(platform) {
|
|
|
4609
4656
|
return void 0;
|
|
4610
4657
|
}
|
|
4611
4658
|
}
|
|
4612
|
-
function flowToTestResult(flow, apiCalls) {
|
|
4659
|
+
function flowToTestResult(flow, apiCalls, actions) {
|
|
4613
4660
|
const consoleLogs = flow.logEntries.length > 0 ? flow.logEntries.map((entry) => ({
|
|
4614
4661
|
level: entry.level.toLowerCase(),
|
|
4615
4662
|
message: entry.message,
|
|
@@ -4637,7 +4684,8 @@ function flowToTestResult(flow, apiCalls) {
|
|
|
4637
4684
|
os: platformToOs(flow.platform),
|
|
4638
4685
|
deviceName: flow.deviceId,
|
|
4639
4686
|
...consoleLogs ? { consoleLogs } : {},
|
|
4640
|
-
...apiCalls && apiCalls.length > 0 ? { apiCalls } : {}
|
|
4687
|
+
...apiCalls && apiCalls.length > 0 ? { apiCalls } : {},
|
|
4688
|
+
...actions && actions.length > 0 ? { actions } : {}
|
|
4641
4689
|
};
|
|
4642
4690
|
}
|
|
4643
4691
|
function logEntriesForFlow(allEntries, flowStartedAt, flowCompletedAt) {
|
|
@@ -4662,7 +4710,13 @@ async function orchestrateReport(input) {
|
|
|
4662
4710
|
const logFiles = input.debugOutputDir ? discoverLogFiles(input.debugOutputDir) : artifacts.logPaths;
|
|
4663
4711
|
const allLogEntries = [];
|
|
4664
4712
|
for (const logFile of logFiles) {
|
|
4665
|
-
|
|
4713
|
+
let mtimeDate;
|
|
4714
|
+
try {
|
|
4715
|
+
const { statSync: statSync5 } = await import("fs");
|
|
4716
|
+
mtimeDate = statSync5(logFile).mtime;
|
|
4717
|
+
} catch {
|
|
4718
|
+
}
|
|
4719
|
+
const entries = parseLogFile(logFile, mtimeDate);
|
|
4666
4720
|
if (entries.length > 0) allLogEntries.push(...entries);
|
|
4667
4721
|
}
|
|
4668
4722
|
let platform = input.platform ?? "unknown";
|
|
@@ -4703,14 +4757,19 @@ async function orchestrateReport(input) {
|
|
|
4703
4757
|
const meta = flowMetadataMap.get(name);
|
|
4704
4758
|
const status = testCase.status === "SUCCESS" ? "passed" : testCase.status === "SKIPPED" ? "skipped" : "failed";
|
|
4705
4759
|
const durationMs = testCase.time * 1e3;
|
|
4706
|
-
const flowStartedAt = startedAt;
|
|
4707
|
-
const flowCompletedAt = new Date(new Date(flowStartedAt).getTime() + durationMs).toISOString();
|
|
4708
4760
|
const flowFile = testCase.classname || name;
|
|
4709
4761
|
const flowBase = (0, import_node_path16.basename)(flowFile, (0, import_node_path16.extname)(flowFile)).toLowerCase();
|
|
4710
4762
|
const recordingPath = flowRecordingMap.get(flowBase) ?? null;
|
|
4711
4763
|
const { commands: flowCmds, matchedAnyFile } = commandsForFlow(commandSteps, flowFile);
|
|
4712
4764
|
const perFlowCommands = matchedAnyFile ? flowCmds.filter((c) => c.category !== "assertion") : allNonAssertions;
|
|
4713
4765
|
const perFlowAssertions = matchedAnyFile ? flowCmds.filter((c) => c.category === "assertion") : allAssertions;
|
|
4766
|
+
const allFlowCmds = [...perFlowCommands, ...perFlowAssertions];
|
|
4767
|
+
const earliestCmdMs = allFlowCmds.reduce((min, c) => {
|
|
4768
|
+
const t = Date.parse(c.timestamp);
|
|
4769
|
+
return Number.isFinite(t) && t < min ? t : min;
|
|
4770
|
+
}, Number.POSITIVE_INFINITY);
|
|
4771
|
+
const flowStartedAt = Number.isFinite(earliestCmdMs) ? new Date(earliestCmdMs).toISOString() : startedAt;
|
|
4772
|
+
const flowCompletedAt = new Date(new Date(flowStartedAt).getTime() + durationMs).toISOString();
|
|
4714
4773
|
flowResults.push({
|
|
4715
4774
|
flowName: name,
|
|
4716
4775
|
flowFile,
|
|
@@ -4832,9 +4891,10 @@ async function orchestrateReport(input) {
|
|
|
4832
4891
|
}
|
|
4833
4892
|
const matchedPaths = new Set(videoArtifacts.map((va) => va.path));
|
|
4834
4893
|
const unmatchedVideos = videoPathsToUpload.filter((v) => !matchedPaths.has(v));
|
|
4835
|
-
const testsForUpload = flowResults.map((f) => {
|
|
4894
|
+
const testsForUpload = flowResults.map((f, i) => {
|
|
4836
4895
|
const testId = `${f.flowFile}::${f.flowName}`;
|
|
4837
|
-
|
|
4896
|
+
const tlTest = timeline[i]?.tests?.[0];
|
|
4897
|
+
return flowToTestResult(f, apiCallsByFlow?.get(testId), tlTest?.actions ?? null);
|
|
4838
4898
|
});
|
|
4839
4899
|
await finalizeAndUpload(
|
|
4840
4900
|
cloudClient,
|