@testrelic/maestro-analytics 1.2.0 → 1.2.1-next.56

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 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+(\w+)\s+(.+)$/;
1325
- var TIMESTAMP_ONLY_REGEX = /^\[?(\d{2}:\d{2}:\d{2}(?:\.\d+)?)\]?\s+(\w+)\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 parseLogContent(content) {
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 today = (/* @__PURE__ */ new Date()).toISOString().split("T")[0];
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: `${today}T${match[1]}`,
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 = (0, import_node_path16.basename)(filePath).replace(/^commands[-_]?/i, "").replace(/\.json$/i, "").toLowerCase();
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) => (0, import_node_path16.basename)(p, (0, import_node_path16.extname)(p)).replace(/^commands[-_]?/i, "").toLowerCase();
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
- const entries = parseLogFile(logFile);
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
- return flowToTestResult(f, apiCallsByFlow?.get(testId));
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,