@testrelic/maestro-analytics 1.0.1 → 1.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cli.cjs +68 -18
- package/dist/index.cjs +44 -44
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +11 -0
- package/dist/index.d.ts +11 -0
- package/dist/index.js +44 -44
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/cli.cjs
CHANGED
|
@@ -1040,7 +1040,7 @@ function mapCommandCategory(category) {
|
|
|
1040
1040
|
case "ai":
|
|
1041
1041
|
return "assertion";
|
|
1042
1042
|
case "navigation":
|
|
1043
|
-
return "
|
|
1043
|
+
return "navigation";
|
|
1044
1044
|
case "device":
|
|
1045
1045
|
return "custom_step";
|
|
1046
1046
|
case "media":
|
|
@@ -3669,6 +3669,31 @@ function buildFlowRecordingMap(commandSteps) {
|
|
|
3669
3669
|
}
|
|
3670
3670
|
return map;
|
|
3671
3671
|
}
|
|
3672
|
+
function commandsForFlow(commandSteps, flowFile) {
|
|
3673
|
+
const flowBase = (0, import_node_path13.basename)(flowFile, (0, import_node_path13.extname)(flowFile)).toLowerCase();
|
|
3674
|
+
if (!flowBase || commandSteps.size === 0) {
|
|
3675
|
+
return { commands: [], matchedAnyFile: false };
|
|
3676
|
+
}
|
|
3677
|
+
const stripCmdPrefix = (p) => (0, import_node_path13.basename)(p, (0, import_node_path13.extname)(p)).replace(/^commands[-_]?/i, "").toLowerCase();
|
|
3678
|
+
for (const [cmdFile, cmds] of commandSteps) {
|
|
3679
|
+
if (stripCmdPrefix(cmdFile) === flowBase) {
|
|
3680
|
+
return { commands: cmds, matchedAnyFile: true };
|
|
3681
|
+
}
|
|
3682
|
+
}
|
|
3683
|
+
for (const [cmdFile, cmds] of commandSteps) {
|
|
3684
|
+
const stripped = stripCmdPrefix(cmdFile);
|
|
3685
|
+
if (stripped.length >= 3 && stripped.includes(flowBase)) {
|
|
3686
|
+
return { commands: cmds, matchedAnyFile: true };
|
|
3687
|
+
}
|
|
3688
|
+
}
|
|
3689
|
+
for (const [cmdFile, cmds] of commandSteps) {
|
|
3690
|
+
const stripped = stripCmdPrefix(cmdFile);
|
|
3691
|
+
if (stripped.length >= 3 && flowBase.includes(stripped)) {
|
|
3692
|
+
return { commands: cmds, matchedAnyFile: true };
|
|
3693
|
+
}
|
|
3694
|
+
}
|
|
3695
|
+
return { commands: [], matchedAnyFile: false };
|
|
3696
|
+
}
|
|
3672
3697
|
function platformToOs(platform) {
|
|
3673
3698
|
switch (platform) {
|
|
3674
3699
|
case "android":
|
|
@@ -3682,6 +3707,12 @@ function platformToOs(platform) {
|
|
|
3682
3707
|
}
|
|
3683
3708
|
}
|
|
3684
3709
|
function flowToTestResult(flow) {
|
|
3710
|
+
const consoleLogs = flow.logEntries.length > 0 ? flow.logEntries.map((entry) => ({
|
|
3711
|
+
level: entry.level.toLowerCase(),
|
|
3712
|
+
message: entry.message,
|
|
3713
|
+
timestamp: entry.timestamp,
|
|
3714
|
+
source: entry.source
|
|
3715
|
+
})) : void 0;
|
|
3685
3716
|
return {
|
|
3686
3717
|
testId: `${flow.flowFile}::${flow.flowName}`,
|
|
3687
3718
|
title: flow.flowName,
|
|
@@ -3701,26 +3732,39 @@ function flowToTestResult(flow) {
|
|
|
3701
3732
|
source: "maestro",
|
|
3702
3733
|
platform: flow.platform !== "unknown" ? flow.platform : void 0,
|
|
3703
3734
|
os: platformToOs(flow.platform),
|
|
3704
|
-
deviceName: flow.deviceId
|
|
3735
|
+
deviceName: flow.deviceId,
|
|
3736
|
+
...consoleLogs ? { consoleLogs } : {}
|
|
3705
3737
|
};
|
|
3706
3738
|
}
|
|
3739
|
+
function logEntriesForFlow(allEntries, flowStartedAt, flowCompletedAt) {
|
|
3740
|
+
if (allEntries.length === 0) return [];
|
|
3741
|
+
const startMs = Date.parse(flowStartedAt);
|
|
3742
|
+
const endMs = Date.parse(flowCompletedAt);
|
|
3743
|
+
if (Number.isNaN(startMs) || Number.isNaN(endMs)) {
|
|
3744
|
+
return allEntries.slice();
|
|
3745
|
+
}
|
|
3746
|
+
return allEntries.filter((entry) => {
|
|
3747
|
+
const t = Date.parse(entry.timestamp);
|
|
3748
|
+
if (Number.isNaN(t)) return true;
|
|
3749
|
+
return t >= startMs && t <= endMs;
|
|
3750
|
+
});
|
|
3751
|
+
}
|
|
3707
3752
|
async function orchestrateReport(input) {
|
|
3708
3753
|
const { config } = input;
|
|
3709
3754
|
const startedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
3710
3755
|
const testRunId = config.testRunId ?? (0, import_node_crypto3.randomUUID)();
|
|
3711
3756
|
const artifacts = collectArtifacts(input.testOutputDir, input.debugOutputDir);
|
|
3712
3757
|
const junitPath = input.junitPath ?? artifacts.junitReportPath;
|
|
3758
|
+
const logFiles = input.debugOutputDir ? discoverLogFiles(input.debugOutputDir) : artifacts.logPaths;
|
|
3759
|
+
const allLogEntries = [];
|
|
3760
|
+
for (const logFile of logFiles) {
|
|
3761
|
+
const entries = parseLogFile(logFile);
|
|
3762
|
+
if (entries.length > 0) allLogEntries.push(...entries);
|
|
3763
|
+
}
|
|
3713
3764
|
let platform = input.platform ?? "unknown";
|
|
3714
|
-
if (platform === "unknown") {
|
|
3715
|
-
const
|
|
3716
|
-
|
|
3717
|
-
const logEntries = parseLogFile(logFile);
|
|
3718
|
-
const detected = detectPlatformFromLogs(logEntries);
|
|
3719
|
-
if (detected !== "unknown") {
|
|
3720
|
-
platform = detected;
|
|
3721
|
-
break;
|
|
3722
|
-
}
|
|
3723
|
-
}
|
|
3765
|
+
if (platform === "unknown" && allLogEntries.length > 0) {
|
|
3766
|
+
const detected = detectPlatformFromLogs(allLogEntries);
|
|
3767
|
+
if (detected !== "unknown") platform = detected;
|
|
3724
3768
|
}
|
|
3725
3769
|
const flowMetadataMap = /* @__PURE__ */ new Map();
|
|
3726
3770
|
if (input.flowsDir && (0, import_node_fs15.existsSync)(input.flowsDir)) {
|
|
@@ -3747,8 +3791,8 @@ async function orchestrateReport(input) {
|
|
|
3747
3791
|
if (junitPath && (0, import_node_fs15.existsSync)(junitPath)) {
|
|
3748
3792
|
const junit = parseJUnitFile(junitPath);
|
|
3749
3793
|
const allCommands = Array.from(commandSteps.values()).flat();
|
|
3750
|
-
const
|
|
3751
|
-
const
|
|
3794
|
+
const allAssertions = allCommands.filter((c) => c.category === "assertion");
|
|
3795
|
+
const allNonAssertions = allCommands.filter((c) => c.category !== "assertion");
|
|
3752
3796
|
for (const suite of junit.testSuites) {
|
|
3753
3797
|
for (const testCase of suite.testCases) {
|
|
3754
3798
|
const name = testCase.name;
|
|
@@ -3760,6 +3804,9 @@ async function orchestrateReport(input) {
|
|
|
3760
3804
|
const flowFile = testCase.classname || name;
|
|
3761
3805
|
const flowBase = (0, import_node_path13.basename)(flowFile, (0, import_node_path13.extname)(flowFile)).toLowerCase();
|
|
3762
3806
|
const recordingPath = flowRecordingMap.get(flowBase) ?? null;
|
|
3807
|
+
const { commands: flowCmds, matchedAnyFile } = commandsForFlow(commandSteps, flowFile);
|
|
3808
|
+
const perFlowCommands = matchedAnyFile ? flowCmds.filter((c) => c.category !== "assertion") : allNonAssertions;
|
|
3809
|
+
const perFlowAssertions = matchedAnyFile ? flowCmds.filter((c) => c.category === "assertion") : allAssertions;
|
|
3763
3810
|
flowResults.push({
|
|
3764
3811
|
flowName: name,
|
|
3765
3812
|
flowFile,
|
|
@@ -3772,12 +3819,12 @@ async function orchestrateReport(input) {
|
|
|
3772
3819
|
completedAt: flowCompletedAt,
|
|
3773
3820
|
tags: meta?.tags ?? [],
|
|
3774
3821
|
properties: meta?.properties ?? {},
|
|
3775
|
-
commands:
|
|
3776
|
-
assertions:
|
|
3822
|
+
commands: perFlowCommands,
|
|
3823
|
+
assertions: perFlowAssertions,
|
|
3777
3824
|
screenshotPaths: matchScreenshotsToFlow(flowFile, artifacts.screenshotPaths),
|
|
3778
3825
|
videoPath: matchVideoToFlow(flowFile, artifacts.videoPaths, recordingPath),
|
|
3779
3826
|
aiDefects: allAiDefects,
|
|
3780
|
-
logEntries:
|
|
3827
|
+
logEntries: logEntriesForFlow(allLogEntries, flowStartedAt, flowCompletedAt),
|
|
3781
3828
|
failureMessage: testCase.failureMessage ?? testCase.errorMessage ?? null,
|
|
3782
3829
|
failureType: testCase.failureType ?? testCase.errorType ?? null,
|
|
3783
3830
|
subflowRefs: meta?.subflowRefs ?? [],
|
|
@@ -3805,7 +3852,10 @@ async function orchestrateReport(input) {
|
|
|
3805
3852
|
screenshotPaths: [],
|
|
3806
3853
|
videoPath: null,
|
|
3807
3854
|
aiDefects: [],
|
|
3808
|
-
|
|
3855
|
+
// No JUnit window to bound by; if maestro.log was parsed, surface
|
|
3856
|
+
// every entry so the platform's Logs tab is still useful for
|
|
3857
|
+
// pre-test setup / runtime failures.
|
|
3858
|
+
logEntries: allLogEntries.slice(),
|
|
3809
3859
|
failureMessage: null,
|
|
3810
3860
|
failureType: null,
|
|
3811
3861
|
subflowRefs: meta.subflowRefs,
|