├── .eslintrc.json ├── .github ├── ISSUE_TEMPLATE │ ├── --bug-report.md │ ├── --documentation.md │ ├── --feature-request.md │ └── --question.md └── stale.yml ├── .gitignore ├── .npmignore ├── .travis.yml ├── CONTRIBUTING.md ├── LICENSE ├── README.md ├── docs └── images │ └── protractor-support.jpg ├── index.js ├── package.json └── test ├── css ├── custom.css └── override.css ├── cucumber ├── config │ ├── full-options.conf.js │ ├── no-log.conf.js │ ├── no-options.conf.js │ └── protractor.shared.conf.js ├── features │ ├── foo.feature │ └── generate.report.feature └── step_definitions │ └── steps.js ├── jasmine.json ├── test.spec.js └── test_util.js /.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "parserOptions": { 3 | "ecmaVersion": 6, 4 | "sourceType": "module" 5 | }, 6 | "env": { 7 | "es6": true, 8 | "node": true, 9 | "protractor": true 10 | }, 11 | "globals": { 12 | "$": true, 13 | "process": true, 14 | "beforeEach": true, 15 | "describe": true, 16 | "it": true, 17 | "expect": true, 18 | "fit": true, 19 | "fdiscribe": true, 20 | "xit": true, 21 | "xdiscribe": true, 22 | "After": true, 23 | "Before": true, 24 | "Given": true, 25 | "When": true, 26 | "Then": true 27 | }, 28 | "root": true, 29 | "rules": { 30 | // Possible Errors 31 | "comma-dangle": [ 32 | 2, 33 | "never" 34 | ], 35 | "no-cond-assign": 2, 36 | "no-console": 0, 37 | "no-constant-condition": 2, 38 | "no-control-regex": 2, 39 | "no-debugger": 2, 40 | "no-dupe-args": 2, 41 | "no-dupe-keys": 2, 42 | "no-duplicate-case": 2, 43 | "no-empty-character-class": 2, 44 | "no-empty": [ 45 | "error", 46 | { 47 | "allowEmptyCatch": true 48 | } 49 | ], 50 | "no-ex-assign": 2, 51 | "no-extra-boolean-cast": 2, 52 | "no-extra-parens": [ 53 | "error", 54 | "all", 55 | { 56 | "nestedBinaryExpressions": false 57 | } 58 | ], 59 | "no-extra-semi": 2, 60 | "no-func-assign": 2, 61 | "no-inner-declarations": 2, 62 | "no-invalid-regexp": 2, 63 | "no-irregular-whitespace": 2, 64 | "no-negated-in-lhs": 2, 65 | "no-obj-calls": 2, 66 | "no-regex-spaces": 2, 67 | "no-sparse-arrays": 2, 68 | "no-unexpected-multiline": 2, 69 | "no-unreachable": 2, 70 | "use-isnan": 2, 71 | "valid-jsdoc": [ 72 | 2, 73 | { 74 | "requireReturn": false, 75 | "requireParamDescription": false, 76 | "requireReturnDescription": false, 77 | "prefer": { 78 | "return": "returns" 79 | } 80 | } 81 | ], 82 | "valid-typeof": 2, 83 | // Best Practices 84 | "accessor-pairs": 2, 85 | "block-scoped-var": 2, 86 | "complexity": [ 87 | "error", 88 | { 89 | "max": 20 90 | } 91 | ], 92 | "consistent-return": 0, 93 | "curly": 2, 94 | "default-case": 2, 95 | "dot-location": [ 96 | 2, 97 | "property" 98 | ], 99 | "dot-notation": 2, 100 | "eqeqeq": 2, 101 | "guard-for-in": 2, 102 | "no-alert": 2, 103 | "no-caller": 2, 104 | "no-case-declarations": 2, 105 | "no-div-regex": 2, 106 | "no-else-return": 1, 107 | "no-empty-pattern": 2, 108 | "no-eq-null": 2, 109 | "no-eval": 2, 110 | "no-extend-native": 2, 111 | "no-extra-bind": 2, 112 | "no-fallthrough": 2, 113 | "no-floating-decimal": 2, 114 | "no-implicit-coercion": 0, 115 | "no-implied-eval": 2, 116 | "no-invalid-this": 0, 117 | "no-iterator": 2, 118 | "no-labels": 2, 119 | "no-lone-blocks": 2, 120 | "no-loop-func": 2, 121 | "no-multi-spaces": 2, 122 | "no-multi-str": 0, 123 | "no-native-reassign": 2, 124 | "no-new-func": 2, 125 | "no-new-wrappers": 2, 126 | "no-new": 0, 127 | "no-octal-escape": 2, 128 | "no-octal": 2, 129 | "no-param-reassign": 2, 130 | "no-process-env": 0, 131 | "no-proto": 2, 132 | "no-redeclare": 2, 133 | "no-return-assign": 2, 134 | "no-script-url": 2, 135 | "no-self-compare": 2, 136 | "no-sequences": 2, 137 | "no-throw-literal": 2, 138 | "no-unused-expressions": [ 139 | 2, 140 | { 141 | "allowShortCircuit": true 142 | } 143 | ], 144 | "no-useless-call": 2, 145 | "no-useless-concat": 2, 146 | "no-void": 2, 147 | "no-warning-comments": 0, 148 | "no-with": 2, 149 | "radix": 2, 150 | "vars-on-top": 0, 151 | // FIXME should be enabled at some point 152 | "wrap-iife": [ 153 | 2, 154 | "inside" 155 | ], 156 | "yoda": [ 157 | 2, 158 | "never", 159 | { 160 | "exceptRange": true 161 | } 162 | ], 163 | // Strict Mode 164 | "strict": [ 165 | 0, 166 | "global" 167 | ], 168 | // Variables 169 | "init-declarations": 0, 170 | "no-catch-shadow": 2, 171 | "no-delete-var": 2, 172 | "no-label-var": 2, 173 | "no-shadow-restricted-names": 2, 174 | "no-shadow": 2, 175 | "no-undef-init": 2, 176 | "no-undef": 2, 177 | "no-undefined": 0, 178 | "no-unused-vars": 1, 179 | "no-use-before-define": 2, 180 | // Stylistic Issues 181 | "array-bracket-spacing": [ 182 | 2, 183 | "never" 184 | ], 185 | "block-spacing": 2, 186 | "brace-style": [ 187 | 2, 188 | "1tbs" 189 | ], 190 | "camelcase": 0, 191 | "comma-spacing": [ 192 | 2, 193 | { 194 | "before": false, 195 | "after": true 196 | } 197 | ], 198 | "comma-style": [ 199 | 2, 200 | "last" 201 | ], 202 | "computed-property-spacing": [ 203 | 2, 204 | "never" 205 | ], 206 | "consistent-this": [ 207 | 2, 208 | "that" 209 | ], 210 | "eol-last": 2, 211 | "func-names": 0, 212 | "func-style": 0, 213 | "id-length": 0, 214 | "id-match": 0, 215 | "indent": [ 216 | "error", 217 | 4, 218 | { 219 | "SwitchCase": 1 220 | } 221 | ], 222 | "jsx-quotes": 0, 223 | "key-spacing": [ 224 | 2, 225 | { 226 | "beforeColon": false, 227 | "afterColon": true 228 | } 229 | ], 230 | "linebreak-style": [ 231 | 2, 232 | "unix" 233 | ], 234 | "lines-around-comment": 0, 235 | "max-nested-callbacks": [ 236 | 2, 237 | 5 238 | ], 239 | "new-cap": [ 240 | "error", 241 | { 242 | "capIsNewExceptions": [ 243 | "After", 244 | "Before", 245 | "Given", 246 | "When", 247 | "Then" 248 | ] 249 | } 250 | ], 251 | "new-parens": 2, 252 | "newline-after-var": 2, 253 | "no-array-constructor": 2, 254 | "no-continue": 2, 255 | "no-inline-comments": 0, 256 | "no-lonely-if": 2, 257 | "no-mixed-spaces-and-tabs": 2, 258 | "no-multiple-empty-lines": [ 259 | 2, 260 | { 261 | "max": 2 262 | } 263 | ], 264 | "no-negated-condition": 2, 265 | "no-nested-ternary": 2, 266 | "no-new-object": 2, 267 | "no-restricted-syntax": 0, 268 | "no-spaced-func": 0, 269 | "no-ternary": 0, 270 | "no-trailing-spaces": 2, 271 | "no-underscore-dangle": 0, 272 | "no-unneeded-ternary": 2, 273 | "object-curly-spacing": [ 274 | 2, 275 | "always", 276 | { 277 | "objectsInObjects": true, 278 | "arraysInObjects": true 279 | } 280 | ], 281 | "one-var": [ 282 | 2, 283 | "never" 284 | ], 285 | "operator-assignment": 2, 286 | "operator-linebreak": [ 287 | 2, 288 | "before" 289 | ], 290 | "padded-blocks": 0, 291 | "quote-props": [ 292 | 2, 293 | "consistent-as-needed" 294 | ], 295 | "quotes": [ 296 | 2, 297 | "single", 298 | "avoid-escape" 299 | ], 300 | "require-jsdoc": 2, 301 | "semi-spacing": [ 302 | 2, 303 | { 304 | "before": false, 305 | "after": true 306 | } 307 | ], 308 | "semi": [ 309 | 0, 310 | "always" 311 | ], 312 | "sort-vars": 0, 313 | "keyword-spacing": 2, 314 | "space-before-blocks": 2, 315 | "space-before-function-paren": [ 316 | "error", 317 | { 318 | "anonymous": "always", 319 | "named": "never" 320 | } 321 | ], 322 | "space-in-parens": [ 323 | 2, 324 | "never" 325 | ], 326 | "space-infix-ops": 2, 327 | "space-unary-ops": 2, 328 | "spaced-comment": [ 329 | 2, 330 | "always", 331 | { 332 | "exceptions": [ 333 | "-", 334 | "+", 335 | "!" 336 | ] 337 | } 338 | ], 339 | "wrap-regex": 2, 340 | // ES6 341 | "arrow-parens": [ 342 | 2, 343 | "always" 344 | ], 345 | // Legacy 346 | "max-depth": [ 347 | 2, 348 | 4 349 | ], 350 | "max-len": [ 351 | 2, 352 | 140 353 | ], 354 | "max-params": [ 355 | 2, 356 | 5 357 | ], 358 | "max-statements": 0, 359 | "no-bitwise": 2, 360 | "no-plusplus": 0 361 | } 362 | } 363 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/--bug-report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: "\U0001F41BBug report" 3 | about: Create a report to help us improve. 4 | 5 | --- 6 | 7 | **Environment (please complete the following information):** 8 | - protractor-multiple-cucumber-html-reporter: [e.g. 1.6.1] 9 | - Node.js version: [e.g. 8.9.1] 10 | - NPM version: [e.g. 5.8.0] 11 | - Platform name and version: [e.g. Windows 10] 12 | - protractor version: [e.g. 5.1.0] 13 | - Cucucmber version: [e.g. 4.2.0] 14 | - protractor-cucumber-framework version: [e.g. 6.0.0] 15 | 16 | **Config of protractor + protractor-multiple-cucumber-html-reporter** 17 | An example of how you configured the reporter in your webdriver.io config 18 | 19 | **Describe the bug** 20 | A clear and concise description of what the bug is. 21 | 22 | **To Reproduce** 23 | Steps to reproduce the behavior: 24 | 25 | [Include code or an example repository that can easily be set up] 26 | 27 | **Expected behavior** 28 | A clear and concise description of what you expected to happen. 29 | 30 | **Log** 31 | If applicable, add logs to help explain your problem. If you don't have log, enable 'debug:true' in the config and paste the log here. 32 | Please use proper markdown to style it 33 | 34 | **Additional context** 35 | Add any other context about the problem here. 36 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/--documentation.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: "\U0001F4D6Documentation" 3 | about: Suggest improvements or report missing/unclear documentation. 4 | 5 | --- 6 | 7 | **Pre-check** 8 | - [ ] I'm aware that I can [edit the docs](https://github.com/wswebcreation/protractor-multiple-cucumber-html-reporter-plugin) and submit a pull request 9 | 10 | **Describe the improvement** 11 | 12 | I'd like to report 13 | - [ ] Unclear documentation 14 | - [ ] A typo 15 | - [ ] Missing documentation 16 | - [ ] Other 17 | 18 | **Description of the improvement / report** 19 | A clear and concise description. 20 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/--feature-request.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: "\U0001F4A1Feature request" 3 | about: Suggest an idea for this module. 4 | 5 | --- 6 | 7 | **Is your feature request related to a problem? Please describe.** 8 | A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] 9 | 10 | **Describe the solution you'd like** 11 | A clear and concise description of what you want to happen. 12 | 13 | **Describe alternatives you've considered** 14 | A clear and concise description of any alternative solutions or features you've considered. 15 | 16 | **Additional context** 17 | Add any other context or screenshots about the feature request here. 18 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/--question.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: "\U0001F4ACQuestion" 3 | about: Ask questions. 4 | 5 | --- 6 | 7 | **Describe your question with as much detail as possible** 8 | A clear and concise question that doesn't require too much conversation. Need more help? [Find me on Gitter](https://gitter.im/wswebcreation/protractor-multiple-cucumber-html-reporter-plugin) 9 | 10 | 11 | **If it's about a specific piece of code, try and include some of it to support your question.** 12 | [...] 13 | 14 | 15 | **Environment (please complete the following information):** 16 | - protractor-multiple-cucumber-html-reporter: [e.g. 1.6.1] 17 | - Node.js version: [e.g. 8.9.1] 18 | - NPM version: [e.g. 5.8.0] 19 | - Platform name and version: [e.g. Windows 10] 20 | - protractor version: [e.g. 5.1.0] 21 | - Cucucmber version: [e.g. 4.2.0] 22 | - protractor-cucumber-framework version: [e.g. 6.0.0] 23 | 24 | 25 | **Additional context** 26 | Add any other context about the problem here. 27 | -------------------------------------------------------------------------------- /.github/stale.yml: -------------------------------------------------------------------------------- 1 | # Number of days of inactivity before an issue becomes stale 2 | daysUntilStale: 90 3 | # Number of days of inactivity before a stale issue is closed 4 | daysUntilClose: 14 5 | # Issues with these labels will never be considered stale 6 | exemptLabels: 7 | - bug 8 | # Label to use when marking an issue as stale 9 | staleLabel: wontfix 10 | # Comment to post when marking an issue as stale. Set to `false` to disable 11 | markComment: > 12 | This issue has been automatically marked as stale because it has not had 13 | recent activity. It will be closed if no further activity occurs. Thank you 14 | for your contributions. 15 | # Comment to post when closing a stale issue. Set to `false` to disable 16 | closeComment: false 17 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .idea/ 2 | .tmp/ 3 | coverage/ 4 | node_modules/ 5 | reports/ 6 | report-path/ 7 | json-output-path/ 8 | package-lock.json 9 | -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | .idea/ 2 | .tmp/ 3 | coverage/ 4 | node_modules/ 5 | reports/ 6 | test/ 7 | docs/ 8 | package-lock.json 9 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | sudo: required 2 | dist: trusty 3 | 4 | language: node_js 5 | node_js: 6 | - "8" 7 | - "10" 8 | 9 | before_install: 10 | - export DISPLAY=:99.0 11 | - sh -e /etc/init.d/xvfb start 12 | - sleep 3 # give xvfb some time to start 13 | 14 | addons: 15 | chrome: stable 16 | 17 | install: 18 | - npm install 19 | 20 | script: 21 | - npm test 22 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributer's Guide 2 | 3 | We welcome contributions - thanks for taking the time to contribute! Here are 4 | some guidelines to help you get started. These are just guidelines, not rules, 5 | use your best judgment and feel free to propose changes to this document in a 6 | pull request. 7 | 8 | ## Discussion 9 | 10 | While not absolutely requir ed, it is encouraged that you first open an 11 | [issue](https://github.com/wswebcreation/protractor-multiple-cucumber-html-reporter-plugin/issues) 12 | for any bug or feature request. This allows discussion on the proper course of 13 | action to take before coding begins. 14 | 15 | ## Building 16 | 17 | ```shell 18 | npm install 19 | ``` 20 | 21 | ## Changing 22 | 23 | Most of the information you need to contribute code changes can [be found here](https://guides.github.com/activities/contributing-to-open-source/). 24 | In short: fork, make your changes and submit a pull request (PR). 25 | 26 | ### Fork 27 | 28 | Fork the project [on Github](https://github.com/wswebcreation/protractor-multiple-cucumber-html-reporter-plugin) 29 | and check out your copy locally: 30 | 31 | ```shell 32 | git clone https://github.com/wswebcreation/protractor-multiple-cucumber-html-reporter-plugin.git 33 | cd protractor-numerator 34 | ``` 35 | 36 | ### Create your branch 37 | 38 | Create a feature branch and start hacking: 39 | 40 | ```shell 41 | git checkout -b my-feature-branch 42 | ``` 43 | 44 | We practice HEAD-based development, which means all changes are applied 45 | directly on top of master. 46 | 47 | ### Commit 48 | 49 | First make sure git knows your name and email address: 50 | 51 | ```shell 52 | git config --global user.name 'John Doe' 53 | git config --global user.email 'john@example.com' 54 | ``` 55 | 56 | **Writing good commit message is important.** A commit message should be around 57 | 50 characters or less and contain a short description of the change and 58 | reference issues fixed (if any). Include `Fixes #N`, where _N_ is the issue 59 | number the commit fixes, if any. 60 | 61 | ### Rebase 62 | 63 | Use `git rebase` (not `git merge`) to sync your work with the core repository 64 | from time to time: 65 | 66 | ```shell 67 | git remote add upstream https://github.com/wswebcreation/protractor-multiple-cucumber-html-reporter-plugin.git 68 | git fetch upstream 69 | git rebase upstream/master 70 | ``` 71 | 72 | ### Test 73 | 74 | Bug fixes and features **should have tests**. Look at other tests to see how 75 | they should be structured. 76 | 77 | This project makes use of code linting and e2e tests to make sure we don't break 78 | anything. Before you submit your pull request make sure you pass all the tests: 79 | 80 | You can run code linting with: `npm run lint`. 81 | You can run all the e2e tests with: `npm test`. 82 | 83 | ### Push 84 | 85 | ```shell 86 | git push origin my-feature-branch 87 | ``` 88 | 89 | Go to https://github.com/yourusername/protractor-multiple-cucumber-html-reporter-plugin.git and press the 90 | _New pull request_ button and fill out the form. 91 | 92 | A good PR comment message can look like this: 93 | 94 | ```text 95 | Explain PR normatively in one line 96 | 97 | Details of PR message are a few lines of text, explaining things 98 | in more detail, possibly giving some background about the issue 99 | being fixed, etc. 100 | 101 | Fixes #142 102 | ``` 103 | 104 | Pull requests are usually reviewed within a few days. If there are comments to 105 | address, apply your changes in new commits (preferably 106 | [fixups](http://git-scm.com/docs/git-commit)) and push to the same branch. 107 | 108 | ### Integration 109 | 110 | When code review is complete, a committer will take your PR and integrate it on 111 | protractor-multiple-cucumber-html-reporter-plugin's master branch. Because we like to keep a linear history 112 | on the master branch, we will normally squash and rebase your branch history. -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2017 wswebcreation 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. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # protractor-multiple-cucumber-html-reporter-plugin 2 | 3 | [![Join the chat at https://gitter.im/wswebcreation/protractor-multiple-cucumber-html-reporter-plugin](https://badges.gitter.im/wswebcreation/protractor-multiple-cucumber-html-reporter-plugin.svg)](https://gitter.im/wswebcreation/protractor-multiple-cucumber-html-reporter-plugin?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge) [![Build Status](https://travis-ci.org/wswebcreation/protractor-multiple-cucumber-html-reporter-plugin.svg?branch=master)](https://travis-ci.org/wswebcreation/protractor-multiple-cucumber-html-reporter-plugin) 4 | [![npm version](https://img.shields.io/npm/v/protractor-multiple-cucumber-html-reporter-plugin.svg)](https://www.npmjs.com/package/protractor-multiple-cucumber-html-reporter-plugin) 5 | [![NPM License](https://img.shields.io/npm/l/protractor-multiple-cucumber-html-reporter-plugin.svg)](https://github.com/wswebcreation/protractor-multiple-cucumber-html-reporter-plugin/blob/master/LICENSE) 6 | 7 | [![NPM](https://nodei.co/npm/protractor-multiple-cucumber-html-reporter-plugin.png)](https://nodei.co/npm/protractor-multiple-cucumber-html-reporter-plugin/) 8 | 9 | # Important News 10 | **Date: 8th of May** I decided to stop maintaining this package per today and add the DEPRECATED status to all versions 11 | 12 | **Date: 16th of January 2020** 13 | 14 | **I will stop maintaining this module per the first of July 2020.** If you look at Protractor you can get the feeling that, in spite of all the effort that the Open Source Community is putting in it, it is not being maintained by Google anymore. 15 | To be honest, I'm pretty sure about it. If you look at the facts, see image below, you will notice that: 16 | - the amount of issues is increasing 17 | - the amount of PR's is increasing 18 | - the activity in the project is decreasing 19 | 20 | This is not giving me a good feeling. As a contributor to the Protractor project in the last years, I also have the feeling that there won't be a big chance that this is going to change. 21 | 22 | I'm not the only one who thinks about Protractor like this, please take a look at [this great article](https://dev.to/davert/5-reasons-you-should-not-use-protractor-in-2019-3l4b) which might also give you some insights. 23 | 24 | I want to thank all contributors for their help in the past 3-4 years and I feel blessed that I was able to help so many people over the last few years. 25 | 26 | ![Protractor Support](./docs/images/protractor-support.jpg) 27 | 28 | This plugin will connect [Protractor](https://www.npmjs.com/package/protractor), [CucumberJS](https://www.npmjs.com/package/cucumber) and [protractor-cucumber-framework](https://www.npmjs.com/package/protractor-cucumber-framework) to generate unique JSON files per feature with only a few lines of code. 29 | It will also replace the extra CucumberJS hook you needed to make in CucumberJS 1 and 2 to generate unique JSON report files. 30 | 31 | > It was born when CucucmberJS 3 [removed](https://github.com/cucumber/cucumber-js/blob/master/CHANGELOG.md#300-2017-08-08) the `registerHandler` and `registerListener`. As of version 3 Protractor and CucumberJS users don't have the possibility to generate and create unique JSON report files. With this module they have. 32 | 33 | You will also get [multiple-cucumber-html-reporter](https://github.com/wswebcreation/multiple-cucumber-html-reporter) as a dependency and use it on the fly to generate beautiful reports. A sample can be found [here](https://wswebcreation.github.io/multiple-cucumber-html-reporter/) 34 | 35 | ## Supported versions 36 | | [Node.js](http://nodejs.org/and) | [Protractor](https://www.npmjs.com/package/protractor) | [CucumberJS](https://www.npmjs.com/package/cucumber) | [protractor-cucumber-framework](https://www.npmjs.com/package/protractor-cucumber-framework) | [multiple-cucumber-html-reporter](https://github.com/wswebcreation/multiple-cucumber-html-reporter) | 37 | | -------------------------------- | ------------------------------------------------------ | ----------------------------------------------------- | ---------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------- | 38 | | 6.x | 4.x | 1.x | 3.1.2 or higher | 1.0.0 or higher | 39 | | 7.x | 5.x | 2.x | | | 40 | | | | 3.x | | | 41 | 42 | ## Installation 43 | `npm install protractor-multiple-cucumber-html-reporter-plugin --save-dev` 44 | 45 | ## Usage 46 | You need to do 2 things. 47 | 48 | 1. Add `format: 'json:.tmp/results.json'` to your `cucumberOpts` in the config. This will tell CucumbjerJS to generate a JSON-file. As of `protractor-cucumber-framework` version 3.1.2 you will get unique JSON files, see also [here](https://github.com/protractor-cucumber-framework/protractor-cucumber-framework#formatters-when-tests-are-sharded-or-with-multi-capabilities). 49 | 2. Add the `protractor-multiple-cucumber-html-reporter-plugin` in the `plugins` block inside 50 | `protractor.config.js`. 51 | 52 | > **!!The path that is defined in the `format` is the path where all files and reports are saved. Advice is not to save the CucumberJS JSON report fil in the root of the project but in for example a `.tmp/` folder!!** 53 | 54 | Here is a short config example of both steps. 55 | 56 | ```javascript 57 | const path = require('path'); 58 | 59 | exports.config = { 60 | 61 | framework: 'custom', 62 | frameworkPath: require.resolve('protractor-cucumber-framework'), 63 | cucumberOpts: { 64 | require: [ 65 | path.resolve(process.cwd(), './**/*.steps.js') 66 | ], 67 | // Tell CucumberJS to save the JSON report 68 | format: 'json:.tmp/results.json', 69 | strict: true 70 | }, 71 | 72 | specs: [ 73 | '*.feature' 74 | ], 75 | 76 | multiCapabilities: [{ 77 | browserName: 'chrome', 78 | shardTestFiles: true, 79 | maxInstances: 2, 80 | chromeOptions: { 81 | args: ['disable-infobars'] 82 | } 83 | }], 84 | 85 | // Here the magic happens 86 | plugins: [{ 87 | package: 'protractor-multiple-cucumber-html-reporter-plugin', 88 | options:{ 89 | // read the options part 90 | } 91 | }] 92 | }; 93 | 94 | ``` 95 | ## Options 96 | If you don't provide `options` the pluging will use the defaults as mentioned below. Options can be added by creating an object like this: 97 | 98 | ```js 99 | plugins: [{ 100 | package: 'protractor-multiple-cucumber-html-reporter-plugin', 101 | options:{ 102 | // read the options part for more options 103 | automaticallyGenerateReport: true, 104 | removeExistingJsonReportFile: true 105 | } 106 | }] 107 | ``` 108 | 109 | ### `automaticallyGenerateReport` 110 | - **Type:** `boolean` 111 | - **Default:** `false` 112 | - **Mandatory:** No 113 | 114 | Setting this option will autimatically generate a new report with `multiple-cucumber-html-reporter`. It will generate a log at the end of the testexection where you can find the report and looks like this. This means that you don't need to call the report module in a seperate node process. For the options of the report see the options `openReportInBrowser`, `reportPath` and `saveCollectedJSON` 115 | 116 | ```shell 117 | ===================================================================================================== 118 | Multiple Cucumber HTML report generated in: 119 | 120 | /Users/wswebcreation/protractor-multiple-cucumber-html-reporter-plugin/.tmp/report/index.html 121 | ===================================================================================================== 122 | ``` 123 | 124 | ### `customData` 125 | - **Type:** `object` 126 | - **Mandatory:** No 127 | 128 | You can add a custom data block to the report like this 129 | 130 | ```js 131 | customData: { 132 | title: 'Run info', 133 | data: [ 134 | {label: 'Project', value: 'Custom project'}, 135 | {label: 'Release', value: '1.2.3'}, 136 | {label: 'Cycle', value: 'B11221.34321'} 137 | ] 138 | } 139 | ``` 140 | 141 | > **THIS WILL ONLY WORK WITH `automaticallyGenerateReport:true`. IF YOU GENERATE THE REPORT LATER PLEASE LOOK AT [multiple-cucumber-html-reporter](https://github.com/wswebcreation/multiple-cucumber-html-reporter#usage)** 142 | 143 | ### `customMetadata` 144 | - **Type:** `boolean` 145 | - **Mandatory:** No 146 | 147 | It is possible to provide custom metadata by setting this variable to `true`. Custom metadata will override the regular metadata completely and potentially have strange formatting bugs if too many (10+) variables are used. 148 | The columns will be in the order defined by the order of the list. 149 | 150 | Adding the metadata is done in the same way as with normal metadata. The metadata is formed as a list of key-value pairs to preserve order: 151 | 152 | ```js 153 | metadata: [ 154 | {name: 'Environment v.', value: '12.3'}, 155 | {name: 'Plugin v.', value: '32.1'}, 156 | {name: 'Variable set', value: 'Foo'} 157 | ] 158 | ``` 159 | 160 | ### `customStyle` 161 | - **Type:** `path` 162 | - **Mandatory:** No 163 | 164 | If you need add some custom style to your report. Add it like this `customStyle: 'your-path-where/custom.css'` 165 | 166 | ### `disableLog` 167 | - **Type:** `boolean` 168 | - **Mandatory:** No 169 | - **Default:** `false` 170 | 171 | This will disable the log so will **NOT** see this. 172 | 173 | ```shell 174 | ===================================================================================================== 175 | Multiple Cucumber HTML report generated in: 176 | 177 | /Users/wswebcreation/protractor-multiple-cucumber-html-reporter-plugin/.tmp/report/index.html 178 | ===================================================================================================== 179 | ``` 180 | 181 | ### `displayDuration` 182 | - **Type:** `boolean` 183 | - **Mandatory:** No 184 | 185 | If set to `true` the duration of steps, scenarios and features is displayed on the Features overview and single feature page in an easily readable format. 186 | This expects the durations in the report to be in **nanoseconds**, which might result in incorrect durations when using a version of Cucumber(JS 2 and 3) that does not report in nanoseconds but in milliseconds. This can be changed to milliseconds by adding the parameter `durationInMS: true`, see below 187 | 188 | > **NOTE: Only the duration of a feature can be shown in the features overview. A total duration over all features CAN NOT be given because the module doesn't know if all features have been run in parallel** 189 | 190 | ### `durationInMS` 191 | - **Type:** `boolean` 192 | - **Default:** `false` 193 | - **Mandatory:** No 194 | 195 | If set to `true` the duration of steps will be expected to be in **milliseconds**, which might result in incorrect durations when using a version of Cucumber(JS 1 or 4) that does report in **nanaseconds**. 196 | This parameter relies on `displayDuration: true` 197 | 198 | ### `jsonOutputPath` 199 | - **Type:** `string` 200 | - **Default:** `json-output-folder` 201 | - **Mandatory:** No 202 | 203 | The directory that will hold all the unique generated JSON files, relative from where the script is started. 204 | 205 | **N.B.:** If you use a npm script from the command line, like for example `npm run generate-report` the `jsonOutputPath` will be relative from the path where the script is executed. Executing it from the root of your project will also search for the `jsonOutputPath ` from the root of you project. 206 | 207 | If you **DON'T** provide this it will generate a `json-output-folder`-folder in the `path` that it defined the `cucumberOpts.format`. 208 | 209 | ### `metadataKey` 210 | - **Type:** `string` 211 | - **Default:** `metadata` 212 | - **Mandatory:** No 213 | 214 | This will be the `key` reference in the `capabilities` or `multiCapabilities` that will refer to where the instance specific data is saved. The metadata is used for the report that will be generated, see also [metadata](#metadata). 215 | 216 | If for example all the metadata is already present in the `capabilities` but with the `key` called `deviceProperties` you can add the option `metadataKey: 'deviceProperties'` and the plugin will automatically copy the `deviceProperties`-object to the `metadata` of the report. 217 | 218 | ### `openReportInBrowser` 219 | - **Type:** `boolean` 220 | - **Default:** `false` 221 | - **Mandatory:** No 222 | 223 | Settign this option will automatically open the generated report in the default browser of the operating system. See also [here](https://github.com/wswebcreation/multiple-cucumber-html-reporter#openreportinbrowser). 224 | 225 | ### `overrideStyle` 226 | - **Type:** `path` 227 | - **Mandatory:** No 228 | 229 | If you need replace default style for your report. Add it like this `overrideStyle: 'your-path-where/custom.css'` 230 | 231 | ### `pageFooter` 232 | - **Type:** `string` 233 | - **Mandatory:** No 234 | 235 | You can customise Page Footer if required. You just need to provide a html string like `

A custom footer in html

` 236 | 237 | ### `pageTitle` 238 | - **Type:** `string` 239 | - **Mandatory:** No 240 | - **Default:** Multiple Cucumber HTML Reporter 241 | 242 | You can change the report title in the HTML head Tag 243 | 244 | ### `removeExistingJsonReportFile` 245 | - **Type:** `boolean` 246 | - **Default:** `false` 247 | - **Mandatory:** No 248 | 249 | Settign this option will remove the previous unique JSON report file if it exists. It will prevent double reports of 1 feature + browser execution combination when generating te report with `multiple-cucumber-html-reporter`. This may come in handy when you rerun your flakey features with for example [protractor-flake](https://github.com/NickTomlin/protractor-flake) 250 | 251 | ### `removeOriginalJsonReportFile` 252 | - **Type:** `boolean` 253 | - **Default:** `false` 254 | - **Mandatory:** No 255 | 256 | Setting this option will remove the original json report file, defined in the `cucumberOpts.format`. It will clean up the folder where you save all your results and may be needed if you have a lot of JSON-files with screenshots in it. 257 | 258 | ### `reportName` 259 | - **Type:** `string` 260 | - **Mandatory:** No 261 | 262 | You can change the report name to a name you want. 263 | 264 | > **THIS WILL ONLY WORK WITH `automaticallyGenerateReport:true`. IF YOU GENERATE THE REPORT LATER PLEASE LOOK AT [multiple-cucumber-html-reporter](https://github.com/wswebcreation/multiple-cucumber-html-reporter#usage)** 265 | 266 | ### `reportPath` 267 | - **Type:** `string` 268 | - **Default:** `report` 269 | - **Mandatory:** No 270 | 271 | The directory in which the report needs to be saved, relative from where the script is started. See also [here](https://github.com/wswebcreation/multiple-cucumber-html-reporter#jsondir). 272 | If you **DON'T** provide this it will generate a `report`-folder in the `path` that it defined the `cucumberOpts.format`. 273 | 274 | N.B.: If you use a npm script from the command line, like for example `npm run generate-report` the `reportPath` will be relative from the path where the script is executed. Executing it from the root of your project will also save the report in the `reportPath` in the root of you project. 275 | 276 | ### `saveCollectedJSON` 277 | - **Type:** `boolean` 278 | - **Default:** `false` 279 | - **Mandatory:** No 280 | 281 | `multiple-cucumber-html-reporter` will first merge all the JSON-files to 1 file and then enrich it with data that is used for the report. If `saveCollectedJSON :true` the merged JSON **AND** the enriched JSON will be saved in the [`reportPath`](#reportpath). They will be saved as: 282 | 283 | - `merged-output.json` 284 | - `enriched-output.json` 285 | 286 | See also [here](https://github.com/wswebcreation/multiple-cucumber-html-reporter#savecollectedjson) 287 | 288 | ## Metadata 289 | The report can also show on which browser / device a feature has been executed. It is shown on the featurs overview in the table as well as on the feature overview in a container. You can add this by adding the following object to your `capabilities` or `multiCapabilities` 290 | 291 | ```js 292 | capabilities: { 293 | browserName: 'chrome', 294 | chromeOptions: { 295 | args: ['disable-infobars'] 296 | }, 297 | // Add this 298 | metadata: { 299 | browser: { 300 | name: 'chrome', 301 | version: '58' 302 | }, 303 | device: 'MacBook Pro 15', 304 | platform: { 305 | name: 'OSX', 306 | version: '10.12.6' 307 | } 308 | } 309 | } 310 | 311 | // Or 312 | multiCapabilities: [{ 313 | browserName: 'chrome', 314 | chromeOptions: { 315 | args: ['disable-infobars'] 316 | }, 317 | // Add this 318 | metadata: { 319 | browser: { 320 | name: 'chrome', 321 | version: '58' 322 | }, 323 | device: 'MacBook Pro 15', 324 | platform: { 325 | name: 'OSX', 326 | version: '10.12.6' 327 | } 328 | } 329 | } 330 | 331 | ``` 332 | 333 | See the metadata information [here](https://github.com/wswebcreation/multiple-cucumber-html-reporter#metadatabrowsername) for the correct values. 334 | 335 | > If you don't provide a `browser.name` or a `browser.version` the module will try to determine this automatically. The rest will be shown as questionmarks in the report 336 | 337 | ## FAQ 338 | 339 | * **Multiple HTML files generated in the in `report/features`-folder, but they are not shown in the overview-page:** See the answer in issue [13](https://github.com/wswebcreation/protractor-multiple-cucumber-html-reporter-plugin/issues/13#issuecomment-377797176) 340 | 341 | ## Changelog/Releases 342 | The Changelog/Releases can be found [here](https://github.com/wswebcreation/protractor-multiple-cucumber-html-reporter-plugin/releases) 343 | 344 | ## Contributing 345 | How to contribute can be found [here](./CONTRIBUTING.md) 346 | 347 | ## Credits 348 | When creating this plugin I got a lot of inspiration from: 349 | 350 | - [protractor-cucumber-framework](https://www.npmjs.com/package/protractor-cucumber-framework) 351 | - [cucumber-js](https://github.com/cucumber/cucumber-js) 352 | - Protractor [plugin-page](https://github.com/angular/protractor/blob/master/docs/plugins.md) 353 | 354 | ## Thanks 355 | If this plugin was helpful for you, please give it a **★ Star** on 356 | [Github](https://github.com/wswebcreation/protractor-multiple-cucumber-html-reporter-plugin) and 357 | [npm](https://www.npmjs.com/package/protractor-multiple-cucumber-html-reporter-plugin) 358 | -------------------------------------------------------------------------------- /docs/images/protractor-support.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wswebcreation/protractor-multiple-cucumber-html-reporter-plugin/f4d30dd1622ff2c03fa4457373a77baa4e4b4995/docs/images/protractor-support.jpg -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const fs = require('fs-extra'); 4 | const path = require('path'); 5 | const multiCucumberHTLMReporter = require('multiple-cucumber-html-reporter'); 6 | 7 | let IS_JSON_FORMAT = false; 8 | let PID_INSTANCE_DATA; 9 | const REPORT_FOLDER = 'report'; 10 | const METADATA_KEY = 'metadata'; 11 | const JSON_OUTPUT_FOLDER = 'json-output-folder'; 12 | const PLUGIN_CONFIG = { 13 | /** 14 | * multiple-cucumber-html-reporter specific options 15 | */ 16 | customMetadata: false, 17 | customStyle: '', 18 | disableLog: false, 19 | displayDuration: false, 20 | durationInMS: false, 21 | openReportInBrowser: false, 22 | overrideStyle: '', 23 | pageFooter: false, 24 | pageTitle: false, 25 | reportPath: REPORT_FOLDER, 26 | saveCollectedJSON: false, 27 | 28 | /** 29 | * protractor-multiple-cucumber-html-reporter-plugin specific options 30 | */ 31 | automaticallyGenerateReport: false, 32 | removeExistingJsonReportFile: false, 33 | removeOriginalJsonReportFile: false, 34 | metadataKey: METADATA_KEY, 35 | 36 | /** 37 | * Used for both modules 38 | */ 39 | jsonOutputPath: JSON_OUTPUT_FOLDER 40 | }; 41 | 42 | /** 43 | * Configures the plugin with the correct data 44 | * 45 | * @see docs/plugins.md 46 | * @returns {Promise} A promise which resolves into a configured setup 47 | * @public 48 | */ 49 | function setup() { 50 | return browser.getProcessedConfig() 51 | .then((configuration) => { 52 | let cucumberFormat = configuration.cucumberOpts.format; 53 | 54 | IS_JSON_FORMAT = cucumberFormat && cucumberFormat.includes('json'); 55 | 56 | if (Array.isArray(cucumberFormat)) { 57 | IS_JSON_FORMAT = cucumberFormat.find((format) => { 58 | cucumberFormat = format; 59 | return format.includes('json') 60 | }); 61 | } 62 | 63 | if (IS_JSON_FORMAT) { 64 | /** 65 | * If options are provided override the values to the PLUGIN_CONFIG object 66 | */ 67 | if (this.config.options) { 68 | Object.assign(PLUGIN_CONFIG, this.config.options); 69 | } 70 | 71 | /** 72 | * Get the JSON folder path and file name if they are still empty 73 | */ 74 | const formatPathMatch = cucumberFormat.match(/(.+):(.+)/); 75 | const filePathMatch = formatPathMatch[2].match(/(.*)\/(.*)\.json/); 76 | 77 | // Get the cucumber results path 78 | PLUGIN_CONFIG.cucumberResultsPath = filePathMatch[1]; 79 | 80 | // Get the cucumber report name 81 | PLUGIN_CONFIG.cucumberReportName = filePathMatch[2]; 82 | 83 | /** 84 | * If the json output folder is still default, then create a new one 85 | */ 86 | if (PLUGIN_CONFIG.jsonOutputPath === JSON_OUTPUT_FOLDER) { 87 | PLUGIN_CONFIG.jsonOutputPath = path.join(PLUGIN_CONFIG.cucumberResultsPath, JSON_OUTPUT_FOLDER); 88 | } 89 | 90 | /** 91 | * Check whether the file name need to be unique 92 | */ 93 | PLUGIN_CONFIG.uniqueReportFileName = (Array.isArray(configuration.multiCapabilities) 94 | && configuration.multiCapabilities.length > 0) 95 | || typeof configuration.getMultiCapabilities === 'function' 96 | || configuration.capabilities.shardTestFiles; 97 | 98 | /** 99 | * Prepare the PID_INSTANCE_DATA 100 | */ 101 | const metadata = { 102 | browser: { 103 | name: '', 104 | version: '' 105 | }, 106 | device: '', 107 | platform: { 108 | name: '', 109 | version: '' 110 | } 111 | }; 112 | 113 | PID_INSTANCE_DATA = { 114 | pid: process.pid, 115 | metadata: Object.assign(metadata, configuration.capabilities[PLUGIN_CONFIG.metadataKey] || {}) 116 | }; 117 | 118 | /** 119 | * Create the needed folders if they are not present 120 | */ 121 | fs.ensureDirSync(PLUGIN_CONFIG.cucumberResultsPath); 122 | fs.ensureDirSync(PLUGIN_CONFIG.jsonOutputPath); 123 | } else { 124 | console.warn('\n### NO `JSON` FORMAT IS SPECIFIED IN THE PROTRACTOR CONF FILE UNDER `cucumberOpts.format ###`\n'); 125 | } 126 | }); 127 | } 128 | 129 | /** 130 | * Enrich the instance data 131 | * 132 | * @see docs/plugins.md 133 | * @returns {Promise} A promise which resolves in a enriched instance data object 134 | * @public 135 | */ 136 | function onPrepare() { 137 | if (IS_JSON_FORMAT) { 138 | return browser.getCapabilities() 139 | .then((capabilities) => { 140 | PID_INSTANCE_DATA.metadata.browser.name = PID_INSTANCE_DATA.metadata.browser.name === '' 141 | ? capabilities.get('browserName').toLowerCase() 142 | : PID_INSTANCE_DATA.metadata.browser.name; 143 | PID_INSTANCE_DATA.metadata.browser.version = (capabilities.get('version') || capabilities.get('browserVersion')) 144 | || PID_INSTANCE_DATA.metadata.browser.version; 145 | }); 146 | } 147 | } 148 | 149 | /** 150 | * When a report file has been made by CucumberJS, parse it by 151 | * - cutting it up to a file per feature 152 | * - adding the instance data to it. 153 | * 154 | * @see docs/plugins.md 155 | * @public 156 | */ 157 | function postResults() { 158 | if (IS_JSON_FORMAT) { 159 | const reportPath = `${path.join(PLUGIN_CONFIG.cucumberResultsPath, PLUGIN_CONFIG.cucumberReportName)}.json`; 160 | const pidReportPath = `${path.join( 161 | PLUGIN_CONFIG.cucumberResultsPath, 162 | PLUGIN_CONFIG.cucumberReportName 163 | )}.${PID_INSTANCE_DATA.pid}.json`; 164 | 165 | if ((!PLUGIN_CONFIG.uniqueReportFileName && fs.existsSync(reportPath)) 166 | || (PLUGIN_CONFIG.uniqueReportFileName && fs.existsSync(pidReportPath)) 167 | ) { 168 | const currentReportPath = PLUGIN_CONFIG.uniqueReportFileName ? pidReportPath : reportPath; 169 | const currentReport = fs.readJsonSync(currentReportPath); 170 | 171 | currentReport.map((singleReport) => { 172 | const featureName = singleReport.name.replace(/\s+/g, '_').replace(/\W/g, '').toLowerCase() || 'noName'; 173 | const browserVersion = PID_INSTANCE_DATA.metadata.browser.version === '' 174 | ? '' 175 | : `.${PID_INSTANCE_DATA.metadata.browser.version}`; 176 | const plaformName = PID_INSTANCE_DATA.metadata.platform.name === '' 177 | ? '' 178 | : `.${PID_INSTANCE_DATA.metadata.platform.name}`; 179 | const plaformVersion = PID_INSTANCE_DATA.metadata.platform.version === '' 180 | ? '' 181 | : `.${PID_INSTANCE_DATA.metadata.platform.version}`; 182 | let fileName = `${featureName}.${PID_INSTANCE_DATA.metadata.browser.name}`; 183 | 184 | fileName += browserVersion + plaformName + plaformVersion; 185 | const filePath = path.join(PLUGIN_CONFIG.jsonOutputPath, `${fileName}_${Date.now()}.json`); 186 | 187 | /** 188 | * If needed remove the previous file if it exists to prevent double reports of 1 feature + browser execution 189 | */ 190 | if (PLUGIN_CONFIG.removeExistingJsonReportFile) { 191 | fs.readdirSync(PLUGIN_CONFIG.jsonOutputPath) 192 | .filter((file) => file.match(new RegExp(fileName, 'ig'))) 193 | .forEach((file) => fs.removeSync(path.resolve(PLUGIN_CONFIG.jsonOutputPath, file))); 194 | } 195 | 196 | /** 197 | * Add the metadata from the running instance to the report 198 | */ 199 | singleReport.metadata = PID_INSTANCE_DATA.metadata; 200 | 201 | /** 202 | * Save the file 203 | */ 204 | fs.writeJsonSync(filePath, JSON.parse(`[${ JSON.stringify(singleReport) }]`), { spaces: 2 }); 205 | }); 206 | 207 | /** 208 | * Remove the original json report file, defined in the `cucumberOpts.format` if needed 209 | */ 210 | if (PLUGIN_CONFIG.removeOriginalJsonReportFile) { 211 | fs.removeSync(currentReportPath); 212 | } 213 | 214 | if ((PLUGIN_CONFIG.customData || PLUGIN_CONFIG.reportName) && !PLUGIN_CONFIG.automaticallyGenerateReport) { 215 | console.warn(` 216 | =========================================================================== 217 | YOU ADDED A CUSTOM reportName AND OR ADDED A cucsomerData-object 218 | WITHOUT SETTING 'automaticallyGenerateReport: true'. 219 | 220 | !!!THIS WILL NOT RESULT IN SETTING THE CUSTOM DATA IN THE REPORT!!! 221 | ===========================================================================`); 222 | } 223 | 224 | /** 225 | * Generate the HTML report if needed 226 | */ 227 | if (PLUGIN_CONFIG.automaticallyGenerateReport) { 228 | /** 229 | * If the reportPath is still default, then add the default folder to the same folder 230 | * where the results are stored 231 | */ 232 | if (PLUGIN_CONFIG.reportPath === REPORT_FOLDER) { 233 | PLUGIN_CONFIG.reportPath = path.join(PLUGIN_CONFIG.cucumberResultsPath, REPORT_FOLDER); 234 | } 235 | 236 | const multiCucumberHTLMReporterConfig = { 237 | customMetadata: PLUGIN_CONFIG.customMetadata, 238 | displayDuration: PLUGIN_CONFIG.displayDuration, 239 | durationInMS: PLUGIN_CONFIG.durationInMS, 240 | disableLog: PLUGIN_CONFIG.disableLog, 241 | jsonDir: PLUGIN_CONFIG.jsonOutputPath, 242 | openReportInBrowser: PLUGIN_CONFIG.openReportInBrowser, 243 | reportPath: PLUGIN_CONFIG.reportPath, 244 | saveCollectedJSON: PLUGIN_CONFIG.saveCollectedJSON 245 | }; 246 | 247 | /** 248 | * Add the custom data if needed 249 | */ 250 | if (PLUGIN_CONFIG.customData) { 251 | multiCucumberHTLMReporterConfig.customData = PLUGIN_CONFIG.customData; 252 | } 253 | 254 | /** 255 | * Add the custom css if needed 256 | */ 257 | if (PLUGIN_CONFIG.customStyle !== '') { 258 | multiCucumberHTLMReporterConfig.customStyle = PLUGIN_CONFIG.customStyle; 259 | } 260 | 261 | /** 262 | * Add the override css if needed 263 | */ 264 | if (PLUGIN_CONFIG.overrideStyle !== '') { 265 | multiCucumberHTLMReporterConfig.overrideStyle = PLUGIN_CONFIG.overrideStyle; 266 | } 267 | 268 | /** 269 | * Add the pageFooter if needed 270 | */ 271 | if (PLUGIN_CONFIG.pageFooter) { 272 | multiCucumberHTLMReporterConfig.pageFooter = PLUGIN_CONFIG.pageFooter; 273 | } 274 | 275 | /** 276 | * Add the pageTitle if needed 277 | */ 278 | if (PLUGIN_CONFIG.pageTitle) { 279 | multiCucumberHTLMReporterConfig.pageTitle = PLUGIN_CONFIG.pageTitle; 280 | } 281 | 282 | /** 283 | * Add the custom report name if needed 284 | */ 285 | if (PLUGIN_CONFIG.reportName) { 286 | multiCucumberHTLMReporterConfig.reportName = PLUGIN_CONFIG.reportName; 287 | } 288 | 289 | multiCucumberHTLMReporter.generate(multiCucumberHTLMReporterConfig); 290 | } 291 | } else { 292 | console.warn(`\n### File: '${reportPath}' or '${pidReportPath}' is not present! ###\n`); 293 | } 294 | } 295 | } 296 | 297 | // Exports 298 | exports.onPrepare = onPrepare; 299 | exports.postResults = postResults; 300 | exports.setup = setup; 301 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "protractor-multiple-cucumber-html-reporter-plugin", 3 | "version": "1.8.1", 4 | "description": "A protractor plugin to use multiple-cucumber-html-reporter with CucumberJs 1, 2 or 3", 5 | "main": "index.js", 6 | "scripts": { 7 | "lint": "node_modules/.bin/eslint *.js test/**/*.js", 8 | "pretest": "webdriver-manager update", 9 | "release": "np", 10 | "test": "npm run lint && node node_modules/.bin/jasmine ./test/test.spec.js", 11 | "test.single": "./node_modules/.bin/protractor ./test/cucumber/config/full-options.conf.js" 12 | }, 13 | "repository": { 14 | "type": "git", 15 | "url": "git+https://github.com/wswebcreation/protractor-multiple-cucumber-html-reporter-plugin.git" 16 | }, 17 | "keywords": [ 18 | "protractor", 19 | "cucumber", 20 | "html", 21 | "test report", 22 | "multiple-cucumber-html-reporter", 23 | "html report", 24 | "json to html" 25 | ], 26 | "author": "wswebcreation", 27 | "license": "MIT", 28 | "bugs": { 29 | "url": "https://github.com/wswebcreation/protractor-multiple-cucumber-html-reporter-plugin/issues" 30 | }, 31 | "homepage": "https://github.com/wswebcreation/protractor-multiple-cucumber-html-reporter-plugin#readme", 32 | "devDependencies": { 33 | "cucumber": "^5.1.0", 34 | "eslint": "^4.11.0", 35 | "glob": "^7.1.2", 36 | "np": "^5.0.2", 37 | "protractor": "^5.4.1", 38 | "protractor-cucumber-framework": "^6.1.1" 39 | }, 40 | "peerDependencies": { 41 | "cucumber": ">= 1.3.0 || >= 2.0.0 || >= 3.0.0", 42 | "protractor-cucumber-framework": ">= 3.1.2", 43 | "protractor": ">= 4.0.0" 44 | }, 45 | "dependencies": { 46 | "fs-extra": "^3.0.1", 47 | "multiple-cucumber-html-reporter": "^1.11.7" 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /test/css/custom.css: -------------------------------------------------------------------------------- 1 | .custom { 2 | width: 100%; 3 | } 4 | -------------------------------------------------------------------------------- /test/css/override.css: -------------------------------------------------------------------------------- 1 | .override { 2 | height: 100%; 3 | } 4 | -------------------------------------------------------------------------------- /test/cucumber/config/full-options.conf.js: -------------------------------------------------------------------------------- 1 | const path = require('path'); 2 | const config = require('./protractor.shared.conf').config; 3 | 4 | config.multiCapabilities = [{ 5 | 'browserName': 'chrome', 6 | 'shardTestFiles': false, 7 | 'maxInstances': 2, 8 | 'goog:chromeOptions': { 9 | args: ['--headless'] 10 | }, 11 | 'deviceProperties': { 12 | browser: { 13 | name: 'chrome', 14 | version: 'latest' 15 | }, 16 | device: 'local development machine', 17 | platform: { 18 | name: 'osx', 19 | version: '10.12.6' 20 | } 21 | } 22 | }]; 23 | 24 | config.plugins = [{ 25 | path: path.resolve(process.cwd(), './'), 26 | options: { 27 | automaticallyGenerateReport: true, 28 | jsonOutputPath: './json-output-path/', 29 | metadataKey: 'deviceProperties', 30 | reportPath: './report-path/', 31 | removeExistingJsonReportFile: true, 32 | removeOriginalJsonReportFile: true, 33 | saveCollectedJSON: true, 34 | reportName: 'You can adjust this report name', 35 | pageFooter: '

A custom page footer

', 36 | pageTitle: 'A custom page title', 37 | customData: { 38 | title: 'Run info', 39 | data: [ 40 | { label: 'Project', value: 'Custom project' }, 41 | { label: 'Release', value: '1.2.3' } 42 | ] 43 | }, 44 | displayDuration: true, 45 | customMetadata: true, 46 | customStyle: path.join(__dirname, './../../css/custom.css'), 47 | overrideStyle: path.join(__dirname, './../../css/override.css') 48 | } 49 | }]; 50 | 51 | exports.config = config; 52 | -------------------------------------------------------------------------------- /test/cucumber/config/no-log.conf.js: -------------------------------------------------------------------------------- 1 | const path = require('path'); 2 | const config = require('./protractor.shared.conf').config; 3 | 4 | config.multiCapabilities = [{ 5 | 'browserName': 'chrome', 6 | 'shardTestFiles': false, 7 | 'maxInstances': 2, 8 | 'goog:chromeOptions': { 9 | args: ['--headless'] 10 | }, 11 | 'deviceProperties': { 12 | browser: { 13 | name: 'chrome', 14 | version: 'latest' 15 | }, 16 | device: 'local development machine', 17 | platform: { 18 | name: 'osx', 19 | version: '10.12.6' 20 | } 21 | } 22 | }]; 23 | 24 | config.plugins = [{ 25 | path: path.resolve(process.cwd(), './'), 26 | options: { 27 | automaticallyGenerateReport: true, 28 | disableLog: true 29 | } 30 | }]; 31 | 32 | exports.config = config; 33 | -------------------------------------------------------------------------------- /test/cucumber/config/no-options.conf.js: -------------------------------------------------------------------------------- 1 | const path = require('path'); 2 | const config = require('./protractor.shared.conf').config; 3 | 4 | config.capabilities = { 5 | 'browserName': 'chrome', 6 | 'shardTestFiles': false, 7 | 'maxInstances': 2, 8 | 'goog:chromeOptions': { 9 | args: ['--headless'] 10 | }, 11 | 'deviceProperties': { 12 | browser: { 13 | name: 'chrome', 14 | version: 'latest' 15 | }, 16 | device: 'local development machine', 17 | platform: { 18 | name: 'osx', 19 | version: '10.12.6' 20 | } 21 | } 22 | }; 23 | 24 | config.plugins = [{ 25 | path: path.resolve(process.cwd(), './') 26 | }]; 27 | 28 | exports.config = config; 29 | -------------------------------------------------------------------------------- /test/cucumber/config/protractor.shared.conf.js: -------------------------------------------------------------------------------- 1 | const path = require('path'); 2 | 3 | exports.config = { 4 | 5 | framework: 'custom', 6 | frameworkPath: require.resolve('protractor-cucumber-framework'), 7 | cucumberOpts: { 8 | require: [ 9 | path.resolve(process.cwd(), './test/cucumber/**/*steps.js') 10 | ], 11 | format: 'json:.tmp/results.json', 12 | strict: true 13 | }, 14 | 15 | specs: [ 16 | path.resolve(process.cwd(), './test/cucumber/**/*.feature') 17 | ] 18 | }; 19 | -------------------------------------------------------------------------------- /test/cucumber/features/foo.feature: -------------------------------------------------------------------------------- 1 | Feature: Foo until you bar 2 | 3 | Scenario: I want to foo 4 | Given I foo 5 | When I foo it 6 | Then I foo the bar 7 | -------------------------------------------------------------------------------- /test/cucumber/features/generate.report.feature: -------------------------------------------------------------------------------- 1 | Feature: Generate a JSON file 2 | 3 | Scenario: I want to generate a report 4 | Given I do something 5 | When I verified it 6 | Then a report will be created 7 | -------------------------------------------------------------------------------- /test/cucumber/step_definitions/steps.js: -------------------------------------------------------------------------------- 1 | const { Before, Given, When, Then } = require('cucumber'); 2 | 3 | // Before(() => Promise.reject('Error')); 4 | 5 | Given('I do something', () => Promise.resolve('I do something')); 6 | 7 | Given('I foo', () => Promise.resolve('I foo')); 8 | 9 | When('I verified it', () => Promise.resolve('I verified it')); 10 | 11 | When('I foo it', () => Promise.resolve('I foo it')); 12 | 13 | Then('a report will be created', () => Promise.resolve('a report will be created')); 14 | 15 | Then('I foo the bar', () => Promise.resolve('I foo the bar')); 16 | -------------------------------------------------------------------------------- /test/jasmine.json: -------------------------------------------------------------------------------- 1 | { 2 | "spec_dir": "test/", 3 | "spec_files": [ 4 | "*.spec.js" 5 | ], 6 | "stopSpecOnExpectationFailure": false, 7 | "random": false 8 | } 9 | -------------------------------------------------------------------------------- /test/test.spec.js: -------------------------------------------------------------------------------- 1 | const fs = require('fs-extra'); 2 | const path = require('path'); 3 | const util = require('./test_util'); 4 | 5 | const BROWSER_NAME = 'chrome'; 6 | const CUCUMBER_RESULTS_FILE_PATH = path.resolve(process.cwd(), './.tmp/'); 7 | const CUCUMBER_RESULTS_FILE_NAME = 'results'; 8 | const CUCUMBER_RESULTS = `${CUCUMBER_RESULTS_FILE_PATH}/${CUCUMBER_RESULTS_FILE_NAME}`; 9 | const JSON_OUTPUT_PATH = path.resolve(CUCUMBER_RESULTS_FILE_PATH, 'json-output-folder'); 10 | const CUCUMBER_FOO_FEATURE_NAME = 'foo_until_you_bar'; 11 | const CUCUMBER_GENERATE_FEATURE_NAME = 'generate_a_json_file'; 12 | 13 | describe('validate plugin and all the options', () => { 14 | beforeEach(() => { 15 | fs.removeSync(CUCUMBER_RESULTS_FILE_PATH); 16 | }); 17 | 18 | it('should create 2 unique json report files with all the defaults', () => { 19 | const cmd = 'test/cucumber/config/no-options.conf.js'; 20 | 21 | return util 22 | .runOne(cmd) 23 | .after(() => { 24 | const cucumberFoo = `${JSON_OUTPUT_PATH}/${CUCUMBER_FOO_FEATURE_NAME}`; 25 | const cucumberGenerate = `${JSON_OUTPUT_PATH}/${CUCUMBER_GENERATE_FEATURE_NAME}`; 26 | const cucumberResults = util.findJsonReportFiles(CUCUMBER_RESULTS)[0]; 27 | const fooResults = util.findJsonReportFiles(cucumberFoo)[0]; 28 | const generateResults = util.findJsonReportFiles(cucumberGenerate)[0]; 29 | const fooJson = fs.readJsonSync(fooResults); 30 | const generateJson = fs.readJsonSync(generateResults); 31 | 32 | // Check original JSON still exists => default `removeOriginalJsonReportFile = false` 33 | expect(cucumberResults).toEqual(`${CUCUMBER_RESULTS}.json`); 34 | 35 | // Check if the jsonOutputPath is default => `jsonOutputPath: './.tmp/json-output-path/'` 36 | expect(fooResults).toContain(`${cucumberFoo}.${BROWSER_NAME}`); 37 | 38 | // Check not metadata has been added from the capability 39 | expect(fooJson[0].metadata.browser.name).toEqual(BROWSER_NAME); 40 | expect(/(\d+)./.test(fooJson[0].metadata.browser.version)).toEqual(true); 41 | expect(fooJson[0].metadata.device).toEqual(''); 42 | expect(fooJson[0].metadata.platform.name).toEqual(''); 43 | expect(fooJson[0].metadata.platform.version).toEqual(''); 44 | 45 | // Check if the jsonOutputPath is default => `jsonOutputPath: './.tmp/json-output-path/'` 46 | expect(generateResults).toContain(`${cucumberGenerate}.${BROWSER_NAME}`); 47 | 48 | // Check not metadata has been added from the capability 49 | expect(generateJson[0].metadata.browser.name).toEqual(BROWSER_NAME); 50 | expect(/(\d+)./.test(generateJson[0].metadata.browser.version)).toEqual(true); 51 | expect(generateJson[0].metadata.device).toEqual(''); 52 | expect(generateJson[0].metadata.platform.name).toEqual(''); 53 | expect(generateJson[0].metadata.platform.version).toEqual(''); 54 | }) 55 | .run(); 56 | }, 11000); 57 | 58 | it('should validate all options and output', () => { 59 | const cmd = 'test/cucumber/config/full-options.conf.js'; 60 | const jsonOutputPath = path.resolve(process.cwd(), './json-output-path'); 61 | const reportPath = path.resolve(process.cwd(), './report-path'); 62 | 63 | // Clear the state 64 | fs.removeSync(jsonOutputPath); 65 | fs.removeSync(reportPath); 66 | 67 | return util 68 | .runOne(cmd) 69 | .expectOutput('Multiple Cucumber HTML report generated in:') 70 | .after(() => { 71 | const cucumberFoo = `${jsonOutputPath}/${CUCUMBER_FOO_FEATURE_NAME}`; 72 | const cucumberGenerate = `${jsonOutputPath}/${CUCUMBER_GENERATE_FEATURE_NAME}`; 73 | const cucumberResults = util.findJsonReportFiles(CUCUMBER_RESULTS)[0]; 74 | const fooResults = util.findJsonReportFiles(cucumberFoo)[0]; 75 | const generateResults = util.findJsonReportFiles(cucumberGenerate)[0]; 76 | const fooJson = fs.readJsonSync(fooResults); 77 | const generateJson = fs.readJsonSync(generateResults); 78 | const report = path.resolve(`${reportPath}/index.html`); 79 | const reportMergedJonOutput = path.resolve(`${reportPath}/merged-output.json`); 80 | const reportEnrichedJsonOutput = path.resolve(`${reportPath}/enriched-output.json`); 81 | const reportEnrichedJson = fs.readJsonSync(reportEnrichedJsonOutput); 82 | const reportContent = fs.readFileSync(report, 'utf8'); 83 | 84 | // Check original JSON has been removed => `removeOriginalJsonReportFile = true` 85 | expect(cucumberResults).not.toBeDefined(); 86 | 87 | // Check if the jsonOutputPath has been changed => `jsonOutputPath: './json-output-path/'` 88 | expect(fooResults).toContain(`${cucumberFoo}.${BROWSER_NAME}`); 89 | 90 | // Check all metadata has been added from the capability 91 | expect(fooJson[0].metadata.browser.name).toEqual(BROWSER_NAME); 92 | expect(/(\d+)./.test(fooJson[0].metadata.browser.version)).toEqual(true); 93 | expect(fooJson[0].metadata.device).toEqual('local development machine'); 94 | expect(fooJson[0].metadata.platform.name).toEqual('osx'); 95 | expect(fooJson[0].metadata.platform.version).toEqual('10.12.6'); 96 | 97 | // Check if the jsonOutputPath has been changed => `jsonOutputPath: './json-output-path/'` 98 | expect(generateResults).toContain(`${cucumberGenerate}.${BROWSER_NAME}`); 99 | 100 | // Check all metadata has been added from the capability 101 | expect(generateJson[0].metadata.browser.name).toEqual(BROWSER_NAME); 102 | expect(/(\d+)./.test(generateJson[0].metadata.browser.version)).toEqual(true); 103 | expect(generateJson[0].metadata.device).toEqual('local development machine'); 104 | expect(generateJson[0].metadata.platform.name).toEqual('osx'); 105 | expect(generateJson[0].metadata.platform.version).toEqual('10.12.6'); 106 | 107 | // Check if the report path has been created with the report in it `reportPath: './report-path/'` 108 | expect(fs.existsSync(report)).toEqual(true); 109 | 110 | // Merged and enriched file has been saved => `saveCollectedJSON: true` 111 | expect(fs.existsSync(reportMergedJonOutput)).toEqual(true); 112 | expect(fs.existsSync(reportEnrichedJsonOutput)).toEqual(true); 113 | 114 | // Check that the report name has been added to the enriched file 115 | expect(reportEnrichedJson.reportName).toEqual('You can adjust this report name'); 116 | expect(reportEnrichedJson.customData.title).toEqual('Run info'); 117 | expect(reportEnrichedJson.customData.data).toEqual([ 118 | { label: 'Project', value: 'Custom project' }, 119 | { label: 'Release', value: '1.2.3' } 120 | ]); 121 | 122 | // Check that the customMetadata has been set 123 | expect(reportEnrichedJson.customMetadata).toEqual(true); 124 | 125 | // Check that the displayDuration has been set 126 | expect(reportEnrichedJson.displayDuration).toEqual(true); 127 | 128 | // Check that customStyle has been set 129 | expect(reportContent).toContain('.custom'); 130 | 131 | // Check that overrideStyle has been set 132 | expect(reportContent).toContain('.override'); 133 | // Check that default style has been overridden 134 | expect(reportContent).not.toContain('.main_conainer'); 135 | 136 | }) 137 | .run(); 138 | }, 11000); 139 | 140 | it('should not print the log', () => { 141 | const cmd = 'test/cucumber/config/no-log.conf.js'; 142 | 143 | return util 144 | .runOne(cmd) 145 | .expectOutput('Running 1 instances of WebDriver') 146 | .run(); 147 | }, 11000); 148 | }); 149 | -------------------------------------------------------------------------------- /test/test_util.js: -------------------------------------------------------------------------------- 1 | // Source: https://github.com/protractor-cucumber-framework/protractor-cucumber-framework/blob/master/test/test_util.js 2 | // Adjusted for own tests 3 | 4 | const glob = require('glob'); 5 | const child_process = require('child_process'); 6 | 7 | const CommandlineTest = function (command) { 8 | const that = this; 9 | 10 | this.command_ = command; 11 | this.expectedExitCode_ = 0; 12 | this.expectedOutput_ = []; 13 | this.expectExitCode = function (exitCode) { 14 | that.expectedExitCode_ = exitCode; 15 | return that; 16 | }; 17 | 18 | this.after = function (callback) { 19 | that.after_ = callback; 20 | return that; 21 | }; 22 | 23 | this.expectOutput = function (output) { 24 | that.expectedOutput_.push(output); 25 | return that; 26 | }; 27 | 28 | this.run = function () { 29 | let output = ''; 30 | 31 | const flushAndFail = (errorMsg) => { 32 | process.stdout.write(output); 33 | throw new Error(errorMsg); 34 | }; 35 | 36 | return new Promise((resolve, reject) => { 37 | const args = that.command_.split(/\s/); 38 | let test_process; 39 | 40 | test_process = child_process.spawn(args[0], args.slice(1)); 41 | test_process.stdout.on('data', (data) => { 42 | output += data 43 | }); 44 | test_process.stderr.on('data', (data) => { 45 | output += data 46 | }); 47 | test_process.on('error', (err) => reject(err)); 48 | test_process.on('exit', (exitCode) => resolve(exitCode)); 49 | }) 50 | .then((exitCode) => { 51 | if (that.expectedExitCode_ !== exitCode) { 52 | flushAndFail( 53 | `expecting exit code: ${that.expectedExitCode_}, actual: ${exitCode}` 54 | ); 55 | } 56 | 57 | that.expectedOutput_.forEach((out) => { 58 | if (output.indexOf(out) < 0) { 59 | flushAndFail(`expecting output '${out}' in '${output}'`); 60 | } 61 | }); 62 | 63 | if (that.after_) { 64 | that.after_(); 65 | } 66 | }); 67 | }; 68 | }; 69 | 70 | /** 71 | * 72 | * @param {string} options 73 | * @returns {CommandlineTest} 74 | */ 75 | function runOne(options) { 76 | const test = new CommandlineTest( 77 | `node node_modules/protractor/bin/protractor ${options}` 78 | ); 79 | 80 | return test; 81 | } 82 | 83 | /** 84 | * Find json report files 85 | * 86 | * @param {string} pattern 87 | * @returns {Array} array of found files 88 | */ 89 | function findJsonReportFiles(pattern) { 90 | return glob.sync(`${pattern}*.json`); 91 | } 92 | 93 | module.exports = { 94 | runOne, 95 | findJsonReportFiles 96 | }; 97 | --------------------------------------------------------------------------------