@saucelabs/sauce-json-reporter
Advanced tools
Comparing version 2.0.0 to 3.0.0
@@ -18,2 +18,44 @@ type HasKeyOf<T> = { | ||
/** | ||
* JUnitTestCase represents a single, individual testcase in JUnit format. | ||
*/ | ||
export declare class JUnitTestCase { | ||
_name: string; | ||
_status: Status; | ||
_time: number; | ||
_timestamp: string; | ||
_videoTimestamp?: number; | ||
properties?: object; | ||
failure?: object; | ||
skipped?: object; | ||
code?: TestCode; | ||
constructor(name: string, status: Status, time: number, properties: object[], timestamp: string, videoTimestamp?: number, output?: string, code?: TestCode); | ||
} | ||
/** | ||
* JUnitTestSuite represents a testsuite in JUnit format. | ||
*/ | ||
export declare class JUnitTestSuite { | ||
_name: string; | ||
_status: Status; | ||
_tests: number; | ||
_failures: number; | ||
_skipped: number; | ||
_time: number; | ||
properties?: object; | ||
testcase: JUnitTestCase[]; | ||
constructor(name: string, status: Status, properties: object[], testcases: JUnitTestCase[]); | ||
} | ||
/** | ||
* JUnitReport represents a JUnit report. | ||
*/ | ||
export declare class JUnitReport { | ||
_status: Status; | ||
_tests: number; | ||
_failures: number; | ||
_skipped: number; | ||
_time: number; | ||
testsuite: JUnitTestSuite[]; | ||
properties?: object; | ||
constructor(testsuites: JUnitTestSuite[], status: Status, properties: object[]); | ||
} | ||
/** | ||
* TestRun represents the entire test run. | ||
@@ -32,2 +74,4 @@ */ | ||
stringify(computeStatus?: boolean): string; | ||
toJUnitObj(): JUnitReport; | ||
toJUnitFile(filepath: string, computeStatus?: boolean): void; | ||
toFile(filepath: string, computeStatus?: boolean): void; | ||
@@ -52,2 +96,3 @@ } | ||
withTest(name: string, options?: HasKeyOf<Test>): Test; | ||
toJUnitObj(): JUnitTestSuite[]; | ||
} | ||
@@ -69,2 +114,3 @@ /** | ||
attach(attachment: Attachment): void; | ||
toJUnitObj(): JUnitTestCase; | ||
} | ||
@@ -71,0 +117,0 @@ /** |
154
lib/index.js
@@ -26,4 +26,5 @@ "use strict"; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
exports.statusOf = exports.TestCode = exports.Test = exports.Suite = exports.TestRun = exports.Status = void 0; | ||
exports.statusOf = exports.TestCode = exports.Test = exports.Suite = exports.TestRun = exports.JUnitReport = exports.JUnitTestSuite = exports.JUnitTestCase = exports.Status = void 0; | ||
const fs = __importStar(require("fs")); | ||
const fast_xml_parser_1 = require("fast-xml-parser"); | ||
var Status; | ||
@@ -36,2 +37,113 @@ (function (Status) { | ||
/** | ||
* JUnitTestCase represents a single, individual testcase in JUnit format. | ||
*/ | ||
class JUnitTestCase { | ||
_name; | ||
_status; | ||
_time; | ||
_timestamp; | ||
_videoTimestamp; | ||
properties; | ||
failure; | ||
skipped; | ||
code; | ||
constructor(name, status, time, properties, timestamp, videoTimestamp, output, code) { | ||
this._name = name; | ||
this._status = status; | ||
this._time = time; | ||
this._timestamp = timestamp; | ||
this._videoTimestamp = videoTimestamp; | ||
this.code = code; | ||
this.properties = (properties.length > 0) ? { property: properties } : undefined; | ||
if (status === Status.Failed) { | ||
this.failure = { output }; | ||
} | ||
if (status === Status.Skipped) { | ||
this.skipped = { output }; | ||
} | ||
} | ||
} | ||
exports.JUnitTestCase = JUnitTestCase; | ||
/** | ||
* JUnitTestSuite represents a testsuite in JUnit format. | ||
*/ | ||
class JUnitTestSuite { | ||
_name; | ||
_status; | ||
_tests; | ||
_failures; | ||
_skipped; | ||
_time; | ||
properties; | ||
testcase; // to correctly build an array using XMLBuilder, testcase should be kept in the singular form. | ||
constructor(name, status, properties, testcases) { | ||
this._name = name; | ||
this._status = status; | ||
this.testcase = testcases; | ||
this._tests = testcases.length; | ||
this._failures = 0; | ||
this._skipped = 0; | ||
this._time = 0; | ||
this.properties = (properties.length > 0) ? { property: properties } : undefined; | ||
this.testcase.forEach(testcase => { | ||
if (testcase._status === Status.Failed) { | ||
this._failures += 1; | ||
} | ||
if (testcase._status === Status.Skipped) { | ||
this._skipped += 1; | ||
} | ||
this._time += testcase._time; | ||
}); | ||
} | ||
} | ||
exports.JUnitTestSuite = JUnitTestSuite; | ||
/** | ||
* JUnitReport represents a JUnit report. | ||
*/ | ||
class JUnitReport { | ||
_status; | ||
_tests; | ||
_failures; | ||
_skipped; | ||
_time; | ||
testsuite; // to correctly build an array using XMLBuilder, testsuite should be kept in the singular form. | ||
properties; | ||
constructor(testsuites, status, properties) { | ||
this.testsuite = testsuites; | ||
this._status = status; | ||
this._failures = 0; | ||
this._skipped = 0; | ||
this._tests = 0; | ||
this._time = 0; | ||
this.properties = (properties.length > 0) ? { property: properties } : undefined; | ||
this.testsuite.forEach(testsuite => { | ||
this._failures += testsuite._failures; | ||
this._skipped += testsuite._skipped; | ||
this._tests += testsuite._tests; | ||
this._time += testsuite._time; | ||
}); | ||
} | ||
} | ||
exports.JUnitReport = JUnitReport; | ||
/** | ||
* toProperty converts attachments and metadata to JUnit properties. | ||
*/ | ||
function toProperty(attachments, metadata) { | ||
const properties = []; | ||
attachments.forEach(attachment => { | ||
properties.push({ | ||
_name: 'attachment', | ||
_value: attachment.name, | ||
'#text': attachment.path, | ||
}); | ||
}); | ||
Object.entries(metadata).forEach(([key, value]) => { | ||
properties.push({ | ||
_name: key, | ||
_value: value, | ||
}); | ||
}); | ||
return properties; | ||
} | ||
/** | ||
* TestRun represents the entire test run. | ||
@@ -77,2 +189,24 @@ */ | ||
} | ||
toJUnitObj() { | ||
const testsuites = []; | ||
this.suites.forEach(suite => { | ||
testsuites.push(...suite.toJUnitObj()); | ||
}); | ||
return new JUnitReport(testsuites, this.status, toProperty(this.attachments, this.metadata)); | ||
} | ||
toJUnitFile(filepath, computeStatus = true) { | ||
if (computeStatus) { | ||
this.computeStatus(); | ||
} | ||
const options = { | ||
ignoreAttributes: false, | ||
attributeNamePrefix: "_", | ||
format: true, | ||
suppressEmptyNode: true, | ||
cdataPropName: "output", | ||
}; | ||
const builder = new fast_xml_parser_1.XMLBuilder(options); | ||
const xml = builder.build({ testsuites: this.toJUnitObj() }); | ||
fs.writeFileSync(filepath, xml); | ||
} | ||
toFile(filepath, computeStatus = true) { | ||
@@ -136,2 +270,15 @@ if (computeStatus) { | ||
} | ||
toJUnitObj() { | ||
const testcases = []; | ||
this.tests.forEach(test => { | ||
testcases.push(test.toJUnitObj()); | ||
}); | ||
const suites = [ | ||
new JUnitTestSuite(this.name, this.status, toProperty(this.attachments, this.metadata), testcases) | ||
]; | ||
this.suites.forEach(suite => { | ||
suites.push(...suite.toJUnitObj()); | ||
}); | ||
return suites; | ||
} | ||
} | ||
@@ -166,2 +313,7 @@ exports.Suite = Suite; | ||
} | ||
toJUnitObj() { | ||
return new JUnitTestCase(this.name, this.status, this.duration, toProperty(this.attachments || [], this.metadata), | ||
// startTime should be a string in this case. Otherwise, XMLBuilder will not recognize the attribute name prefix. | ||
this.startTime.toISOString(), this.videoTimestamp, this.output, this.code); | ||
} | ||
} | ||
@@ -168,0 +320,0 @@ exports.Test = Test; |
{ | ||
"name": "@saucelabs/sauce-json-reporter", | ||
"version": "2.0.0", | ||
"version": "3.0.0", | ||
"description": "", | ||
@@ -29,2 +29,3 @@ "main": "lib/index.js", | ||
"eslint": "8.39.0", | ||
"fast-xml-parser": "4.2.4", | ||
"jest": "29.5.0", | ||
@@ -31,0 +32,0 @@ "json-schema-library": "7.4.7", |
114
README.md
@@ -13,16 +13,37 @@ # sauce-json-reporter-js | ||
let r = new TestRun() | ||
r.attach({name: 'screenshot.png', path:'./screenshot.png'}) | ||
const s1 = r.withSuite('somegroup') | ||
const s2 = s1.withSuite('somefile.test.js') | ||
s1.attach({name: 'screenshot1.png', path: './screenshot1.png'}) | ||
s2.withTest('yay', { | ||
status: Status.Passed, | ||
duration: 123, | ||
attachments: [ | ||
{name: 'video.mp4', path: './video.mp4'}, | ||
{name: 'screenshot2.png', path: './screenshot2.png'} | ||
], | ||
}) | ||
s2.withTest('nay', { | ||
status: Status.Failed, | ||
output: 'test failed', | ||
duration: 123, | ||
attachments: [ | ||
{name: 'video.mp4', path: './video.mp4'}, | ||
] | ||
}) | ||
s2.withTest('oops', { | ||
status: Status.Skipped, | ||
output: 'test skipped', | ||
duration: 123, | ||
}) | ||
r.metadata = { | ||
'id': '123' | ||
} | ||
r.stringify() // returns a JSON string representing the entire test run | ||
// or | ||
r.toFile('myreport.json') // writes the JSON to a file instead | ||
r.toFile('my_json_report.json') // writes the JSON to a file instead | ||
// or | ||
r.toJUnitFile('my_junit_report.xml') // converts and writes the result to a JUnit file | ||
``` | ||
@@ -35,3 +56,8 @@ | ||
"status": "failed", | ||
"attachments": [], | ||
"attachments": [ | ||
{ | ||
"name": "screenshot.png", | ||
"path": "./screenshot.png" | ||
} | ||
], | ||
"suites": [ | ||
@@ -41,2 +67,3 @@ { | ||
"status": "failed", | ||
"metadata": {}, | ||
"suites": [ | ||
@@ -46,2 +73,3 @@ { | ||
"status": "failed", | ||
"metadata": {}, | ||
"suites": [], | ||
@@ -53,5 +81,15 @@ "attachments": [], | ||
"status": "passed", | ||
"startTime": "2021-11-09T21:10:59.550Z", | ||
"duration": 123, | ||
"attachments": [] | ||
"startTime": "2023-06-19T18:26:06.227Z", | ||
"attachments": [ | ||
{ | ||
"name": "video.mp4", | ||
"path": "./video.mp4" | ||
}, | ||
{ | ||
"name": "screenshot2.png", | ||
"path": "./screenshot2.png" | ||
} | ||
], | ||
"metadata": {} | ||
}, | ||
@@ -61,5 +99,21 @@ { | ||
"status": "failed", | ||
"startTime": "2021-11-09T21:10:59.550Z", | ||
"duration": 123, | ||
"attachments": [] | ||
"output": "test failed", | ||
"startTime": "2023-06-19T18:26:06.227Z", | ||
"attachments": [ | ||
{ | ||
"name": "video.mp4", | ||
"path": "./video.mp4" | ||
} | ||
], | ||
"metadata": {} | ||
}, | ||
{ | ||
"name": "oops", | ||
"status": "skipped", | ||
"duration": 123, | ||
"output": "test skipped", | ||
"startTime": "2023-06-19T18:26:06.227Z", | ||
"attachments": [], | ||
"metadata": {} | ||
} | ||
@@ -69,7 +123,51 @@ ] | ||
], | ||
"attachments": [], | ||
"attachments": [ | ||
{ | ||
"name": "screenshot1.png", | ||
"path": "./screenshot1.png" | ||
} | ||
], | ||
"tests": [] | ||
} | ||
] | ||
], | ||
"metadata": { | ||
"id": "123" | ||
} | ||
} | ||
``` | ||
The resulting JUnit file of the above example is: | ||
``` | ||
<testsuites status="failed" tests="3" failures="1" skipped="1" time="369"> | ||
<testsuite name="somegroup" status="failed" tests="0" failures="0" skipped="0" time="0"> | ||
<properties> | ||
<property name="attachment" value="screenshot1.png">./screenshot1.png</property> | ||
</properties> | ||
</testsuite> | ||
<testsuite name="somefile.test.js" status="failed" tests="3" failures="1" skipped="1" time="369"> | ||
<testcase name="yay" status="passed" time="123" timestamp="2023-06-20T21:32:07.467Z"> | ||
<properties> | ||
<property name="attachment" value="video.mp4">./video.mp4</property> | ||
<property name="attachment" value="screenshot2.png">./screenshot2.png</property> | ||
</properties> | ||
</testcase> | ||
<testcase name="nay" status="failed" time="123" timestamp="2023-06-20T21:32:07.467Z"> | ||
<properties> | ||
<property name="attachment" value="video.mp4">./video.mp4</property> | ||
</properties> | ||
<failure> | ||
<![CDATA[test failed]]> | ||
</failure> | ||
</testcase> | ||
<testcase name="oops" status="skipped" time="123" timestamp="2023-06-20T21:32:07.467Z"> | ||
<skipped> | ||
<![CDATA[test skipped]]> | ||
</skipped> | ||
</testcase> | ||
</testsuite> | ||
<properties> | ||
<property name="attachment" value="screenshot.png">./screenshot.png</property> | ||
<property name="id" value="123"/> | ||
</properties> | ||
</testsuites> | ||
``` |
Sorry, the diff of this file is not supported yet
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
Major refactor
Supply chain riskPackage has recently undergone a major refactor. It may be unstable or indicate significant internal changes. Use caution when updating to versions that include significant changes.
Found 1 instance in 1 package
23944
478
166
0
12