@testrelic/playwright-analytics 2.1.4 → 2.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/README.md +3 -1
- package/dist/cli.cjs +3 -1
- package/dist/index.cjs +170 -30
- 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 +170 -30
- package/dist/index.js.map +1 -1
- package/dist/merge.cjs +1 -1
- package/dist/merge.cjs.map +1 -1
- package/dist/merge.js +1 -1
- package/dist/merge.js.map +1 -1
- package/package.json +2 -2
- package/timeline-schema.json +51 -1
package/dist/merge.cjs
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
'use strict';var crypto=require('crypto'),fs=require('fs'),path=require('path'),core=require('@testrelic/core');async function k(
|
|
1
|
+
'use strict';var crypto=require('crypto'),fs=require('fs'),path=require('path'),core=require('@testrelic/core');async function k(f,a){let s=[];for(let t of f){let e;try{e=fs.readFileSync(t,"utf-8");}catch(o){throw core.createError(core.ErrorCode.MERGE_READ_FAILED,`Failed to read file: ${t}`,o)}let n;try{n=JSON.parse(e);}catch(o){throw core.createError(core.ErrorCode.MERGE_INVALID_SCHEMA,`Invalid JSON in file: ${t}`,o)}if(!core.isValidTestRunReport(n))throw core.createError(core.ErrorCode.MERGE_INVALID_SCHEMA,`Invalid report schema in file: ${t}`);s.push(n);}let p=s.map(t=>t.testRunId),i=[];for(let t of s)i.push(...t.timeline);i.sort((t,e)=>{let n="visitedAt"in t?t.visitedAt:t.timestamp,o="visitedAt"in e?e.visitedAt:e.timestamp;return new Date(n).getTime()-new Date(o).getTime()});let d=v(s,i.length),m=s.reduce((t,e)=>e.startedAt<t?e.startedAt:t,s[0]?.startedAt??new Date().toISOString()),l=s.reduce((t,e)=>e.completedAt>t?e.completedAt:t,s[0]?.completedAt??new Date().toISOString()),c=new Date(l).getTime()-new Date(m).getTime(),u={schemaVersion:s[0]?.schemaVersion??"1.0.0",testRunId:a.testRunId??crypto.randomUUID(),startedAt:m,completedAt:l,totalDuration:c,summary:d,ci:s.find(t=>t.ci!==null)?.ci??null,metadata:s.find(t=>t.metadata!==null)?.metadata??null,timeline:i,shardRunIds:p},y=path.dirname(a.output);return fs.mkdirSync(y,{recursive:true}),fs.writeFileSync(a.output,JSON.stringify(u,null,2),"utf-8"),u}function v(f,a){let s=0,p=0,i=0,d=0,m=0,l=0,c=0,u=0,y=0,t=0,e=0,n=new Set,o=new Set,S={},g={"2xx":0,"3xx":0,"4xx":0,"5xx":0,error:0};for(let r of f)s+=r.summary.total,p+=r.summary.passed,i+=r.summary.failed,d+=r.summary.flaky,m+=r.summary.skipped,l+=r.summary.timedout??0,c+=r.summary.totalApiCalls??0,u+=r.summary.totalAssertions??0,y+=r.summary.passedAssertions??0,t+=r.summary.failedAssertions??0,e+=r.summary.totalNavigations??0;return {total:s,passed:p,failed:i,flaky:d,skipped:m,timedout:l,totalApiCalls:c,uniqueApiUrls:n.size,apiCallsByMethod:S,apiCallsByStatusRange:g,apiResponseTime:null,totalAssertions:u,passedAssertions:y,failedAssertions:t,totalNavigations:e,uniqueNavigationUrls:o.size,totalTimelineSteps:a,totalActionSteps:0,actionStepsByCategory:{}}}exports.mergeReports=k;//# sourceMappingURL=merge.cjs.map
|
|
2
2
|
//# sourceMappingURL=merge.cjs.map
|
package/dist/merge.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/merge.ts"],"names":["mergeReports","files","options","reports","file","raw","readFileSync","err","createError","ErrorCode","parsed","isValidTestRunReport","shardRunIds","r","allTimelines","report","a","b","timeA","timeB","summary","recalculateSummary","startedAt","earliest","completedAt","latest","totalDuration","merged","randomUUID","dir","dirname","mkdirSync","writeFileSync","timelineLength","total","passed","failed","flaky","skipped","timedout","totalApiCalls","totalAssertions","passedAssertions","failedAssertions","totalNavigations","apiUrlSet","navUrlSet","methodCounts","statusRanges"],"mappings":"gHAmBA,eAAsBA,CAAAA,CACpBC,EACAC,CAAAA,CACwB,CACxB,IAAMC,CAAAA,CAA2B,
|
|
1
|
+
{"version":3,"sources":["../src/merge.ts"],"names":["mergeReports","files","options","reports","file","raw","readFileSync","err","createError","ErrorCode","parsed","isValidTestRunReport","shardRunIds","r","allTimelines","report","a","b","timeA","timeB","summary","recalculateSummary","startedAt","earliest","completedAt","latest","totalDuration","merged","randomUUID","dir","dirname","mkdirSync","writeFileSync","timelineLength","total","passed","failed","flaky","skipped","timedout","totalApiCalls","totalAssertions","passedAssertions","failedAssertions","totalNavigations","apiUrlSet","navUrlSet","methodCounts","statusRanges"],"mappings":"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","file":"merge.cjs","sourcesContent":["/**\n * @testrelic/playwright-analytics/merge\n *\n * Merges multiple shard report files into a single unified timeline.\n * Tree-shakeable: separate entry point from the main reporter.\n */\n\nimport { randomUUID } from 'node:crypto';\nimport { readFileSync, writeFileSync, mkdirSync } from 'node:fs';\nimport { dirname } from 'node:path';\nimport type {\n TestRunReport,\n MergeOptions,\n Summary,\n TimelineEntry,\n TimelineStep,\n} from '@testrelic/core';\nimport { isValidTestRunReport, createError, ErrorCode } from '@testrelic/core';\n\nexport async function mergeReports(\n files: string[],\n options: MergeOptions,\n): Promise<TestRunReport> {\n const reports: TestRunReport[] = [];\n\n for (const file of files) {\n let raw: string;\n try {\n raw = readFileSync(file, 'utf-8');\n } catch (err) {\n throw createError(\n ErrorCode.MERGE_READ_FAILED,\n `Failed to read file: ${file}`,\n err,\n );\n }\n\n let parsed: unknown;\n try {\n parsed = JSON.parse(raw);\n } catch (err) {\n throw createError(\n ErrorCode.MERGE_INVALID_SCHEMA,\n `Invalid JSON in file: ${file}`,\n err,\n );\n }\n\n if (!isValidTestRunReport(parsed)) {\n throw createError(\n ErrorCode.MERGE_INVALID_SCHEMA,\n `Invalid report schema in file: ${file}`,\n );\n }\n\n reports.push(parsed);\n }\n\n // Collect shard IDs\n const shardRunIds = reports.map((r) => r.testRunId);\n\n // Merge timelines chronologically\n const allTimelines: (TimelineEntry | TimelineStep)[] = [];\n for (const report of reports) {\n allTimelines.push(...report.timeline);\n }\n allTimelines.sort((a, b) => {\n const timeA = 'visitedAt' in a ? a.visitedAt : a.timestamp;\n const timeB = 'visitedAt' in b ? b.visitedAt : b.timestamp;\n return new Date(timeA).getTime() - new Date(timeB).getTime();\n });\n\n // Recalculate summary across all shards\n const summary = recalculateSummary(reports, allTimelines.length);\n\n // Compute timing\n const startedAt = reports.reduce(\n (earliest, r) => (r.startedAt < earliest ? r.startedAt : earliest),\n reports[0]?.startedAt ?? new Date().toISOString(),\n );\n const completedAt = reports.reduce(\n (latest, r) => (r.completedAt > latest ? r.completedAt : latest),\n reports[0]?.completedAt ?? new Date().toISOString(),\n );\n const totalDuration = new Date(completedAt).getTime() - new Date(startedAt).getTime();\n\n const merged: TestRunReport = {\n schemaVersion: reports[0]?.schemaVersion ?? '1.0.0',\n testRunId: options.testRunId ?? randomUUID(),\n startedAt,\n completedAt,\n totalDuration,\n summary,\n ci: reports.find((r) => r.ci !== null)?.ci ?? null,\n metadata: reports.find((r) => r.metadata !== null)?.metadata ?? null,\n timeline: allTimelines,\n shardRunIds,\n };\n\n // Write to disk\n const dir = dirname(options.output);\n mkdirSync(dir, { recursive: true });\n writeFileSync(options.output, JSON.stringify(merged, null, 2), 'utf-8');\n\n return merged;\n}\n\nfunction recalculateSummary(reports: TestRunReport[], timelineLength: number): Summary {\n let total = 0;\n let passed = 0;\n let failed = 0;\n let flaky = 0;\n let skipped = 0;\n let timedout = 0;\n let totalApiCalls = 0;\n let totalAssertions = 0;\n let passedAssertions = 0;\n let failedAssertions = 0;\n let totalNavigations = 0;\n const apiUrlSet = new Set<string>();\n const navUrlSet = new Set<string>();\n const methodCounts: Record<string, number> = {};\n const statusRanges = { '2xx': 0, '3xx': 0, '4xx': 0, '5xx': 0, error: 0 };\n const allResponseTimes: number[] = [];\n\n for (const report of reports) {\n total += report.summary.total;\n passed += report.summary.passed;\n failed += report.summary.failed;\n flaky += report.summary.flaky;\n skipped += report.summary.skipped;\n timedout += report.summary.timedout ?? 0;\n totalApiCalls += report.summary.totalApiCalls ?? 0;\n totalAssertions += report.summary.totalAssertions ?? 0;\n passedAssertions += report.summary.passedAssertions ?? 0;\n failedAssertions += report.summary.failedAssertions ?? 0;\n totalNavigations += report.summary.totalNavigations ?? 0;\n }\n\n return {\n total, passed, failed, flaky, skipped, timedout,\n totalApiCalls, uniqueApiUrls: apiUrlSet.size,\n apiCallsByMethod: methodCounts,\n apiCallsByStatusRange: statusRanges,\n apiResponseTime: null,\n totalAssertions, passedAssertions, failedAssertions,\n totalNavigations, uniqueNavigationUrls: navUrlSet.size,\n totalTimelineSteps: timelineLength,\n totalActionSteps: 0,\n actionStepsByCategory: {},\n };\n}\n"]}
|
package/dist/merge.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import {randomUUID}from'crypto';import {readFileSync,mkdirSync,writeFileSync}from'fs';import {dirname}from'path';import {createError,ErrorCode,isValidTestRunReport}from'@testrelic/core';async function k(
|
|
1
|
+
import {randomUUID}from'crypto';import {readFileSync,mkdirSync,writeFileSync}from'fs';import {dirname}from'path';import {createError,ErrorCode,isValidTestRunReport}from'@testrelic/core';async function k(f,a){let s=[];for(let t of f){let e;try{e=readFileSync(t,"utf-8");}catch(o){throw createError(ErrorCode.MERGE_READ_FAILED,`Failed to read file: ${t}`,o)}let n;try{n=JSON.parse(e);}catch(o){throw createError(ErrorCode.MERGE_INVALID_SCHEMA,`Invalid JSON in file: ${t}`,o)}if(!isValidTestRunReport(n))throw createError(ErrorCode.MERGE_INVALID_SCHEMA,`Invalid report schema in file: ${t}`);s.push(n);}let p=s.map(t=>t.testRunId),i=[];for(let t of s)i.push(...t.timeline);i.sort((t,e)=>{let n="visitedAt"in t?t.visitedAt:t.timestamp,o="visitedAt"in e?e.visitedAt:e.timestamp;return new Date(n).getTime()-new Date(o).getTime()});let d=v(s,i.length),m=s.reduce((t,e)=>e.startedAt<t?e.startedAt:t,s[0]?.startedAt??new Date().toISOString()),l=s.reduce((t,e)=>e.completedAt>t?e.completedAt:t,s[0]?.completedAt??new Date().toISOString()),c=new Date(l).getTime()-new Date(m).getTime(),u={schemaVersion:s[0]?.schemaVersion??"1.0.0",testRunId:a.testRunId??randomUUID(),startedAt:m,completedAt:l,totalDuration:c,summary:d,ci:s.find(t=>t.ci!==null)?.ci??null,metadata:s.find(t=>t.metadata!==null)?.metadata??null,timeline:i,shardRunIds:p},y=dirname(a.output);return mkdirSync(y,{recursive:true}),writeFileSync(a.output,JSON.stringify(u,null,2),"utf-8"),u}function v(f,a){let s=0,p=0,i=0,d=0,m=0,l=0,c=0,u=0,y=0,t=0,e=0,n=new Set,o=new Set,S={},g={"2xx":0,"3xx":0,"4xx":0,"5xx":0,error:0};for(let r of f)s+=r.summary.total,p+=r.summary.passed,i+=r.summary.failed,d+=r.summary.flaky,m+=r.summary.skipped,l+=r.summary.timedout??0,c+=r.summary.totalApiCalls??0,u+=r.summary.totalAssertions??0,y+=r.summary.passedAssertions??0,t+=r.summary.failedAssertions??0,e+=r.summary.totalNavigations??0;return {total:s,passed:p,failed:i,flaky:d,skipped:m,timedout:l,totalApiCalls:c,uniqueApiUrls:n.size,apiCallsByMethod:S,apiCallsByStatusRange:g,apiResponseTime:null,totalAssertions:u,passedAssertions:y,failedAssertions:t,totalNavigations:e,uniqueNavigationUrls:o.size,totalTimelineSteps:a,totalActionSteps:0,actionStepsByCategory:{}}}export{k as mergeReports};//# sourceMappingURL=merge.js.map
|
|
2
2
|
//# sourceMappingURL=merge.js.map
|
package/dist/merge.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/merge.ts"],"names":["mergeReports","files","options","reports","file","raw","readFileSync","err","createError","ErrorCode","parsed","isValidTestRunReport","shardRunIds","r","allTimelines","report","a","b","timeA","timeB","summary","recalculateSummary","startedAt","earliest","completedAt","latest","totalDuration","merged","randomUUID","dir","dirname","mkdirSync","writeFileSync","timelineLength","total","passed","failed","flaky","skipped","timedout","totalApiCalls","totalAssertions","passedAssertions","failedAssertions","totalNavigations","apiUrlSet","navUrlSet","methodCounts","statusRanges"],"mappings":"0LAmBA,eAAsBA,CAAAA,CACpBC,EACAC,CAAAA,CACwB,CACxB,IAAMC,CAAAA,CAA2B,
|
|
1
|
+
{"version":3,"sources":["../src/merge.ts"],"names":["mergeReports","files","options","reports","file","raw","readFileSync","err","createError","ErrorCode","parsed","isValidTestRunReport","shardRunIds","r","allTimelines","report","a","b","timeA","timeB","summary","recalculateSummary","startedAt","earliest","completedAt","latest","totalDuration","merged","randomUUID","dir","dirname","mkdirSync","writeFileSync","timelineLength","total","passed","failed","flaky","skipped","timedout","totalApiCalls","totalAssertions","passedAssertions","failedAssertions","totalNavigations","apiUrlSet","navUrlSet","methodCounts","statusRanges"],"mappings":"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","file":"merge.js","sourcesContent":["/**\n * @testrelic/playwright-analytics/merge\n *\n * Merges multiple shard report files into a single unified timeline.\n * Tree-shakeable: separate entry point from the main reporter.\n */\n\nimport { randomUUID } from 'node:crypto';\nimport { readFileSync, writeFileSync, mkdirSync } from 'node:fs';\nimport { dirname } from 'node:path';\nimport type {\n TestRunReport,\n MergeOptions,\n Summary,\n TimelineEntry,\n TimelineStep,\n} from '@testrelic/core';\nimport { isValidTestRunReport, createError, ErrorCode } from '@testrelic/core';\n\nexport async function mergeReports(\n files: string[],\n options: MergeOptions,\n): Promise<TestRunReport> {\n const reports: TestRunReport[] = [];\n\n for (const file of files) {\n let raw: string;\n try {\n raw = readFileSync(file, 'utf-8');\n } catch (err) {\n throw createError(\n ErrorCode.MERGE_READ_FAILED,\n `Failed to read file: ${file}`,\n err,\n );\n }\n\n let parsed: unknown;\n try {\n parsed = JSON.parse(raw);\n } catch (err) {\n throw createError(\n ErrorCode.MERGE_INVALID_SCHEMA,\n `Invalid JSON in file: ${file}`,\n err,\n );\n }\n\n if (!isValidTestRunReport(parsed)) {\n throw createError(\n ErrorCode.MERGE_INVALID_SCHEMA,\n `Invalid report schema in file: ${file}`,\n );\n }\n\n reports.push(parsed);\n }\n\n // Collect shard IDs\n const shardRunIds = reports.map((r) => r.testRunId);\n\n // Merge timelines chronologically\n const allTimelines: (TimelineEntry | TimelineStep)[] = [];\n for (const report of reports) {\n allTimelines.push(...report.timeline);\n }\n allTimelines.sort((a, b) => {\n const timeA = 'visitedAt' in a ? a.visitedAt : a.timestamp;\n const timeB = 'visitedAt' in b ? b.visitedAt : b.timestamp;\n return new Date(timeA).getTime() - new Date(timeB).getTime();\n });\n\n // Recalculate summary across all shards\n const summary = recalculateSummary(reports, allTimelines.length);\n\n // Compute timing\n const startedAt = reports.reduce(\n (earliest, r) => (r.startedAt < earliest ? r.startedAt : earliest),\n reports[0]?.startedAt ?? new Date().toISOString(),\n );\n const completedAt = reports.reduce(\n (latest, r) => (r.completedAt > latest ? r.completedAt : latest),\n reports[0]?.completedAt ?? new Date().toISOString(),\n );\n const totalDuration = new Date(completedAt).getTime() - new Date(startedAt).getTime();\n\n const merged: TestRunReport = {\n schemaVersion: reports[0]?.schemaVersion ?? '1.0.0',\n testRunId: options.testRunId ?? randomUUID(),\n startedAt,\n completedAt,\n totalDuration,\n summary,\n ci: reports.find((r) => r.ci !== null)?.ci ?? null,\n metadata: reports.find((r) => r.metadata !== null)?.metadata ?? null,\n timeline: allTimelines,\n shardRunIds,\n };\n\n // Write to disk\n const dir = dirname(options.output);\n mkdirSync(dir, { recursive: true });\n writeFileSync(options.output, JSON.stringify(merged, null, 2), 'utf-8');\n\n return merged;\n}\n\nfunction recalculateSummary(reports: TestRunReport[], timelineLength: number): Summary {\n let total = 0;\n let passed = 0;\n let failed = 0;\n let flaky = 0;\n let skipped = 0;\n let timedout = 0;\n let totalApiCalls = 0;\n let totalAssertions = 0;\n let passedAssertions = 0;\n let failedAssertions = 0;\n let totalNavigations = 0;\n const apiUrlSet = new Set<string>();\n const navUrlSet = new Set<string>();\n const methodCounts: Record<string, number> = {};\n const statusRanges = { '2xx': 0, '3xx': 0, '4xx': 0, '5xx': 0, error: 0 };\n const allResponseTimes: number[] = [];\n\n for (const report of reports) {\n total += report.summary.total;\n passed += report.summary.passed;\n failed += report.summary.failed;\n flaky += report.summary.flaky;\n skipped += report.summary.skipped;\n timedout += report.summary.timedout ?? 0;\n totalApiCalls += report.summary.totalApiCalls ?? 0;\n totalAssertions += report.summary.totalAssertions ?? 0;\n passedAssertions += report.summary.passedAssertions ?? 0;\n failedAssertions += report.summary.failedAssertions ?? 0;\n totalNavigations += report.summary.totalNavigations ?? 0;\n }\n\n return {\n total, passed, failed, flaky, skipped, timedout,\n totalApiCalls, uniqueApiUrls: apiUrlSet.size,\n apiCallsByMethod: methodCounts,\n apiCallsByStatusRange: statusRanges,\n apiResponseTime: null,\n totalAssertions, passedAssertions, failedAssertions,\n totalNavigations, uniqueNavigationUrls: navUrlSet.size,\n totalTimelineSteps: timelineLength,\n totalActionSteps: 0,\n actionStepsByCategory: {},\n };\n}\n"]}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@testrelic/playwright-analytics",
|
|
3
|
-
"version": "2.1
|
|
3
|
+
"version": "2.2.1",
|
|
4
4
|
"description": "Playwright test analytics reporter with E2E navigation tracking, API call capture, network stats, failure diagnostics, and interactive HTML reports",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"playwright",
|
|
@@ -81,7 +81,7 @@
|
|
|
81
81
|
"@playwright/test": ">=1.35.0"
|
|
82
82
|
},
|
|
83
83
|
"dependencies": {
|
|
84
|
-
"@testrelic/core": "2.1
|
|
84
|
+
"@testrelic/core": "2.2.1"
|
|
85
85
|
},
|
|
86
86
|
"devDependencies": {
|
|
87
87
|
"@playwright/test": "^1.35.0",
|
package/timeline-schema.json
CHANGED
|
@@ -74,7 +74,13 @@
|
|
|
74
74
|
"passed": { "type": "integer", "minimum": 0 },
|
|
75
75
|
"failed": { "type": "integer", "minimum": 0 },
|
|
76
76
|
"flaky": { "type": "integer", "minimum": 0 },
|
|
77
|
-
"skipped": { "type": "integer", "minimum": 0 }
|
|
77
|
+
"skipped": { "type": "integer", "minimum": 0 },
|
|
78
|
+
"totalActionSteps": { "type": "integer", "minimum": 0, "description": "Total number of action steps across all tests" },
|
|
79
|
+
"actionStepsByCategory": {
|
|
80
|
+
"type": "object",
|
|
81
|
+
"description": "Action step counts grouped by category (ui_action, assertion, custom_step)",
|
|
82
|
+
"additionalProperties": { "type": "integer", "minimum": 0 }
|
|
83
|
+
}
|
|
78
84
|
}
|
|
79
85
|
},
|
|
80
86
|
"CIMetadata": {
|
|
@@ -150,6 +156,50 @@
|
|
|
150
156
|
{ "$ref": "#/definitions/FailureDiagnostic" },
|
|
151
157
|
{ "type": "null" }
|
|
152
158
|
]
|
|
159
|
+
},
|
|
160
|
+
"actions": {
|
|
161
|
+
"oneOf": [
|
|
162
|
+
{
|
|
163
|
+
"type": "array",
|
|
164
|
+
"items": { "$ref": "#/definitions/ActionStep" },
|
|
165
|
+
"description": "Chronological action steps captured during the test"
|
|
166
|
+
},
|
|
167
|
+
{ "type": "null" }
|
|
168
|
+
]
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
},
|
|
172
|
+
"ActionStep": {
|
|
173
|
+
"type": "object",
|
|
174
|
+
"required": ["title", "category", "timestamp", "duration", "videoOffset", "status", "error", "children"],
|
|
175
|
+
"description": "A single action step captured from Playwright test execution",
|
|
176
|
+
"properties": {
|
|
177
|
+
"title": { "type": "string", "description": "Human-readable description of the action" },
|
|
178
|
+
"category": {
|
|
179
|
+
"type": "string",
|
|
180
|
+
"enum": ["ui_action", "assertion", "custom_step"],
|
|
181
|
+
"description": "Action category: ui_action (pw:api), assertion (expect), custom_step (test.step)"
|
|
182
|
+
},
|
|
183
|
+
"timestamp": { "type": "string", "format": "date-time", "description": "ISO-8601 timestamp when the action started" },
|
|
184
|
+
"duration": { "type": "number", "minimum": 0, "description": "Duration in milliseconds" },
|
|
185
|
+
"videoOffset": {
|
|
186
|
+
"type": ["number", "null"],
|
|
187
|
+
"minimum": 0,
|
|
188
|
+
"description": "Offset in seconds from test start, for video sync. Null when no video artifact exists"
|
|
189
|
+
},
|
|
190
|
+
"status": {
|
|
191
|
+
"type": "string",
|
|
192
|
+
"enum": ["passed", "failed"],
|
|
193
|
+
"description": "Whether the action step passed or failed"
|
|
194
|
+
},
|
|
195
|
+
"error": {
|
|
196
|
+
"type": ["string", "null"],
|
|
197
|
+
"description": "Error message if the action failed"
|
|
198
|
+
},
|
|
199
|
+
"children": {
|
|
200
|
+
"type": "array",
|
|
201
|
+
"items": { "$ref": "#/definitions/ActionStep" },
|
|
202
|
+
"description": "Nested child action steps"
|
|
153
203
|
}
|
|
154
204
|
}
|
|
155
205
|
},
|