Skip to content

Commit

Permalink
feat:
Browse files Browse the repository at this point in the history
- remove node v12, 14, 16 from supported versions
- add support for project names
  • Loading branch information
itereshchenkov committed Jun 23, 2024
1 parent 2fd843b commit 9696c3d
Show file tree
Hide file tree
Showing 6 changed files with 110 additions and 70 deletions.
27 changes: 13 additions & 14 deletions .github/workflows/package.yml
Original file line number Diff line number Diff line change
Expand Up @@ -4,23 +4,22 @@ on: [push]

jobs:
build:

runs-on: ubuntu-latest

strategy:
matrix:
node-version: [12, 14, 16, 18]
node-version: [18, 20]

steps:
- uses: actions/checkout@v3
- name: Use Node.js ${{ matrix.node-version }}
uses: actions/setup-node@v3
with:
node-version: ${{ matrix.node-version }}
- name: npm install, build, and test
run: |
npm ci
npm run build --if-present
npm test
env:
CI: true
- uses: actions/checkout@v4
- name: Use Node.js ${{ matrix.node-version }}
uses: actions/setup-node@v3
with:
node-version: ${{ matrix.node-version }}
- name: npm install, build, and test
run: |
npm ci
npm run build --if-present
npm test
env:
CI: true
10 changes: 8 additions & 2 deletions __tests__/data.json
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,14 @@
"testResults": [
{ "ancestorTitles": ["path2", "to", "test3"], "title": "title3", "status": "passed", "duration": 123 },
{ "ancestorTitles": ["path2", "to", "test4"], "title": "title4", "status": "failed", "duration": 123 },
{ "ancestorTitles": ["path2", "to", "test5"], "title": "title5", "status": "failed", "failureMessages": ["Unexpected exception\n at path/to/file1.js:1\n at path/to/file2.js:2"], "duration": 123 },
{
"ancestorTitles": ["path2", "to", "test5"],
"title": "title5",
"status": "failed",
"failureMessages": ["Unexpected exception\n at path/to/file1.js:1\n at path/to/file2.js:2"],
"duration": 123
},
{ "ancestorTitles": ["path2", "to", "constructor"], "title": "title6", "status": "passed", "duration": 123 }
]
}
]
]
57 changes: 34 additions & 23 deletions __tests__/formatter.js
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,9 @@ const consoleOutput = [
["##teamcity[testSuiteFinished name='test4' flowId='12345']"],
["##teamcity[testSuiteStarted name='test5' flowId='12345']"],
["##teamcity[testStarted name='title5' flowId='12345']"],
["##teamcity[testFailed name='title5' message='Unexpected exception' details='at path/to/file1.js:1|n at path/to/file2.js:2' flowId='12345']"],
[
"##teamcity[testFailed name='title5' message='Unexpected exception' details='at path/to/file1.js:1|n at path/to/file2.js:2' flowId='12345']",
],
["##teamcity[testFinished name='title5' duration='123' flowId='12345']"],
["##teamcity[testSuiteFinished name='test5' flowId='12345']"],
["##teamcity[testSuiteStarted name='constructor' flowId='12345']"],
Expand All @@ -43,7 +45,7 @@ const consoleOutput = [
["##teamcity[testSuiteFinished name='constructor' flowId='12345']"],
["##teamcity[testSuiteFinished name='to' flowId='12345']"],
["##teamcity[testSuiteFinished name='path2' flowId='12345']"],
["##teamcity[testSuiteFinished name='foo/__tests__/file2.js' flowId='12345']"]
["##teamcity[testSuiteFinished name='foo/__tests__/file2.js' flowId='12345']"],
];

describe("jest-teamcity", () => {
Expand All @@ -52,8 +54,8 @@ describe("jest-teamcity", () => {
let formatterFn = formatter.log;

beforeAll(() => {
console.log = jest.fn().mockImplementation(s => s);
const formatterMock = path.sep == "/" ? s => formatterFn(s) : s => formatterFn(s.replace(/\\/g, "/"));
console.log = jest.fn().mockImplementation((s) => s);
const formatterMock = path.sep == "/" ? (s) => formatterFn(s) : (s) => formatterFn(s.replace(/\\/g, "/"));
formatter.log = jest.fn().mockImplementation(formatterMock);
});

Expand All @@ -77,14 +79,14 @@ describe("jest-teamcity", () => {
test("escape", () => {
expect(
formatter.escape(`|test[test2]|
test3`)
test3`),
).toEqual("||test|[test2|]|||ntest3");
});
});

describe("printTestLog", () => {
test("empty tests", () => {
["", null, undefined, {}, [], 0].forEach(data => {
["", null, undefined, {}, [], 0].forEach((data) => {
formatter.printTestLog(data);
expect(console.log.mock.calls).toHaveLength(0);
});
Expand Down Expand Up @@ -118,18 +120,18 @@ test3`)
path: expect.objectContaining({
to: expect.objectContaining({
test1: expect.any(Object),
test2: expect.any(Object)
})
})
test2: expect.any(Object),
}),
}),
},
[file2Key]: {
path2: expect.objectContaining({
to: expect.objectContaining({
test3: expect.any(Object),
test4: expect.any(Object)
})
})
}
test4: expect.any(Object),
}),
}),
},
});
});
});
Expand All @@ -140,19 +142,28 @@ test3`)
});

test("textExecError", () => {
formatter.formatReport([{
"testFilePath": "/Users/spec-with-error/failing.spec.ts",
"testResults": [],
"testExecError": {
"message": "Error:\nSomething bad is happened!",
"stack": "Error: Timeout of 181000 waiting for jest process 168 reached!\nThat means that your test suite, the spec file, took too much time to execute. Try spliting the spec to multiple specs.\n at Timeout._onTimeout (evalmachine.<anonymous>:1901:31)\n at listOnTimeout (internal/timers.js:549:17)\n at processTimers (internal/timers.js:492:7)",
"type": "Error"
}
}], "/Users/spec-with-error", "12345");
formatter.formatReport(
[
{
testFilePath: "/Users/spec-with-error/failing.spec.ts",
testResults: [],
testExecError: {
message: "Error:\nSomething bad is happened!",
stack:
"Error: Timeout of 181000 waiting for jest process 168 reached!\nThat means that your test suite, the spec file, took too much time to execute. Try spliting the spec to multiple specs.\n at Timeout._onTimeout (evalmachine.<anonymous>:1901:31)\n at listOnTimeout (internal/timers.js:549:17)\n at processTimers (internal/timers.js:492:7)",
type: "Error",
},
},
],
"/Users/spec-with-error",
"12345",
);
expect(console.log.mock.calls).toEqual([
["##teamcity[testSuiteStarted name='failing.spec.ts' flowId='12345']"],
["##teamcity[testStarted name='Jest failed to execute suite' flowId='12345']"],
["##teamcity[testFailed name='Jest failed to execute suite' message='Error:|nSomething bad is happened!' details='Error: Timeout of 181000 waiting for jest process 168 reached!|nThat means that your test suite, the spec file, took too much time to execute. Try spliting the spec to multiple specs.|n at Timeout._onTimeout (evalmachine.<anonymous>:1901:31)|n at listOnTimeout (internal/timers.js:549:17)|n at processTimers (internal/timers.js:492:7)' flowId='12345']"],
[
"##teamcity[testFailed name='Jest failed to execute suite' message='Error:|nSomething bad is happened!' details='Error: Timeout of 181000 waiting for jest process 168 reached!|nThat means that your test suite, the spec file, took too much time to execute. Try spliting the spec to multiple specs.|n at Timeout._onTimeout (evalmachine.<anonymous>:1901:31)|n at listOnTimeout (internal/timers.js:549:17)|n at processTimers (internal/timers.js:492:7)' flowId='12345']",
],
["##teamcity[testFinished name='Jest failed to execute suite' duration='0' flowId='12345']"],
["##teamcity[testSuiteFinished name='failing.spec.ts' flowId='12345']"],
]);
Expand Down
10 changes: 6 additions & 4 deletions __tests__/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,9 @@ const consoleOutput = [
["##teamcity[testSuiteFinished name='test4' flowId='12345']"],
["##teamcity[testSuiteStarted name='test5' flowId='12345']"],
["##teamcity[testStarted name='title5' flowId='12345']"],
["##teamcity[testFailed name='title5' message='Unexpected exception' details='at path/to/file1.js:1|n at path/to/file2.js:2' flowId='12345']"],
[
"##teamcity[testFailed name='title5' message='Unexpected exception' details='at path/to/file1.js:1|n at path/to/file2.js:2' flowId='12345']",
],
["##teamcity[testFinished name='title5' duration='123' flowId='12345']"],
["##teamcity[testSuiteFinished name='test5' flowId='12345']"],
["##teamcity[testSuiteStarted name='constructor' flowId='12345']"],
Expand All @@ -38,15 +40,15 @@ const consoleOutput = [
["##teamcity[testSuiteFinished name='constructor' flowId='12345']"],
["##teamcity[testSuiteFinished name='to' flowId='12345']"],
["##teamcity[testSuiteFinished name='path2' flowId='12345']"],
["##teamcity[testSuiteFinished name='foo/__tests__/file2.js' flowId='12345']"]
["##teamcity[testSuiteFinished name='foo/__tests__/file2.js' flowId='12345']"],
];
const testData = require("./data");
const reporter = require("../lib/index");

describe("jest-teamcity", () => {
beforeAll(() => {
process.env.TEAMCITY_FLOWID = 12345;
console.log = jest.fn().mockImplementation(s => s);
console.log = jest.fn().mockImplementation((s) => s);
});

beforeEach(() => {
Expand All @@ -58,7 +60,7 @@ describe("jest-teamcity", () => {
process.env.TEAMCITY_VERSION = "0.0.0";

const originalCwd = process.cwd();
process.cwd = function() {
process.cwd = function () {
return "/Users/test";
};
reporter({ testResults: testData });
Expand Down
63 changes: 42 additions & 21 deletions lib/formatter.js
Original file line number Diff line number Diff line change
Expand Up @@ -34,20 +34,20 @@ module.exports = {
*/
printTestLog(tests, flowId) {
if (tests) {
Object.keys(tests).forEach(suiteName => {
Object.keys(tests).forEach((suiteName) => {
if (suiteName === "_tests_") {
// print test details
tests[suiteName].forEach(test => {
tests[suiteName].forEach((test) => {
this.log(`##teamcity[testStarted name='${this.escape(test.title)}' flowId='${flowId}']`);
switch (test.status) {
case "failed":
if (test.failureMessages) {
test.failureMessages.forEach(error => {
test.failureMessages.forEach((error) => {
const [message, ...stack] = error.split(errorMessageStackSeparator);
this.log(
`##teamcity[testFailed name='${this.escape(test.title)}' message='${this.escape(
message
)}' details='${this.escape(stack.join(errorMessageStackSeparator))}' flowId='${flowId}']`
message,
)}' details='${this.escape(stack.join(errorMessageStackSeparator))}' flowId='${flowId}']`,
);
});
} else {
Expand All @@ -57,7 +57,7 @@ module.exports = {
break;
case "pending":
this.log(
`##teamcity[testIgnored name='${this.escape(test.title)}' message='pending' flowId='${flowId}']`
`##teamcity[testIgnored name='${this.escape(test.title)}' message='pending' flowId='${flowId}']`,
);
break;
case "passed":
Expand All @@ -66,14 +66,12 @@ module.exports = {
this.log(
`##teamcity[testFinished name='${this.escape(test.title)}' duration='${
test.duration
}' flowId='${flowId}']`
}' flowId='${flowId}']`,
);
});
} else {
// print suite names
this.log(`##teamcity[testSuiteStarted name='${this.escape(suiteName)}' flowId='${flowId}']`);
this.printTestLog(tests[suiteName], flowId);
this.log(`##teamcity[testSuiteFinished name='${this.escape(suiteName)}' flowId='${flowId}']`);
this.printSuiteBlock(suiteName, flowId, () => this.printTestLog(tests[suiteName], flowId));
}
});
}
Expand Down Expand Up @@ -101,9 +99,9 @@ module.exports = {
}

const suites = {};
testResults.forEach(testFile => {
testResults.forEach((testFile) => {
const filename = path.relative(cwd, testFile.testFilePath);
testFile.testResults.forEach(test => {
testFile.testResults.forEach((test) => {
const path = [filename].concat(test.ancestorTitles);

// find current suite, creating each level if necessary
Expand All @@ -126,24 +124,47 @@ module.exports = {

if (testFile.testExecError) {
suites[filename] = suites[filename] || {};
suites[filename]['_tests_'] = [{
status: 'failed',
title: 'Jest failed to execute suite',
duration: 0,
failureMessages: [`${testFile.testExecError.message}${errorMessageStackSeparator}${testFile.testExecError.stack}`],
}];
suites[filename]["_tests_"] = [
{
status: "failed",
title: "Jest failed to execute suite",
duration: 0,
failureMessages: [
`${testFile.testExecError.message}${errorMessageStackSeparator}${testFile.testExecError.stack}`,
],
},
];
}
});

return suites;
},

/**
* @param {string} suiteName
* @param {string} flowId
* @param {function} printLogFn
*/
printSuiteBlock(suiteName, flowId, printLogFn) {
this.log(`##teamcity[testSuiteStarted name='${this.escape(suiteName)}' flowId='${flowId}']`);
printLogFn();
this.log(`##teamcity[testSuiteFinished name='${this.escape(suiteName)}' flowId='${flowId}']`);
},

/**
* Formats and outputs tests results
* @param {array} testResults
* @param {string} cwd
* @param {string} flowId
* @param {string} [projectSuiteName]
*/
formatReport(testResults, cwd, flowId) {
formatReport(testResults, cwd, flowId, projectSuiteName) {
const suites = this.collectSuites(testResults, cwd);
this.printTestLog(suites, flowId);
}

if (projectSuiteName) {
this.printSuiteBlock(projectSuiteName, flowId, () => this.printTestLog(suites, flowId));
} else {
this.printTestLog(suites, flowId);
}
},
};
13 changes: 7 additions & 6 deletions lib/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,24 +4,25 @@

const formatter = require("./formatter");

module.exports = function(result) {
module.exports = function (result) {
const flowId = process.env.TEAMCITY_FLOWID || process.pid.toString();
const teamCityVersion = process.env.TEAMCITY_VERSION;

const projectSuiteName = process.env.PROJECT_NAME;

// Constructor call means usage as a Jest reporter
// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/new.target
if (new.target) {
if (teamCityVersion) {
this.onTestResult = (_, result) => {
formatter.formatReport([result], process.cwd(), flowId)
}
formatter.formatReport([result], process.cwd(), flowId, projectSuiteName);
};
}

return
return;
}

if (teamCityVersion) {
formatter.formatReport(result.testResults, process.cwd(), flowId);
formatter.formatReport(result.testResults, process.cwd(), flowId, projectSuiteName);
}

return result;
Expand Down

0 comments on commit 9696c3d

Please sign in to comment.