diff --git a/.travis.yml b/.travis.yml
index 271d9ca..317b8f6 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -1,12 +1,10 @@
language: node_js
node_js:
- - "8"
- "10"
- "12"
+ - "13"
+ - "14"
env:
- - JEST_VERSION=^22.0.0
- - JEST_VERSION=^23.0.0
- - JEST_VERSION=^24.0.0
- - JEST_VERSION=^24.9.0
+ - JEST_VERSION=^26.0.0
script:
- npm run test:ci
diff --git a/README.md b/README.md
index 9b98a85..d5c65bf 100644
--- a/README.md
+++ b/README.md
@@ -3,6 +3,8 @@
# jest-junit
A Jest reporter that creates compatible junit xml files
+Note: as of jest-junit 11.0.0 NodeJS >= 10.12.0 is required.
+
## Installation
```shell
yarn add --dev jest-junit
@@ -217,3 +219,28 @@ renders
```
+
+#### Adding custom testsuite properties
+New feature as of jest-junit 11.0.0!
+
+Create a file in your project root directory named junitProperties.js:
+```js
+module.exports = () => {
+ return {
+ key: "value"
+ }
+});
+```
+
+Will render
+```xml
+
+
+
+
+
+
+
+
+
+```
diff --git a/__tests__/__snapshots__/buildJsonResults.test.js.snap b/__tests__/__snapshots__/buildJsonResults.test.js.snap
index 5e8a628..9eb31b9 100644
--- a/__tests__/__snapshots__/buildJsonResults.test.js.snap
+++ b/__tests__/__snapshots__/buildJsonResults.test.js.snap
@@ -24,6 +24,18 @@ Object {
"timestamp": "2018-02-10T14:52:31",
},
},
+ Object {
+ "properties": Array [
+ Object {
+ "property": Object {
+ "_attr": Object {
+ "name": "best-tester",
+ "value": "Jason Palmer",
+ },
+ },
+ },
+ ],
+ },
Object {
"testcase": Array [
Object {
@@ -50,6 +62,18 @@ Object {
"timestamp": "2018-02-10T14:52:31",
},
},
+ Object {
+ "properties": Array [
+ Object {
+ "property": Object {
+ "_attr": Object {
+ "name": "best-tester",
+ "value": "Jason Palmer",
+ },
+ },
+ },
+ ],
+ },
Object {
"testcase": Array [
Object {
diff --git a/__tests__/buildJsonResults.test.js b/__tests__/buildJsonResults.test.js
index 163e09f..6e95680 100644
--- a/__tests__/buildJsonResults.test.js
+++ b/__tests__/buildJsonResults.test.js
@@ -52,7 +52,8 @@ describe('buildJsonResults', () => {
Object.assign({}, constants.DEFAULT_OPTIONS, {
classNameTemplate: "{filename}"
}));
- expect(jsonResults.testsuites[1].testsuite[1].testcase[0]._attr.classname).toBe('foo.test.js');
+
+ expect(jsonResults.testsuites[1].testsuite[2].testcase[0]._attr.classname).toBe('foo.test.js');
});
it('should support return the function result when classNameTemplate is a function', () => {
@@ -63,7 +64,7 @@ describe('buildJsonResults', () => {
return 'function called with vars: ' + Object.keys(vars).join(', ');
}
}));
- expect(jsonResults.testsuites[1].testsuite[1].testcase[0]._attr.classname)
+ expect(jsonResults.testsuites[1].testsuite[2].testcase[0]._attr.classname)
.toBe('function called with vars: filepath, filename, classname, title, displayName');
});
@@ -73,7 +74,7 @@ describe('buildJsonResults', () => {
Object.assign({}, constants.DEFAULT_OPTIONS, {
titleTemplate: "{filepath}"
}));
- expect(jsonResults.testsuites[1].testsuite[1].testcase[0]._attr.name).toBe('path/to/test/__tests__/foo.test.js');
+ expect(jsonResults.testsuites[1].testsuite[2].testcase[0]._attr.name).toBe('path/to/test/__tests__/foo.test.js');
});
it('should return the proper filepath when suiteNameTemplate is "{filepath}" and usePathForSuiteName is "false"', () => {
@@ -116,7 +117,7 @@ describe('buildJsonResults', () => {
const noFailingTestsReport = require('../__mocks__/no-failing-tests.json');
const jsonResults = buildJsonResults(noFailingTestsReport, '/',
Object.assign({}, constants.DEFAULT_OPTIONS));
- expect(jsonResults.testsuites[1].testsuite[1].testcase[0]._attr.classname).toBe('foo baz should bar');
+ expect(jsonResults.testsuites[1].testsuite[2].testcase[0]._attr.classname).toBe('foo baz should bar');
});
it('should return the proper classname when ancestorSeparator is customized', () => {
@@ -125,14 +126,14 @@ describe('buildJsonResults', () => {
Object.assign({}, constants.DEFAULT_OPTIONS, {
ancestorSeparator: " › "
}));
- expect(jsonResults.testsuites[1].testsuite[1].testcase[0]._attr.classname).toBe('foo › baz should bar');
+ expect(jsonResults.testsuites[1].testsuite[2].testcase[0]._attr.classname).toBe('foo › baz should bar');
});
it('should parse failure messages for failing tests', () => {
const failingTestsReport = require('../__mocks__/failing-tests.json');
const jsonResults = buildJsonResults(failingTestsReport, '/path/to/test', constants.DEFAULT_OPTIONS);
- const failureMsg = jsonResults.testsuites[1].testsuite[1].testcase[1].failure;
+ const failureMsg = jsonResults.testsuites[1].testsuite[2].testcase[1].failure;
// Make sure no escape codes are there that exist in the mock
expect(failureMsg.includes('\u001b')).toBe(false);
@@ -158,7 +159,7 @@ describe('buildJsonResults', () => {
it('should not return the file name by default', () => {
const noFailingTestsReport = require('../__mocks__/no-failing-tests.json');
const jsonResults = buildJsonResults(noFailingTestsReport, '/', constants.DEFAULT_OPTIONS);
- expect(jsonResults.testsuites[1].testsuite[1].testcase[0]._attr.file).toBe(undefined);
+ expect(jsonResults.testsuites[1].testsuite[2].testcase[0]._attr.file).toBe(undefined);
});
it('should return the file name when addFileAttribute is "true"', () => {
@@ -167,7 +168,7 @@ describe('buildJsonResults', () => {
Object.assign({}, constants.DEFAULT_OPTIONS, {
addFileAttribute: "true"
}));
- expect(jsonResults.testsuites[1].testsuite[1].testcase[0]._attr.file).toBe('path/to/test/__tests__/foo.test.js');
+ expect(jsonResults.testsuites[1].testsuite[2].testcase[0]._attr.file).toBe('path/to/test/__tests__/foo.test.js');
});
it('should show output of console if includeConsoleOutput is true', () => {
@@ -189,7 +190,7 @@ describe('buildJsonResults', () => {
expect(jsonResults.testsuites[1].testsuite[1]['system-out']).not.toBeDefined();
});
-
+
it('should show short console output if includeShortConsoleOutput is true', () => {
const reportWithShortConsoleOutput = require('../__mocks__/test-with-console-output.json');
const jsonResults = buildJsonResults(reportWithShortConsoleOutput, '/',
@@ -206,7 +207,7 @@ describe('buildJsonResults', () => {
Object.assign({}, constants.DEFAULT_OPTIONS, {
includeShortConsoleOutput: "false"
}));
-
- expect(jsonResults.testsuites[1].testsuite[1]['system-out']).not.toBeDefined();
+
+ expect(jsonResults.testsuites[1].testsuite[2]['system-out']).not.toBeDefined();
});
});
diff --git a/__tests__/getOptions.test.js b/__tests__/getOptions.test.js
index 9ed5999..48fcb9b 100644
--- a/__tests__/getOptions.test.js
+++ b/__tests__/getOptions.test.js
@@ -6,7 +6,7 @@ const getOptions = require('../utils/getOptions.js');
jest.mock('fs', () => {
return Object.assign(
{},
- require.requireActual('fs'),
+ jest.requireActual('fs'),
{
existsSync: jest.fn().mockReturnValue(true)
}
diff --git a/__tests__/testResultProcessor.test.js b/__tests__/testResultProcessor.test.js
index 7da9194..194b8da 100644
--- a/__tests__/testResultProcessor.test.js
+++ b/__tests__/testResultProcessor.test.js
@@ -3,7 +3,7 @@
jest.mock('mkdirp', () => {
return Object.assign(
{},
- require.requireActual('mkdirp'),
+ jest.requireActual('mkdirp'),
{
sync: jest.fn()
}
@@ -13,7 +13,7 @@ jest.mock('mkdirp', () => {
jest.mock('fs', () => {
return Object.assign(
{},
- require.requireActual('fs'),
+ jest.requireActual('fs'),
{
writeFileSync: jest.fn()
}
diff --git a/constants/index.js b/constants/index.js
index d40b320..a0bd786 100644
--- a/constants/index.js
+++ b/constants/index.js
@@ -14,6 +14,7 @@ module.exports = {
JEST_JUNIT_INCLUDE_CONSOLE_OUTPUT: 'includeConsoleOutput',
JEST_JUNIT_INCLUDE_SHORT_CONSOLE_OUTPUT: 'includeShortConsoleOutput',
JEST_USE_PATH_FOR_SUITE_NAME: 'usePathForSuiteName',
+ JEST_JUNIT_TEST_SUITE_PROPERTIES_JSON_FILE: 'testSuitePropertiesFile'
},
DEFAULT_OPTIONS: {
suiteName: 'jest tests',
@@ -27,7 +28,8 @@ module.exports = {
usePathForSuiteName: 'false',
addFileAttribute: 'false',
includeConsoleOutput: 'false',
- includeShortConsoleOutput: 'false'
+ includeShortConsoleOutput: 'false',
+ testSuitePropertiesFile: 'junitProperties.js'
},
CLASSNAME_VAR: 'classname',
FILENAME_VAR: 'filename',
diff --git a/jest.config.js b/jest.config.js
index 214e2f7..b26860f 100644
--- a/jest.config.js
+++ b/jest.config.js
@@ -12,5 +12,6 @@ module.exports = {
"/integration-tests/testResultsProcessor/",
"/integration-tests/reporter/"
],
- setupFilesAfterEnv: ["/__tests__/lib/setupTests.js"]
+ setupFilesAfterEnv: ["/__tests__/lib/setupTests.js"],
+ reporters: ['default', '.']
};
diff --git a/junitProperties.js b/junitProperties.js
new file mode 100644
index 0000000..b4f911f
--- /dev/null
+++ b/junitProperties.js
@@ -0,0 +1,5 @@
+module.exports = (suite) => {
+ return {
+ 'best-tester': 'Jason Palmer'
+ }
+}
diff --git a/package.json b/package.json
index 02efc8f..ce890db 100644
--- a/package.json
+++ b/package.json
@@ -1,13 +1,13 @@
{
"name": "jest-junit",
- "version": "10.0.0",
+ "version": "11.0.0",
"description": "A jest reporter that generates junit xml files",
"main": "index.js",
"repository": "https://github.com/jest-community/jest-junit",
"author": "Jason Palmer",
"license": "Apache-2.0",
"engines": {
- "node": ">=8.0.0"
+ "node": ">=10.12.0"
},
"files": [
"index.js",
@@ -21,7 +21,7 @@
},
"dependencies": {
"jest-validate": "^24.9.0",
- "mkdirp": "^0.5.1",
+ "mkdirp": "^1.0.4",
"strip-ansi": "^5.2.0",
"uuid": "^3.3.3",
"xml": "^1.0.1"
diff --git a/utils/buildJsonResults.js b/utils/buildJsonResults.js
index 87a0fd1..ffcba9d 100644
--- a/utils/buildJsonResults.js
+++ b/utils/buildJsonResults.js
@@ -3,7 +3,7 @@
const stripAnsi = require('strip-ansi');
const constants = require('../constants/index');
const path = require('path');
-
+const fs = require('fs');
// Wrap the varName with template tags
const toTemplateTag = function (varName) {
@@ -35,6 +35,10 @@ const executionTime = function (startTime, endTime) {
}
module.exports = function (report, appDirectory, options) {
+ // Check if there is a junitProperties.js (or whatever they called it)
+ const junitSuitePropertiesFilePath = path.join(process.cwd(), options.testSuitePropertiesFile);
+ let ignoreSuitePropertiesCheck = !fs.existsSync(junitSuitePropertiesFilePath);
+
// Generate a single XML file for all jest tests
let jsonResults = {
'testsuites': [{
@@ -114,7 +118,7 @@ module.exports = function (report, appDirectory, options) {
testSuite.testsuite.push(testSuiteConsole);
}
-
+
// Write short stdout console output if available
if (options.includeShortConsoleOutput === 'true' && suite.console && suite.console.length) {
// Extract and then Stringify the console message value
@@ -129,6 +133,30 @@ module.exports = function (report, appDirectory, options) {
testSuite.testsuite.push(testSuiteConsole);
}
+ if (!ignoreSuitePropertiesCheck) {
+ let junitSuiteProperties = require(junitSuitePropertiesFilePath)(suite);
+
+ // Add any test suite properties
+ let testSuitePropertyMain = {
+ 'properties': []
+ };
+
+ Object.keys(junitSuiteProperties).forEach((p) => {
+ let testSuiteProperty = {
+ 'property': {
+ _attr: {
+ name: p,
+ value: replaceVars(junitSuiteProperties[p], suiteNameVariables)
+ }
+ }
+ };
+
+ testSuitePropertyMain.properties.push(testSuiteProperty);
+ });
+
+ testSuite.testsuite.push(testSuitePropertyMain);
+ }
+
// Iterate through test cases
suite.testResults.forEach((tc) => {
const classname = tc.ancestorTitles.join(options.ancestorSeparator);
diff --git a/yarn.lock b/yarn.lock
index 3e0576b..4441354 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -2237,6 +2237,11 @@ minimist@^1.1.1, minimist@^1.2.0:
resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.0.tgz#a35008b20f41383eec1fb914f4cd5df79a264284"
integrity sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=
+minimist@^1.2.5:
+ version "1.2.5"
+ resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.5.tgz#67d66014b66a6a8aaa0c083c5fd58df4e4e97602"
+ integrity sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==
+
minimist@~0.0.1:
version "0.0.10"
resolved "https://registry.yarnpkg.com/minimist/-/minimist-0.0.10.tgz#de3f98543dbf96082be48ad1a0c7cda836301dcf"
@@ -2265,13 +2270,25 @@ mixin-deep@^1.2.0:
for-in "^1.0.2"
is-extendable "^1.0.1"
-mkdirp@^0.5.0, mkdirp@^0.5.1:
+mkdirp@^0.5.0:
version "0.5.1"
resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-0.5.1.tgz#30057438eac6cf7f8c4767f38648d6697d75c903"
integrity sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM=
dependencies:
minimist "0.0.8"
+mkdirp@^0.5.1:
+ version "0.5.5"
+ resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-0.5.5.tgz#d91cefd62d1436ca0f41620e251288d420099def"
+ integrity sha512-NKmAlESf6jMGym1++R0Ra7wvhV+wFW63FaSOFPwRahvea0gMUcGUhVeAg/0BC0wiv9ih5NYPB1Wn1UEI1/L+xQ==
+ dependencies:
+ minimist "^1.2.5"
+
+mkdirp@^1.0.4:
+ version "1.0.4"
+ resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-1.0.4.tgz#3eb5ed62622756d79a5f0e2a221dfebad75c2f7e"
+ integrity sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==
+
ms@2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/ms/-/ms-2.0.0.tgz#5608aeadfc00be6c2901df5f9861788de0d597c8"