diff --git a/README.md b/README.md index f5f2198..454f61d 100755 --- a/README.md +++ b/README.md @@ -62,6 +62,11 @@ Please refer to the [e2e_spec.coffee](spec/e2e_spec.coffee) for more details on ## Changelog +### 3.2.0 + +- Support name and test count attributes for the root test suites element. Thanks to [Simeon Cheeseman](https://github.com/SimeonC). +- Describe parameter types and return types with JSDoc. Thanks to [Simeon Cheeseman](https://github.com/SimeonC). + ### 3.1.0 - Add support for generic properties for test cases. Thanks to [Pietro Ferrulli](https://github.com/Pi-fe). diff --git a/spec/e2e_spec.js b/spec/e2e_spec.js index cda7d2e..b184f90 100644 --- a/spec/e2e_spec.js +++ b/spec/e2e_spec.js @@ -14,8 +14,21 @@ describe('JUnit Report builder', function () { }), ); - const reportWith = (content) => - '\n' + '\n' + content + '\n' + ''; + const parseProperties = (properties = {}) => { + let result = ''; + ['tests', 'failures', 'errors', 'skipped'].forEach((key) => { + result += key + '="' + (properties[key] || 0) + '" '; + }); + for (const key in properties) { + if (['tests', 'failures', 'errors', 'skipped'].includes(key)) { + continue; + } + result += key + '="' + properties[key] + '" '; + } + return result.trim(); + }; + + const reportWith = (content, testSuitesProperties) => '\n' + content; it('should produce a report identical to the expected one', function () { builder.testCase().className('root.test.Class1'); @@ -40,12 +53,22 @@ describe('JUnit Report builder', function () { expect(actual).toBe(expected); }); - it('should produce an empty list of test suites when nothing reported', () => + it('should produce an empty list of test suites when nothing reported', () => { + expect(builder.build()).toBe( + // prettier-ignore + '\n' + + '', + ); + }); + + it('should set testsuites name', () => { + builder.name('testSuitesName'); expect(builder.build()).toBe( // prettier-ignore '\n' + - '', - )); + '', + ); + }); it('should produce an empty test suite when a test suite reported', function () { builder.testSuite(); @@ -53,7 +76,9 @@ describe('JUnit Report builder', function () { expect(builder.build()).toBe( reportWith( // prettier-ignore - ' ', + '\n' + + ' \n' + + '', ), ); }); @@ -64,7 +89,9 @@ describe('JUnit Report builder', function () { expect(builder.build()).toBe( reportWith( // prettier-ignore - ' ', + '\n' + + ' \n' + + '', ), ); }); @@ -75,9 +102,11 @@ describe('JUnit Report builder', function () { expect(builder.build()).toBe( reportWith( // prettier-ignore + '\n' + ' \n' + ' \n' + - ' ', + ' \n' + + '', ), ); }); @@ -88,9 +117,11 @@ describe('JUnit Report builder', function () { expect(builder.build()).toBe( reportWith( // prettier-ignore + '\n' + ' \n' + ' \n' + - ' ', + ' \n' + + '', ), ); }); @@ -101,9 +132,11 @@ describe('JUnit Report builder', function () { expect(builder.build()).toBe( reportWith( // prettier-ignore + '\n' + ' \n' + ' \n' + - ' ', + ' \n' + + '', ), ); }); @@ -114,9 +147,11 @@ describe('JUnit Report builder', function () { expect(builder.build()).toBe( reportWith( // prettier-ignore + '\n' + ' \n' + ' \n' + - ' ', + ' \n' + + '', ), ); }); @@ -127,9 +162,11 @@ describe('JUnit Report builder', function () { expect(builder.build()).toBe( reportWith( // prettier-ignore + '\n' + ' \n' + ' \n' + - ' ', + ' \n' + + '', ), ); }); @@ -140,11 +177,13 @@ describe('JUnit Report builder', function () { expect(builder.build()).toBe( reportWith( // prettier-ignore + '\n' + ' \n' + ' \n' + ' \n' + ' \n' + - ' ', + ' \n' + + '', ), ); }); @@ -155,11 +194,13 @@ describe('JUnit Report builder', function () { expect(builder.build()).toBe( reportWith( // prettier-ignore + '\n' + ' \n' + ' \n' + ' \n' + ' \n' + - ' ', + ' \n' + + '', ), ); }); @@ -170,11 +211,13 @@ describe('JUnit Report builder', function () { expect(builder.build()).toBe( reportWith( // prettier-ignore + '\n' + ' \n' + ' \n' + ' \n' + ' \n' + - ' ', + ' \n' + + '', ), ); }); @@ -185,7 +228,9 @@ describe('JUnit Report builder', function () { expect(builder.build()).toBe( reportWith( // prettier-ignore - ' ', + '\n' + + ' \n' + + '', ), ); }); @@ -196,7 +241,9 @@ describe('JUnit Report builder', function () { expect(builder.build()).toBe( reportWith( // prettier-ignore - ' ', + '\n' + + ' \n' + + '', ), ); }); @@ -207,9 +254,11 @@ describe('JUnit Report builder', function () { expect(builder.build()).toBe( reportWith( // prettier-ignore + '\n' + ' \n' + ' \n' + - ' ', + ' \n' + + '', ), ); }); @@ -220,11 +269,13 @@ describe('JUnit Report builder', function () { expect(builder.build()).toBe( reportWith( // prettier-ignore + '\n' + ' \n' + ' \n' + ' \n' + ' \n' + - ' ', + ' \n' + + '', ), ); }); @@ -235,11 +286,13 @@ describe('JUnit Report builder', function () { expect(builder.build()).toBe( reportWith( // prettier-ignore + '\n' + ' \n' + ' \n' + ' \n' + ' \n' + - ' ', + ' \n' + + '', ), ); }); @@ -254,6 +307,7 @@ describe('JUnit Report builder', function () { expect(builder.build()).toBe( reportWith( // prettier-ignore + '\n' + ' \n' + ' \n' + ' \n' + @@ -261,7 +315,8 @@ describe('JUnit Report builder', function () { ' [[ATTACHMENT|absolute/path/to/attachment]]\n' + ' \n' + ' \n' + - ' ', + ' \n' + + '', ), ); }); @@ -274,9 +329,11 @@ describe('JUnit Report builder', function () { expect(builder.build()).toBe( reportWith( // prettier-ignore + '\n' + ' \n' + ' \n' + - ' ', + ' \n' + + '', ), ); }); @@ -287,9 +344,11 @@ describe('JUnit Report builder', function () { expect(builder.build()).toBe( reportWith( // prettier-ignore + '\n' + ' \n' + ' \n' + - ' ', + ' \n' + + '', ), ); }); @@ -300,9 +359,11 @@ describe('JUnit Report builder', function () { expect(builder.build()).toBe( reportWith( // prettier-ignore + '\n' + ' \n' + ' \n' + - ' ', + ' \n' + + '', ), ); }); @@ -313,9 +374,11 @@ describe('JUnit Report builder', function () { expect(builder.build()).toBe( reportWith( // prettier-ignore + '\n' + ' \n' + ' \n' + - ' ', + ' \n' + + '', ), ); }); diff --git a/spec/expected_report.xml b/spec/expected_report.xml index 5ef522d..d0ffe04 100755 --- a/spec/expected_report.xml +++ b/spec/expected_report.xml @@ -1,5 +1,5 @@ - + diff --git a/spec/test_case_spec.js b/spec/test_case_spec.js index a3ad455..d81e15d 100644 --- a/spec/test_case_spec.js +++ b/spec/test_case_spec.js @@ -1,4 +1,4 @@ -const TestCase = require('../src/test_case'); +const { TestCase } = require('../src/test_case'); describe('Test Case builder', function () { let testCase = null; diff --git a/spec/test_suite_spec.js b/spec/test_suite_spec.js index dc5fe69..bca4d4e 100644 --- a/spec/test_suite_spec.js +++ b/spec/test_suite_spec.js @@ -1,4 +1,4 @@ -const TestSuite = require('../src/test_suite'); +const { TestSuite } = require('../src/test_suite'); describe('Test Suite builder', function () { let testSuite = null; @@ -9,7 +9,13 @@ describe('Test Suite builder', function () { beforeEach(function () { const factory = jasmine.createSpyObj('factory', ['newTestCase']); - testCase = jasmine.createSpyObj('testCase', ['build', 'getFailureCount', 'getErrorCount', 'getSkippedCount']); + testCase = jasmine.createSpyObj('testCase', [ + 'build', + 'getFailureCount', + 'getErrorCount', + 'getSkippedCount', + 'getTestCaseCount', + ]); factory.newTestCase.and.callFake(() => testCase); @@ -33,6 +39,8 @@ describe('Test Suite builder', function () { } }); + testCase.getTestCaseCount.and.callFake(() => 1); + testCase.getFailureCount.and.callFake(() => 0); testCase.getErrorCount.and.callFake(() => 0); diff --git a/src/builder.js b/src/builder.js index 017b5cc..f08b8fe 100644 --- a/src/builder.js +++ b/src/builder.js @@ -1,43 +1,64 @@ -var _ = require('lodash'); -var xmlBuilder = require('xmlbuilder'); +// @ts-check var path = require('path'); var makeDir = require('make-dir'); var fs = require('fs'); -var TestSuite = require('./test_suite'); -var TestCase = require('./test_case'); +var { TestSuites } = require('./test_suites'); -function JUnitReportBuilder(factory) { - this._factory = factory; - this._testSuitesAndCases = []; +class JUnitReportBuilder { + /** + * @param {import('./factory').Factory} factory + */ + constructor(factory) { + this._factory = factory; + this._rootTestSuites = new TestSuites(factory); + } + + /** + * @param {string} reportPath + */ + writeTo(reportPath) { + makeDir.sync(path.dirname(reportPath)); + fs.writeFileSync(reportPath, this.build(), 'utf8'); + } + + /** + * @returns {string} + */ + build() { + var xmlTree = this._rootTestSuites.build(); + return xmlTree.end({ pretty: true }); + } + + /** + * @param {string} name + * @returns {JUnitReportBuilder} + * @chainable + */ + name(name) { + this._rootTestSuites.name(name); + return this; + } + + /** + * @returns {import('./test_suite').TestSuite} + */ + testSuite() { + return this._rootTestSuites.testSuite(); + } + + /** + * @returns {import('./test_case').TestCase} + */ + testCase() { + return this._rootTestSuites.testCase(); + } + + /** + * @returns {JUnitReportBuilder} + */ + newBuilder() { + return this._factory.newBuilder(); + } } -JUnitReportBuilder.prototype.writeTo = function (reportPath) { - makeDir.sync(path.dirname(reportPath)); - fs.writeFileSync(reportPath, this.build(), 'utf8'); -}; - -JUnitReportBuilder.prototype.build = function () { - var xmlTree = xmlBuilder.create('testsuites', { encoding: 'UTF-8', invalidCharReplacement: '' }); - _.forEach(this._testSuitesAndCases, function (suiteOrCase) { - suiteOrCase.build(xmlTree); - }); - return xmlTree.end({ pretty: true }); -}; - -JUnitReportBuilder.prototype.testSuite = function () { - var suite = this._factory.newTestSuite(); - this._testSuitesAndCases.push(suite); - return suite; -}; - -JUnitReportBuilder.prototype.testCase = function () { - var testCase = this._factory.newTestCase(); - this._testSuitesAndCases.push(testCase); - return testCase; -}; - -JUnitReportBuilder.prototype.newBuilder = function () { - return this._factory.newBuilder(); -}; - -module.exports = JUnitReportBuilder; +module.exports = { Builder: JUnitReportBuilder }; diff --git a/src/factory.js b/src/factory.js index 4bbeffb..d17d36e 100644 --- a/src/factory.js +++ b/src/factory.js @@ -1,19 +1,36 @@ -var Builder = require('./builder'); -var TestSuite = require('./test_suite'); -var TestCase = require('./test_case'); +var { Builder } = require('./builder'); +var { TestSuites } = require('./test_suites'); +var { TestSuite } = require('./test_suite'); +var { TestCase } = require('./test_case'); -function Factory() {} +class Factory { + /** + * @returns {import('./builder').Builder} + */ + newBuilder() { + return new Builder(this); + } -Factory.prototype.newBuilder = function () { - return new Builder(this); -}; + /** + * @returns {import('./test_suite').TestSuite} + */ + newTestSuite() { + return new TestSuite(this); + } -Factory.prototype.newTestSuite = function () { - return new TestSuite(this); -}; + /** + * @returns {import('./test_case').TestCase} + */ + newTestCase() { + return new TestCase(this); + } -Factory.prototype.newTestCase = function () { - return new TestCase(this); -}; + /** + * @returns {import('./test_suites').TestSuites} + */ + newTestSuites() { + return new TestSuites(this); + } +} -module.exports = Factory; +module.exports = { Factory: Factory }; diff --git a/src/index.js b/src/index.js index af0080b..efb1484 100644 --- a/src/index.js +++ b/src/index.js @@ -1,3 +1,3 @@ -var Factory = require('./factory'); +var { Factory } = require('./factory'); module.exports = new Factory().newBuilder(); diff --git a/src/test_case.js b/src/test_case.js index bb1b900..666f3be 100755 --- a/src/test_case.js +++ b/src/test_case.js @@ -1,144 +1,195 @@ +// @ts-check var _ = require('lodash'); -function TestCase() { - this._error = false; - this._failure = false; - this._skipped = false; - this._standardOutput = undefined; - this._standardError = undefined; - this._stacktrace = undefined; - this._attributes = {}; - this._errorAttributes = {}; - this._failureAttributes = {}; - this._errorAttachment = undefined; - this._errorContent = undefined; - this._properties = []; -} +var { TestNode } = require('./test_node'); + +class TestCase extends TestNode { + /** + * @param {import('./factory').Factory} factory + */ + constructor(factory) { + super(factory, 'testcase'); + this._error = false; + this._failure = false; + this._skipped = false; + this._standardOutput = undefined; + this._standardError = undefined; + this._stacktrace = undefined; + this._errorAttributes = {}; + this._failureAttributes = {}; + this._errorAttachment = undefined; + this._errorContent = undefined; + } -TestCase.prototype.className = function (className) { - this._attributes.classname = className; - return this; -}; - -TestCase.prototype.name = function (name) { - this._attributes.name = name; - return this; -}; - -TestCase.prototype.time = function (timeInSeconds) { - this._attributes.time = timeInSeconds; - return this; -}; - -TestCase.prototype.file = function (filepath) { - this._attributes.file = filepath; - return this; -}; - -TestCase.prototype.failure = function (message, type) { - this._failure = true; - if (message) { - this._failureAttributes.message = message; + /** + * @param {string} className + * @returns {TestCase} + * @chainable + */ + className(className) { + this._attributes.classname = className; + return this; } - if (type) { - this._failureAttributes.type = type; + + /** + * @param {string} filepath + * @returns {TestCase} + * @chainable + */ + file(filepath) { + this._attributes.file = filepath; + return this; } - return this; -}; -TestCase.prototype.error = function (message, type, content) { - this._error = true; - if (message) { - this._errorAttributes.message = message; + /** + * @param {string} [message] + * @param {string} [type] + * @returns {TestCase} + * @chainable + */ + failure(message, type) { + this._failure = true; + if (message) { + this._failureAttributes.message = message; + } + if (type) { + this._failureAttributes.type = type; + } + return this; } - if (type) { - this._errorAttributes.type = type; + + /** + * @param {string} [message] + * @param {string} [type] + * @param {string} [content] + * @returns {TestCase} + * @chainable + */ + error(message, type, content) { + this._error = true; + if (message) { + this._errorAttributes.message = message; + } + if (type) { + this._errorAttributes.type = type; + } + if (content) { + this._errorContent = content; + } + return this; } - if (content) { - this._errorContent = content; + + /** + * @param {string} [stacktrace] + * @returns {TestCase} + * @chainable + */ + stacktrace(stacktrace) { + this._failure = true; + this._stacktrace = stacktrace; + return this; } - return this; -}; - -TestCase.prototype.stacktrace = function (stacktrace) { - this._failure = true; - this._stacktrace = stacktrace; - return this; -}; - -TestCase.prototype.skipped = function () { - this._skipped = true; - return this; -}; - -TestCase.prototype.standardOutput = function (log) { - this._standardOutput = log; - return this; -}; - -TestCase.prototype.standardError = function (log) { - this._standardError = log; - return this; -}; - -TestCase.prototype.getFailureCount = function () { - return Number(this._failure); -}; - -TestCase.prototype.getErrorCount = function () { - return Number(this._error); -}; - -TestCase.prototype.getSkippedCount = function () { - return Number(this._skipped); -}; - -TestCase.prototype.errorAttachment = function (path) { - this._errorAttachment = path; - return this; -}; - -TestCase.prototype.property = function (name, value) { - this._properties.push({ name: name, value: value }); - return this; -}; - -TestCase.prototype.build = function (parentElement) { - var testCaseElement = parentElement.ele('testcase', this._attributes); - if (this._properties.length) { - var propertiesElement = testCaseElement.ele('properties'); - _.forEach(this._properties, function (property) { - propertiesElement.ele('property', { - name: property.name, - value: property.value, - }); - }); + + /** + * @returns {TestCase} + * @chainable + */ + skipped() { + this._skipped = true; + return this; } - if (this._failure) { - var failureElement = testCaseElement.ele('failure', this._failureAttributes); - if (this._stacktrace) { - failureElement.cdata(this._stacktrace); - } + + /** + * @param {string} [log] + * @returns {TestCase} + * @chainable + */ + standardOutput(log) { + this._standardOutput = log; + return this; } - if (this._error) { - var errorElement = testCaseElement.ele('error', this._errorAttributes); - if (this._errorContent) { - errorElement.cdata(this._errorContent); - } + + /** + * @param {string} [log] + * @returns {TestCase} + * @chainable + */ + standardError(log) { + this._standardError = log; + return this; } - if (this._skipped) { - testCaseElement.ele('skipped'); + + /** + * @returns {number} + */ + getTestCaseCount() { + return 1; + } + + /** + * @returns {number} + */ + getFailureCount() { + return Number(this._failure); + } + + /** + * @returns {number} + */ + getErrorCount() { + return Number(this._error); + } + + /** + * @returns {number} + */ + getSkippedCount() { + return Number(this._skipped); } - if (this._standardOutput) { - testCaseElement.ele('system-out').cdata(this._standardOutput); + + /** + * + * @param {string} path + * @returns {TestCase} + * @chainable + */ + errorAttachment(path) { + this._errorAttachment = path; + return this; } - var systemError; - if (this._standardError) { - systemError = testCaseElement.ele('system-err').cdata(this._standardError); - if (this._errorAttachment) { - systemError.txt('[[ATTACHMENT|' + this._errorAttachment + ']]'); + /** + * @param {import('xmlbuilder').XMLElement} parentElement + */ + build(parentElement) { + const testCaseElement = this.buildNode(this.createElement(parentElement)); + if (this._failure) { + var failureElement = testCaseElement.ele('failure', this._failureAttributes); + if (this._stacktrace) { + failureElement.cdata(this._stacktrace); + } + } + if (this._error) { + var errorElement = testCaseElement.ele('error', this._errorAttributes); + if (this._errorContent) { + errorElement.cdata(this._errorContent); + } + } + if (this._skipped) { + testCaseElement.ele('skipped'); + } + if (this._standardOutput) { + testCaseElement.ele('system-out').cdata(this._standardOutput); } + var systemError; + if (this._standardError) { + systemError = testCaseElement.ele('system-err').cdata(this._standardError); + + if (this._errorAttachment) { + systemError.txt('[[ATTACHMENT|' + this._errorAttachment + ']]'); + } + } + return testCaseElement; } -}; +} -module.exports = TestCase; +module.exports = { TestCase: TestCase }; diff --git a/src/test_group.js b/src/test_group.js new file mode 100755 index 0000000..a2ae6d1 --- /dev/null +++ b/src/test_group.js @@ -0,0 +1,111 @@ +// @ts-check +var _ = require('lodash'); +var formatDate = require('date-format').asString; +var { TestNode } = require('./test_node'); + +class TestGroup extends TestNode { + /** + * @param {import('./factory').Factory} factory + * @param {string} elementName + */ + constructor(factory, elementName) { + super(factory, elementName); + this._children = []; + } + + /** + * @param {string|Date} timestamp + * @returns {TestGroup} + * @chainable + */ + timestamp(timestamp) { + if (_.isDate(timestamp)) { + this._attributes.timestamp = formatDate('yyyy-MM-ddThh:mm:ss', timestamp); + } else { + this._attributes.timestamp = timestamp; + } + return this; + } + + /** + * @returns {import('./test_case').TestCase} + */ + testCase() { + var testCase = this._factory.newTestCase(); + this._children.push(testCase); + return testCase; + } + + /** + * @returns {number} + */ + getTestCaseCount() { + return this._sumTestCaseCounts(function (testCase) { + return testCase.getTestCaseCount(); + }); + } + + /** + * @returns {number} + */ + getFailureCount() { + return this._sumTestCaseCounts(function (testCase) { + return testCase.getFailureCount(); + }); + } + + /** + * @returns {number} + */ + getErrorCount() { + return this._sumTestCaseCounts(function (testCase) { + return testCase.getErrorCount(); + }); + } + + /** + * @returns {number} + */ + getSkippedCount() { + return this._sumTestCaseCounts(function (testCase) { + return testCase.getSkippedCount(); + }); + } + + /** + * @protected + * @param {Function} counterFunction + * @returns {number} + */ + _sumTestCaseCounts(counterFunction) { + var counts = _.map(this._children, counterFunction); + return _.sum(counts); + } + + /** + * @param {import('xmlbuilder').XMLElement} [parentElement] + * @returns {import('xmlbuilder').XMLElement} + */ + build(parentElement) { + this._attributes.tests = this.getTestCaseCount(); + this._attributes.failures = this.getFailureCount(); + this._attributes.errors = this.getErrorCount(); + this._attributes.skipped = this.getSkippedCount(); + return super.build(parentElement); + } + + /** + * @protected + * @param {import('xmlbuilder').XMLElement} element + * @returns {import('xmlbuilder').XMLElement} + */ + buildNode(element) { + element = super.buildNode(element); + _.forEach(this._children, function (child) { + child.build(element); + }); + return element; + } +} + +module.exports = { TestGroup: TestGroup }; diff --git a/src/test_node.js b/src/test_node.js new file mode 100644 index 0000000..7f3ba7d --- /dev/null +++ b/src/test_node.js @@ -0,0 +1,126 @@ +// @ts-check +var _ = require('lodash'); +var xmlBuilder = require('xmlbuilder'); + +class TestNode { + /** + * @param {import('./factory').Factory} factory + * @param {string} elementName + */ + constructor(factory, elementName) { + this._factory = factory; + this._elementName = elementName; + this._attributes = {}; + this._properties = []; + } + + /** + * @param {string} name + * @param {string} value + * @returns {TestNode} + * @chainable + */ + property(name, value) { + this._properties.push({ name: name, value: value }); + return this; + } + + /** + * @param {string} name + * @returns {TestNode} + * @chainable + */ + name(name) { + this._attributes.name = name; + return this; + } + + /** + * @param {string} timeInSeconds + * @returns {TestNode} + * @chainable + */ + time(timeInSeconds) { + this._attributes.time = timeInSeconds; + return this; + } + + /** + * @returns {number} + */ + getTestCaseCount() { + throw new Error('Not implemented'); + } + + /** + * @returns {number} + */ + getFailureCount() { + throw new Error('Not implemented'); + } + + /** + * @returns {number} + */ + getErrorCount() { + throw new Error('Not implemented'); + } + + /** + * @returns {number} + */ + getSkippedCount() { + throw new Error('Not implemented'); + } + + /** + * @param {import('xmlbuilder').XMLElement} [parentElement] + */ + build(parentElement) { + return this.buildNode(this.createElement(parentElement)); + } + + /** + * @protected + * @param {import('xmlbuilder').XMLElement} [parentElement] + * @returns {import('xmlbuilder').XMLElement} + */ + createElement(parentElement) { + if (parentElement) { + return parentElement.ele(this._elementName, this._attributes); + } + return this.createRootElement(); + } + + /** + * @private + * @returns {import('xmlbuilder').XMLElement} + */ + createRootElement() { + const element = xmlBuilder.create(this._elementName, { encoding: 'UTF-8', invalidCharReplacement: '' }); + Object.keys(this._attributes).forEach((key) => { + element.att(key, this._attributes[key]); + }); + return element; + } + + /** + * @protected + * @param {import('xmlbuilder').XMLElement} element + * @returns {import('xmlbuilder').XMLElement} + */ + buildNode(element) { + if (this._properties.length) { + var propertiesElement = element.ele('properties'); + _.forEach(this._properties, function (property) { + propertiesElement.ele('property', { + name: property.name, + value: property.value, + }); + }); + } + return element; + } +} + +module.exports = { TestNode: TestNode }; diff --git a/src/test_suite.js b/src/test_suite.js index ea43f79..91e343b 100755 --- a/src/test_suite.js +++ b/src/test_suite.js @@ -1,86 +1,14 @@ +// @ts-check var _ = require('lodash'); -var formatDate = require('date-format').asString; - -function TestSuite(factory) { - this._factory = factory; - this._attributes = {}; - this._testCases = []; - this._properties = []; -} - -TestSuite.prototype.name = function (name) { - this._attributes.name = name; - return this; -}; - -TestSuite.prototype.time = function (timeInSeconds) { - this._attributes.time = timeInSeconds; - return this; -}; - -TestSuite.prototype.timestamp = function (timestamp) { - if (_.isDate(timestamp)) { - this._attributes.timestamp = formatDate('yyyy-MM-ddThh:mm:ss', timestamp); - } else { - this._attributes.timestamp = timestamp; - } - return this; -}; - -TestSuite.prototype.property = function (name, value) { - this._properties.push({ name: name, value: value }); - return this; -}; - -TestSuite.prototype.testCase = function () { - var testCase = this._factory.newTestCase(); - this._testCases.push(testCase); - return testCase; -}; - -TestSuite.prototype.getFailureCount = function () { - return this._sumTestCaseCounts(function (testCase) { - return testCase.getFailureCount(); - }); -}; - -TestSuite.prototype.getErrorCount = function () { - return this._sumTestCaseCounts(function (testCase) { - return testCase.getErrorCount(); - }); -}; - -TestSuite.prototype.getSkippedCount = function () { - return this._sumTestCaseCounts(function (testCase) { - return testCase.getSkippedCount(); - }); -}; - -TestSuite.prototype._sumTestCaseCounts = function (counterFunction) { - var counts = _.map(this._testCases, counterFunction); - return _.sum(counts); -}; - -TestSuite.prototype.build = function (parentElement) { - this._attributes.tests = this._testCases.length; - this._attributes.failures = this.getFailureCount(); - this._attributes.errors = this.getErrorCount(); - this._attributes.skipped = this.getSkippedCount(); - var suiteElement = parentElement.ele('testsuite', this._attributes); - - if (this._properties.length) { - var propertiesElement = suiteElement.ele('properties'); - _.forEach(this._properties, function (property) { - propertiesElement.ele('property', { - name: property.name, - value: property.value, - }); - }); +var { TestGroup } = require('./test_group'); + +class TestSuite extends TestGroup { + /** + * @param {import('./factory').Factory} factory + */ + constructor(factory) { + super(factory, 'testsuite'); } +} - _.forEach(this._testCases, function (testCase) { - testCase.build(suiteElement); - }); -}; - -module.exports = TestSuite; +module.exports = { TestSuite: TestSuite }; diff --git a/src/test_suites.js b/src/test_suites.js new file mode 100755 index 0000000..a32fba1 --- /dev/null +++ b/src/test_suites.js @@ -0,0 +1,22 @@ +// @ts-check +var { TestGroup } = require('./test_group'); + +class TestSuites extends TestGroup { + /** + * @param {import('./factory').Factory} factory + */ + constructor(factory) { + super(factory, 'testsuites'); + } + + /** + * @returns {import('./test_suite').TestSuite} + */ + testSuite() { + var suite = this._factory.newTestSuite(); + this._children.push(suite); + return suite; + } +} + +module.exports = { TestSuites: TestSuites };