├── .npmrc ├── testMochaVersions.sh ├── testMochaVersions.ps1 ├── .travis.yml ├── .gitignore ├── .npmignore ├── test ├── browser │ ├── sumTest.js │ └── mocha.html ├── example │ ├── rootHooks.js │ ├── exampleSimple.js │ ├── failingHook.js │ ├── failingIgnoreHook.js │ └── failingIgnoreHookNoRoot.js ├── test_data │ └── simple.js ├── functional │ ├── thisSkipExample.js │ ├── topLevelSuiteOutput.js │ ├── testOutput.js │ ├── useStdError.js │ ├── recordHookFailuresDisabled.js │ ├── actualVsExpected.js │ ├── testMultipleOptions.js │ ├── recordHookFailuresEnabled.js │ ├── displayIgnoredAsIgnored.js │ ├── ignoreHookWithNameEnabled.js │ └── ignoreHookWithNameDisabled.js ├── testHelpers.js ├── programmatic │ └── testOutput.js └── excluded tests │ ├── ignoreHookWithNameEnabledRoot.js │ └── ignoreHookWithNameDisabledRoot.js ├── appveyor.yml ├── .github ├── ISSUE_TEMPLATE.md └── PULL_REQUEST_TEMPLATE.md ├── LICENSE ├── package.json ├── .eslintrc.json ├── lib ├── teamcityBrowser.js └── teamcity.js ├── CHANGELOG.md └── README.md /.npmrc: -------------------------------------------------------------------------------- 1 | package-lock=false -------------------------------------------------------------------------------- /testMochaVersions.sh: -------------------------------------------------------------------------------- 1 | npm install mocha@6 2 | npm run test 3 | npm install mocha@7 4 | npm run test 5 | npm install mocha@8 6 | npm run test -------------------------------------------------------------------------------- /testMochaVersions.ps1: -------------------------------------------------------------------------------- 1 | npm install mocha@6 2 | npm run test 3 | npm install mocha@7 4 | npm run test 5 | npm install mocha@8 6 | npm run test -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | 3 | script: 4 | - npm run lint 5 | - npm install mocha 6 | - npm test 7 | 8 | node_js: 9 | - "10" 10 | - "lts/*" 11 | 12 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | lib-cov 2 | *.seed 3 | *.log 4 | *.csv 5 | *.dat 6 | *.out 7 | *.pid 8 | *.gz 9 | 10 | pids 11 | logs 12 | results 13 | 14 | npm-debug.log 15 | node_modules 16 | \.idea/ 17 | package-lock.json 18 | 19 | test/browser/teamcityBrowser.js -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | #tests 2 | test 3 | coverage 4 | 5 | #build tools 6 | .travis.yml 7 | .jenkins.yml 8 | .codeclimate.yml 9 | appveyor.yml 10 | 11 | #linters 12 | .jscsrc 13 | .jshintrc 14 | .eslintrc* 15 | 16 | #editor settings 17 | .idea 18 | .editorconfig 19 | 20 | #github settings 21 | .github 22 | 23 | #custom scripts 24 | testMochaVersions.ps1 25 | testMochaVersions.sh -------------------------------------------------------------------------------- /test/browser/sumTest.js: -------------------------------------------------------------------------------- 1 | /*global chai*/ 2 | /*eslint-env browser*/ 3 | /*eslint strict: ["error", "never"]*/ 4 | 5 | 6 | describe('sum', function () { 7 | it('should return sum of arguments fail', function () { 8 | chai.expect(1).to.equal(3); 9 | }); 10 | 11 | it('should return sum of arguments pass', function () { 12 | chai.expect(1).to.equal(1); 13 | }); 14 | 15 | }); -------------------------------------------------------------------------------- /test/example/rootHooks.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Created by dj-glock on 04/06/2021. 3 | */ 4 | 'use strict'; 5 | const assert = require('assert'); 6 | 7 | exports.mochaHooks = () => { 8 | return { 9 | beforeEach: [ 10 | function beforeEachRootHookNoReporting() { 11 | assert.strictEqual(1, 1); 12 | } 13 | ], 14 | afterEach: [ 15 | function afterEachRoot() { 16 | assert.strictEqual(1, 1); 17 | }, 18 | ] 19 | }; 20 | }; -------------------------------------------------------------------------------- /test/test_data/simple.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Created by jamie on 12/08/2017. 3 | */ 4 | 'use strict'; 5 | const assert = require('assert'); 6 | describe('Top Describe', function () { 7 | it('Passing Test @pass', function () { 8 | assert.equal(1, 1); 9 | }); 10 | it('Failing Test @fail', function () { 11 | assert.equal(2, 1); 12 | }); 13 | it.skip('Skipped Test @skip', function () { 14 | assert.equal(2, 1); 15 | }); 16 | }); -------------------------------------------------------------------------------- /test/example/exampleSimple.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Created by jamie on 12/08/2017. 3 | */ 4 | 'use strict'; 5 | const assert = require('assert'); 6 | describe('Example Test Top Describe', function () { 7 | it('Example Test Passing Test @pass', function () { 8 | assert.equal(1, 1); 9 | }); 10 | it('Example Test Failing Test @fail', function () { 11 | assert.equal(2, 1); 12 | }); 13 | it.skip('Example Test Skipped Test @skip', function () { 14 | assert.equal(2, 1); 15 | }); 16 | }); -------------------------------------------------------------------------------- /appveyor.yml: -------------------------------------------------------------------------------- 1 | # Test against the latest version of this Node.js version 2 | environment: 3 | matrix: 4 | # node.js 5 | - nodejs_version: "6" 6 | - nodejs_version: "8" 7 | # Install scripts. (runs after repo cloning) 8 | install: 9 | # Get the latest stable version of Node.js or io.js 10 | - ps: Install-Product node $env:nodejs_version 11 | # install modules 12 | - npm install 13 | 14 | # Post-install test scripts. 15 | test_script: 16 | # Output useful info for debugging. 17 | - node --version 18 | - npm --version 19 | # run tests 20 | - npm test 21 | 22 | # Don't actually build. 23 | build: off -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | ## The problem 2 | 3 | Briefly describe the issue you are experiencing (or the feature you want to add) 4 | 5 | ## Environment 6 | 7 | * What mocha-teamcity-reporter version are you using 8 | * Version of NPM/yarn etc and Node.js 9 | * Version of teamcity (If Applicable) 10 | 11 | ## Details 12 | 13 | * If necessary, describe the problem you have been experiencing in more detail. 14 | * Such as a stacktrace or error message 15 | 16 | ## An example test or code sample 17 | 18 | * Please remember that with sample code it's easier to reproduce the bug and it's much faster to fix it. 19 | * Not needed for feature requests or questions 20 | -------------------------------------------------------------------------------- /test/browser/mocha.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Mocha Tests 5 | 6 | 7 | 8 |

Test results in console

9 |
10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 27 | 28 | -------------------------------------------------------------------------------- /test/functional/thisSkipExample.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | describe('something', function () { 4 | let stubA; 5 | 6 | function stubThingA(){ 7 | console.log('stubbingThing A'); 8 | } 9 | function stubThingB(){ 10 | console.log('stubbingThing B'); 11 | } 12 | 13 | function reasons(){ 14 | return false; 15 | } 16 | 17 | before(function () { 18 | stubA = stubThingA(); 19 | if (reasons()) { 20 | this.skip(); 21 | } 22 | }); 23 | 24 | describe('something else', function () { 25 | let stubB; 26 | 27 | before(function () { 28 | stubB = stubThingB(); 29 | }); 30 | 31 | after(function () { 32 | // this nested `after` does *all* of the teardown. 33 | stubA.restore(); 34 | stubB.restore(); 35 | }); 36 | }); 37 | 38 | // what the author should do instead is create an `after` 39 | // hook here, which calls `stubA.restore()`. 40 | }); -------------------------------------------------------------------------------- /test/example/failingHook.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Created by jamie on 12/08/2017. 3 | */ 4 | 'use strict'; 5 | const assert = require('assert'); 6 | describe('Hook Test Top Describe Fail', function () { 7 | 8 | before(function () { 9 | throw new Error('Before hook error fail'); 10 | }); 11 | 12 | it('Test Passing Test @pass', function () { 13 | assert.equal(1, 1); 14 | }); 15 | it('Test Failing Test @fail', function () { 16 | assert.equal(2, 1); 17 | }); 18 | it.skip(' Test Skipped Test @skip', function () { 19 | assert.equal(2, 1); 20 | }); 21 | }); 22 | describe('Hook Test Top Describe Pass', function () { 23 | 24 | before(function () { 25 | assert.equal(1, 1); 26 | }); 27 | 28 | it('Test Failing Test @fail', function () { 29 | assert.equal(2, 1); 30 | }); 31 | 32 | it('Test Passing Test @pass', function () { 33 | assert.equal(1, 1); 34 | }); 35 | }); -------------------------------------------------------------------------------- /test/testHelpers.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const os = require('os'); 4 | const path = require('path'); 5 | 6 | function logMochaOutput(stdout, stderr) { 7 | console.log('Observed Reporter Output'); 8 | console.log('|#####################|'); 9 | console.log(stdout); 10 | console.log('|#####################|'); 11 | 12 | if (stderr) { 13 | console.log('|#####################|'); 14 | console.log('stderr:'); 15 | console.log('|#####################|'); 16 | console.log(stderr); 17 | console.log('|#####################|'); 18 | } 19 | } 20 | 21 | 22 | function getMochaPath() { 23 | if (os.platform() === 'win32') { 24 | return path.resolve('node_modules', '.bin', 'mocha.cmd'); 25 | } else { 26 | return path.resolve('node_modules', '.bin', 'mocha'); 27 | } 28 | } 29 | 30 | function getTestDataPath(){ 31 | return path.join('test', 'test_data'); 32 | } 33 | 34 | module.exports = { 35 | logMochaOutput, 36 | getMochaPath, 37 | getTestDataPath 38 | }; 39 | -------------------------------------------------------------------------------- /test/programmatic/testOutput.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const Mocha = require('mocha'); 4 | const fs = require('fs'); 5 | const path = require('path'); 6 | const {getTestDataPath } = require('../testHelpers'); 7 | const testDataDir = getTestDataPath(); 8 | 9 | const { 10 | EVENT_RUN_END 11 | } = Mocha.Runner.constants; 12 | 13 | //Assumes running from root project dir 14 | describe('Can use reporter programmatically', function () { 15 | let mocha; 16 | 17 | before(function () { 18 | // Instantiate a Mocha instance. 19 | mocha = new Mocha({ 20 | reporter: path.join('lib', 'teamcity') 21 | }); 22 | fs.readdirSync(testDataDir).filter(function (file){ 23 | // Only keep the .js files 24 | return file.substr(-3) === '.js'; 25 | }).forEach(function (file){ 26 | mocha.addFile( 27 | path.join(testDataDir, file) 28 | ); 29 | }); 30 | }); 31 | it('programmatic reporter can get to exit', function (done) { 32 | // Run the tests. 33 | const mochaRunner = mocha.run(); 34 | mochaRunner.on(EVENT_RUN_END, function () { 35 | done(); 36 | }); 37 | }); 38 | }); -------------------------------------------------------------------------------- /test/example/failingIgnoreHook.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Created by dj-glock on 04/06/2021. 3 | */ 4 | 'use strict'; 5 | const assert = require('assert'); 6 | 7 | describe('Hook Test Top Describe Fail', function () { 8 | before(function () { 9 | throw new Error('Before hook error fail'); 10 | }); 11 | 12 | it('Test Passing Test @pass', function () { 13 | assert.strictEqual(1, 1); 14 | }); 15 | it('Test Failing Test @fail', function () { 16 | assert.strictEqual(2, 1); 17 | }); 18 | it.skip(' Test Skipped Test @skip', function () { 19 | assert.strictEqual(2, 1); 20 | }); 21 | 22 | after(function afterHookNoReporting() { 23 | throw new Error('After hook error fail'); 24 | }); 25 | }); 26 | 27 | describe('Hook Test Top Describe Pass', function () { 28 | before(function () { 29 | assert.strictEqual(1, 1); 30 | }); 31 | 32 | it('Test Failing Test @fail', function () { 33 | assert.strictEqual(2, 1); 34 | }); 35 | 36 | it('Test Passing Test @pass', function () { 37 | assert.strictEqual(1, 1); 38 | }); 39 | 40 | after(function afterHookNoReporting() { 41 | assert.strictEqual(1, 1); 42 | }); 43 | }); -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2016 Jamie Sherriff 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. -------------------------------------------------------------------------------- /.github/PULL_REQUEST_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | ## Proposed changes 2 | 3 | Describe the big picture of your changes here to communicate to the maintainers why we should accept this pull request. If it fixes a bug or resolves a feature request, be sure to link to that issue. 4 | 5 | ## Types of changes 6 | 7 | What types of changes does your code introduce 8 | _Put an `x` in the boxes that apply_ 9 | 10 | - [ ] Bugfix (non-breaking change which fixes an issue) 11 | - [ ] New feature (non-breaking change which adds functionality) 12 | - [ ] Breaking change (fix or feature that would cause existing functionality to not work as expected) 13 | 14 | ## Checklist 15 | 16 | _Put an `x` in the boxes that apply. You can also fill these out after creating the PR. If you're unsure about any of them, don't hesitate to ask. We're here to help! This is simply a reminder of what we are going to look for before merging your code._ 17 | 18 | - [ ] Lint and unit tests pass locally with my changes 19 | - [ ] I have added tests that prove my fix is effective or that my feature works 20 | - [ ] I have added necessary documentation (if appropriate) 21 | 22 | ## Further comments 23 | 24 | If this is a relatively large or complex change, kick off the discussion by explaining why you chose the solution you did and what alternatives you considered, etc... -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "mocha-teamcity-reporter", 3 | "engines": { 4 | "node": ">=6" 5 | }, 6 | "version": "4.2.0", 7 | "description": "teamcity reporter for mocha", 8 | "main": "./lib/teamcity.js", 9 | "directories": { 10 | "lib": "./lib" 11 | }, 12 | "scripts": { 13 | "test": "mocha test/functional test/programmatic", 14 | "test-teamcity": "mocha test/functional test/programmatic --reporter lib/teamcity.js", 15 | "test-teamcity-example": "mocha test/example/exampleSimple.js --reporter lib/teamcity.js", 16 | "lint": "eslint lib/*.js test/**/*.js", 17 | "lint-fix": "eslint --fix lib/*.js test/**/*.js" 18 | }, 19 | "repository": { 20 | "type": "git", 21 | "url": "https://github.com/travisjeffery/mocha-teamcity-reporter" 22 | }, 23 | "keywords": [ 24 | "mocha", 25 | "teamcity", 26 | "reporter", 27 | "jetbrains" 28 | ], 29 | "peerDependencies": { 30 | "mocha": ">=6" 31 | }, 32 | "author": "travis jeffery", 33 | "maintainers": [ 34 | { 35 | "name": "Jamie Sherriff", 36 | "url": "https://github.com/jamie-sherriff" 37 | } 38 | ], 39 | "license": "MIT", 40 | "bugs": { 41 | "url": "https://github.com/travisjeffery/mocha-teamcity-reporter/issues" 42 | }, 43 | "devDependencies": { 44 | "chai": "^4.3.4", 45 | "eslint": "^7.25.0", 46 | "mocha": "^8.3.2" 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /test/example/failingIgnoreHookNoRoot.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Created by dj-glock on 04/06/2021. 3 | */ 4 | 'use strict'; 5 | const assert = require('assert'); 6 | 7 | describe('Hook Test Top Describe Fail', function () { 8 | before(function () { 9 | assert.strictEqual(1, 1); 10 | }); 11 | 12 | // Name of the function is an additional check for case when ignoreHookWithName is undefined 13 | beforeEach(function undefinedbeforeEachHookNoReporting() { 14 | throw new Error('Before each hook error fail'); 15 | }); 16 | 17 | it('Test Passing Test @pass', function () { 18 | assert.strictEqual(1, 1); 19 | }); 20 | 21 | // Name of the function is an additional check for case when ignoreHookWithName is undefined 22 | afterEach(function undefinedafterEachHook() { 23 | throw new Error('After each hook error fail'); 24 | }); 25 | 26 | after(function afterHookNoReporting() { 27 | throw new Error('After hook error fail'); 28 | }); 29 | }); 30 | 31 | describe('Hook Test Top Describe Pass', function () { 32 | before(function () { 33 | assert.strictEqual(1, 1); 34 | }); 35 | 36 | beforeEach(function beforeEachHookNoReporting() { 37 | assert.strictEqual(1, 1); 38 | }); 39 | 40 | it('Test Failing Test @fail', function () { 41 | assert.strictEqual(2, 1); 42 | }); 43 | 44 | it('Test Passing Test @pass', function () { 45 | assert.strictEqual(1, 1); 46 | }); 47 | 48 | afterEach(function afterEachHookNoReporting() { 49 | assert.strictEqual(1, 1); 50 | }); 51 | 52 | after(function afterHook() { 53 | assert.strictEqual(1, 1); 54 | }); 55 | }); -------------------------------------------------------------------------------- /.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "env": { 3 | "es6": true, 4 | "node": true, 5 | "browser": true, 6 | "mocha": true 7 | }, 8 | "extends": "eslint:recommended", 9 | "rules": { 10 | "no-useless-return": "error", 11 | "no-undefined": "error", 12 | "no-undef-init": "error", 13 | "no-eq-null": "error", 14 | "eqeqeq": "error", 15 | "no-alert": "error", 16 | "no-caller": "error", 17 | "no-shadow": "error", 18 | "no-new-func": "error", 19 | "default-case": "error", 20 | "comma-spacing": "error", 21 | "no-shadow-restricted-names": "error", 22 | "no-path-concat": "error", 23 | "no-use-before-define": "error", 24 | "no-duplicate-imports":"error", 25 | "no-useless-rename":"error", 26 | "space-in-parens":"error", 27 | "no-multiple-empty-lines": ["error", {"max":2, "maxEOF": 0, "maxBOF":1}], 28 | "space-before-function-paren": ["error", { 29 | "anonymous": "always", 30 | "named": "never", 31 | "asyncArrow": "always" 32 | }], 33 | "max-params":["error", 5], 34 | "key-spacing":"error", 35 | "strict": ["error", "global"], 36 | "prefer-promise-reject-errors":"error", 37 | "camelcase": ["error", { "properties": "never" }], 38 | "no-unused-vars": ["warn", { "vars": "all", "args": "after-used"}], 39 | "max-len": [ 40 | "error", 41 | 160, 42 | { 43 | "ignoreComments": true, 44 | "ignoreTrailingComments": true, 45 | "ignoreRegExpLiterals": true 46 | } 47 | ], 48 | //"no-mixed-spaces-and-tab": ["error", "smart-tabs"], 49 | "no-console": 0, 50 | "quotes": [ 51 | "error", 52 | "single", 53 | { 54 | "allowTemplateLiterals": true, 55 | "avoidEscape": true 56 | } 57 | ], 58 | "semi": [ 59 | "error", 60 | "always" 61 | ], 62 | //ES6 specific 63 | "arrow-spacing": "error", 64 | "template-curly-spacing": "error", 65 | 66 | //Rules to tighten below 67 | "complexity": ["error", 12], //Want to up this to like 5 68 | "arrow-body-style": ["off", "as-needed", {"requireReturnForObjectLiteral" : true}], 69 | "no-regex-spaces": "off", 70 | "no-var":"off", 71 | "prefer-arrow-callback": "off", 72 | "indent": [ 73 | "off", 74 | "tab" 75 | ] 76 | } 77 | } -------------------------------------------------------------------------------- /lib/teamcityBrowser.js: -------------------------------------------------------------------------------- 1 | /*eslint max-len: ["error", { "code": 220 }]*/ 2 | 3 | 'use strict'; 4 | /** 5 | * Teamcity doc reference https://confluence.jetbrains.com/display/TCD10/Build+Script+Interaction+with+TeamCity 6 | * 7 | * Module dependencies. 8 | */ 9 | var Base = window.Mocha.reporters.Base; 10 | var log; 11 | if(typeof window.customLogFunction === 'function'){ 12 | log = window.customLogFunction; 13 | } else{ 14 | log = console.log; 15 | } 16 | 17 | 18 | function escape(str) { 19 | if (!str) return ''; 20 | return str 21 | .toString() 22 | .replace(/\x1B.*?m/g, '') // eslint-disable-line no-control-regex 23 | .replace(/\|/g, '||') 24 | .replace(/\n/g, '|n') 25 | .replace(/\r/g, '|r') 26 | .replace(/\[/g, '|[') 27 | .replace(/\]/g, '|]') 28 | .replace(/\u0085/g, '|x') 29 | .replace(/\u2028/g, '|l') 30 | .replace(/\u2029/g, '|p') 31 | .replace(/'/g, '|\''); 32 | } 33 | 34 | /** 35 | * Initialize a new `Teamcity` reporter for the browser. 36 | * 37 | * @param {Runner} runner 38 | * @param {options} options 39 | * @api public 40 | */ 41 | function teamcity(runner) { 42 | Base.call(this, runner); 43 | var stats = this.stats; 44 | var flowId = document.title || new Date().getTime(); 45 | 46 | runner.on('suite', function (suite) { 47 | if (suite.root) return; 48 | suite.startDate = new Date(); 49 | log('##teamcity[testSuiteStarted name=\'' + escape(suite.title) + '\' flowId=\'' + flowId + '\']'); 50 | }); 51 | 52 | runner.on('test', function (test) { 53 | log('##teamcity[testStarted name=\'' + escape(test.title) + '\' flowId=\'' + flowId + '\' captureStandardOutput=\'true\']'); 54 | }); 55 | 56 | runner.on('fail', function (test, err) { 57 | log('##teamcity[testFailed name=\'' + escape(test.title) + '\' flowId=\'' + flowId + '\' message=\'' + escape(err.message) + '\' captureStandardOutput=\'true\' details=\'' + escape(err.stack) + '\']'); 58 | }); 59 | 60 | runner.on('pending', function (test) { 61 | log('##teamcity[testIgnored name=\'' + escape(test.title) + '\' flowId=\'' + flowId + '\' message=\'pending\']'); 62 | }); 63 | 64 | runner.on('test end', function (test) { 65 | log('##teamcity[testFinished name=\'' + escape(test.title) + '\' flowId=\'' + flowId + '\' duration=\'' + test.duration + '\']'); 66 | }); 67 | 68 | runner.on('suite end', function (suite) { 69 | if (suite.root) return; 70 | log('##teamcity[testSuiteFinished name=\'' + escape(suite.title) + '\' duration=\'' + (new Date() - suite.startDate) + '\' flowId=\'' + flowId + '\']'); 71 | }); 72 | 73 | runner.on('end', function () { 74 | var duration; 75 | (typeof stats === 'undefined') ? duration = '0' : duration = stats.duration; 76 | log('##teamcity[testSuiteFinished name=\'mocha.suite\' duration=\'' + duration + '\' flowId=\'' + flowId + '\']'); 77 | }); 78 | } 79 | 80 | window.Mocha.reporters.teamcity = teamcity; 81 | -------------------------------------------------------------------------------- /test/functional/topLevelSuiteOutput.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | const {execFile} = require('child_process'); 3 | const {assert} = require('chai'); 4 | const {logMochaOutput, getMochaPath} = require('../testHelpers'); 5 | const internalMochaPath = getMochaPath(); 6 | const path = require('path'); 7 | 8 | describe('Check TeamCity Output is correct with outer suite', function () { 9 | let teamCityStdout, teamCityStderr, teamCityOutputArray; 10 | 11 | function verifyResults() { 12 | it('stdout output should exist', function () { 13 | assert.isOk(teamCityStdout, 'has output'); 14 | assert.isOk(teamCityOutputArray, 'array of output is populated'); 15 | assert.isOk(teamCityOutputArray.length >= 9, 'at least 9 lines of output'); 16 | }); 17 | 18 | it('stderr output should not exist', function () { 19 | assert.isOk(teamCityStderr.length === 0); 20 | }); 21 | 22 | it('Prefix suite start is present', function () { 23 | const rowToCheck = teamCityOutputArray[0]; 24 | assert.isOk(/##teamcity\[testSuiteStarted/.test(rowToCheck)); 25 | assert.isOk(/name='test-outer-suite-name'/.test(rowToCheck)); 26 | assert.isOk(/flowId=/.test(rowToCheck)); 27 | assert.isOk(/]/.test(rowToCheck)); 28 | }); 29 | 30 | it('Prefix suite end is present', function () { 31 | const rowToCheck = teamCityOutputArray[9]; 32 | assert.isOk(/##teamcity\[testSuiteFinished/.test(rowToCheck)); 33 | assert.isNotOk(/name='mocha.suite'/.test(rowToCheck)); 34 | assert.isOk(/duration=/.test(rowToCheck)); 35 | assert.isOk(/flowId=/.test(rowToCheck)); 36 | assert.isOk(/]/.test(rowToCheck)); 37 | }); 38 | 39 | it('Skip Test Finished is ignored', function () { 40 | const rowToCheck = teamCityOutputArray[7]; 41 | assert.isOk(/##teamcity\[testIgnored/.test(rowToCheck)); 42 | assert.isOk(/name='Skipped Test @skip'/.test(rowToCheck)); 43 | assert.isOk(/flowId=/.test(rowToCheck)); 44 | assert.isOk(/message='Skipped Test @skip'/.test(rowToCheck)); 45 | assert.isNotOk(/duration=/.test(rowToCheck)); 46 | assert.isOk(/]/.test(rowToCheck)); 47 | }); 48 | 49 | it('Suite Root Finished is OK', function () { 50 | const rowToCheck = teamCityOutputArray[9]; 51 | assert.isOk(/##teamcity\[testSuiteFinished/.test(rowToCheck)); 52 | assert.isNotOk(/name='mocha.suite'/.test(rowToCheck)); 53 | assert.isOk(/duration=/.test(rowToCheck)); 54 | assert.isOk(/flowId=/.test(rowToCheck)); 55 | assert.isOk(/]/.test(rowToCheck)); 56 | }); 57 | } 58 | 59 | describe('specified as an env var', function () { 60 | before(function (done) { 61 | const opts = { 62 | env: Object.assign({ 63 | MOCHA_TEAMCITY_TOP_LEVEL_SUITE: 'test-outer-suite-name' 64 | }, process.env) 65 | }; 66 | 67 | execFile(internalMochaPath, [ 68 | path.join('test', 'test_data', 'simple.js'), 69 | '--reporter', 70 | 'lib/teamcity' 71 | ], opts, (err, stdout, stderr) => { 72 | teamCityStdout = stdout; 73 | teamCityStderr = stderr; 74 | teamCityOutputArray = stdout.split('\n'); 75 | logMochaOutput(stdout, stderr); 76 | done(); 77 | }); 78 | }); 79 | verifyResults(); 80 | }); 81 | 82 | describe('specified with --reporter-options', function () { 83 | before(function (done) { 84 | execFile(internalMochaPath, [ 85 | path.join('test', 'test_data', 'simple.js'), 86 | '--reporter', 87 | 'lib/teamcity', 88 | '--reporter-options', 89 | 'topLevelSuite=test-outer-suite-name' 90 | ], (err, stdout, stderr) => { 91 | teamCityStdout = stdout; 92 | teamCityStderr = stderr; 93 | teamCityOutputArray = stdout.split('\n'); 94 | logMochaOutput(stdout, stderr); 95 | done(); 96 | }); 97 | }); 98 | verifyResults(); 99 | }); 100 | 101 | }); 102 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | 4.2.0 / 2021-11-21 2 | ================== 3 | 4 | * Solve # Issue 65 "Pending tests are displayed as Passed in teamcity UI" by PR #66 (@fernyb ) 5 | * Solved by producing correct output as per teamcity documentation, functionality can be disabled by reporter option `displayIgnoredAsIgnored` 6 | 7 | 4.1.0 / 2021-05-30 8 | ================== 9 | 10 | * Solve # Issue 61 "Failed tests appears as successful in TeamCity" by PR #63 (@DJ-Glock) 11 | * Solved by adding postfix _hook for flowId for hooks. FlowIds will never intersect. 12 | 13 | 4.0.0 / 2021-05-03 14 | ================== 15 | 16 | * Breaking: Only supported on node.js 6 and above 17 | * Breaking: Only Mocha version 6 and above is now supported 18 | * Please remain on `mocha-teamcity-reporter@3` if this is an issue 19 | * New reporter option `ignoreHookWithName` to skip reporting for hooks with title containing some word (@DJ-Glock) 20 | * Implement 'hook end' event (@DJ-GLock) 21 | * General maintenance and tidy up (@DJ-Glock) 22 | 23 | 3.0.0 / 2019-01-21 24 | ================== 25 | 26 | * Change mocha to peer dependency 27 | * Support mocha version 6 28 | * Breaking: focus on only support node.js environments (Please ) 29 | * Breaking: Remove phantomJs support only supports environments which have require 30 | * Potential Breaking: Remove Redundant top level mocha.suite 31 | * Drop the duration on messages if mocha returns undefined/null (for example skipped test) TeamCity will then use received timestamps to calculate duration 32 | * Support Show diff between expected and actual values 33 | 34 | TODO 35 | comparisonFailure service message attributes 36 | 37 | 2.5.2 / 2019-01-21 38 | ================== 39 | 40 | * Restrict mocha dependency to less than 6 due to compatibility issues 41 | 42 | 2.5.1 / 2018-09-27 43 | ================== 44 | 45 | * Vuejs/Webpack compatibility, solves #45 46 | 47 | 2.5.0 / 2018-09-18 48 | ================== 49 | 50 | * Add test in hook option, solves #35 51 | 52 | 2.4.0 / 2018-04-18 53 | ================== 54 | 55 | * Add Browser Support back in by use of separate file, solves #41 56 | 57 | 2.3.0 / 2018-04-18 58 | ================== 59 | 60 | * Add use stdError option solves #31 61 | 62 | 2.2.2 / 2018-02-27 63 | ================== 64 | 65 | * Fix issue #39 66 | * Add real teamcity tests 67 | 68 | 2.2.1 / 2018-02-06 69 | ================== 70 | 71 | * Merge pull request #38 reporterOptions are optional from chge/master 72 | * Add a test to catch above PR 73 | 74 | 2.2.0 / 2018-02-05 75 | ================== 76 | 77 | * Maintenance Update, Merged forked branch back into master branch by Travis 78 | * Breaking change, supports node 4+ only 79 | * Support flowId's 80 | * Why use flowIds? Flow tracking is necessary, for example, to distinguish separate processes running in parallel 81 | * This defaults to process.pid, so it works with concurrent task runners (Gulp/Grunt etc) 82 | * Other small bug fixes 83 | * Functional tests 84 | 85 | 2.1.0 / 2017-10-19 86 | ================== 87 | 88 | * add top level suite option (@davidmfoley) 89 | 90 | 2.0.1 / 2017-08-22 91 | ================== 92 | 93 | * Bug Fixes 94 | 95 | 2.0.0 / 2017-08-20 96 | ================== 97 | 98 | * Add FlowId 99 | * Minimum nodejs engine of 4+ 100 | * Add some eslint magic 101 | 102 | 1.1.0 / 2016-08-24 103 | ================== 104 | 105 | * Support running in the browser with Mocha without require.js 106 | 107 | 1.0.1 / 2016-07-18 108 | ================== 109 | 110 | * Remove escape sequences from reporting 111 | * Fix NaN duration 112 | 113 | 1.0.0 / 2015-09-30 114 | ================== 115 | 116 | * Merge pull request #14 from debitoor/master 117 | * add err.stack to details of error 118 | * Merge pull request #8 from bdefore/master 119 | * Update teamcity.js 120 | * Merge pull request #6 from pandell/mocha-phantomjs 121 | -------------------------------------------------------------------------------- /test/functional/testOutput.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Created by jamie on 12/08/2017. 3 | * Must be run from the root project dir 4 | */ 5 | 'use strict'; 6 | 7 | const {execFile} = require('child_process'); 8 | const {assert} = require('chai'); 9 | const path = require('path'); 10 | 11 | const { logMochaOutput, getMochaPath } = require('../testHelpers'); 12 | 13 | const internalMochaPath = getMochaPath(); 14 | 15 | describe('Check TeamCity Output is correct', function () { 16 | let teamCityStdout, teamCityStderr, teamCityOutputArray; 17 | 18 | before(function (done) { 19 | execFile(internalMochaPath, [path.join('test', 'test_data', 'simple.js'), '--reporter', 'lib/teamcity'], (err, stdout, stderr) => { 20 | teamCityStdout = stdout; 21 | teamCityStderr = stderr; 22 | teamCityOutputArray = stdout.split('\n'); 23 | logMochaOutput(stdout, stderr); 24 | done(); 25 | }); 26 | }); 27 | 28 | it('Output should exist', function () { 29 | assert.isOk(teamCityStdout); 30 | assert.isOk(teamCityOutputArray); 31 | assert.isOk(teamCityStderr.length === 0); 32 | assert.isOk(teamCityOutputArray.length >= 10); 33 | }); 34 | 35 | it('Suite started is OK', function () { 36 | const rowToCheck = teamCityOutputArray[0]; 37 | assert.isOk(/##teamcity\[testSuiteStarted/.test(rowToCheck)); 38 | assert.isOk(/name='Top Describe'/.test(rowToCheck)); 39 | assert.isOk(/flowId=/.test(rowToCheck)); 40 | assert.isOk(/]/.test(rowToCheck)); 41 | }); 42 | 43 | it('Test started is OK', function () { 44 | const rowToCheck = teamCityOutputArray[1]; 45 | assert.isOk(/##teamcity\[testStarted/.test(rowToCheck)); 46 | assert.isOk(/name='Passing Test @pass'/.test(rowToCheck)); 47 | assert.isOk(/flowId=/.test(rowToCheck)); 48 | assert.isOk(/]/.test(rowToCheck)); 49 | }); 50 | 51 | it('Passing Test Finished is OK', function () { 52 | const rowToCheck = teamCityOutputArray[2]; 53 | assert.isOk(/##teamcity\[testFinished/.test(rowToCheck)); 54 | assert.isOk(/name='Passing Test @pass'/.test(rowToCheck)); 55 | assert.isOk(/flowId=/.test(rowToCheck)); 56 | assert.isOk(/duration=/.test(rowToCheck)); 57 | assert.isOk(/]/.test(rowToCheck)); 58 | }); 59 | 60 | it('Test Failed Started is OK', function () { 61 | const rowToCheck = teamCityOutputArray[3]; 62 | assert.isOk(/##teamcity\[testStarted/.test(rowToCheck)); 63 | assert.isOk(/name='Failing Test @fail'/.test(rowToCheck)); 64 | assert.isOk(/flowId=/.test(rowToCheck)); 65 | assert.isOk(/duration=/.test(rowToCheck) === false); 66 | assert.isOk(/]/.test(rowToCheck)); 67 | }); 68 | 69 | it('Test Failed is Failing', function () { 70 | const rowToCheck = teamCityOutputArray[4]; 71 | assert.isOk(/##teamcity\[testFailed/.test(rowToCheck)); 72 | assert.isOk(/name='Failing Test @fail'/.test(rowToCheck)); 73 | assert.isOk(/flowId=/.test(rowToCheck)); 74 | assert.isOk(/duration=/.test(rowToCheck) === false); 75 | assert.isOk(/details='/.test(rowToCheck)); 76 | assert.isOk(/AssertionError/.test(rowToCheck)); 77 | assert.isOk(/|n/.test(rowToCheck)); 78 | assert.isOk(/|simple.js:11:11/.test(rowToCheck)); 79 | assert.isOk(/captureStandardOutput='true'/.test(rowToCheck)); 80 | assert.isOk(/]/.test(rowToCheck)); 81 | }); 82 | 83 | it('Failing Test Finished is OK', function () { 84 | const rowToCheck = teamCityOutputArray[5]; 85 | assert.isOk(/##teamcity\[testFinished/.test(rowToCheck)); 86 | assert.isOk(/name='Failing Test @fail'/.test(rowToCheck)); 87 | assert.isOk(/flowId=/.test(rowToCheck)); 88 | assert.isOk(/duration=/.test(rowToCheck)); 89 | assert.isOk(/]/.test(rowToCheck)); 90 | }); 91 | 92 | it('Skip Test Finished is ignored', function () { 93 | const rowToCheck = teamCityOutputArray[6]; 94 | assert.isOk(/##teamcity\[testIgnored/.test(rowToCheck)); 95 | assert.isOk(/name='Skipped Test @skip'/.test(rowToCheck)); 96 | assert.isOk(/flowId=/.test(rowToCheck)); 97 | assert.isOk(/message='Skipped Test @skip'/.test(rowToCheck)); 98 | assert.isOk(/duration=/.test(rowToCheck) === false); 99 | assert.isOk(/]/.test(rowToCheck)); 100 | }); 101 | 102 | it('Skip Test Finished is OK', function () { 103 | const rowToCheck = teamCityOutputArray[7]; 104 | assert.isOk(/##teamcity\[testFinished/.test(rowToCheck)); 105 | assert.isOk(/name='Skipped Test @skip'/.test(rowToCheck)); 106 | assert.isOk(/flowId=/.test(rowToCheck)); 107 | assert.isNotOk(/duration=/.test(rowToCheck)); 108 | assert.isOk(/]/.test(rowToCheck)); 109 | }); 110 | 111 | it('Suite Finished is OK', function () { 112 | const rowToCheck = teamCityOutputArray[8]; 113 | assert.isOk(/##teamcity\[testSuiteFinished/.test(rowToCheck)); 114 | assert.isOk(/name='Top Describe'/.test(rowToCheck)); 115 | assert.isOk(/duration=/.test(rowToCheck)); 116 | assert.isOk(/flowId=/.test(rowToCheck)); 117 | assert.isOk(/]/.test(rowToCheck)); 118 | }); 119 | 120 | it('Suite Root Finished is OK', function () { 121 | const rowToCheck = teamCityOutputArray[8]; 122 | assert.isEmpty(teamCityOutputArray[9], 'Last row should be empty'); 123 | assert.isOk(/##teamcity\[testSuiteFinished/.test(rowToCheck)); 124 | assert.isNotOk(/name='mocha.suite'/.test(rowToCheck)); 125 | assert.isOk(/duration=/.test(rowToCheck)); 126 | assert.isOk(/flowId=/.test(rowToCheck)); 127 | assert.isOk(/]/.test(rowToCheck)); 128 | }); 129 | 130 | }); 131 | -------------------------------------------------------------------------------- /test/functional/useStdError.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | const {execFile} = require('child_process'); 3 | const {assert} = require('chai'); 4 | const { logMochaOutput, getMochaPath } = require('../testHelpers'); 5 | const internalMochaPath = getMochaPath(); 6 | const path = require('path'); 7 | 8 | describe('Check TeamCity Output is correct with stdError option', function () { 9 | let teamCityStdout, teamCityStderr, teamCityOutputArray, teamCityErrorOutputArray; 10 | function verifyResults() { 11 | it('stdout output should exist', function () { 12 | assert.isOk(teamCityStdout, 'has output'); 13 | assert.isOk(teamCityOutputArray, 'array of output is populated'); 14 | assert.isOk(teamCityOutputArray.length >= 9, 'at least 9 lines of output'); 15 | assert.lengthOf(teamCityOutputArray, 9); 16 | }); 17 | 18 | it('stderr output should exist', function () { 19 | assert.isOk(teamCityStderr); 20 | assert.isAbove(teamCityStderr.length, 15); 21 | assert.lengthOf(teamCityErrorOutputArray, 2); 22 | }); 23 | 24 | 25 | it('stdout output should exist', function () { 26 | assert.isOk(teamCityStdout); 27 | assert.isOk(teamCityOutputArray); 28 | assert.isOk(teamCityOutputArray.length >= 9); 29 | }); 30 | 31 | it('Suite started is OK', function () { 32 | const rowToCheck = teamCityOutputArray[0]; 33 | assert.isOk(/##teamcity\[testSuiteStarted/.test(rowToCheck)); 34 | assert.isOk(/name='Top Describe'/.test(rowToCheck)); 35 | assert.isOk(/flowId=/.test(rowToCheck)); 36 | assert.isOk(/]/.test(rowToCheck)); 37 | }); 38 | 39 | it('Test started is OK', function () { 40 | const rowToCheck = teamCityOutputArray[1]; 41 | assert.match(rowToCheck, /##teamcity\[testStarted/); 42 | assert.match(rowToCheck, /name='Passing Test @pass'/); 43 | assert.match(rowToCheck, /flowId=/); 44 | assert.match(rowToCheck, /]/); 45 | }); 46 | 47 | it('Passing Test Finished is OK', function () { 48 | const rowToCheck = teamCityOutputArray[2]; 49 | assert.isOk(/##teamcity\[testFinished/.test(rowToCheck)); 50 | assert.isOk(/name='Passing Test @pass'/.test(rowToCheck)); 51 | assert.isOk(/flowId=/.test(rowToCheck)); 52 | assert.isOk(/duration=/.test(rowToCheck)); 53 | assert.isOk(/]/.test(rowToCheck)); 54 | }); 55 | 56 | it('Test Failed Started is OK', function () { 57 | const rowToCheck = teamCityOutputArray[3]; 58 | assert.isOk(/##teamcity\[testStarted/.test(rowToCheck)); 59 | assert.isOk(/name='Failing Test @fail'/.test(rowToCheck)); 60 | assert.isOk(/flowId=/.test(rowToCheck)); 61 | assert.isOk(/duration=/.test(rowToCheck) === false); 62 | assert.isOk(/]/.test(rowToCheck)); 63 | }); 64 | 65 | it('Test Failed is Failing', function () { 66 | const rowToCheck = teamCityErrorOutputArray; 67 | assert.isOk(/##teamcity\[testFailed/.test(rowToCheck)); 68 | assert.isOk(/name='Failing Test @fail'/.test(rowToCheck)); 69 | assert.isOk(/flowId=/.test(rowToCheck)); 70 | assert.isOk(/duration=/.test(rowToCheck) === false); 71 | assert.isOk(/details='/.test(rowToCheck)); 72 | assert.isOk(/AssertionError/.test(rowToCheck)); 73 | assert.isOk(/|n/.test(rowToCheck)); 74 | assert.isOk(/|simple.js:11:11/.test(rowToCheck)); 75 | assert.isOk(/captureStandardOutput='true'/.test(rowToCheck)); 76 | assert.isOk(/]/.test(rowToCheck)); 77 | }); 78 | 79 | it('Failing Test Finished is OK', function () { 80 | const rowToCheck = teamCityOutputArray[4]; 81 | assert.match(rowToCheck, /##teamcity\[testFinished/); 82 | assert.match(rowToCheck, /name='Failing Test @fail'/); 83 | assert.match(rowToCheck, /flowId=/); 84 | assert.match(rowToCheck, /duration=/); 85 | assert.match(rowToCheck, /]/); 86 | }); 87 | 88 | it('Skip Test Finished is ignored', function () { 89 | const rowToCheck = teamCityOutputArray[5]; 90 | assert.isOk(/##teamcity\[testIgnored/.test(rowToCheck)); 91 | assert.isOk(/name='Skipped Test @skip'/.test(rowToCheck)); 92 | assert.isOk(/flowId=/.test(rowToCheck)); 93 | assert.isOk(/message='Skipped Test @skip'/.test(rowToCheck)); 94 | assert.isOk(/duration=/.test(rowToCheck) === false); 95 | assert.isOk(/]/.test(rowToCheck)); 96 | }); 97 | 98 | it('Skip Test Finished is OK', function () { 99 | const rowToCheck = teamCityOutputArray[6]; 100 | assert.isOk(/##teamcity\[testFinished/.test(rowToCheck)); 101 | assert.isOk(/name='Skipped Test @skip'/.test(rowToCheck)); 102 | assert.isOk(/flowId=/.test(rowToCheck)); 103 | assert.isNotOk(/duration=/.test(rowToCheck)); 104 | assert.isOk(/]/.test(rowToCheck)); 105 | }); 106 | 107 | it('Suite Finished is OK', function () { 108 | const rowToCheck = teamCityOutputArray[7]; 109 | assert.isOk(/##teamcity\[testSuiteFinished/.test(rowToCheck)); 110 | assert.isOk(/name='Top Describe'/.test(rowToCheck)); 111 | assert.isOk(/duration=/.test(rowToCheck)); 112 | assert.isOk(/flowId=/.test(rowToCheck)); 113 | assert.isOk(/]/.test(rowToCheck)); 114 | }); 115 | 116 | it('Suite Root Finished is OK', function () { 117 | const rowToCheck = teamCityOutputArray[7]; 118 | assert.isOk(/##teamcity\[testSuiteFinished/.test(rowToCheck)); 119 | assert.isNotOk(/name='mocha.suite'/.test(rowToCheck)); 120 | assert.isOk(/duration=/.test(rowToCheck)); 121 | assert.isOk(/flowId=/.test(rowToCheck)); 122 | assert.isOk(/]/.test(rowToCheck)); 123 | }); 124 | } 125 | 126 | describe('specified as an env var', function () { 127 | before(function (done) { 128 | const opts = { 129 | env: Object.assign({ 130 | ['USE_STD_ERROR']: 'true' 131 | }, process.env) 132 | }; 133 | 134 | execFile(internalMochaPath, [ 135 | 'test/test_data', 136 | '--reporter', 137 | 'lib/teamcity' 138 | ], opts, (err, stdout, stderr) => { 139 | teamCityStdout = stdout; 140 | teamCityStderr = stderr; 141 | teamCityOutputArray = stdout.split('\n'); 142 | teamCityErrorOutputArray = stderr.split('\n'); 143 | logMochaOutput(stdout, stderr); 144 | done(); 145 | }); 146 | }); 147 | verifyResults(); 148 | }); 149 | 150 | describe('specified with --reporter-options', function () { 151 | before(function (done) { 152 | execFile(internalMochaPath, [ 153 | path.join('test', 'test_data', 'simple.js'), 154 | '--reporter', 155 | 'lib/teamcity', 156 | '--reporter-options', 157 | 'useStdError=true' 158 | ], (err, stdout, stderr) => { 159 | teamCityStdout = stdout; 160 | teamCityStderr = stderr; 161 | teamCityOutputArray = stdout.split('\n'); 162 | teamCityErrorOutputArray = stderr.split('\n'); 163 | logMochaOutput(stdout, stderr); 164 | done(); 165 | }); 166 | }); 167 | verifyResults(); 168 | }); 169 | 170 | }); 171 | -------------------------------------------------------------------------------- /test/functional/recordHookFailuresDisabled.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | const {execFile} = require('child_process'); 3 | const {assert} = require('chai'); 4 | const { logMochaOutput, getMochaPath } = require('../testHelpers'); 5 | const internalMochaPath = getMochaPath(); 6 | const path = require('path'); 7 | 8 | describe('Check TeamCity Output is correct with recordHookFailures option disabled', function () { 9 | let teamCityStdout, teamCityStderr, teamCityOutputArray; 10 | function verifyResults() { 11 | it('1 stdout output should exist', function () { 12 | assert.isOk(teamCityStdout, 'has output'); 13 | assert.isOk(teamCityOutputArray, 'array of output is populated'); 14 | assert.lengthOf(teamCityOutputArray, 11); 15 | assert.isEmpty(teamCityOutputArray[10]); 16 | }); 17 | 18 | it('2 stderr output should not exist', function () { 19 | assert.isEmpty(teamCityStderr); 20 | }); 21 | 22 | it('3 testSuiteStarted for Suite1', function () { 23 | const rowToCheck = teamCityOutputArray[0]; 24 | assert.isOk(/##teamcity\[testSuiteStarted/.test(rowToCheck)); 25 | assert.isOk(/name='Hook Test Top Describe Fail'/.test(rowToCheck)); 26 | assert.isOk(/flowId=/.test(rowToCheck)); 27 | assert.notMatch(rowToCheck, /flowId=.*_hook/); 28 | assert.isOk(/]/.test(rowToCheck)); 29 | }); 30 | 31 | it('4 testFailed for before all hook', function () { 32 | const rowToCheck = teamCityOutputArray[1]; 33 | assert.isOk(/##teamcity\[testFailed/.test(rowToCheck)); 34 | assert.isOk(/name='"before all" hook/.test(rowToCheck)); 35 | assert.isOk(/details='/.test(rowToCheck)); 36 | assert.isOk(/Error: Before hook error fail/.test(rowToCheck)); 37 | assert.isOk(/|n/.test(rowToCheck)); 38 | assert.isOk(/|failingHook.js:29:12/.test(rowToCheck)); 39 | assert.isOk(/captureStandardOutput='true'/.test(rowToCheck)); 40 | assert.match(rowToCheck, /flowId=.*_hook/); 41 | assert.isOk(/duration=/.test(rowToCheck) === false); 42 | assert.isOk(/]/.test(rowToCheck)); 43 | }); 44 | 45 | it('5 testSuiteFinished for Suite1', function () { 46 | const rowToCheck = teamCityOutputArray[2]; 47 | assert.isOk(/##teamcity\[testSuiteFinished/.test(rowToCheck)); 48 | assert.isOk(/name='Hook Test Top Describe Fail'/.test(rowToCheck)); 49 | assert.isOk(/duration=/.test(rowToCheck)); 50 | assert.isOk(/flowId=/.test(rowToCheck)); 51 | assert.notMatch(rowToCheck, /flowId=.*_hook/); 52 | assert.isOk(/]/.test(rowToCheck)); 53 | }); 54 | 55 | it('6 testSuiteStarted for Suite2', function () { 56 | const rowToCheck = teamCityOutputArray[3]; 57 | assert.isOk(/##teamcity\[testSuiteStarted/.test(rowToCheck)); 58 | assert.isOk(/name='Hook Test Top Describe Pass'/.test(rowToCheck)); 59 | assert.isOk(/flowId=/.test(rowToCheck)); 60 | assert.notMatch(rowToCheck, /flowId=.*_hook/); 61 | assert.isOk(/]/.test(rowToCheck)); 62 | }); 63 | 64 | it('7 testStarted for Failing Test', function () { 65 | const rowToCheck = teamCityOutputArray[4]; 66 | assert.isOk(/##teamcity\[testStarted/.test(rowToCheck)); 67 | assert.isOk(/name='Test Failing Test @fail'/.test(rowToCheck)); 68 | assert.isOk(/flowId=/.test(rowToCheck)); 69 | assert.notMatch(rowToCheck, /flowId=.*_hook/); 70 | assert.isOk(/]/.test(rowToCheck)); 71 | }); 72 | 73 | it('8 testFailed for Failing Test', function () { 74 | const rowToCheck = teamCityOutputArray[5]; 75 | assert.isOk(/##teamcity\[testFailed/.test(rowToCheck)); 76 | assert.isOk(/name='Test Failing Test @fail'/.test(rowToCheck)); 77 | assert.isOk(/flowId=/.test(rowToCheck)); 78 | assert.notMatch(rowToCheck, /flowId=.*_hook/); 79 | assert.isOk(/duration=/.test(rowToCheck) === false); 80 | assert.isOk(/details='/.test(rowToCheck)); 81 | assert.isOk(/AssertionError/.test(rowToCheck)); 82 | assert.isOk(/|n/.test(rowToCheck)); 83 | assert.isOk(/|failingHook.js:29:12/.test(rowToCheck)); 84 | assert.isOk(/captureStandardOutput='true'/.test(rowToCheck)); 85 | assert.isOk(/]/.test(rowToCheck)); 86 | }); 87 | 88 | it('9 testFinished for Failing Test', function () { 89 | const rowToCheck = teamCityOutputArray[6]; 90 | assert.match(rowToCheck, /##teamcity\[testFinished/); 91 | assert.match(rowToCheck, /name='Test Failing Test @fail'/); 92 | assert.match(rowToCheck, /flowId=/); 93 | assert.match(rowToCheck, /duration=/); 94 | assert.match(rowToCheck, /]/); 95 | }); 96 | 97 | it('10 testStarted for Passing Test', function () { 98 | const rowToCheck = teamCityOutputArray[7]; 99 | assert.isOk(/##teamcity\[testStarted/.test(rowToCheck)); 100 | assert.isOk(/name='Test Passing Test @pass'/.test(rowToCheck)); 101 | assert.isOk(/flowId=/.test(rowToCheck)); 102 | assert.notMatch(rowToCheck, /flowId=.*_hook/); 103 | assert.isOk(/]/.test(rowToCheck)); 104 | }); 105 | 106 | 107 | it('11 testFinished for Passing Test', function () { 108 | const rowToCheck = teamCityOutputArray[8]; 109 | assert.isOk(/##teamcity\[testFinished/.test(rowToCheck)); 110 | assert.isOk(/name='Test Passing Test @pass'/.test(rowToCheck)); 111 | assert.isOk(/flowId=/.test(rowToCheck)); 112 | assert.notMatch(rowToCheck, /flowId=.*_hook/); 113 | assert.isOk(/duration=/.test(rowToCheck)); 114 | assert.isOk(/]/.test(rowToCheck)); 115 | }); 116 | 117 | it('12 testSuiteFinished for Suite2', function () { 118 | const rowToCheck = teamCityOutputArray[9]; 119 | assert.isOk(/##teamcity\[testSuiteFinished/.test(rowToCheck)); 120 | assert.isOk(/name='Hook Test Top Describe Pass'/.test(rowToCheck)); 121 | assert.isOk(/duration=/.test(rowToCheck)); 122 | assert.isOk(/flowId=/.test(rowToCheck)); 123 | assert.notMatch(rowToCheck, /flowId=.*_hook/); 124 | assert.isOk(/]/.test(rowToCheck)); 125 | }); 126 | } 127 | 128 | describe('specified as an env var', function () { 129 | before(function (done) { 130 | const opts = {}; 131 | 132 | execFile(internalMochaPath, [ 133 | 'test/example/failingHook.js', 134 | '--reporter', 135 | 'lib/teamcity' 136 | ], opts, (err, stdout, stderr) => { 137 | teamCityStdout = stdout; 138 | teamCityStderr = stderr; 139 | teamCityOutputArray = stdout.split('\n'); 140 | logMochaOutput(stdout, stderr); 141 | done(); 142 | }); 143 | }); 144 | verifyResults(); 145 | }); 146 | 147 | describe('specified with --reporter-options', function () { 148 | before(function (done) { 149 | execFile(internalMochaPath, [ 150 | path.join('test', 'example', 'failingHook.js'), 151 | '--reporter', 152 | 'lib/teamcity' 153 | ], (err, stdout, stderr) => { 154 | teamCityStdout = stdout; 155 | teamCityStderr = stderr; 156 | teamCityOutputArray = stdout.split('\n'); 157 | logMochaOutput(stdout, stderr); 158 | done(); 159 | }); 160 | }); 161 | verifyResults(); 162 | }); 163 | }); 164 | -------------------------------------------------------------------------------- /test/functional/actualVsExpected.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | const {execFile} = require('child_process'); 3 | const {assert} = require('chai'); 4 | const { logMochaOutput, getMochaPath } = require('../testHelpers'); 5 | const internalMochaPath = getMochaPath(); 6 | const path = require('path'); 7 | 8 | describe('Check TeamCity Output is correct with actualVsExpected option', function () { 9 | let teamCityStdout, teamCityStderr, teamCityOutputArray, teamCityErrorOutputArray; 10 | function verifyResults() { 11 | it('stdout output should exist', function () { 12 | assert.isOk(teamCityStdout, 'has output'); 13 | assert.isOk(teamCityOutputArray, 'array of output is populated'); 14 | assert.isOk(teamCityOutputArray.length >= 10, 'at least 10 lines of output'); 15 | assert.lengthOf(teamCityOutputArray, 10); 16 | }); 17 | 18 | it('stderr output should exist', function () { 19 | assert.isNotOk(teamCityStderr); 20 | assert.isOk(teamCityStderr.length === 0); 21 | assert.lengthOf(teamCityErrorOutputArray, 1); 22 | }); 23 | 24 | 25 | it('stdout output should exist', function () { 26 | assert.isOk(teamCityStdout); 27 | assert.isOk(teamCityOutputArray); 28 | assert.isOk(teamCityOutputArray.length >= 9); 29 | }); 30 | 31 | it('Suite started is OK', function () { 32 | const rowToCheck = teamCityOutputArray[0]; 33 | assert.isOk(/##teamcity\[testSuiteStarted/.test(rowToCheck)); 34 | assert.isOk(/name='Top Describe'/.test(rowToCheck)); 35 | assert.isOk(/flowId=/.test(rowToCheck)); 36 | assert.isOk(/]/.test(rowToCheck)); 37 | }); 38 | 39 | it('Test started is OK', function () { 40 | const rowToCheck = teamCityOutputArray[1]; 41 | assert.match(rowToCheck, /##teamcity\[testStarted/); 42 | assert.match(rowToCheck, /name='Passing Test @pass'/); 43 | assert.match(rowToCheck, /flowId=/); 44 | assert.match(rowToCheck, /]/); 45 | }); 46 | 47 | it('Passing Test Finished is OK', function () { 48 | const rowToCheck = teamCityOutputArray[2]; 49 | assert.isOk(/##teamcity\[testFinished/.test(rowToCheck)); 50 | assert.isOk(/name='Passing Test @pass'/.test(rowToCheck)); 51 | assert.isOk(/flowId=/.test(rowToCheck)); 52 | assert.isOk(/duration=/.test(rowToCheck)); 53 | assert.isOk(/]/.test(rowToCheck)); 54 | }); 55 | 56 | it('Test Failed Started is OK', function () { 57 | const rowToCheck = teamCityOutputArray[3]; 58 | assert.isOk(/##teamcity\[testStarted/.test(rowToCheck)); 59 | assert.isOk(/name='Failing Test @fail'/.test(rowToCheck)); 60 | assert.isOk(/flowId=/.test(rowToCheck)); 61 | assert.isOk(/duration=/.test(rowToCheck) === false); 62 | assert.isOk(/]/.test(rowToCheck)); 63 | }); 64 | 65 | it('Test Failed is Failing', function () { 66 | const rowToCheck = teamCityOutputArray[4]; 67 | assert.isOk(/##teamcity\[testFailed/.test(rowToCheck)); 68 | assert.isOk(/name='Failing Test @fail'/.test(rowToCheck)); 69 | assert.isOk(/flowId=/.test(rowToCheck)); 70 | assert.isOk(/duration=/.test(rowToCheck) === false); 71 | assert.isOk(/details='/.test(rowToCheck)); 72 | assert.isOk(/AssertionError/.test(rowToCheck)); 73 | assert.isOk(/|n/.test(rowToCheck)); 74 | assert.isOk(/|simple.js:11:11/.test(rowToCheck)); 75 | assert.isOk(/captureStandardOutput='true'/.test(rowToCheck)); 76 | assert.isOk(/]/.test(rowToCheck)); 77 | //check keys 78 | assert.match(rowToCheck, /actual=/); 79 | assert.match(rowToCheck, /expected=/); 80 | //check both key and value 81 | assert.match(rowToCheck, /actual='2'/); 82 | assert.match(rowToCheck, /type='comparisonFailure'/); 83 | assert.match(rowToCheck, /expected='1'/); 84 | }); 85 | 86 | it('Failing Test Finished is OK', function () { 87 | const rowToCheck = teamCityOutputArray[5]; 88 | assert.match(rowToCheck, /##teamcity\[testFinished/); 89 | assert.match(rowToCheck, /name='Failing Test @fail'/); 90 | assert.match(rowToCheck, /flowId=/); 91 | assert.match(rowToCheck, /duration=/); 92 | assert.match(rowToCheck, /]/); 93 | }); 94 | 95 | it('Skip Test Finished is ignored', function () { 96 | const rowToCheck = teamCityOutputArray[6]; 97 | assert.isOk(/##teamcity\[testIgnored/.test(rowToCheck)); 98 | assert.isOk(/name='Skipped Test @skip'/.test(rowToCheck)); 99 | assert.isOk(/flowId=/.test(rowToCheck)); 100 | assert.isOk(/message='Skipped Test @skip'/.test(rowToCheck)); 101 | assert.isOk(/duration=/.test(rowToCheck) === false); 102 | assert.isOk(/]/.test(rowToCheck)); 103 | }); 104 | 105 | it('Skip Test Finished is OK', function () { 106 | const rowToCheck = teamCityOutputArray[7]; 107 | assert.isOk(/##teamcity\[testFinished/.test(rowToCheck)); 108 | assert.isOk(/name='Skipped Test @skip'/.test(rowToCheck)); 109 | assert.isOk(/flowId=/.test(rowToCheck)); 110 | assert.isNotOk(/duration=/.test(rowToCheck)); 111 | assert.isOk(/]/.test(rowToCheck)); 112 | }); 113 | 114 | it('Suite Finished is OK', function () { 115 | const rowToCheck = teamCityOutputArray[8]; 116 | assert.isOk(/##teamcity\[testSuiteFinished/.test(rowToCheck)); 117 | assert.isOk(/name='Top Describe'/.test(rowToCheck)); 118 | assert.isOk(/duration=/.test(rowToCheck)); 119 | assert.isOk(/flowId=/.test(rowToCheck)); 120 | assert.isOk(/]/.test(rowToCheck)); 121 | }); 122 | 123 | it('Suite Root Finished is OK', function () { 124 | const rowToCheck = teamCityOutputArray[8]; 125 | assert.isOk(/##teamcity\[testSuiteFinished/.test(rowToCheck)); 126 | assert.isNotOk(/name='mocha.suite'/.test(rowToCheck)); 127 | assert.isOk(/duration=/.test(rowToCheck)); 128 | assert.isOk(/flowId=/.test(rowToCheck)); 129 | assert.isOk(/]/.test(rowToCheck)); 130 | }); 131 | } 132 | 133 | describe('specified as an env var', function () { 134 | before(function (done) { 135 | const opts = { 136 | env: Object.assign({ 137 | ['ACTUAL_VS_EXPECTED']: 'true' 138 | }, process.env) 139 | }; 140 | 141 | execFile(internalMochaPath, [ 142 | 'test/test_data', 143 | '--reporter', 144 | 'lib/teamcity' 145 | ], opts, (err, stdout, stderr) => { 146 | teamCityStdout = stdout; 147 | teamCityStderr = stderr; 148 | teamCityOutputArray = stdout.split('\n'); 149 | teamCityErrorOutputArray = stderr.split('\n'); 150 | logMochaOutput(stdout, stderr); 151 | done(); 152 | }); 153 | }); 154 | verifyResults(); 155 | }); 156 | 157 | describe('specified with --reporter-options', function () { 158 | before(function (done) { 159 | execFile(internalMochaPath, [ 160 | path.join('test', 'test_data', 'simple.js'), 161 | '--reporter', 162 | 'lib/teamcity', 163 | '--reporter-options', 164 | 'actualVsExpected=true' 165 | ], (err, stdout, stderr) => { 166 | teamCityStdout = stdout; 167 | teamCityStderr = stderr; 168 | teamCityOutputArray = stdout.split('\n'); 169 | teamCityErrorOutputArray = stderr.split('\n'); 170 | logMochaOutput(stdout, stderr); 171 | done(); 172 | }); 173 | }); 174 | verifyResults(); 175 | }); 176 | 177 | }); 178 | -------------------------------------------------------------------------------- /test/functional/testMultipleOptions.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | const {execFile} = require('child_process'); 3 | const {assert} = require('chai'); 4 | const { logMochaOutput, getMochaPath } = require('../testHelpers'); 5 | const internalMochaPath = getMochaPath(); 6 | const path = require('path'); 7 | 8 | describe('Check TeamCity Output is correct with stdError and actualVsExpected options', function () { 9 | let teamCityStdout, teamCityStderr, teamCityOutputArray, teamCityErrorOutputArray; 10 | function verifyResults() { 11 | 12 | it('stdout output should exist', function () { 13 | assert.isOk(teamCityStdout, 'has output'); 14 | assert.isOk(teamCityOutputArray, 'array of output is populated'); 15 | assert.isOk(teamCityOutputArray.length >= 9, 'at least 9 lines of output'); 16 | assert.lengthOf(teamCityOutputArray, 9); 17 | }); 18 | 19 | it('stderr output should exist', function () { 20 | assert.isOk(teamCityStderr); 21 | assert.isAbove(teamCityStderr.length, 15); 22 | assert.lengthOf(teamCityErrorOutputArray, 2); 23 | }); 24 | 25 | 26 | it('stdout output should exist', function () { 27 | assert.isOk(teamCityStdout); 28 | assert.isOk(teamCityOutputArray); 29 | assert.isOk(teamCityOutputArray.length >= 9); 30 | }); 31 | 32 | it('Suite started is OK', function () { 33 | const rowToCheck = teamCityOutputArray[0]; 34 | assert.isOk(/##teamcity\[testSuiteStarted/.test(rowToCheck)); 35 | assert.isOk(/name='Top Describe'/.test(rowToCheck)); 36 | assert.isOk(/flowId=/.test(rowToCheck)); 37 | assert.isOk(/]/.test(rowToCheck)); 38 | }); 39 | 40 | it('Test started is OK', function () { 41 | const rowToCheck = teamCityOutputArray[1]; 42 | assert.match(rowToCheck, /##teamcity\[testStarted/); 43 | assert.match(rowToCheck, /name='Passing Test @pass'/); 44 | assert.match(rowToCheck, /flowId=/); 45 | assert.match(rowToCheck, /]/); 46 | }); 47 | 48 | it('Passing Test Finished is OK', function () { 49 | const rowToCheck = teamCityOutputArray[2]; 50 | assert.isOk(/##teamcity\[testFinished/.test(rowToCheck)); 51 | assert.isOk(/name='Passing Test @pass'/.test(rowToCheck)); 52 | assert.isOk(/flowId=/.test(rowToCheck)); 53 | assert.isOk(/duration=/.test(rowToCheck)); 54 | assert.isOk(/]/.test(rowToCheck)); 55 | }); 56 | 57 | it('Test Failed Started is OK', function () { 58 | const rowToCheck = teamCityOutputArray[3]; 59 | assert.isOk(/##teamcity\[testStarted/.test(rowToCheck)); 60 | assert.isOk(/name='Failing Test @fail'/.test(rowToCheck)); 61 | assert.isOk(/flowId=/.test(rowToCheck)); 62 | assert.isOk(/duration=/.test(rowToCheck) === false); 63 | assert.isOk(/]/.test(rowToCheck)); 64 | }); 65 | 66 | it('Test Failed is Failing', function () { 67 | const rowToCheck = teamCityErrorOutputArray; 68 | assert.isOk(/##teamcity\[testFailed/.test(rowToCheck)); 69 | assert.isOk(/name='Failing Test @fail'/.test(rowToCheck)); 70 | assert.isOk(/flowId=/.test(rowToCheck)); 71 | assert.isOk(/duration=/.test(rowToCheck) === false); 72 | assert.isOk(/details='/.test(rowToCheck)); 73 | assert.isOk(/AssertionError/.test(rowToCheck)); 74 | assert.isOk(/|n/.test(rowToCheck)); 75 | assert.isOk(/|simple.js:11:11/.test(rowToCheck)); 76 | assert.isOk(/captureStandardOutput='true'/.test(rowToCheck)); 77 | assert.isOk(/]/.test(rowToCheck)); 78 | //check keys 79 | assert.match(rowToCheck, /actual=/); 80 | assert.match(rowToCheck, /expected=/); 81 | //check both key and value 82 | assert.match(rowToCheck, /actual='2'/); 83 | assert.match(rowToCheck, /expected='1'/); 84 | }); 85 | 86 | it('Failing Test Finished is OK', function () { 87 | const rowToCheck = teamCityOutputArray[4]; 88 | assert.match(rowToCheck, /##teamcity\[testFinished/); 89 | assert.match(rowToCheck, /name='Failing Test @fail'/); 90 | assert.match(rowToCheck, /flowId=/); 91 | assert.match(rowToCheck, /duration=/); 92 | assert.match(rowToCheck, /]/); 93 | }); 94 | 95 | it('Skip Test Finished is ignored', function () { 96 | const rowToCheck = teamCityOutputArray[5]; 97 | assert.isOk(/##teamcity\[testIgnored/.test(rowToCheck)); 98 | assert.isOk(/name='Skipped Test @skip'/.test(rowToCheck)); 99 | assert.isOk(/flowId=/.test(rowToCheck)); 100 | assert.isOk(/message='Skipped Test @skip'/.test(rowToCheck)); 101 | assert.isOk(/duration=/.test(rowToCheck) === false); 102 | assert.isOk(/]/.test(rowToCheck)); 103 | }); 104 | 105 | it('Skip Test Finished is OK', function () { 106 | const rowToCheck = teamCityOutputArray[6]; 107 | assert.isOk(/##teamcity\[testFinished/.test(rowToCheck)); 108 | assert.isOk(/name='Skipped Test @skip'/.test(rowToCheck)); 109 | assert.isOk(/flowId=/.test(rowToCheck)); 110 | assert.isNotOk(/duration=/.test(rowToCheck)); 111 | assert.isOk(/]/.test(rowToCheck)); 112 | }); 113 | 114 | it('Suite Finished is OK', function () { 115 | const rowToCheck = teamCityOutputArray[7]; 116 | assert.isOk(/##teamcity\[testSuiteFinished/.test(rowToCheck)); 117 | assert.isOk(/name='Top Describe'/.test(rowToCheck)); 118 | assert.isOk(/duration=/.test(rowToCheck)); 119 | assert.isOk(/flowId=/.test(rowToCheck)); 120 | assert.isOk(/]/.test(rowToCheck)); 121 | }); 122 | 123 | it('Suite Root Finished is OK', function () { 124 | const rowToCheck = teamCityOutputArray[7]; 125 | assert.isOk(/##teamcity\[testSuiteFinished/.test(rowToCheck)); 126 | assert.isNotOk(/name='mocha.suite'/.test(rowToCheck)); 127 | assert.isOk(/duration=/.test(rowToCheck)); 128 | assert.isOk(/flowId=/.test(rowToCheck)); 129 | assert.isOk(/]/.test(rowToCheck)); 130 | }); 131 | } 132 | 133 | describe('specified as an env var', function () { 134 | before(function (done) { 135 | const opts = { 136 | env: Object.assign({ 137 | ['ACTUAL_VS_EXPECTED']: 'true', 138 | ['USE_STD_ERROR']: 'true' 139 | }, process.env) 140 | }; 141 | 142 | execFile(internalMochaPath, [ 143 | 'test/test_data', 144 | '--reporter', 145 | 'lib/teamcity' 146 | ], opts, (err, stdout, stderr) => { 147 | teamCityStdout = stdout; 148 | teamCityStderr = stderr; 149 | teamCityOutputArray = stdout.split('\n'); 150 | teamCityErrorOutputArray = stderr.split('\n'); 151 | logMochaOutput(stdout, stderr); 152 | done(); 153 | }); 154 | }); 155 | verifyResults(); 156 | }); 157 | 158 | describe('specified with --reporter-options', function () { 159 | before(function (done) { 160 | execFile(internalMochaPath, [ 161 | path.join('test', 'test_data', 'simple.js'), 162 | '--reporter', 163 | 'lib/teamcity', 164 | '--reporter-options', 165 | 'actualVsExpected=true,useStdError=true' 166 | ], (err, stdout, stderr) => { 167 | teamCityStdout = stdout; 168 | teamCityStderr = stderr; 169 | teamCityOutputArray = stdout.split('\n'); 170 | teamCityErrorOutputArray = stderr.split('\n'); 171 | logMochaOutput(stdout, stderr); 172 | done(); 173 | }); 174 | }); 175 | verifyResults(); 176 | }); 177 | 178 | }); 179 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | [![NPM version](https://badge.fury.io/js/mocha-teamcity-reporter.svg)](http://badge.fury.io/js/mocha-teamcity-reporter) 2 | [![TeamCity Build Status](https://teamcity.jetbrains.com/guestAuth/app/rest/builds/buildType:(id:TeamCityThirdPartyPlugins_MochaTeamcityReporter_Build)/statusIcon.svg)](https://teamcity.jetbrains.com/viewType.html?buildTypeId=TeamCityThirdPartyPlugins_MochaTeamcityReporter_Build&branch_TeamCityThirdPartyPlugins_MochaTeamcityReporter=%3Cdefault%3E&tab=buildTypeStatusDiv) 3 | [![Monthly Downloads](https://img.shields.io/npm/dm/mocha-teamcity-reporter.svg)](https://npmjs.org/package/mocha-teamcity-reporter) 4 | 5 | # mocha-teamcity-reporter # 6 | 7 | `mocha-teamcity-reporter` Teamcity reporter which makes it possible to display test results in real-time, makes test information 8 | available on the Tests tab of the Build Results page. 9 | 10 | ## Version 4.x changes 11 | 12 | * Breaking: Only supported on node.js 6 and above 13 | * Breaking: Only Mocha version 6 and above is now supported 14 | * Please remain on `mocha-teamcity-reporter@3` if this is an issue 15 | * New reporter option `ignoreHookWithName` to skip reporting for hooks with title containing some word (@DJ-Glock) 16 | * Added postfix _hook for flowId for hooks to ensure flowIds will never intersect. 17 | * Implement 'hook end' event (@DJ-GLock) 18 | * General maintenance and tidy up (@DJ-Glock) 19 | 20 | ## Mocha@6 notes 21 | 22 | * recordHookFailures option may not work as intended as mocha6 is now doing this itself 23 | 24 | ## Requirements 25 | 26 | * NodeJs 6+ 27 | * Mocha 6+ 28 | * Web Browser supporting ES5 29 | 30 | ## To Install 31 | 32 | In your project run a npm install command: 33 | 34 | ``` npm install mocha-teamcity-reporter --save-dev ``` 35 | 36 | Basically, have your project's package.json be like: 37 | 38 | ``` js 39 | { 40 | "devDependencies": { 41 | "mocha-teamcity-reporter": ">=2.0.0" 42 | } 43 | } 44 | ``` 45 | 46 | ## Usage 47 | 48 | describes using third party reporters in mocha. 49 | 50 | Then call mocha with: 51 | 52 | `mocha --reporter mocha-teamcity-reporter test` 53 | 54 | ## Running In Browser 55 | 56 | * Use `lib/teamcityBrowser` 57 | * Has option parsing stripped out for the moment 58 | * Example use can be found in `test\browser` 59 | * Custom log function can be set with window.customLogFunction 60 | 61 | ## Customisation 62 | 63 | ### TeamCity flowId 64 | 65 | Can set flowId like: 66 | `mocha test --reporter mocha-teamcity-reporter --reporter-options flowId=gobbledygook` 67 | 68 | ### Top-level suite name 69 | 70 | Can set a top-level suite name, which will wrap all other suites. 71 | This is useful for reading test output when running multiple suites in a single build 72 | 73 | * Environment variable: MOCHA_TEAMCITY_TOP_LEVEL_SUITE= 74 | * Reporter option: topLevelSuite= 75 | 76 | ### log test failures with std error 77 | 78 | To enable this please 79 | Please note this will probaly be made default in the next major version 80 | 81 | * Environment variable: USE_STD_ERROR=true 82 | * Reporter option: useStdError=true 83 | 84 | ### Record hook failures 85 | 86 | Record failures for hooks such as before/after etc 87 | Please note this will probably be made default in the next major version 88 | 89 | * Environment variable: RECORD_HOOK_FAILURES=true 90 | * Reporter option: recordHookFailures=true 91 | 92 | ### Display Ignored tests as ignored 93 | 94 | Display skip tests as ignored 95 | 96 | * Environment variable: DISPLAY_IGNORED_AS_IGNORED=true 97 | * Reporter option: displayIgnoredAsIgnored=true 98 | 99 | 100 | ### Ignore hooks with title contains some text 101 | 102 | This option should be used in pair with recordHookFailures. It allows you to skip reporting of hooks containing some word. Including root hooks. 103 | 104 | * Environment variable: IGNORE_HOOK_WITH_NAME=HookNoReporting 105 | * Reporter option: ignoreHookWithName=HookNoReporting 106 | 107 | Example: 108 | `mocha test --reporter mocha-teamcity-reporter --reporter-options recordHookFailures --reporter-options ignoreHookWithName=HookNoReporting` 109 | 110 | For root hooks defined the following way: 111 | 112 | ``` 113 | exports.mochaHooks = () => { 114 | return { 115 | beforeEach: [ 116 | function beforeEachRootHookNoReporting() { 117 | assert.strictEqual(1, 1); 118 | } 119 | ], 120 | afterEach: [ 121 | function afterEachRoot() { 122 | assert.strictEqual(1, 1); 123 | }, 124 | ] 125 | }; 126 | }; 127 | ``` 128 | 129 | beforeEach hook beforeEachRootHookNoReporting() will not be reported as testStarted. But hook afterEachRoot() will be reported: 130 | 131 | ### Show diff between expected and actual values 132 | 133 | This will allow a hyperlink to appear to compare actual vs expected 134 | Please note this requires the error thrown in mocha to have the properties actual and expected. For example an assertionError has this 135 | 136 | * Environment variable: ACTUAL_VS_EXPECTED=true 137 | * Reporter option: actualVsExpected=true 138 | 139 | This will be shown in teamcity like this: 140 | 141 | ``` 142 | AssertionError [ERR_ASSERTION]: 2 == 1 143 | at Context. (test/test_data/simple.js:11:11) 144 | ======= Failed test run #10 ========== 145 | Show diff between expected and actual values 146 | ``` 147 | 148 | ### Setting options 149 | 150 | * Set with reporter-options: 151 | 152 | `mocha test --reporter mocha-teamcity-reporter --reporter-options topLevelSuite=top-level-suite-name` 153 | `mocha test --reporter mocha-teamcity-reporter --reporter-options useStdError=true` 154 | `mocha test --reporter mocha-teamcity-reporter --reporter-options useStdError=true` 155 | 156 | * Set with environment variable 157 | 158 | `MOCHA_TEAMCITY_TOP_LEVEL_SUITE='top-level-suite-name' mocha test --reporter mocha-teamcity-reporter` 159 | 160 | ### Multiple reporters 161 | 162 | This is not supported out of the box by this plugin but have a look at the following: 163 | 164 | * Using 165 | * 166 | * Or view for more details 167 | 168 | ## View on live Teamcity 169 | 170 | * Project can be viewed at 171 | [Pubic Teamcity at Jetbrains mocha reporter](https://teamcity.jetbrains.com/project.html?projectId=TeamCityThirdPartyPlugins_MochaTeamcityReporter) 172 | * Thanks to JetBrains for hosting 173 | 174 | ## Contributions 175 | 176 | * Always Welcome 177 | * Would prefer if customisation is added it is controlled via mocha options or environment variables 178 | * Only requirement is for code to pass linting and functional tests 179 | 180 | ## Run example test in project 181 | 182 | `mocha test/test_data/simple.js --reporter mocha-teamcity-reporter` or `npm run test-teamcity-example` 183 | 184 | ## Reference Information 185 | 186 | 187 | -------------------------------------------------------------------------------- /test/functional/recordHookFailuresEnabled.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | const {execFile} = require('child_process'); 3 | const {assert} = require('chai'); 4 | const { logMochaOutput, getMochaPath } = require('../testHelpers'); 5 | const internalMochaPath = getMochaPath(); 6 | const path = require('path'); 7 | 8 | describe('Check TeamCity Output is correct with recordHookFailures option', function () { 9 | let teamCityStdout, teamCityStderr, teamCityOutputArray; 10 | function verifyResults() { 11 | it('1 stdout output should exist', function () { 12 | assert.isOk(teamCityStdout, 'has output'); 13 | assert.isOk(teamCityOutputArray, 'array of output is populated'); 14 | assert.lengthOf(teamCityOutputArray, 15); 15 | assert.isEmpty(teamCityOutputArray[14]); 16 | }); 17 | 18 | it('2 stderr output should not exist', function () { 19 | assert.isEmpty(teamCityStderr); 20 | }); 21 | 22 | it('3 testSuiteStarted for Suite1', function () { 23 | const rowToCheck = teamCityOutputArray[0]; 24 | assert.isOk(/##teamcity\[testSuiteStarted/.test(rowToCheck)); 25 | assert.isOk(/name='Hook Test Top Describe Fail'/.test(rowToCheck)); 26 | assert.isOk(/flowId=/.test(rowToCheck)); 27 | assert.notMatch(rowToCheck, /flowId=.*_hook/); 28 | assert.isOk(/]/.test(rowToCheck)); 29 | }); 30 | 31 | it('4 testStarted for before all hook', function () { 32 | const rowToCheck = teamCityOutputArray[1]; 33 | assert.match(rowToCheck, /##teamcity\[testStarted/); 34 | assert.match(rowToCheck, /name='"before all" hook/); 35 | assert.match(rowToCheck, /flowId=.*_hook/); 36 | assert.match(rowToCheck, /]/); 37 | }); 38 | 39 | it('5 testFailed for before all hook', function () { 40 | const rowToCheck = teamCityOutputArray[2]; 41 | assert.isOk(/##teamcity\[testFailed/.test(rowToCheck)); 42 | assert.isOk(/name='"before all" hook/.test(rowToCheck)); 43 | assert.isOk(/details='/.test(rowToCheck)); 44 | assert.isOk(/Error: Before hook error fail/.test(rowToCheck)); 45 | assert.isOk(/|n/.test(rowToCheck)); 46 | assert.isOk(/|failingHook.js:29:12/.test(rowToCheck)); 47 | assert.isOk(/captureStandardOutput='true'/.test(rowToCheck)); 48 | assert.match(rowToCheck, /flowId=.*_hook/); 49 | assert.isOk(/duration=/.test(rowToCheck) === false); 50 | assert.isOk(/]/.test(rowToCheck)); 51 | }); 52 | 53 | it('6 Hook testFinished for before all hook', function () { 54 | const rowToCheck = teamCityOutputArray[3]; 55 | assert.isOk(/##teamcity\[testFinished/.test(rowToCheck)); 56 | assert.isOk(/name='"before all" hook/.test(rowToCheck)); 57 | assert.match(rowToCheck, /flowId=.*_hook/); 58 | assert.isOk(/duration=/.test(rowToCheck)); 59 | assert.isOk(/]/.test(rowToCheck)); 60 | }); 61 | 62 | it('7 testSuiteFinished for Suite1', function () { 63 | const rowToCheck = teamCityOutputArray[4]; 64 | assert.isOk(/##teamcity\[testSuiteFinished/.test(rowToCheck)); 65 | assert.isOk(/name='Hook Test Top Describe Fail'/.test(rowToCheck)); 66 | assert.isOk(/duration=/.test(rowToCheck)); 67 | assert.isOk(/flowId=/.test(rowToCheck)); 68 | assert.notMatch(rowToCheck, /flowId=.*_hook/); 69 | assert.isOk(/]/.test(rowToCheck)); 70 | }); 71 | 72 | it('8 testSuiteStarted for Suite2', function () { 73 | const rowToCheck = teamCityOutputArray[5]; 74 | assert.isOk(/##teamcity\[testSuiteStarted/.test(rowToCheck)); 75 | assert.isOk(/name='Hook Test Top Describe Pass'/.test(rowToCheck)); 76 | assert.isOk(/flowId=/.test(rowToCheck)); 77 | assert.notMatch(rowToCheck, /flowId=.*_hook/); 78 | assert.isOk(/]/.test(rowToCheck)); 79 | }); 80 | 81 | it('9 testStarted for before all hook', function () { 82 | const rowToCheck = teamCityOutputArray[6]; 83 | assert.isOk(/##teamcity\[testStarted/.test(rowToCheck)); 84 | assert.isOk(/name='"before all" hook/.test(rowToCheck)); 85 | assert.match(rowToCheck, /flowId=.*_hook/); 86 | assert.isOk(/]/.test(rowToCheck)); 87 | }); 88 | 89 | it('10 Hook testFinished for before all hook', function () { 90 | const rowToCheck = teamCityOutputArray[7]; 91 | assert.isOk(/##teamcity\[testFinished/.test(rowToCheck)); 92 | assert.isOk(/name='"before all" hook/.test(rowToCheck)); 93 | assert.match(rowToCheck, /flowId=.*_hook/); 94 | assert.isOk(/duration=/.test(rowToCheck)); 95 | assert.isOk(/]/.test(rowToCheck)); 96 | }); 97 | 98 | it('11 testStarted for Failing Test', function () { 99 | const rowToCheck = teamCityOutputArray[8]; 100 | assert.isOk(/##teamcity\[testStarted/.test(rowToCheck)); 101 | assert.isOk(/name='Test Failing Test @fail'/.test(rowToCheck)); 102 | assert.isOk(/flowId=/.test(rowToCheck)); 103 | assert.notMatch(rowToCheck, /flowId=.*_hook/); 104 | assert.isOk(/]/.test(rowToCheck)); 105 | }); 106 | 107 | it('12 testFailed for Failing Test', function () { 108 | const rowToCheck = teamCityOutputArray[9]; 109 | assert.isOk(/##teamcity\[testFailed/.test(rowToCheck)); 110 | assert.isOk(/name='Test Failing Test @fail'/.test(rowToCheck)); 111 | assert.isOk(/flowId=/.test(rowToCheck)); 112 | assert.notMatch(rowToCheck, /flowId=.*_hook/); 113 | assert.isOk(/duration=/.test(rowToCheck) === false); 114 | assert.isOk(/details='/.test(rowToCheck)); 115 | assert.isOk(/AssertionError/.test(rowToCheck)); 116 | assert.isOk(/|n/.test(rowToCheck)); 117 | assert.isOk(/|failingHook.js:29:12/.test(rowToCheck)); 118 | assert.isOk(/captureStandardOutput='true'/.test(rowToCheck)); 119 | assert.isOk(/]/.test(rowToCheck)); 120 | }); 121 | 122 | it('13 testFinished for Failing Test', function () { 123 | const rowToCheck = teamCityOutputArray[10]; 124 | assert.match(rowToCheck, /##teamcity\[testFinished/); 125 | assert.match(rowToCheck, /name='Test Failing Test @fail'/); 126 | assert.match(rowToCheck, /flowId=/); 127 | assert.match(rowToCheck, /duration=/); 128 | assert.match(rowToCheck, /]/); 129 | }); 130 | 131 | it('14 testStarted for Passing Test', function () { 132 | const rowToCheck = teamCityOutputArray[11]; 133 | assert.isOk(/##teamcity\[testStarted/.test(rowToCheck)); 134 | assert.isOk(/name='Test Passing Test @pass'/.test(rowToCheck)); 135 | assert.isOk(/flowId=/.test(rowToCheck)); 136 | assert.notMatch(rowToCheck, /flowId=.*_hook/); 137 | assert.isOk(/]/.test(rowToCheck)); 138 | }); 139 | 140 | 141 | it('15 testFinished for Passing Test', function () { 142 | const rowToCheck = teamCityOutputArray[12]; 143 | assert.isOk(/##teamcity\[testFinished/.test(rowToCheck)); 144 | assert.isOk(/name='Test Passing Test @pass'/.test(rowToCheck)); 145 | assert.isOk(/flowId=/.test(rowToCheck)); 146 | assert.notMatch(rowToCheck, /flowId=.*_hook/); 147 | assert.isOk(/duration=/.test(rowToCheck)); 148 | assert.isOk(/]/.test(rowToCheck)); 149 | }); 150 | 151 | it('16 testSuiteFinished for Suite2', function () { 152 | const rowToCheck = teamCityOutputArray[13]; 153 | assert.isOk(/##teamcity\[testSuiteFinished/.test(rowToCheck)); 154 | assert.isOk(/name='Hook Test Top Describe Pass'/.test(rowToCheck)); 155 | assert.isOk(/duration=/.test(rowToCheck)); 156 | assert.isOk(/flowId=/.test(rowToCheck)); 157 | assert.notMatch(rowToCheck, /flowId=.*_hook/); 158 | assert.isOk(/]/.test(rowToCheck)); 159 | }); 160 | } 161 | 162 | describe('specified as an env var', function () { 163 | before(function (done) { 164 | const opts = { 165 | env: Object.assign({ 166 | ['RECORD_HOOK_FAILURES']: 'true' 167 | }, process.env) 168 | }; 169 | 170 | execFile(internalMochaPath, [ 171 | 'test/example/failingHook.js', 172 | '--reporter', 173 | 'lib/teamcity' 174 | ], opts, (err, stdout, stderr) => { 175 | teamCityStdout = stdout; 176 | teamCityStderr = stderr; 177 | teamCityOutputArray = stdout.split('\n'); 178 | logMochaOutput(stdout, stderr); 179 | done(); 180 | }); 181 | }); 182 | verifyResults(); 183 | }); 184 | 185 | describe('specified with --reporter-options', function () { 186 | before(function (done) { 187 | execFile(internalMochaPath, [ 188 | path.join('test', 'example', 'failingHook.js'), 189 | '--reporter', 190 | 'lib/teamcity', 191 | '--reporter-options', 192 | 'recordHookFailures=true' 193 | ], (err, stdout, stderr) => { 194 | teamCityStdout = stdout; 195 | teamCityStderr = stderr; 196 | teamCityOutputArray = stdout.split('\n'); 197 | logMochaOutput(stdout, stderr); 198 | done(); 199 | }); 200 | }); 201 | verifyResults(); 202 | }); 203 | }); 204 | -------------------------------------------------------------------------------- /test/excluded tests/ignoreHookWithNameEnabledRoot.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | const {execFile} = require('child_process'); 3 | const {assert} = require('chai'); 4 | const { logMochaOutput, getMochaPath } = require('../testHelpers'); 5 | const internalMochaPath = getMochaPath(); 6 | const path = require('path'); 7 | 8 | // Skipped because this suite should be run only for mocha 8 or higher 9 | describe.skip('Check TeamCity Output is correct with ignoreHookWithName and root hooks option', function () { 10 | let teamCityStdout, teamCityStderr, teamCityOutputArray; 11 | function verifyResults() { 12 | it('stdout output should exist', function () { 13 | assert.isOk(teamCityStdout, 'has output'); 14 | assert.isOk(teamCityOutputArray, 'array of output is populated'); 15 | assert.lengthOf(teamCityOutputArray, 16); 16 | assert.isEmpty(teamCityOutputArray[15]); 17 | }); 18 | 19 | it('stderr output should not exist', function () { 20 | assert.isEmpty(teamCityStderr); 21 | }); 22 | 23 | 24 | it('stdout output should exist', function () { 25 | assert.isOk(teamCityStdout); 26 | assert.isOk(teamCityOutputArray); 27 | assert.isOk(teamCityOutputArray.length >= 12); 28 | }); 29 | 30 | it('Suite1 started is OK', function () { 31 | const rowToCheck = teamCityOutputArray[0]; 32 | assert.isOk(/##teamcity\[testSuiteStarted/.test(rowToCheck)); 33 | assert.isOk(/name='Hook Test Top Describe Fail'/.test(rowToCheck)); 34 | assert.isOk(/flowId=/.test(rowToCheck)); 35 | assert.isOk(/]/.test(rowToCheck)); 36 | }); 37 | 38 | it('Hook Test started is OK', function () { 39 | const rowToCheck = teamCityOutputArray[1]; 40 | assert.match(rowToCheck, /##teamcity\[testStarted/); 41 | assert.match(rowToCheck, /name='"before all" hook/); 42 | assert.match(rowToCheck, /flowId=/); 43 | assert.match(rowToCheck, /]/); 44 | }); 45 | 46 | it('Hook Test Failed is OK', function () { 47 | const rowToCheck = teamCityOutputArray[2]; 48 | assert.isOk(/##teamcity\[testFailed/.test(rowToCheck)); 49 | assert.isOk(/name='"before all" hook/.test(rowToCheck)); 50 | assert.isOk(/details='/.test(rowToCheck)); 51 | assert.isOk(/Error: Before hook error fail/.test(rowToCheck)); 52 | assert.isOk(/|n/.test(rowToCheck)); 53 | assert.isOk(/|failingHook.js:29:12/.test(rowToCheck)); 54 | assert.isOk(/captureStandardOutput='true'/.test(rowToCheck)); 55 | assert.isOk(/flowId=/.test(rowToCheck)); 56 | assert.isOk(/duration=/.test(rowToCheck) === false); 57 | assert.isOk(/]/.test(rowToCheck)); 58 | }); 59 | 60 | it('Suite1 Finished is OK', function () { 61 | const rowToCheck = teamCityOutputArray[4]; 62 | assert.isOk(/##teamcity\[testSuiteFinished/.test(rowToCheck)); 63 | assert.isOk(/name='Hook Test Top Describe Fail'/.test(rowToCheck)); 64 | assert.isOk(/duration=/.test(rowToCheck)); 65 | assert.isOk(/flowId=/.test(rowToCheck)); 66 | assert.isOk(/]/.test(rowToCheck)); 67 | }); 68 | 69 | it('Suite2 started is OK', function () { 70 | const rowToCheck = teamCityOutputArray[5]; 71 | assert.isOk(/##teamcity\[testSuiteStarted/.test(rowToCheck)); 72 | assert.isOk(/name='Hook Test Top Describe Pass'/.test(rowToCheck)); 73 | assert.isOk(/flowId=/.test(rowToCheck)); 74 | assert.isOk(/]/.test(rowToCheck)); 75 | }); 76 | 77 | 78 | it('Test started is OK', function () { 79 | const rowToCheck = teamCityOutputArray[11]; 80 | assert.isOk(/##teamcity\[testStarted/.test(rowToCheck)); 81 | assert.isOk(/name='Test Passing Test @pass'/.test(rowToCheck)); 82 | assert.isOk(/flowId=/.test(rowToCheck)); 83 | assert.isOk(/]/.test(rowToCheck)); 84 | }); 85 | 86 | 87 | it('Passing Test Finished is OK', function () { 88 | const rowToCheck = teamCityOutputArray[12]; 89 | assert.isOk(/##teamcity\[testFinished/.test(rowToCheck)); 90 | assert.isOk(/name='Test Passing Test @pass'/.test(rowToCheck)); 91 | assert.isOk(/flowId=/.test(rowToCheck)); 92 | assert.isOk(/duration=/.test(rowToCheck)); 93 | assert.isOk(/]/.test(rowToCheck)); 94 | }); 95 | 96 | it('Test Failed is Failing', function () { 97 | const rowToCheck = teamCityOutputArray[8]; 98 | assert.isOk(/##teamcity\[testFailed/.test(rowToCheck)); 99 | assert.isOk(/name='Test Failing Test @fail'/.test(rowToCheck)); 100 | assert.isOk(/flowId=/.test(rowToCheck)); 101 | assert.isOk(/duration=/.test(rowToCheck) === false); 102 | assert.isOk(/details='/.test(rowToCheck)); 103 | assert.isOk(/AssertionError/.test(rowToCheck)); 104 | assert.isOk(/|n/.test(rowToCheck)); 105 | assert.isOk(/|failingHook.js:29:12/.test(rowToCheck)); 106 | assert.isOk(/captureStandardOutput='true'/.test(rowToCheck)); 107 | assert.isOk(/]/.test(rowToCheck)); 108 | }); 109 | 110 | it('Failing Test Finished is OK', function () { 111 | const rowToCheck = teamCityOutputArray[9]; 112 | assert.match(rowToCheck, /##teamcity\[testFinished/); 113 | assert.match(rowToCheck, /name='Test Failing Test @fail'/); 114 | assert.match(rowToCheck, /flowId=/); 115 | assert.match(rowToCheck, /duration=/); 116 | assert.match(rowToCheck, /]/); 117 | }); 118 | 119 | it('Suite2 Finished is OK', function () { 120 | const rowToCheck = teamCityOutputArray[14]; 121 | assert.isOk(/##teamcity\[testSuiteFinished/.test(rowToCheck)); 122 | assert.isOk(/name='Hook Test Top Describe Pass'/.test(rowToCheck)); 123 | assert.isOk(/duration=/.test(rowToCheck)); 124 | assert.isOk(/flowId=/.test(rowToCheck)); 125 | assert.isOk(/]/.test(rowToCheck)); 126 | }); 127 | 128 | it('Suite Root Finished is OK', function () { 129 | const rowToCheck = teamCityOutputArray[14]; 130 | assert.isOk(/##teamcity\[testSuiteFinished/.test(rowToCheck)); 131 | assert.isNotOk(/name='mocha.suite'/.test(rowToCheck)); 132 | assert.isOk(/duration=/.test(rowToCheck)); 133 | assert.isOk(/flowId=/.test(rowToCheck)); 134 | assert.isOk(/]/.test(rowToCheck)); 135 | }); 136 | 137 | it('After hook failed test is OK', function () { 138 | const rowToCheck = teamCityOutputArray[3]; 139 | assert.match(rowToCheck, /##teamcity\[testFailed/); 140 | assert.match(rowToCheck, /"after all" hook: afterHookNoReporting for/); 141 | assert.match(rowToCheck, /flowId=/); 142 | assert.match(rowToCheck, /]/); 143 | }); 144 | 145 | it('After each root hook for failed test is OK', function () { 146 | const rowToCheck = teamCityOutputArray[10]; 147 | assert.match(rowToCheck, /##teamcity\[testStarted/); 148 | assert.match(rowToCheck, /"after each" hook: afterEachRoot for "Test Failing Test/); 149 | assert.match(rowToCheck, /flowId=/); 150 | assert.match(rowToCheck, /]/); 151 | }); 152 | 153 | it('After each root hook for passed test is OK', function () { 154 | const rowToCheck = teamCityOutputArray[13]; 155 | assert.match(rowToCheck, /##teamcity\[testStarted/); 156 | assert.match(rowToCheck, /"after each" hook: afterEachRoot for "Test Passing Test/); 157 | assert.match(rowToCheck, /flowId=/); 158 | assert.match(rowToCheck, /]/); 159 | }); 160 | } 161 | 162 | describe('specified as an env var', function () { 163 | before(function (done) { 164 | const opts = { 165 | env: Object.assign({ 166 | ['RECORD_HOOK_FAILURES']: 'true', 167 | ['IGNORE_HOOK_WITH_NAME']: 'HookNoReporting' 168 | }, process.env) 169 | }; 170 | 171 | execFile(internalMochaPath, [ 172 | path.join('test', 'example', 'failingIgnoreHook.js'), 173 | '--reporter', 174 | 'lib/teamcity', 175 | `--require=${path.join('test', 'example', 'rootHooks.js')}` 176 | ], opts, (err, stdout, stderr) => { 177 | teamCityStdout = stdout; 178 | teamCityStderr = stderr; 179 | teamCityOutputArray = stdout.split('\n'); 180 | logMochaOutput(stdout, stderr); 181 | done(); 182 | }); 183 | }); 184 | verifyResults(); 185 | }); 186 | 187 | describe('specified with --reporter-options', function () { 188 | before(function (done) { 189 | execFile(internalMochaPath, [ 190 | path.join('test', 'example', 'failingIgnoreHook.js'), 191 | '--reporter', 192 | 'lib/teamcity', 193 | '--reporter-options', 194 | 'recordHookFailures=true', 195 | '--reporter-options', 196 | 'ignoreHookWithName=HookNoReporting', 197 | `--require=${path.join('test', 'example', 'rootHooks.js')}` 198 | ], (err, stdout, stderr) => { 199 | teamCityStdout = stdout; 200 | teamCityStderr = stderr; 201 | teamCityOutputArray = stdout.split('\n'); 202 | logMochaOutput(stdout, stderr); 203 | done(); 204 | }); 205 | }); 206 | verifyResults(); 207 | }); 208 | }); 209 | -------------------------------------------------------------------------------- /test/functional/displayIgnoredAsIgnored.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | const {execFile} = require('child_process'); 3 | const {assert} = require('chai'); 4 | const { logMochaOutput, getMochaPath } = require('../testHelpers'); 5 | const internalMochaPath = getMochaPath(); 6 | const path = require('path'); 7 | 8 | describe('Check TeamCity Output is correct with displayIgnoredAsIgnored option', function () { 9 | let teamCityStdout, teamCityStderr, teamCityOutputArray; 10 | function verifyResults(displayIgnoredAsIgnored) { 11 | it('Output should exist', function () { 12 | assert.isOk(teamCityStdout); 13 | assert.isOk(teamCityOutputArray); 14 | assert.isOk(teamCityStderr.length === 0); 15 | assert.isOk(teamCityOutputArray.length >= 9); 16 | 17 | if (displayIgnoredAsIgnored) { 18 | assert.isOk(teamCityOutputArray.length === 9); 19 | } else { 20 | assert.isOk(teamCityOutputArray.length === 10); 21 | } 22 | }); 23 | 24 | it('Suite started is OK', function () { 25 | const rowToCheck = teamCityOutputArray[0]; 26 | assert.isOk(/##teamcity\[testSuiteStarted/.test(rowToCheck)); 27 | assert.isOk(/name='Top Describe'/.test(rowToCheck)); 28 | assert.isOk(/flowId=/.test(rowToCheck)); 29 | assert.isOk(/]/.test(rowToCheck)); 30 | }); 31 | 32 | it('Test started is OK', function () { 33 | const rowToCheck = teamCityOutputArray[1]; 34 | assert.isOk(/##teamcity\[testStarted/.test(rowToCheck)); 35 | assert.isOk(/name='Passing Test @pass'/.test(rowToCheck)); 36 | assert.isOk(/flowId=/.test(rowToCheck)); 37 | assert.isOk(/]/.test(rowToCheck)); 38 | }); 39 | 40 | it('Passing Test Finished is OK', function () { 41 | const rowToCheck = teamCityOutputArray[2]; 42 | assert.isOk(/##teamcity\[testFinished/.test(rowToCheck)); 43 | assert.isOk(/name='Passing Test @pass'/.test(rowToCheck)); 44 | assert.isOk(/flowId=/.test(rowToCheck)); 45 | assert.isOk(/duration=/.test(rowToCheck)); 46 | assert.isOk(/]/.test(rowToCheck)); 47 | }); 48 | 49 | it('Test Failed Started is OK', function () { 50 | const rowToCheck = teamCityOutputArray[3]; 51 | assert.isOk(/##teamcity\[testStarted/.test(rowToCheck)); 52 | assert.isOk(/name='Failing Test @fail'/.test(rowToCheck)); 53 | assert.isOk(/flowId=/.test(rowToCheck)); 54 | assert.isOk(/duration=/.test(rowToCheck) === false); 55 | assert.isOk(/]/.test(rowToCheck)); 56 | }); 57 | 58 | it('Test Failed is Failing', function () { 59 | const rowToCheck = teamCityOutputArray[4]; 60 | assert.isOk(/##teamcity\[testFailed/.test(rowToCheck)); 61 | assert.isOk(/name='Failing Test @fail'/.test(rowToCheck)); 62 | assert.isOk(/flowId=/.test(rowToCheck)); 63 | assert.isOk(/duration=/.test(rowToCheck) === false); 64 | assert.isOk(/details='/.test(rowToCheck)); 65 | assert.isOk(/AssertionError/.test(rowToCheck)); 66 | assert.isOk(/|n/.test(rowToCheck)); 67 | assert.isOk(/|simple.js:11:11/.test(rowToCheck)); 68 | assert.isOk(/captureStandardOutput='true'/.test(rowToCheck)); 69 | assert.isOk(/]/.test(rowToCheck)); 70 | }); 71 | 72 | it('Failing Test Finished is OK', function () { 73 | const rowToCheck = teamCityOutputArray[5]; 74 | assert.isOk(/##teamcity\[testFinished/.test(rowToCheck)); 75 | assert.isOk(/name='Failing Test @fail'/.test(rowToCheck)); 76 | assert.isOk(/flowId=/.test(rowToCheck)); 77 | assert.isOk(/duration=/.test(rowToCheck)); 78 | assert.isOk(/]/.test(rowToCheck)); 79 | }); 80 | 81 | it('Skip Test Finished is ignored', function () { 82 | const rowToCheck = teamCityOutputArray[6]; 83 | const report = teamCityOutputArray.join('\n'); 84 | 85 | assert.isOk(/##teamcity\[testIgnored/.test(rowToCheck)); 86 | assert.isOk(/name='Skipped Test @skip'/.test(rowToCheck)); 87 | assert.isOk(/flowId=/.test(rowToCheck)); 88 | assert.isOk(/message='Skipped Test @skip'/.test(rowToCheck)); 89 | assert.isOk(/duration=/.test(rowToCheck) === false); 90 | assert.isOk(/]/.test(rowToCheck)); 91 | 92 | if (displayIgnoredAsIgnored) { 93 | assert.isOk(new RegExp("##teamcity\\[testIgnored name='Skipped Test @skip' message='Skipped Test @skip' flowId='\\d+']").test(report), 'testIgnored'); 94 | assert.isNotOk(new RegExp("##teamcity\\[testFinished name='Skipped Test @skip'").test(report), 'testFinished'); 95 | } else { 96 | assert.isOk(new RegExp("##teamcity\\[testIgnored name='Skipped Test @skip' message='Skipped Test @skip' flowId='\\d+']").test(report), 'testIgnored'); 97 | assert.isOk(new RegExp("##teamcity\\[testFinished name='Skipped Test @skip'").test(report), 'testFinished'); 98 | } 99 | }); 100 | 101 | it('Skip Test Finished is OK', function () { 102 | const report = teamCityOutputArray.join('\n'); 103 | if (displayIgnoredAsIgnored) { 104 | assert.isNotOk(/##teamcity\[testFinished name='Skipped Test @skip/.test(report)); 105 | } else { 106 | assert.isOk(/##teamcity\[testFinished name='Skipped Test @skip/.test(report)); 107 | } 108 | }); 109 | 110 | it('Suite Finished is OK', function () { 111 | const rowToCheck = displayIgnoredAsIgnored ? teamCityOutputArray[7] : teamCityOutputArray[8]; 112 | assert.isOk(/##teamcity\[testSuiteFinished/.test(rowToCheck)); 113 | assert.isOk(/name='Top Describe'/.test(rowToCheck)); 114 | assert.isOk(/duration=/.test(rowToCheck)); 115 | assert.isOk(/flowId=/.test(rowToCheck)); 116 | assert.isOk(/]/.test(rowToCheck)); 117 | }); 118 | 119 | it('Suite Root Finished is OK', function () { 120 | const rowToCheck = displayIgnoredAsIgnored ? teamCityOutputArray[7] : teamCityOutputArray[8]; 121 | if (displayIgnoredAsIgnored) { 122 | assert.isEmpty(teamCityOutputArray[8], 'Last row should be empty'); 123 | } else { 124 | assert.isEmpty(teamCityOutputArray[9], 'Last row should be empty'); 125 | } 126 | assert.isOk(/##teamcity\[testSuiteFinished/.test(rowToCheck)); 127 | assert.isNotOk(/name='mocha.suite'/.test(rowToCheck)); 128 | assert.isOk(/duration=/.test(rowToCheck)); 129 | assert.isOk(/flowId=/.test(rowToCheck)); 130 | assert.isOk(/]/.test(rowToCheck)); 131 | }); 132 | } 133 | 134 | describe('specified as an env var', function () { 135 | before(function (done) { 136 | const opts = { 137 | env: Object.assign({ 138 | ['DISPLAY_IGNORED_AS_IGNORED']: 'true' 139 | }, process.env) 140 | }; 141 | 142 | execFile(internalMochaPath, [ 143 | 'test/test_data', 144 | '--reporter', 145 | 'lib/teamcity' 146 | ], opts, (err, stdout, stderr) => { 147 | teamCityStdout = stdout; 148 | teamCityStderr = stderr; 149 | teamCityOutputArray = stdout.split('\n'); 150 | logMochaOutput(stdout, stderr); 151 | done(); 152 | }); 153 | }); 154 | verifyResults(true); 155 | }); 156 | 157 | describe('specified with --reporter-options', function () { 158 | before(function (done) { 159 | execFile(internalMochaPath, [ 160 | path.join('test', 'test_data', 'simple.js'), 161 | '--reporter', 162 | 'lib/teamcity', 163 | '--reporter-options', 164 | 'displayIgnoredAsIgnored=true' 165 | ], (err, stdout, stderr) => { 166 | teamCityStdout = stdout; 167 | teamCityStderr = stderr; 168 | teamCityOutputArray = stdout.split('\n'); 169 | logMochaOutput(stdout, stderr); 170 | done(); 171 | }); 172 | }); 173 | verifyResults(true); 174 | }); 175 | 176 | describe('specified as an env var as false', function () { 177 | before(function (done) { 178 | const opts = { 179 | env: Object.assign({ 180 | ['DISPLAY_IGNORED_AS_IGNORED']: 'false' 181 | }, process.env) 182 | }; 183 | 184 | execFile(internalMochaPath, [ 185 | 'test/test_data', 186 | '--reporter', 187 | 'lib/teamcity' 188 | ], opts, (err, stdout, stderr) => { 189 | teamCityStdout = stdout; 190 | teamCityStderr = stderr; 191 | teamCityOutputArray = stdout.split('\n'); 192 | logMochaOutput(stdout, stderr); 193 | done(); 194 | }); 195 | }); 196 | verifyResults(false); 197 | }); 198 | 199 | describe('specified with --reporter-options as false', function () { 200 | before(function (done) { 201 | execFile(internalMochaPath, [ 202 | path.join('test', 'test_data', 'simple.js'), 203 | '--reporter', 204 | 'lib/teamcity', 205 | '--reporter-options', 206 | 'displayIgnoredAsIgnored=false' 207 | ], (err, stdout, stderr) => { 208 | teamCityStdout = stdout; 209 | teamCityStderr = stderr; 210 | teamCityOutputArray = stdout.split('\n'); 211 | logMochaOutput(stdout, stderr); 212 | done(); 213 | }); 214 | }); 215 | verifyResults(false); 216 | }); 217 | 218 | }); 219 | -------------------------------------------------------------------------------- /lib/teamcity.js: -------------------------------------------------------------------------------- 1 | /* eslint-disable complexity */ 2 | /** 3 | * Teamcity doc reference https://confluence.jetbrains.com/display/TCD10/Build+Script+Interaction+with+TeamCity 4 | * 5 | * Module dependencies. 6 | */ 7 | 'use strict'; 8 | 9 | const processPID = process.pid.toString(); 10 | const TEST_IGNORED = `##teamcity[testIgnored name='%s' message='%s' flowId='%s']`; 11 | const SUITE_START = `##teamcity[testSuiteStarted name='%s' flowId='%s']`; 12 | const SUITE_END = `##teamcity[testSuiteFinished name='%s' duration='%s' flowId='%s']`; 13 | const SUITE_END_NO_DURATION = `##teamcity[testSuiteFinished name='%s' flowId='%s']`; 14 | const TEST_START = `##teamcity[testStarted name='%s' captureStandardOutput='true' flowId='%s']`; 15 | const TEST_FAILED = `##teamcity[testFailed name='%s' message='%s' details='%s' captureStandardOutput='true' flowId='%s']`; 16 | const TEST_FAILED_COMPARISON = `##teamcity[testFailed type='comparisonFailure' name='%s' message='%s' \ 17 | details='%s' captureStandardOutput='true' actual='%s' expected='%s' flowId='%s']`; 18 | const TEST_END = `##teamcity[testFinished name='%s' duration='%s' flowId='%s']`; 19 | const TEST_END_NO_DURATION = `##teamcity[testFinished name='%s' flowId='%s']`; 20 | 21 | const Mocha = require('mocha'); 22 | const { 23 | EVENT_SUITE_BEGIN, 24 | EVENT_TEST_BEGIN, 25 | EVENT_TEST_FAIL, 26 | EVENT_TEST_PENDING, 27 | EVENT_TEST_END, 28 | EVENT_HOOK_BEGIN, 29 | EVENT_HOOK_END, 30 | EVENT_SUITE_END, 31 | EVENT_RUN_END 32 | } = Mocha.Runner.constants; 33 | 34 | const util = require('util'); 35 | 36 | let Base, log, logError; 37 | 38 | Base = require('mocha').reporters.Base; 39 | log = console.log; 40 | logError = console.error; 41 | 42 | /** 43 | * Escape the given `str`. 44 | */ 45 | 46 | function escape(str) { 47 | if (!str) return ''; 48 | return str 49 | .toString() 50 | .replace(/\x1B.*?m/g, '') // eslint-disable-line no-control-regex 51 | .replace(/\|/g, '||') 52 | .replace(/\n/g, '|n') 53 | .replace(/\r/g, '|r') 54 | .replace(/\[/g, '|[') 55 | .replace(/\]/g, '|]') 56 | .replace(/\u0085/g, '|x') 57 | .replace(/\u2028/g, '|l') 58 | .replace(/\u2029/g, '|p') 59 | .replace(/'/g, '|\''); 60 | } 61 | 62 | function isNil(value) { 63 | return value == null; // eslint-disable-line 64 | } 65 | 66 | function formatString() { 67 | let formattedArguments = []; 68 | const args = Array.prototype.slice.call(arguments, 0); 69 | // Format all arguments for TC display (it escapes using the pipe char). 70 | let tcMessage = args.shift(); 71 | args.forEach((param) => { 72 | formattedArguments.push(escape(param)); 73 | }); 74 | formattedArguments.unshift(tcMessage); 75 | return util.format.apply(util, formattedArguments); 76 | } 77 | 78 | 79 | /** 80 | * Initialize a new `Teamcity` reporter. 81 | * 82 | * @param {Runner} runner 83 | * @param {options} options 84 | * @api public 85 | */ 86 | 87 | function Teamcity(runner, options) { 88 | options = options || {}; 89 | const reporterOptions = options.reporterOptions || {}; 90 | let flowId, useStdError, recordHookFailures, actualVsExpected, ignoreHookWithName, displayIgnoredAsIgnored; 91 | (reporterOptions.flowId) ? flowId = reporterOptions.flowId : flowId = process.env['MOCHA_TEAMCITY_FLOWID'] || processPID; 92 | (reporterOptions.useStdError) ? useStdError = reporterOptions.useStdError : useStdError = process.env['USE_STD_ERROR']; 93 | (reporterOptions.recordHookFailures) ? recordHookFailures = reporterOptions.recordHookFailures : recordHookFailures = process.env['RECORD_HOOK_FAILURES']; 94 | (reporterOptions.actualVsExpected) ? actualVsExpected = reporterOptions.actualVsExpected : actualVsExpected = process.env['ACTUAL_VS_EXPECTED']; 95 | (reporterOptions.ignoreHookWithName) ? ignoreHookWithName = reporterOptions.ignoreHookWithName : ignoreHookWithName = process.env['IGNORE_HOOK_WITH_NAME']; 96 | (reporterOptions.displayIgnoredAsIgnored) ? displayIgnoredAsIgnored = reporterOptions.displayIgnoredAsIgnored : displayIgnoredAsIgnored = process.env['DISPLAY_IGNORED_AS_IGNORED']; 97 | (useStdError) ? useStdError = (useStdError.toLowerCase() === 'true') : useStdError = false; 98 | (recordHookFailures) ? recordHookFailures = (recordHookFailures.toLowerCase() === 'true') : recordHookFailures = false; 99 | (displayIgnoredAsIgnored) ? displayIgnoredAsIgnored = (displayIgnoredAsIgnored.toLowerCase() === 'true') : displayIgnoredAsIgnored = false; 100 | (ignoreHookWithName) ? ignoreHookWithName : null; 101 | actualVsExpected ? actualVsExpected = (actualVsExpected.toLowerCase() === 'true') : actualVsExpected = false; 102 | Base.call(this, runner); 103 | let stats = this.stats; 104 | const topLevelSuite = reporterOptions.topLevelSuite || process.env['MOCHA_TEAMCITY_TOP_LEVEL_SUITE']; 105 | 106 | const hookFlowId = `${flowId}_hook`; 107 | const ignoredTests = {}; 108 | const testState = { pending: 0 }; 109 | 110 | runner.on(EVENT_SUITE_BEGIN, function (suite) { 111 | if (suite.root) { 112 | if (topLevelSuite) { 113 | log(formatString(SUITE_START, topLevelSuite, flowId)); 114 | } 115 | return; 116 | } 117 | suite.startDate = new Date(); 118 | log(formatString(SUITE_START, suite.title, flowId)); 119 | }); 120 | 121 | runner.on(EVENT_TEST_BEGIN, function (test) { 122 | if (displayIgnoredAsIgnored && ignoredTests[`${test.title}-${flowId}`] === testState.pending) { 123 | return; 124 | } 125 | log(formatString(TEST_START, test.title, flowId)); 126 | }); 127 | 128 | runner.on(EVENT_TEST_FAIL, function (test, err) { 129 | let isHook = false; 130 | if (test.title.includes(`"before all" hook`) || 131 | test.title.includes(`"before each" hook`) || 132 | test.title.includes(`"after all" hook`) || 133 | test.title.includes(`"after each" hook`) 134 | ) { 135 | isHook = true; 136 | } 137 | 138 | const testFlowId = (isHook) ? hookFlowId : flowId; 139 | if(actualVsExpected && (err.actual && err.expected)){ 140 | if (useStdError) { 141 | logError(formatString(TEST_FAILED_COMPARISON, test.title, err.message, err.stack, err.actual, err.expected, testFlowId)); 142 | } else { 143 | log(formatString(TEST_FAILED_COMPARISON, test.title, err.message, err.stack, err.actual, err.expected, testFlowId)); 144 | } 145 | } else{ 146 | if (useStdError) { 147 | logError(formatString(TEST_FAILED, test.title, err.message, err.stack, testFlowId)); 148 | } else { 149 | log(formatString(TEST_FAILED, test.title, err.message, err.stack, testFlowId)); 150 | } 151 | } 152 | // Log testFinished for failed hook (hook end event is not fired for failed hook) 153 | if (recordHookFailures && !ignoreHookWithName || recordHookFailures && ignoreHookWithName && !test.title.includes(ignoreHookWithName)) { 154 | if (isHook) { 155 | if(isNil(test.duration)){ 156 | log(formatString(TEST_END_NO_DURATION, test.title, hookFlowId)); 157 | } else { 158 | log(formatString(TEST_END, test.title, test.duration.toString(), hookFlowId)); 159 | } 160 | } 161 | } 162 | }); 163 | 164 | runner.on(EVENT_TEST_PENDING, function (test) { 165 | log(formatString(TEST_IGNORED, test.title, test.title, flowId)); 166 | if (displayIgnoredAsIgnored) ignoredTests[`${test.title}-${flowId}`] = testState.pending; 167 | }); 168 | 169 | runner.on(EVENT_TEST_END, function (test) { 170 | if (displayIgnoredAsIgnored && ignoredTests[`${test.title}-${flowId}`] === testState.pending) { 171 | delete ignoredTests[`${test.title}-${flowId}`]; 172 | return; 173 | } 174 | if(isNil(test.duration)){ 175 | log(formatString(TEST_END_NO_DURATION, test.title, flowId)); 176 | } else { 177 | log(formatString(TEST_END, test.title, test.duration.toString(), flowId)); 178 | } 179 | }); 180 | 181 | runner.on(EVENT_HOOK_BEGIN, function (test) { 182 | if (recordHookFailures && !ignoreHookWithName || recordHookFailures && ignoreHookWithName && !test.title.includes(ignoreHookWithName)) { 183 | log(formatString(TEST_START, test.title, hookFlowId)); 184 | } 185 | }); 186 | 187 | runner.on(EVENT_HOOK_END, function (test) { 188 | if (recordHookFailures && !ignoreHookWithName || recordHookFailures && ignoreHookWithName && !test.title.includes(ignoreHookWithName)) { 189 | if(isNil(test.duration)){ 190 | log(formatString(TEST_END_NO_DURATION, test.title, hookFlowId)); 191 | } else { 192 | log(formatString(TEST_END, test.title, test.duration.toString(), hookFlowId)); 193 | } 194 | } 195 | }); 196 | 197 | runner.on(EVENT_SUITE_END, function (suite) { 198 | if (suite.root) return; 199 | log(formatString(SUITE_END, suite.title, new Date() - suite.startDate, flowId)); 200 | }); 201 | 202 | runner.on(EVENT_RUN_END, function () { 203 | let duration; 204 | (typeof stats === 'undefined') ? duration = null : duration = stats.duration; 205 | if (topLevelSuite) { 206 | isNil(duration) ? log(formatString(SUITE_END_NO_DURATION, topLevelSuite, flowId)) : log(formatString(SUITE_END, topLevelSuite, duration, flowId)); 207 | } 208 | }); 209 | } 210 | 211 | 212 | /** 213 | * Expose `Teamcity`. 214 | */ 215 | 216 | exports = module.exports = Teamcity; 217 | -------------------------------------------------------------------------------- /test/excluded tests/ignoreHookWithNameDisabledRoot.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | const {execFile} = require('child_process'); 3 | const {assert} = require('chai'); 4 | const { logMochaOutput, getMochaPath } = require('../testHelpers'); 5 | const internalMochaPath = getMochaPath(); 6 | const path = require('path'); 7 | 8 | // Skipped because this suite should be run only for mocha 8 or higher 9 | describe.skip('Check TeamCity Output is correct with recordHookFailures and root hooks but with no ignoreHookWithName option', function () { 10 | let teamCityStdout, teamCityStderr, teamCityOutputArray; 11 | function verifyResults() { 12 | it('stdout output should exist', function () { 13 | assert.isOk(teamCityStdout, 'has output'); 14 | assert.isOk(teamCityOutputArray, 'array of output is populated'); 15 | assert.lengthOf(teamCityOutputArray, 20); 16 | assert.isEmpty(teamCityOutputArray[19]); 17 | }); 18 | 19 | it('stderr output should not exist', function () { 20 | assert.isEmpty(teamCityStderr); 21 | }); 22 | 23 | 24 | it('stdout output should exist', function () { 25 | assert.isOk(teamCityStdout); 26 | assert.isOk(teamCityOutputArray); 27 | assert.isOk(teamCityOutputArray.length >= 12); 28 | }); 29 | 30 | it('Suite1 started is OK', function () { 31 | const rowToCheck = teamCityOutputArray[0]; 32 | assert.isOk(/##teamcity\[testSuiteStarted/.test(rowToCheck)); 33 | assert.isOk(/name='Hook Test Top Describe Fail'/.test(rowToCheck)); 34 | assert.isOk(/flowId=/.test(rowToCheck)); 35 | assert.isOk(/]/.test(rowToCheck)); 36 | }); 37 | 38 | it('Hook Test started is OK', function () { 39 | const rowToCheck = teamCityOutputArray[1]; 40 | assert.match(rowToCheck, /##teamcity\[testStarted/); 41 | assert.match(rowToCheck, /name='"before all" hook/); 42 | assert.match(rowToCheck, /flowId=/); 43 | assert.match(rowToCheck, /]/); 44 | }); 45 | 46 | it('Hook Test Failed is OK', function () { 47 | const rowToCheck = teamCityOutputArray[2]; 48 | assert.isOk(/##teamcity\[testFailed/.test(rowToCheck)); 49 | assert.isOk(/name='"before all" hook/.test(rowToCheck)); 50 | assert.isOk(/details='/.test(rowToCheck)); 51 | assert.isOk(/Error: Before hook error fail/.test(rowToCheck)); 52 | assert.isOk(/|n/.test(rowToCheck)); 53 | assert.isOk(/|failingHook.js:29:12/.test(rowToCheck)); 54 | assert.isOk(/captureStandardOutput='true'/.test(rowToCheck)); 55 | assert.isOk(/flowId=/.test(rowToCheck)); 56 | assert.isOk(/duration=/.test(rowToCheck) === false); 57 | assert.isOk(/]/.test(rowToCheck)); 58 | }); 59 | 60 | it('Suite1 Finished is OK', function () { 61 | const rowToCheck = teamCityOutputArray[5]; 62 | assert.isOk(/##teamcity\[testSuiteFinished/.test(rowToCheck)); 63 | assert.isOk(/name='Hook Test Top Describe Fail'/.test(rowToCheck)); 64 | assert.isOk(/duration=/.test(rowToCheck)); 65 | assert.isOk(/flowId=/.test(rowToCheck)); 66 | assert.isOk(/]/.test(rowToCheck)); 67 | }); 68 | 69 | it('Suite2 started is OK', function () { 70 | const rowToCheck = teamCityOutputArray[6]; 71 | assert.isOk(/##teamcity\[testSuiteStarted/.test(rowToCheck)); 72 | assert.isOk(/name='Hook Test Top Describe Pass'/.test(rowToCheck)); 73 | assert.isOk(/flowId=/.test(rowToCheck)); 74 | assert.isOk(/]/.test(rowToCheck)); 75 | }); 76 | 77 | 78 | it('Test started is OK', function () { 79 | const rowToCheck = teamCityOutputArray[13]; 80 | assert.isOk(/##teamcity\[testStarted/.test(rowToCheck)); 81 | assert.isOk(/name='Test Passing Test @pass'/.test(rowToCheck)); 82 | assert.isOk(/flowId=/.test(rowToCheck)); 83 | assert.isOk(/]/.test(rowToCheck)); 84 | }); 85 | 86 | 87 | it('Passing Test Finished is OK', function () { 88 | const rowToCheck = teamCityOutputArray[15]; 89 | assert.isOk(/##teamcity\[testFinished/.test(rowToCheck)); 90 | assert.isOk(/name='Test Passing Test @pass'/.test(rowToCheck)); 91 | assert.isOk(/flowId=/.test(rowToCheck)); 92 | assert.isOk(/duration=/.test(rowToCheck)); 93 | assert.isOk(/]/.test(rowToCheck)); 94 | }); 95 | 96 | it('Test Failed is Failing', function () { 97 | const rowToCheck = teamCityOutputArray[10]; 98 | assert.isOk(/##teamcity\[testFailed/.test(rowToCheck)); 99 | assert.isOk(/name='Test Failing Test @fail'/.test(rowToCheck)); 100 | assert.isOk(/flowId=/.test(rowToCheck)); 101 | assert.isOk(/duration=/.test(rowToCheck) === false); 102 | assert.isOk(/details='/.test(rowToCheck)); 103 | assert.isOk(/AssertionError/.test(rowToCheck)); 104 | assert.isOk(/|n/.test(rowToCheck)); 105 | assert.isOk(/|failingHook.js:29:12/.test(rowToCheck)); 106 | assert.isOk(/captureStandardOutput='true'/.test(rowToCheck)); 107 | assert.isOk(/]/.test(rowToCheck)); 108 | }); 109 | 110 | it('Failing Test Finished is OK', function () { 111 | const rowToCheck = teamCityOutputArray[11]; 112 | assert.match(rowToCheck, /##teamcity\[testFinished/); 113 | assert.match(rowToCheck, /name='Test Failing Test @fail'/); 114 | assert.match(rowToCheck, /flowId=/); 115 | assert.match(rowToCheck, /duration=/); 116 | assert.match(rowToCheck, /]/); 117 | }); 118 | 119 | it('Suite2 Finished is OK', function () { 120 | const rowToCheck = teamCityOutputArray[18]; 121 | assert.isOk(/##teamcity\[testSuiteFinished/.test(rowToCheck)); 122 | assert.isOk(/name='Hook Test Top Describe Pass'/.test(rowToCheck)); 123 | assert.isOk(/duration=/.test(rowToCheck)); 124 | assert.isOk(/flowId=/.test(rowToCheck)); 125 | assert.isOk(/]/.test(rowToCheck)); 126 | }); 127 | 128 | it('Suite Root Finished is OK', function () { 129 | const rowToCheck = teamCityOutputArray[18]; 130 | assert.isOk(/##teamcity\[testSuiteFinished/.test(rowToCheck)); 131 | assert.isNotOk(/name='mocha.suite'/.test(rowToCheck)); 132 | assert.isOk(/duration=/.test(rowToCheck)); 133 | assert.isOk(/flowId=/.test(rowToCheck)); 134 | assert.isOk(/]/.test(rowToCheck)); 135 | }); 136 | 137 | it('After hook failed - started test is OK', function () { 138 | const rowToCheck = teamCityOutputArray[3]; 139 | assert.match(rowToCheck, /##teamcity\[testStarted/); 140 | assert.match(rowToCheck, /"after all" hook: afterHookNoReporting for " Test Skipped Test/); 141 | assert.match(rowToCheck, /flowId=/); 142 | assert.match(rowToCheck, /]/); 143 | }); 144 | 145 | it('After hook failed - failed test is OK', function () { 146 | const rowToCheck = teamCityOutputArray[4]; 147 | assert.match(rowToCheck, /##teamcity\[testFailed/); 148 | assert.match(rowToCheck, /"after all" hook: afterHookNoReporting for " Test Skipped Test/); 149 | assert.match(rowToCheck, /flowId=/); 150 | assert.match(rowToCheck, /]/); 151 | }); 152 | 153 | it('Before each root hook for failed test is OK', function () { 154 | const rowToCheck = teamCityOutputArray[9]; 155 | assert.match(rowToCheck, /##teamcity\[testStarted/); 156 | assert.match(rowToCheck, /"before each" hook: beforeEachRootHookNoReporting for "Test Failing Test/); 157 | assert.match(rowToCheck, /flowId=/); 158 | assert.match(rowToCheck, /]/); 159 | }); 160 | 161 | it('Before each root hook for passed test is OK', function () { 162 | const rowToCheck = teamCityOutputArray[14]; 163 | assert.match(rowToCheck, /##teamcity\[testStarted/); 164 | assert.match(rowToCheck, /"before each" hook: beforeEachRootHookNoReporting for "Test Passing Test/); 165 | assert.match(rowToCheck, /flowId=/); 166 | assert.match(rowToCheck, /]/); 167 | }); 168 | 169 | it('After each root hook for failed test is OK', function () { 170 | const rowToCheck = teamCityOutputArray[12]; 171 | assert.match(rowToCheck, /##teamcity\[testStarted/); 172 | assert.match(rowToCheck, /"after each" hook: afterEachRoot for "Test Failing Test/); 173 | assert.match(rowToCheck, /flowId=/); 174 | assert.match(rowToCheck, /]/); 175 | }); 176 | 177 | it('After each root hook for passed test is OK', function () { 178 | const rowToCheck = teamCityOutputArray[16]; 179 | assert.match(rowToCheck, /##teamcity\[testStarted/); 180 | assert.match(rowToCheck, /"after each" hook: afterEachRoot for "Test Passing Test/); 181 | assert.match(rowToCheck, /flowId=/); 182 | assert.match(rowToCheck, /]/); 183 | }); 184 | 185 | it('After hook started test is OK', function () { 186 | const rowToCheck = teamCityOutputArray[17]; 187 | assert.match(rowToCheck, /##teamcity\[testStarted/); 188 | assert.match(rowToCheck, /"after all" hook: afterHookNoReporting for "Test Passing Test/); 189 | assert.match(rowToCheck, /flowId=/); 190 | assert.match(rowToCheck, /]/); 191 | }); 192 | } 193 | 194 | describe('specified as an env var', function () { 195 | before(function (done) { 196 | const opts = { 197 | env: Object.assign({ 198 | ['RECORD_HOOK_FAILURES']: 'true' 199 | }, process.env) 200 | }; 201 | 202 | execFile(internalMochaPath, [ 203 | path.join('test', 'example', 'failingIgnoreHook.js'), 204 | '--reporter', 205 | 'lib/teamcity', 206 | `--require=${path.join('test', 'example', 'rootHooks.js')}` 207 | ], opts, (err, stdout, stderr) => { 208 | teamCityStdout = stdout; 209 | teamCityStderr = stderr; 210 | teamCityOutputArray = stdout.split('\n'); 211 | logMochaOutput(stdout, stderr); 212 | done(); 213 | }); 214 | }); 215 | verifyResults(); 216 | }); 217 | 218 | describe('specified with --reporter-options', function () { 219 | before(function (done) { 220 | execFile(internalMochaPath, [ 221 | path.join('test', 'example', 'failingIgnoreHook.js'), 222 | '--reporter', 223 | 'lib/teamcity', 224 | '--reporter-options', 225 | 'recordHookFailures=true', 226 | `--require=${path.join('test', 'example', 'rootHooks.js')}` 227 | ], (err, stdout, stderr) => { 228 | teamCityStdout = stdout; 229 | teamCityStderr = stderr; 230 | teamCityOutputArray = stdout.split('\n'); 231 | logMochaOutput(stdout, stderr); 232 | done(); 233 | }); 234 | }); 235 | verifyResults(); 236 | }); 237 | }); 238 | -------------------------------------------------------------------------------- /test/functional/ignoreHookWithNameEnabled.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | const {execFile} = require('child_process'); 3 | const {assert} = require('chai'); 4 | const { logMochaOutput, getMochaPath } = require('../testHelpers'); 5 | const internalMochaPath = getMochaPath(); 6 | const path = require('path'); 7 | 8 | describe('Check TeamCity Output is correct with ignoreHookWithName option', function () { 9 | let teamCityStdout, teamCityStderr, teamCityOutputArray; 10 | function verifyResults() { 11 | it('1 stdout output should exist', function () { 12 | assert.isOk(teamCityStdout, 'has output'); 13 | assert.isOk(teamCityOutputArray, 'array of output is populated'); 14 | assert.lengthOf(teamCityOutputArray, 22); 15 | assert.isEmpty(teamCityOutputArray[21]); 16 | }); 17 | 18 | it('2 stderr output should not exist', function () { 19 | assert.isEmpty(teamCityStderr); 20 | }); 21 | 22 | it('3 Suite1 testSuiteStarted', function () { 23 | const rowToCheck = teamCityOutputArray[0]; 24 | assert.isOk(/##teamcity\[testSuiteStarted/.test(rowToCheck)); 25 | assert.isOk(/name='Hook Test Top Describe Fail'/.test(rowToCheck)); 26 | assert.isOk(/flowId=/.test(rowToCheck)); 27 | assert.isOk(/]/.test(rowToCheck)); 28 | }); 29 | 30 | it('4 Before all testStarted', function () { 31 | const rowToCheck = teamCityOutputArray[1]; 32 | assert.match(rowToCheck, /##teamcity\[testStarted/); 33 | assert.match(rowToCheck, /name='"before all" hook/); 34 | assert.match(rowToCheck, /flowId=/); 35 | assert.match(rowToCheck, /]/); 36 | }); 37 | 38 | it('5 Before all testFinished', function () { 39 | const rowToCheck = teamCityOutputArray[2]; 40 | assert.match(rowToCheck, /##teamcity\[testFinished/); 41 | assert.match(rowToCheck, /name='"before all" hook/); 42 | assert.match(rowToCheck, /flowId=/); 43 | assert.match(rowToCheck, /duration=/); 44 | assert.match(rowToCheck, /]/); 45 | }); 46 | 47 | it('6 Passing test testStarted', function () { 48 | const rowToCheck = teamCityOutputArray[3]; 49 | assert.isOk(/##teamcity\[testStarted/.test(rowToCheck)); 50 | assert.isOk(/name='Test Passing Test @pass'/.test(rowToCheck)); 51 | assert.isOk(/flowId=/.test(rowToCheck)); 52 | assert.isOk(/]/.test(rowToCheck)); 53 | }); 54 | 55 | it('7 Before each hook testFailed', function () { 56 | const rowToCheck = teamCityOutputArray[4]; 57 | assert.isOk(/##teamcity\[testFailed/.test(rowToCheck)); 58 | assert.isOk(/"before each" hook: undefinedbeforeEachHookNoReporting for "Test Passing Test @pass"'/.test(rowToCheck)); 59 | assert.isOk(/message='Before each hook error fail'/.test(rowToCheck)); 60 | assert.isOk(/flowId=/.test(rowToCheck)); 61 | assert.isOk(/]/.test(rowToCheck)); 62 | }); 63 | 64 | it('8 After each hook testStarted', function () { 65 | const rowToCheck = teamCityOutputArray[5]; 66 | assert.isOk(/##teamcity\[testStarted/.test(rowToCheck)); 67 | assert.isOk(/"after each" hook: undefinedafterEachHook/.test(rowToCheck)); 68 | assert.isOk(/flowId=/.test(rowToCheck)); 69 | assert.isOk(/]/.test(rowToCheck)); 70 | }); 71 | 72 | it('8 After each hook testFailed', function () { 73 | const rowToCheck = teamCityOutputArray[6]; 74 | assert.isOk(/##teamcity\[testFailed/.test(rowToCheck)); 75 | assert.isOk(/"after each" hook: undefinedafterEachHook for "Test Passing Test @pass"'/.test(rowToCheck)); 76 | assert.isOk(/message='After each hook error fail'/.test(rowToCheck)); 77 | assert.isOk(/flowId=/.test(rowToCheck)); 78 | assert.isOk(/]/.test(rowToCheck)); 79 | }); 80 | 81 | it('10 After each hook testFinished', function () { 82 | const rowToCheck = teamCityOutputArray[7]; 83 | assert.match(rowToCheck, /##teamcity\[testFinished/); 84 | assert.match(rowToCheck, /"after each" hook: undefinedafterEachHook/); 85 | assert.match(rowToCheck, /flowId=/); 86 | assert.match(rowToCheck, /duration=/); 87 | assert.match(rowToCheck, /]/); 88 | }); 89 | 90 | it('11 After hook testFailed', function () { 91 | const rowToCheck = teamCityOutputArray[8]; 92 | assert.isOk(/##teamcity\[testFailed/.test(rowToCheck)); 93 | assert.isOk(/"after all" hook: afterHookNoReporting for "Test Passing Test @pass"/.test(rowToCheck)); 94 | assert.isOk(/message='After hook error fail'/.test(rowToCheck)); 95 | assert.isOk(/flowId=/.test(rowToCheck)); 96 | assert.isOk(/]/.test(rowToCheck)); 97 | }); 98 | 99 | it('12 Suite1 testSuiteFinished', function () { 100 | const rowToCheck = teamCityOutputArray[9]; 101 | assert.isOk(/##teamcity\[testSuiteFinished/.test(rowToCheck)); 102 | assert.isOk(/name='Hook Test Top Describe Fail'/.test(rowToCheck)); 103 | assert.isOk(/duration=/.test(rowToCheck)); 104 | assert.isOk(/flowId=/.test(rowToCheck)); 105 | assert.isOk(/]/.test(rowToCheck)); 106 | }); 107 | 108 | it('13 Suite2 testSuiteStarted', function () { 109 | const rowToCheck = teamCityOutputArray[10]; 110 | assert.isOk(/##teamcity\[testSuiteStarted/.test(rowToCheck)); 111 | assert.isOk(/name='Hook Test Top Describe Pass'/.test(rowToCheck)); 112 | assert.isOk(/flowId=/.test(rowToCheck)); 113 | assert.isOk(/]/.test(rowToCheck)); 114 | }); 115 | 116 | it('14 Before hook testStarted', function () { 117 | const rowToCheck = teamCityOutputArray[11]; 118 | assert.isOk(/##teamcity\[testStarted/.test(rowToCheck)); 119 | assert.isOk(/name='"before all" hook/.test(rowToCheck)); 120 | assert.isOk(/flowId=/.test(rowToCheck)); 121 | assert.isOk(/]/.test(rowToCheck)); 122 | }); 123 | 124 | it('15 Before hook testFinished', function () { 125 | const rowToCheck = teamCityOutputArray[12]; 126 | assert.match(rowToCheck, /##teamcity\[testFinished/); 127 | assert.match(rowToCheck, /name='"before all" hook/); 128 | assert.match(rowToCheck, /flowId=/); 129 | assert.match(rowToCheck, /duration=/); 130 | assert.match(rowToCheck, /]/); 131 | }); 132 | 133 | it('16 Failing test testStarted', function () { 134 | const rowToCheck = teamCityOutputArray[13]; 135 | assert.isOk(/##teamcity\[testStarted/.test(rowToCheck)); 136 | assert.isOk(/name='Test Failing Test @fail'/.test(rowToCheck)); 137 | assert.isOk(/flowId=/.test(rowToCheck)); 138 | assert.isOk(/]/.test(rowToCheck)); 139 | }); 140 | 141 | it('17 Failing test testFailed', function () { 142 | const rowToCheck = teamCityOutputArray[14]; 143 | assert.isOk(/##teamcity\[testFailed/.test(rowToCheck)); 144 | assert.isOk(/name='Test Failing Test @fail'/.test(rowToCheck)); 145 | assert.isOk(/flowId=/.test(rowToCheck)); 146 | assert.isOk(/duration=/.test(rowToCheck) === false); 147 | assert.isOk(/details='/.test(rowToCheck)); 148 | assert.isOk(/AssertionError/.test(rowToCheck)); 149 | assert.isOk(/|n/.test(rowToCheck)); 150 | assert.isOk(/|failingHook.js:29:12/.test(rowToCheck)); 151 | assert.isOk(/captureStandardOutput='true'/.test(rowToCheck)); 152 | assert.isOk(/]/.test(rowToCheck)); 153 | }); 154 | 155 | it('18 Failing Test testFinished', function () { 156 | const rowToCheck = teamCityOutputArray[15]; 157 | assert.match(rowToCheck, /##teamcity\[testFinished/); 158 | assert.match(rowToCheck, /name='Test Failing Test @fail'/); 159 | assert.match(rowToCheck, /flowId=/); 160 | assert.match(rowToCheck, /duration=/); 161 | assert.match(rowToCheck, /]/); 162 | }); 163 | 164 | it('19 Passing test testStarted', function () { 165 | const rowToCheck = teamCityOutputArray[16]; 166 | assert.isOk(/##teamcity\[testStarted/.test(rowToCheck)); 167 | assert.isOk(/name='Test Passing Test @pass'/.test(rowToCheck)); 168 | assert.isOk(/flowId=/.test(rowToCheck)); 169 | assert.isOk(/]/.test(rowToCheck)); 170 | }); 171 | 172 | it('20 Passing test testFinished', function () { 173 | const rowToCheck = teamCityOutputArray[17]; 174 | assert.isOk(/##teamcity\[testFinished/.test(rowToCheck)); 175 | assert.isOk(/name='Test Passing Test @pass'/.test(rowToCheck)); 176 | assert.isOk(/duration=/.test(rowToCheck)); 177 | assert.isOk(/flowId=/.test(rowToCheck)); 178 | assert.isOk(/]/.test(rowToCheck)); 179 | }); 180 | 181 | it('21 After hook testStarted', function () { 182 | const rowToCheck = teamCityOutputArray[18]; 183 | assert.isOk(/##teamcity\[testStarted/.test(rowToCheck)); 184 | assert.isOk(/name='"after all" hook: afterHook/.test(rowToCheck)); 185 | assert.isOk(/flowId=/.test(rowToCheck)); 186 | assert.isOk(/]/.test(rowToCheck)); 187 | }); 188 | 189 | it('22 After hook testFinished', function () { 190 | const rowToCheck = teamCityOutputArray[19]; 191 | assert.match(rowToCheck, /##teamcity\[testFinished/); 192 | assert.match(rowToCheck, /name='"after all" hook: afterHook/); 193 | assert.match(rowToCheck, /flowId=/); 194 | assert.match(rowToCheck, /duration=/); 195 | assert.match(rowToCheck, /]/); 196 | }); 197 | 198 | it('19 Suite2 Finished is OK', function () { 199 | const rowToCheck = teamCityOutputArray[20]; 200 | assert.isOk(/##teamcity\[testSuiteFinished/.test(rowToCheck)); 201 | assert.isOk(/name='Hook Test Top Describe Pass'/.test(rowToCheck)); 202 | assert.isOk(/duration=/.test(rowToCheck)); 203 | assert.isOk(/flowId=/.test(rowToCheck)); 204 | assert.isOk(/]/.test(rowToCheck)); 205 | }); 206 | } 207 | 208 | describe('specified as an env var', function () { 209 | before(function (done) { 210 | const opts = { 211 | env: Object.assign({ 212 | ['RECORD_HOOK_FAILURES']: 'true', 213 | ['IGNORE_HOOK_WITH_NAME']: 'HookNoReporting' 214 | }, process.env) 215 | }; 216 | 217 | execFile(internalMochaPath, [ 218 | path.join('test', 'example', 'failingIgnoreHookNoRoot.js'), 219 | '--reporter', 220 | 'lib/teamcity' 221 | ], opts, (err, stdout, stderr) => { 222 | teamCityStdout = stdout; 223 | teamCityStderr = stderr; 224 | teamCityOutputArray = stdout.split('\n'); 225 | logMochaOutput(stdout, stderr); 226 | done(); 227 | }); 228 | }); 229 | verifyResults(); 230 | }); 231 | 232 | describe('specified with --reporter-options', function () { 233 | before(function (done) { 234 | execFile(internalMochaPath, [ 235 | path.join('test', 'example', 'failingIgnoreHookNoRoot.js'), 236 | '--reporter', 237 | 'lib/teamcity', 238 | '--reporter-options', 239 | 'recordHookFailures=true', 240 | '--reporter-options', 241 | 'ignoreHookWithName=HookNoReporting' 242 | ], (err, stdout, stderr) => { 243 | teamCityStdout = stdout; 244 | teamCityStderr = stderr; 245 | teamCityOutputArray = stdout.split('\n'); 246 | logMochaOutput(stdout, stderr); 247 | done(); 248 | }); 249 | }); 250 | verifyResults(); 251 | }); 252 | }); 253 | -------------------------------------------------------------------------------- /test/functional/ignoreHookWithNameDisabled.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | const {execFile} = require('child_process'); 3 | const {assert} = require('chai'); 4 | const { logMochaOutput, getMochaPath } = require('../testHelpers'); 5 | const internalMochaPath = getMochaPath(); 6 | const path = require('path'); 7 | 8 | describe('Check TeamCity Output is correct with recordHookFailures but with no ignoreHookWithName option', function () { 9 | let teamCityStdout, teamCityStderr, teamCityOutputArray; 10 | function verifyResults() { 11 | it('1 stdout output should exist', function () { 12 | assert.isOk(teamCityStdout, 'has output'); 13 | assert.isOk(teamCityOutputArray, 'array of output is populated'); 14 | assert.lengthOf(teamCityOutputArray, 34); 15 | assert.isEmpty(teamCityOutputArray[33]); 16 | }); 17 | 18 | it('2 stderr output should not exist', function () { 19 | assert.isEmpty(teamCityStderr); 20 | }); 21 | 22 | it('3 Suite1 testSuiteStarted', function () { 23 | const rowToCheck = teamCityOutputArray[0]; 24 | assert.isOk(/##teamcity\[testSuiteStarted/.test(rowToCheck)); 25 | assert.isOk(/name='Hook Test Top Describe Fail'/.test(rowToCheck)); 26 | assert.isOk(/flowId=/.test(rowToCheck)); 27 | assert.isOk(/]/.test(rowToCheck)); 28 | }); 29 | 30 | it('4 Before all testStarted', function () { 31 | const rowToCheck = teamCityOutputArray[1]; 32 | assert.match(rowToCheck, /##teamcity\[testStarted/); 33 | assert.match(rowToCheck, /name='"before all" hook/); 34 | assert.match(rowToCheck, /flowId=/); 35 | assert.match(rowToCheck, /]/); 36 | }); 37 | 38 | it('5 Before all testFinished', function () { 39 | const rowToCheck = teamCityOutputArray[2]; 40 | assert.match(rowToCheck, /##teamcity\[testFinished/); 41 | assert.match(rowToCheck, /name='"before all" hook/); 42 | assert.match(rowToCheck, /flowId=/); 43 | assert.match(rowToCheck, /duration=/); 44 | assert.match(rowToCheck, /]/); 45 | }); 46 | 47 | it('6 Passing test testStarted', function () { 48 | const rowToCheck = teamCityOutputArray[3]; 49 | assert.isOk(/##teamcity\[testStarted/.test(rowToCheck)); 50 | assert.isOk(/name='Test Passing Test @pass'/.test(rowToCheck)); 51 | assert.isOk(/flowId=/.test(rowToCheck)); 52 | assert.isOk(/]/.test(rowToCheck)); 53 | }); 54 | 55 | it('7 Before each hook testStarted', function () { 56 | const rowToCheck = teamCityOutputArray[4]; 57 | assert.isOk(/##teamcity\[testStarted/.test(rowToCheck)); 58 | assert.isOk(/name='"before each" hook: undefinedbeforeEachHookNoReporting/.test(rowToCheck)); 59 | assert.isOk(/flowId=/.test(rowToCheck)); 60 | assert.isOk(/]/.test(rowToCheck)); 61 | }); 62 | 63 | it('8 Before each hook testFailed', function () { 64 | const rowToCheck = teamCityOutputArray[5]; 65 | assert.isOk(/##teamcity\[testFailed/.test(rowToCheck)); 66 | assert.isOk(/"before each" hook: undefinedbeforeEachHookNoReporting/.test(rowToCheck)); 67 | assert.isOk(/message='Before each hook error fail'/.test(rowToCheck)); 68 | assert.isOk(/flowId=/.test(rowToCheck)); 69 | assert.isOk(/]/.test(rowToCheck)); 70 | }); 71 | 72 | it('9 Before all testFinished', function () { 73 | const rowToCheck = teamCityOutputArray[6]; 74 | assert.match(rowToCheck, /##teamcity\[testFinished/); 75 | assert.match(rowToCheck, /"before each" hook: undefinedbeforeEachHookNoReporting/); 76 | assert.match(rowToCheck, /flowId=/); 77 | assert.match(rowToCheck, /duration=/); 78 | assert.match(rowToCheck, /]/); 79 | }); 80 | 81 | it('8 After each hook testStarted', function () { 82 | const rowToCheck = teamCityOutputArray[7]; 83 | assert.isOk(/##teamcity\[testStarted/.test(rowToCheck)); 84 | assert.isOk(/"after each" hook: undefinedafterEachHook/.test(rowToCheck)); 85 | assert.isOk(/flowId=/.test(rowToCheck)); 86 | assert.isOk(/]/.test(rowToCheck)); 87 | }); 88 | 89 | it('9 After each hook testFailed', function () { 90 | const rowToCheck = teamCityOutputArray[8]; 91 | assert.isOk(/##teamcity\[testFailed/.test(rowToCheck)); 92 | assert.isOk(/"after each" hook: undefinedafterEachHook for "Test Passing Test @pass"'/.test(rowToCheck)); 93 | assert.isOk(/message='After each hook error fail'/.test(rowToCheck)); 94 | assert.isOk(/flowId=/.test(rowToCheck)); 95 | assert.isOk(/]/.test(rowToCheck)); 96 | }); 97 | 98 | it('10 After each hook testFinished', function () { 99 | const rowToCheck = teamCityOutputArray[9]; 100 | assert.match(rowToCheck, /##teamcity\[testFinished/); 101 | assert.match(rowToCheck, /"after each" hook: undefinedafterEachHook/); 102 | assert.match(rowToCheck, /flowId=/); 103 | assert.match(rowToCheck, /duration=/); 104 | assert.match(rowToCheck, /]/); 105 | }); 106 | 107 | it('11 After hook testStarted', function () { 108 | const rowToCheck = teamCityOutputArray[10]; 109 | assert.isOk(/##teamcity\[testStarted/.test(rowToCheck)); 110 | assert.isOk(/name='"after all" hook/.test(rowToCheck)); 111 | assert.isOk(/flowId=/.test(rowToCheck)); 112 | assert.isOk(/]/.test(rowToCheck)); 113 | }); 114 | 115 | it('12 After hook testFailed', function () { 116 | const rowToCheck = teamCityOutputArray[11]; 117 | assert.isOk(/##teamcity\[testFailed/.test(rowToCheck)); 118 | assert.isOk(/"after all" hook: afterHookNoReporting for "Test Passing Test @pass"/.test(rowToCheck)); 119 | assert.isOk(/message='After hook error fail'/.test(rowToCheck)); 120 | assert.isOk(/flowId=/.test(rowToCheck)); 121 | assert.isOk(/]/.test(rowToCheck)); 122 | }); 123 | 124 | it('13 After hook testFinished', function () { 125 | const rowToCheck = teamCityOutputArray[12]; 126 | assert.match(rowToCheck, /##teamcity\[testFinished/); 127 | assert.match(rowToCheck, /"after all" hook: afterHookNoReporting/); 128 | assert.match(rowToCheck, /flowId=/); 129 | assert.match(rowToCheck, /duration=/); 130 | assert.match(rowToCheck, /]/); 131 | }); 132 | 133 | it('14 Suite1 testSuiteFinished', function () { 134 | const rowToCheck = teamCityOutputArray[13]; 135 | assert.isOk(/##teamcity\[testSuiteFinished/.test(rowToCheck)); 136 | assert.isOk(/name='Hook Test Top Describe Fail'/.test(rowToCheck)); 137 | assert.isOk(/duration=/.test(rowToCheck)); 138 | assert.isOk(/flowId=/.test(rowToCheck)); 139 | assert.isOk(/]/.test(rowToCheck)); 140 | }); 141 | 142 | it('15 Suite2 testSuiteStarted', function () { 143 | const rowToCheck = teamCityOutputArray[14]; 144 | assert.isOk(/##teamcity\[testSuiteStarted/.test(rowToCheck)); 145 | assert.isOk(/name='Hook Test Top Describe Pass'/.test(rowToCheck)); 146 | assert.isOk(/flowId=/.test(rowToCheck)); 147 | assert.isOk(/]/.test(rowToCheck)); 148 | }); 149 | 150 | it('16 Before hook testStarted', function () { 151 | const rowToCheck = teamCityOutputArray[15]; 152 | assert.isOk(/##teamcity\[testStarted/.test(rowToCheck)); 153 | assert.isOk(/name='"before all" hook/.test(rowToCheck)); 154 | assert.isOk(/flowId=/.test(rowToCheck)); 155 | assert.isOk(/]/.test(rowToCheck)); 156 | }); 157 | 158 | it('17 Before hook testFinished', function () { 159 | const rowToCheck = teamCityOutputArray[16]; 160 | assert.match(rowToCheck, /##teamcity\[testFinished/); 161 | assert.match(rowToCheck, /name='"before all" hook/); 162 | assert.match(rowToCheck, /flowId=/); 163 | assert.match(rowToCheck, /duration=/); 164 | assert.match(rowToCheck, /]/); 165 | }); 166 | 167 | it('18 Failing test testStarted', function () { 168 | const rowToCheck = teamCityOutputArray[17]; 169 | assert.isOk(/##teamcity\[testStarted/.test(rowToCheck)); 170 | assert.isOk(/name='Test Failing Test @fail'/.test(rowToCheck)); 171 | assert.isOk(/flowId=/.test(rowToCheck)); 172 | assert.isOk(/]/.test(rowToCheck)); 173 | }); 174 | 175 | it('19 Before each hook testStarted', function () { 176 | const rowToCheck = teamCityOutputArray[18]; 177 | assert.isOk(/##teamcity\[testStarted/.test(rowToCheck)); 178 | assert.isOk(/name='"before each" hook: beforeEachHookNoReporting/.test(rowToCheck)); 179 | assert.isOk(/flowId=/.test(rowToCheck)); 180 | assert.isOk(/]/.test(rowToCheck)); 181 | }); 182 | 183 | it('20 Before each hook testFinished', function () { 184 | const rowToCheck = teamCityOutputArray[19]; 185 | assert.match(rowToCheck, /##teamcity\[testFinished/); 186 | assert.match(rowToCheck, /name='"before each" hook: beforeEachHookNoReporting/); 187 | assert.match(rowToCheck, /flowId=/); 188 | assert.match(rowToCheck, /duration=/); 189 | assert.match(rowToCheck, /]/); 190 | }); 191 | 192 | it('21 Failing test testFailed', function () { 193 | const rowToCheck = teamCityOutputArray[20]; 194 | assert.isOk(/##teamcity\[testFailed/.test(rowToCheck)); 195 | assert.isOk(/name='Test Failing Test @fail'/.test(rowToCheck)); 196 | assert.isOk(/flowId=/.test(rowToCheck)); 197 | assert.isOk(/duration=/.test(rowToCheck) === false); 198 | assert.isOk(/details='/.test(rowToCheck)); 199 | assert.isOk(/AssertionError/.test(rowToCheck)); 200 | assert.isOk(/|n/.test(rowToCheck)); 201 | assert.isOk(/|failingHook.js:29:12/.test(rowToCheck)); 202 | assert.isOk(/captureStandardOutput='true'/.test(rowToCheck)); 203 | assert.isOk(/]/.test(rowToCheck)); 204 | }); 205 | 206 | it('22 Failing Test testFinished', function () { 207 | const rowToCheck = teamCityOutputArray[21]; 208 | assert.match(rowToCheck, /##teamcity\[testFinished/); 209 | assert.match(rowToCheck, /name='Test Failing Test @fail'/); 210 | assert.match(rowToCheck, /flowId=/); 211 | assert.match(rowToCheck, /duration=/); 212 | assert.match(rowToCheck, /]/); 213 | }); 214 | 215 | it('23 After each hook testStarted', function () { 216 | const rowToCheck = teamCityOutputArray[22]; 217 | assert.isOk(/##teamcity\[testStarted/.test(rowToCheck)); 218 | assert.isOk(/name='"after each" hook: afterEachHookNoReporting/.test(rowToCheck)); 219 | assert.isOk(/flowId=/.test(rowToCheck)); 220 | assert.isOk(/]/.test(rowToCheck)); 221 | }); 222 | 223 | it('24 After each hook testFinished', function () { 224 | const rowToCheck = teamCityOutputArray[23]; 225 | assert.match(rowToCheck, /##teamcity\[testFinished/); 226 | assert.match(rowToCheck, /name='"after each" hook: afterEachHookNoReporting/); 227 | assert.match(rowToCheck, /flowId=/); 228 | assert.match(rowToCheck, /duration=/); 229 | assert.match(rowToCheck, /]/); 230 | }); 231 | 232 | it('25 Passing test testStarted', function () { 233 | const rowToCheck = teamCityOutputArray[24]; 234 | assert.isOk(/##teamcity\[testStarted/.test(rowToCheck)); 235 | assert.isOk(/name='Test Passing Test @pass'/.test(rowToCheck)); 236 | assert.isOk(/flowId=/.test(rowToCheck)); 237 | assert.isOk(/]/.test(rowToCheck)); 238 | }); 239 | 240 | it('26 Before each hook testStarted', function () { 241 | const rowToCheck = teamCityOutputArray[25]; 242 | assert.isOk(/##teamcity\[testStarted/.test(rowToCheck)); 243 | assert.isOk(/name='"before each" hook: beforeEachHookNoReporting/.test(rowToCheck)); 244 | assert.isOk(/flowId=/.test(rowToCheck)); 245 | assert.isOk(/]/.test(rowToCheck)); 246 | }); 247 | 248 | it('27 Before each hook testFinished', function () { 249 | const rowToCheck = teamCityOutputArray[26]; 250 | assert.match(rowToCheck, /##teamcity\[testFinished/); 251 | assert.match(rowToCheck, /"before each" hook: beforeEachHookNoReporting/); 252 | assert.match(rowToCheck, /flowId=/); 253 | assert.match(rowToCheck, /duration=/); 254 | assert.match(rowToCheck, /]/); 255 | }); 256 | 257 | it('28 Passing test testFinished', function () { 258 | const rowToCheck = teamCityOutputArray[27]; 259 | assert.isOk(/##teamcity\[testFinished/.test(rowToCheck)); 260 | assert.isOk(/name='Test Passing Test @pass'/.test(rowToCheck)); 261 | assert.isOk(/duration=/.test(rowToCheck)); 262 | assert.isOk(/flowId=/.test(rowToCheck)); 263 | assert.isOk(/]/.test(rowToCheck)); 264 | }); 265 | 266 | it('29 After each hook testStarted', function () { 267 | const rowToCheck = teamCityOutputArray[28]; 268 | assert.isOk(/##teamcity\[testStarted/.test(rowToCheck)); 269 | assert.isOk(/name='"after each" hook: afterEachHookNoReporting/.test(rowToCheck)); 270 | assert.isOk(/flowId=/.test(rowToCheck)); 271 | assert.isOk(/]/.test(rowToCheck)); 272 | }); 273 | 274 | it('30 After each hook testFinished', function () { 275 | const rowToCheck = teamCityOutputArray[29]; 276 | assert.match(rowToCheck, /##teamcity\[testFinished/); 277 | assert.match(rowToCheck, /name='"after each" hook: afterEachHookNoReporting/); 278 | assert.match(rowToCheck, /flowId=/); 279 | assert.match(rowToCheck, /duration=/); 280 | assert.match(rowToCheck, /]/); 281 | }); 282 | 283 | it('31 After hook testStarted', function () { 284 | const rowToCheck = teamCityOutputArray[30]; 285 | assert.isOk(/##teamcity\[testStarted/.test(rowToCheck)); 286 | assert.isOk(/name='"after all" hook: afterHook/.test(rowToCheck)); 287 | assert.isOk(/flowId=/.test(rowToCheck)); 288 | assert.isOk(/]/.test(rowToCheck)); 289 | }); 290 | 291 | it('32 After hook testFinished', function () { 292 | const rowToCheck = teamCityOutputArray[31]; 293 | assert.match(rowToCheck, /##teamcity\[testFinished/); 294 | assert.match(rowToCheck, /name='"after all" hook: afterHook/); 295 | assert.match(rowToCheck, /flowId=/); 296 | assert.match(rowToCheck, /duration=/); 297 | assert.match(rowToCheck, /]/); 298 | }); 299 | 300 | it('33 Suite2 Finished', function () { 301 | const rowToCheck = teamCityOutputArray[32]; 302 | assert.isOk(/##teamcity\[testSuiteFinished/.test(rowToCheck)); 303 | assert.isOk(/name='Hook Test Top Describe Pass'/.test(rowToCheck)); 304 | assert.isOk(/duration=/.test(rowToCheck)); 305 | assert.isOk(/flowId=/.test(rowToCheck)); 306 | assert.isOk(/]/.test(rowToCheck)); 307 | }); 308 | } 309 | 310 | describe('specified as an env var', function () { 311 | before(function (done) { 312 | const opts = { 313 | env: Object.assign({ 314 | ['RECORD_HOOK_FAILURES']: 'true' 315 | }, process.env) 316 | }; 317 | 318 | execFile(internalMochaPath, [ 319 | path.join('test', 'example', 'failingIgnoreHookNoRoot.js'), 320 | '--reporter', 321 | 'lib/teamcity' 322 | ], opts, (err, stdout, stderr) => { 323 | teamCityStdout = stdout; 324 | teamCityStderr = stderr; 325 | teamCityOutputArray = stdout.split('\n'); 326 | logMochaOutput(stdout, stderr); 327 | done(); 328 | }); 329 | }); 330 | verifyResults(); 331 | }); 332 | 333 | describe('specified with --reporter-options', function () { 334 | before(function (done) { 335 | execFile(internalMochaPath, [ 336 | path.join('test', 'example', 'failingIgnoreHookNoRoot.js'), 337 | '--reporter', 338 | 'lib/teamcity', 339 | '--reporter-options', 340 | 'recordHookFailures=true' 341 | ], (err, stdout, stderr) => { 342 | teamCityStdout = stdout; 343 | teamCityStderr = stderr; 344 | teamCityOutputArray = stdout.split('\n'); 345 | logMochaOutput(stdout, stderr); 346 | done(); 347 | }); 348 | }); 349 | verifyResults(); 350 | }); 351 | }); 352 | --------------------------------------------------------------------------------