├── .github ├── FUNDING.yml ├── ISSUE_TEMPLATE │ ├── --bug-report.md │ ├── --documentation.md │ ├── --feature-request.md │ └── --question.md ├── dependabot.yml ├── stale.yml └── workflows │ ├── codeql.yml │ ├── release.yml │ └── test.yml ├── .gitignore ├── .npmignore ├── .release-it.json ├── .vscode └── launch.json ├── CHANGELOG.md ├── LICENSE ├── README.md ├── docs ├── CONTRIBUTING.md ├── WEBDRIVER.IO.MD └── images │ ├── browsers-features-overview.jpg │ ├── custom-data.jpg │ ├── custom-metadata-features-overview.jpg │ ├── features-custom-metadata.jpg │ ├── nanoseconds_to_time.png │ ├── overview-metadata.jpg │ ├── scenario-app-metadata.jpg │ ├── scenario-browser-metadata.jpg │ └── scenario-no-metadata.jpg ├── examples └── cypress │ ├── .bin │ └── cucumber-json-formatter │ ├── .cypress-cucumber-preprocessorrc.json │ ├── README.md │ ├── cucumber-html-report.ts │ ├── cypress.config.ts │ ├── cypress │ ├── e2e │ │ ├── gmail │ │ │ ├── gmail.feature │ │ │ └── gmail.step.ts │ │ └── google │ │ │ ├── google.feature │ │ │ └── google.step.ts │ ├── fixtures │ │ └── example.json │ └── support │ │ ├── commands.ts │ │ └── e2e.ts │ ├── package.json │ ├── pnpm-lock.yaml │ └── tsconfig.json ├── lib ├── collect-jsons.js └── generate-report.js ├── package.json ├── pnpm-lock.yaml ├── templates ├── assets │ ├── css │ │ ├── bootstrap.min.css │ │ ├── dataTables.bootstrap.min.css │ │ ├── font-awesome.min.css │ │ ├── responsive.bootstrap5.min.css │ │ └── responsive.dataTables.min.css │ ├── fonts │ │ ├── FontAwesome.otf │ │ ├── fontawesome-webfont.eot │ │ ├── fontawesome-webfont.svg │ │ ├── fontawesome-webfont.ttf │ │ ├── fontawesome-webfont.woff │ │ ├── fontawesome-webfont.woff2 │ │ ├── glyphicons-halflings-regular.eot │ │ ├── glyphicons-halflings-regular.svg │ │ ├── glyphicons-halflings-regular.ttf │ │ ├── glyphicons-halflings-regular.woff │ │ └── glyphicons-halflings-regular.woff2 │ ├── img │ │ └── ghost.svg │ └── js │ │ ├── Chart.min.js │ │ ├── bootstrap.min.js │ │ ├── darkmode.js │ │ ├── dataTables.responsive.min.js │ │ ├── datatables.bootstrap5.min.js │ │ ├── datatables.jquery.min.js │ │ ├── datatables.min.js │ │ ├── html5shiv.min.js │ │ ├── jquery.min.js │ │ └── responsive.bootstrap5.js ├── components │ ├── custom-data.tmpl │ ├── feature-custom-metadata-overview.tmpl │ ├── feature-metadata-overview.tmpl │ ├── features-overview-custom-metadata.tmpl │ ├── features-overview.chart.tmpl │ ├── features-overview.tmpl │ ├── scenarios-overview.chart.tmpl │ └── scenarios.tmpl ├── feature-overview.index.tmpl ├── features-overview.index.tmpl ├── generic.js └── style.css └── test ├── custom.css ├── my.css ├── test.js └── unit ├── collect-jsons.spec.js ├── data ├── collect-json │ └── no-metadata.json ├── custom-metadata-json │ ├── ambiguous_scenarios_specified_v1.json │ ├── ambiguous_scenarios_specified_v2.json │ ├── failure_report_v3.json │ ├── multiple_different_attachements.json │ ├── pending_scenarios_specified_v1.json │ ├── skipped_scenarios.json │ └── undefined_scenarios_specified_v3.json ├── embedded-array-json │ ├── embedded_success_v1.json │ ├── failure_report_multi_attachment_v3.json │ ├── failure_report_v3.json │ ├── happy_flow_v2.json │ └── happy_flow_v3.json ├── json │ ├── after.hook.error.json │ ├── ambiguous_scenarios_specified_v1.json │ ├── ambiguous_scenarios_specified_v2.json │ ├── ambiguous_scenarios_specified_v3.json │ ├── background.json │ ├── background2.json │ ├── before-and-after-hook.json │ ├── before.hook.error.json │ ├── embeddings.json │ ├── empty_json.json │ ├── failure_report_v1.json │ ├── failure_report_v2.json │ ├── failure_report_v3.json │ ├── happy_flow_app_android.json │ ├── happy_flow_app_ios.json │ ├── happy_flow_v1.json │ ├── happy_flow_v2.json │ ├── happy_flow_v3.json │ ├── no_elements.json │ ├── no_elements_v2.json │ ├── no_elements_v3.json │ ├── pending_scenarios_specified_v1.json │ ├── pending_scenarios_specified_v2.json │ ├── pending_scenarios_specified_v3.json │ ├── skipped_scenarios.json │ ├── undefined_scenarios_specified_v1.json │ ├── undefined_scenarios_specified_v2.json │ ├── undefined_scenarios_specified_v3.json │ ├── undefined_succeeded_pending_scenarios_specified_v3.json │ └── with_rest_doc_string.json ├── no-jsons │ └── empty.md └── output │ ├── merged-output.json │ └── provided-metadata.json ├── generate-json.spec.js └── jasmine.json /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | # These are supported funding model platforms 2 | 3 | github: WasiqB 4 | # patreon: # Replace with a single Patreon username 5 | # open_collective: # Replace with a single Open Collective username 6 | # ko_fi: # Replace with a single Ko-fi username 7 | # tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel 8 | # community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry 9 | # liberapay: # Replace with a single Liberapay username 10 | # issuehunt: # Replace with a single IssueHunt username 11 | # otechie: # Replace with a single Otechie username 12 | # lfx_crowdfunding: # Replace with a single LFX Crowdfunding project-name e.g., cloud-foundry 13 | # custom: # Replace with up to 4 custom sponsorship URLs e.g., ['link1', 'link2'] 14 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/--bug-report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: "\U0001F41BBug report" 3 | about: Create a report to help us improve. 4 | --- 5 | 6 | # Environment (please complete the following information) 7 | 8 | - multiple-cucumber-html-reporter: [e.g. 1.10.0] 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 | - Cucumber version: [e.g. 4.2.0] 13 | 14 | ## Config of multiple-cucumber-html-reporter 15 | 16 | An example of how you configured the reporter in your config 17 | 18 | ## Describe the bug 19 | 20 | A clear and concise description of what the bug is. 21 | 22 | ## To Reproduce 23 | 24 | Steps to reproduce the behavior: 25 | 26 | [Include code or an example repository that can easily be set up] 27 | 28 | ## Expected behavior 29 | 30 | A clear and concise description of what you expected to happen. 31 | 32 | ## Log 33 | 34 | If applicable, add logs to help explain your problem. If you don't have logs, enable 'debug:true' in the config and paste the log here. 35 | Please use proper markdown to style it 36 | 37 | ## Additional context 38 | 39 | Add any other context about the problem here. 40 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/--documentation.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: "\U0001F4D6Documentation" 3 | about: Suggest improvements or report missing/unclear documentation. 4 | --- 5 | 6 | # Pre-check 7 | 8 | - [ ] I'm aware that I can [edit the docs](https://github.com/WasiqB/multiple-cucumber-html-reporter-plugin) and submit a pull request 9 | 10 | ## Describe the improvement 11 | 12 | I'd like to report 13 | 14 | - [ ] Unclear documentation 15 | - [ ] A typo 16 | - [ ] Missing documentation 17 | - [ ] Other 18 | 19 | ## Description of the improvement / report 20 | 21 | A clear and concise description. 22 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/--feature-request.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: "\U0001F4A1Feature request" 3 | about: Suggest an idea for this module. 4 | --- 5 | 6 | # Is your feature request related to a problem? Please describe 7 | 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 | 12 | A clear and concise description of what you want to happen. 13 | 14 | ## Describe alternatives you've considered 15 | 16 | A clear and concise description of any alternative solutions or features you've considered. 17 | 18 | ## Additional context 19 | 20 | Add any other context or screenshots about the feature request here. 21 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/--question.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: "\U0001F4ACQuestion" 3 | about: Ask questions. 4 | --- 5 | 6 | # Describe your question with as much detail as possible 7 | 8 | A clear and concise question that doesn't require too much conversation. Need more help? [Find me on Gitter](https://gitter.im/WasiqB/multiple-cucumber-html-reporter-plugin) 9 | 10 | **If it's about a specific piece of code, try and include some of it to support your question.** 11 | [...] 12 | 13 | ## Environment (please complete the following information) 14 | 15 | - multiple-cucumber-html-reporter: [e.g. 1.10.0] 16 | - Node.js version: [e.g. 8.9.1] 17 | - NPM version: [e.g. 5.8.0] 18 | - Platform name and version: [e.g. Windows 10] 19 | - Cucumber version: [e.g. 4.2.0] 20 | 21 | ## Additional context 22 | 23 | Add any other context about the problem here. 24 | -------------------------------------------------------------------------------- /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | version: 2 2 | updates: 3 | - package-ecosystem: "npm" 4 | directory: "/" 5 | target-branch: "main" 6 | versioning-strategy: increase 7 | open-pull-requests-limit: 100 8 | schedule: 9 | interval: "weekly" 10 | timezone: "Asia/Calcutta" 11 | groups: 12 | dependencies: 13 | update-types: ["major", "minor", "patch"] 14 | 15 | - package-ecosystem: "npm" 16 | directory: "/examples/cypress" 17 | target-branch: "main" 18 | versioning-strategy: increase 19 | open-pull-requests-limit: 100 20 | schedule: 21 | interval: "weekly" 22 | timezone: "Asia/Calcutta" 23 | groups: 24 | dependencies: 25 | update-types: ["major", "minor", "patch"] 26 | 27 | - package-ecosystem: "github-actions" 28 | directory: "/" 29 | schedule: 30 | interval: "weekly" 31 | groups: 32 | dependencies: 33 | dependency-type: "production" 34 | -------------------------------------------------------------------------------- /.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 | - feature 9 | - PR please ;-) 10 | # Label to use when marking an issue as stale 11 | staleLabel: wontfix 12 | # Comment to post when marking an issue as stale. Set to `false` to disable 13 | markComment: > 14 | This issue has been automatically marked as stale because it has not had 15 | recent activity. It will be closed if no further activity occurs. Thank you 16 | for your contributions. 17 | # Comment to post when closing a stale issue. Set to `false` to disable 18 | closeComment: false 19 | -------------------------------------------------------------------------------- /.github/workflows/codeql.yml: -------------------------------------------------------------------------------- 1 | name: CodeQL Scannings 2 | 3 | on: 4 | push: 5 | branches: 6 | - main 7 | 8 | pull_request: 9 | branches: 10 | - main 11 | 12 | jobs: 13 | analyze: 14 | name: Analyze 15 | runs-on: ubuntu-latest 16 | permissions: 17 | actions: read 18 | contents: read 19 | security-events: write 20 | 21 | steps: 22 | - name: Checkout repository 23 | uses: actions/checkout@v4 24 | 25 | - name: Initialize CodeQL 26 | uses: github/codeql-action/init@v3 27 | with: 28 | languages: javascript 29 | 30 | - name: Auto build typescript 31 | uses: github/codeql-action/autobuild@v3 32 | 33 | - name: Perform CodeQL Analysis 34 | uses: github/codeql-action/analyze@v3 35 | -------------------------------------------------------------------------------- /.github/workflows/release.yml: -------------------------------------------------------------------------------- 1 | name: Release to NPM and GitHub 2 | 3 | on: 4 | workflow_dispatch: 5 | inputs: 6 | releaseType: 7 | description: "Release Type" 8 | required: true 9 | type: choice 10 | options: 11 | - "major" 12 | - "minor" 13 | - "patch" 14 | default: "patch" 15 | 16 | jobs: 17 | release: 18 | runs-on: ubuntu-latest 19 | 20 | env: 21 | NPM_TOKEN: ${{ secrets.NPM_TOKEN }} 22 | PUSH_TOKEN: ${{ secrets.PUSH_TOKEN }} 23 | 24 | steps: 25 | - uses: actions/checkout@v4 26 | with: 27 | fetch-depth: 0 28 | token: ${{ env.PUSH_TOKEN }} 29 | 30 | - name: Import GPG key 31 | uses: crazy-max/ghaction-import-gpg@v6 32 | with: 33 | gpg_private_key: ${{ secrets.GPG_PRIVATE_KEY }} 34 | passphrase: ${{ secrets.GPG_PASSPHRASE }} 35 | git_user_signingkey: true 36 | git_commit_gpgsign: true 37 | 38 | - name: git config 39 | run: | 40 | git config user.name "${GITHUB_ACTOR}" 41 | git config user.email "${GITHUB_ACTOR}@users.noreply.github.com" 42 | 43 | - uses: pnpm/action-setup@v4 44 | 45 | - name: Setup Node.js 46 | uses: actions/setup-node@v4 47 | with: 48 | node-version: 18 49 | cache: "pnpm" 50 | 51 | - name: NPM Setup 52 | run: pnpm config set //registry.npmjs.org/:_authToken $NPM_TOKEN 53 | 54 | - name: Install Dependencies 55 | run: pnpm i 56 | 57 | - name: Release 58 | run: pnpm release.${{ github.event.inputs.releaseType }} 59 | -------------------------------------------------------------------------------- /.github/workflows/test.yml: -------------------------------------------------------------------------------- 1 | name: Test multiple-cucumber-html-reporter 2 | 3 | on: 4 | push: 5 | branches: 6 | - main 7 | pull_request: 8 | branches: 9 | - main 10 | 11 | jobs: 12 | test: 13 | runs-on: ubuntu-latest 14 | 15 | strategy: 16 | matrix: 17 | node-version: [18, 20] 18 | 19 | steps: 20 | - name: Checkout Repository 21 | uses: actions/checkout@v4 22 | 23 | - uses: pnpm/action-setup@v4 24 | 25 | - name: Setup Node.js ${{ matrix.node-version }} 26 | uses: actions/setup-node@v4 27 | with: 28 | node-version: ${{ matrix.node-version }} 29 | cache: "pnpm" 30 | 31 | - name: Install dependencies 32 | run: pnpm i 33 | 34 | - name: Run Unit Tests and generate coverage report 35 | run: pnpm unit.test.coverage 36 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .idea/ 2 | .tmp/ 3 | coverage/ 4 | node_modules/ 5 | .nyc_output/ 6 | reports/ 7 | .DS_Store 8 | yarn.lock 9 | *.tgz 10 | 11 | videos/ 12 | screenshots/ 13 | downloads/ 14 | *.ndjson 15 | dist/ 16 | .run/ -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | .idea/ 2 | .tmp/ 3 | coverage/ 4 | node_modules/ 5 | reports/ 6 | test/ 7 | docs/ 8 | .vscode/ 9 | -------------------------------------------------------------------------------- /.release-it.json: -------------------------------------------------------------------------------- 1 | { 2 | "git": { 3 | "requireBranch": "main", 4 | "commitMessage": "chore: release v${version}", 5 | "commit": true, 6 | "tag": true, 7 | "push": true, 8 | "requireCleanWorkingDir": true 9 | }, 10 | "github": { 11 | "release": true, 12 | "tokenRef": "PUSH_TOKEN", 13 | "comments": { 14 | "submit": true, 15 | "issue": ":rocket: _This issue has been resolved in v${version}. See [${releaseName}](${releaseUrl}) for release notes._", 16 | "pr": ":rocket: _This pull request is included in v${version}. See [${releaseName}](${releaseUrl}) for release notes._" 17 | } 18 | }, 19 | "npm": { 20 | "publish": true 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /.vscode/launch.json: -------------------------------------------------------------------------------- 1 | { 2 | // Use IntelliSense to learn about possible attributes. 3 | // Hover to view descriptions of existing attributes. 4 | // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 5 | "version": "0.2.0", 6 | "configurations": [ 7 | { 8 | "type": "node", 9 | "request": "launch", 10 | "name": "debug test.js", 11 | "skipFiles": [ 12 | "/**" 13 | ], 14 | "program": "${workspaceFolder}/test/test.js" 15 | }, 16 | { 17 | "type": "node", 18 | "name": "debug current spec file", 19 | "request": "launch", 20 | "args": [ 21 | "${file}" 22 | ], 23 | "cwd": "${workspaceFolder}", 24 | "console": "integratedTerminal", 25 | "internalConsoleOptions": "neverOpen", 26 | "program": "${workspaceFolder}/node_modules/jasmine/bin/jasmine", 27 | "sourceMaps": true, 28 | "runtimeArgs": [ 29 | "--nolazy", 30 | "--inspect-brk" 31 | ] 32 | } 33 | ] 34 | } -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2023 Wasiq Bhamla 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 | -------------------------------------------------------------------------------- /docs/CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | Contributing 2 | ============ 3 | 4 | We try to achieve a 100% coverage on the Javascript code. Please make sure that each addition reaches this goal. 5 | 6 | ## Tests 7 | - clone the project to your local machine 8 | - do a npm install 9 | 10 | To create a report use `npm test`. To create a report and also check the coverage use `unit.test.coverage` 11 | 12 | ## Documentation 13 | 14 | Make any applicable changes to the documentation. 15 | -------------------------------------------------------------------------------- /docs/WEBDRIVER.IO.MD: -------------------------------------------------------------------------------- 1 | # webdriver.io 2 | 3 | > **NOTE:
** 4 | > If you are using WebdriverIO V4 follow the instructions below, if you are using WebdriverIO V5, please check [here](https://github.com/WasiqB/wdio-cucumberjs-json-reporter). 5 | 6 | Follow the installation instructions as mentioned in [Install](../README.MD#install) 7 | 8 | ## Using wdio-cucumber-framework version 2.2.0 or higher? 9 | 10 | If you are using the latest version of `wdio-cucumber-framework` version `2.2.0` or higher, you can now use a new reporter called [wdio-multiple-cucumber-html-reporter](https://github.com/WasiqB/wdio-multiple-cucumber-html-reporter). 11 | It works nicely with this reporter. 12 | 13 | ## Using wdio-cucumber-framework version lower than 2.2.0? 14 | 15 | > **NOTE: THIS INSTRUCTION ONLY WORKS WITH `CucumberJS` V2 AND THUS `wdio-cucumber-framework` V1.1.1. I'M WORKING ON A SOLUTION FOR THE LATEST VERSION OF `CucumberJS` V4 AND THUS `wdio-cucumber-framework` > V2.0.0** 16 | 17 | > **THIS REPORT WORKS BASED ON EXTRA PROVIDED BROWSER INFO IN THE REPORT. PLEASE FOLLOWING THE INSTRUCTIONS BELOW / IN THE README.MD TO ADD THAT INFO. IF YOU DON'T YOU MAY GET ERRORS GENERATING THE REPORT** 18 | 19 | ## Step 1: Add extra dependency 20 | 21 | Add an extra dependency to your project so you can save the extra JSON-files. 22 | 23 | `npm install fs-extra --save-dev` 24 | 25 | This dependency will be used in the next step. 26 | 27 | ## Steps 2: Create a report hook 28 | 29 | Create a file called `report.hook.js` and save it in your project. I always have a specific folder in which I save my configs and hooks like `./e2e/config/hooks`. 30 | 31 | This hook will do the following: 32 | 33 | - is will take the complete report (of all features that have been run) and cut it into small reports per feature. 34 | - each report will get the instance data that is needed to show the name/version of the browser/platform in the report, see the comments in the hook, see also [MetaData](../README.MD#metadata-1) 35 | 36 | Add the following code to it: 37 | 38 | ```js 39 | import Cucumber, { defineSupportCode } from "cucumber"; 40 | import { ensureDirSync, writeJsonSync } from "fs-extra"; 41 | import { join } from "path"; 42 | 43 | const jsonFormatter = new Cucumber.JsonFormatter(); 44 | const projectRoot = process.cwd(); 45 | 46 | /** 47 | * This hook is needed to generate a json-file for the reporting 48 | */ 49 | defineSupportCode(({ registerListener }) => { 50 | registerListener(jsonFormatter); 51 | 52 | return generateAndSaveJsonFile(); 53 | 54 | /** 55 | * Generate and save the report json files 56 | */ 57 | function generateAndSaveJsonFile() { 58 | jsonFormatter.log = (report) => { 59 | adjustAndSaveJsonFile(device.desiredCapabilities, report); 60 | }; 61 | } 62 | 63 | /** 64 | * Adjust and save the json files 65 | */ 66 | function adjustAndSaveJsonFile(capabilities, report) { 67 | const jsonReport = JSON.parse(report); 68 | if (jsonReport.length > 0) { 69 | const featureName = 70 | jsonReport[0].name 71 | .replace(/\s+/g, "_") 72 | .replace(/\W/g, "") 73 | .toLowerCase() || "noName"; 74 | const snapshotPath = join(projectRoot, ".tmp/json-output"); 75 | const filePath = join( 76 | snapshotPath, 77 | `${featureName}.${ 78 | capabilities.browserName 79 | }.${new Date().getTime()}.json` 80 | ); // eslint-disable-line 81 | 82 | // The report is enriched with data of the running instance, this is needed to show the name/version of the browser/platform in the report 83 | jsonReport[0].metadata = { 84 | browser: { 85 | name: capabilities.browserName, 86 | version: "60", // Add your version or dynamically add your version here 87 | }, 88 | device: "local development machine", 89 | platform: { 90 | name: "osx", // Add your platform name here 91 | version: "10.12.6", // Add your platform version here 92 | }, 93 | }; 94 | 95 | ensureDirSync(snapshotPath); 96 | 97 | writeJsonSync(filePath, jsonReport, { spaces: 2 }); 98 | } 99 | } 100 | }); 101 | ``` 102 | 103 | ## Step 3: Create an after scenario hook 104 | 105 | If you want to attach a screenshot after failure to the report hook create a file called `after.scenario.js` and save it to your project. 106 | 107 | ```js 108 | import { After, Status } from "cucumber"; 109 | 110 | After(function (scenarioResult) { 111 | if (scenarioResult.status === Status.FAILED) { 112 | // Attach the original state 113 | const screenshot = browser.saveScreenshot(); 114 | world.attach(screenshot, "image/png"); 115 | } 116 | 117 | return Promise.resolve(scenarioResult.status); 118 | }); 119 | ``` 120 | 121 | ### Step 4: Change the wdio.config 122 | 123 | Add the following lines of code to your `wdio.conf.js` file (or however you call it). See [Options](../README.MD#options) for more options 124 | 125 | ```js 126 | const report = require("multiple-cucumber-html-reporter"); // this will add the reporter to your config 127 | 128 | // Add a `onComplete`-hook with the following data. 129 | onComplete: () => { 130 | report.generate({ 131 | jsonDir: ".tmp/json-output/", 132 | reportPath: ".tmp/report/", 133 | }); 134 | }; 135 | ``` 136 | 137 | The `jsonDir` will tell the module where to find the the generated JSON-report files, the `reportPath` will be the path to where the reports are saved. 138 | -------------------------------------------------------------------------------- /docs/images/browsers-features-overview.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/WasiqB/multiple-cucumber-html-reporter/937e125631970392cbff8b1237a45dc47e21f595/docs/images/browsers-features-overview.jpg -------------------------------------------------------------------------------- /docs/images/custom-data.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/WasiqB/multiple-cucumber-html-reporter/937e125631970392cbff8b1237a45dc47e21f595/docs/images/custom-data.jpg -------------------------------------------------------------------------------- /docs/images/custom-metadata-features-overview.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/WasiqB/multiple-cucumber-html-reporter/937e125631970392cbff8b1237a45dc47e21f595/docs/images/custom-metadata-features-overview.jpg -------------------------------------------------------------------------------- /docs/images/features-custom-metadata.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/WasiqB/multiple-cucumber-html-reporter/937e125631970392cbff8b1237a45dc47e21f595/docs/images/features-custom-metadata.jpg -------------------------------------------------------------------------------- /docs/images/nanoseconds_to_time.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/WasiqB/multiple-cucumber-html-reporter/937e125631970392cbff8b1237a45dc47e21f595/docs/images/nanoseconds_to_time.png -------------------------------------------------------------------------------- /docs/images/overview-metadata.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/WasiqB/multiple-cucumber-html-reporter/937e125631970392cbff8b1237a45dc47e21f595/docs/images/overview-metadata.jpg -------------------------------------------------------------------------------- /docs/images/scenario-app-metadata.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/WasiqB/multiple-cucumber-html-reporter/937e125631970392cbff8b1237a45dc47e21f595/docs/images/scenario-app-metadata.jpg -------------------------------------------------------------------------------- /docs/images/scenario-browser-metadata.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/WasiqB/multiple-cucumber-html-reporter/937e125631970392cbff8b1237a45dc47e21f595/docs/images/scenario-browser-metadata.jpg -------------------------------------------------------------------------------- /docs/images/scenario-no-metadata.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/WasiqB/multiple-cucumber-html-reporter/937e125631970392cbff8b1237a45dc47e21f595/docs/images/scenario-no-metadata.jpg -------------------------------------------------------------------------------- /examples/cypress/.bin/cucumber-json-formatter: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/WasiqB/multiple-cucumber-html-reporter/937e125631970392cbff8b1237a45dc47e21f595/examples/cypress/.bin/cucumber-json-formatter -------------------------------------------------------------------------------- /examples/cypress/.cypress-cucumber-preprocessorrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "json": { 3 | "enabled": true, 4 | "formatter": ".bin/cucumber-json-formatter", 5 | "output": ".run/reports/json/cucumber-report.json" 6 | }, 7 | "messages": { 8 | "enabled": true, 9 | "output": ".run/reports/messages/cucumber-report.json" 10 | }, 11 | "stepDefinitions": ["cypress/e2e/[filepath].step.{js,ts}"] 12 | } 13 | -------------------------------------------------------------------------------- /examples/cypress/README.md: -------------------------------------------------------------------------------- 1 | # Sample Cypress Cucumber 2 | 3 | This is a sample project for Cypress and Cucumber with Multiple HTML Cucumber Reporter. 4 | 5 | ## Setup 6 | 7 | Install PNPM by following the guide [here](https://pnpm.io/installation). 8 | 9 | ## Run the Test 10 | 11 | Install the dependencies by running the following command: 12 | 13 | ```shell 14 | pnpm install 15 | ``` 16 | 17 | Run the tests by using the following command: 18 | 19 | ```shell 20 | pnpm test 21 | ``` 22 | 23 | Generate the HTML reporter by using the following command: 24 | 25 | ```shell 26 | pnpm report 27 | ``` 28 | -------------------------------------------------------------------------------- /examples/cypress/cucumber-html-report.ts: -------------------------------------------------------------------------------- 1 | import { generate } from "multiple-cucumber-html-reporter"; 2 | import dayjs from "dayjs"; 3 | import { readFileSync } from "fs"; 4 | 5 | const data = readFileSync("./.run/results.json", { 6 | encoding: "utf8", 7 | flag: "r", 8 | }); 9 | const runInfos = JSON.parse(data); 10 | 11 | let mapOs = (os: string) => { 12 | if (os.startsWith("win")) { 13 | return "windows"; 14 | } else if (os.startsWith("darwin")) { 15 | return "osx"; 16 | } else if (os.startsWith("linux")) { 17 | return "linux"; 18 | } else if (os.startsWith("ubuntu")) { 19 | return "ubuntu"; 20 | } else if (os.startsWith("android")) { 21 | return "android"; 22 | } else if (os.startsWith("ios")) { 23 | return "ios"; 24 | } 25 | }; 26 | 27 | generate({ 28 | jsonDir: "./.run/reports/json/", 29 | reportPath: "./.run/html-report/", 30 | openReportInBrowser: true, 31 | useCDN: false, 32 | metadata: { 33 | browser: { 34 | name: 35 | runInfos.browserName === "chromium" ? "chrome" : runInfos.browserName, 36 | version: runInfos.browserVersion, 37 | }, 38 | platform: { 39 | name: mapOs(runInfos.osName), 40 | version: runInfos.osVersion, 41 | }, 42 | }, 43 | customData: { 44 | title: "Run Info", 45 | data: [ 46 | { label: "Project", value: "Sample " }, 47 | { label: "Release", value: "1.0.0" }, 48 | { label: "Cypress Version", value: runInfos["cypressVersion"] }, 49 | { label: "Node Version", value: runInfos["nodeVersion"] }, 50 | { 51 | label: "Execution Start Time", 52 | value: dayjs(runInfos["startedTestsAt"]).format( 53 | "YYYY-MM-DD HH:mm:ss.SSS" 54 | ), 55 | }, 56 | { 57 | label: "Execution End Time", 58 | value: dayjs(runInfos["endedTestsAt"]).format( 59 | "YYYY-MM-DD HH:mm:ss.SSS" 60 | ), 61 | }, 62 | ], 63 | }, 64 | pageTitle: "Sample", 65 | reportName: "Sample", 66 | displayDuration: true, 67 | displayReportTime: true, 68 | }); 69 | -------------------------------------------------------------------------------- /examples/cypress/cypress.config.ts: -------------------------------------------------------------------------------- 1 | import { defineConfig } from "cypress"; 2 | import { writeFileSync } from "fs"; 3 | import createBundler from "@bahmutov/cypress-esbuild-preprocessor"; 4 | import { 5 | addCucumberPreprocessorPlugin, 6 | afterRunHandler, 7 | } from "@badeball/cypress-cucumber-preprocessor"; 8 | import { createEsbuildPlugin } from "@badeball/cypress-cucumber-preprocessor/esbuild"; 9 | 10 | async function setupNodeEvents( 11 | on: Cypress.PluginEvents, 12 | config: Cypress.PluginConfigOptions 13 | ): Promise { 14 | await addCucumberPreprocessorPlugin(on, config); 15 | 16 | on( 17 | "file:preprocessor", 18 | createBundler({ 19 | plugins: [createEsbuildPlugin(config)], 20 | }) 21 | ); 22 | on( 23 | "after:run", 24 | async ( 25 | results: 26 | | CypressCommandLine.CypressRunResult 27 | | CypressCommandLine.CypressFailedRunResult 28 | ): Promise => { 29 | if (results) { 30 | await afterRunHandler(config); 31 | writeFileSync(".run/results.json", JSON.stringify(results)); 32 | } 33 | } 34 | ); 35 | 36 | return config; 37 | } 38 | 39 | export default defineConfig({ 40 | e2e: { 41 | specPattern: "cypress/e2e/**/*.feature", 42 | setupNodeEvents, 43 | defaultCommandTimeout: 60000, 44 | pageLoadTimeout: 60000, 45 | video: false, 46 | experimentalInteractiveRunEvents: true, 47 | downloadsFolder: "./cypress/.run/downloads", 48 | fixturesFolder: "./cypress/.run/fixtures", 49 | screenshotsFolder: "./cypress/.run/screenshots", 50 | videosFolder: "./cypress/.run/videos", 51 | }, 52 | }); 53 | -------------------------------------------------------------------------------- /examples/cypress/cypress/e2e/gmail/gmail.feature: -------------------------------------------------------------------------------- 1 | Feature: Gmail Opening 2 | 3 | Scenario: Gmail 4 | 5 | Given I open the gmail 6 | 7 | -------------------------------------------------------------------------------- /examples/cypress/cypress/e2e/gmail/gmail.step.ts: -------------------------------------------------------------------------------- 1 | /// 2 | 3 | import { Given } from "@badeball/cypress-cucumber-preprocessor"; 4 | 5 | Given("I open the gmail", () => { 6 | cy.visit("https://mail.google.com"); 7 | }); 8 | -------------------------------------------------------------------------------- /examples/cypress/cypress/e2e/google/google.feature: -------------------------------------------------------------------------------- 1 | Feature: Google Opening 2 | 3 | Scenario: Google 4 | 5 | Given I open the google 6 | 7 | -------------------------------------------------------------------------------- /examples/cypress/cypress/e2e/google/google.step.ts: -------------------------------------------------------------------------------- 1 | /// 2 | 3 | import { Given } from "@badeball/cypress-cucumber-preprocessor"; 4 | 5 | Given("I open the google", () => { 6 | cy.visit("https://www.google.co.in"); 7 | }); 8 | -------------------------------------------------------------------------------- /examples/cypress/cypress/fixtures/example.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "Using fixtures to represent data", 3 | "email": "hello@cypress.io", 4 | "body": "Fixtures are a great way to mock data for responses to routes" 5 | } 6 | -------------------------------------------------------------------------------- /examples/cypress/cypress/support/commands.ts: -------------------------------------------------------------------------------- 1 | // *********************************************** 2 | // This example commands.js shows you how to 3 | // create various custom commands and overwrite 4 | // existing commands. 5 | // 6 | // For more comprehensive examples of custom 7 | // commands please read more here: 8 | // https://on.cypress.io/custom-commands 9 | // *********************************************** 10 | // 11 | // 12 | // -- This is a parent command -- 13 | // Cypress.Commands.add('login', (email, password) => { ... }) 14 | // 15 | // 16 | // -- This is a child command -- 17 | // Cypress.Commands.add('drag', { prevSubject: 'element'}, (subject, options) => { ... }) 18 | // 19 | // 20 | // -- This is a dual command -- 21 | // Cypress.Commands.add('dismiss', { prevSubject: 'optional'}, (subject, options) => { ... }) 22 | // 23 | // 24 | // -- This will overwrite an existing command -- 25 | // Cypress.Commands.overwrite('visit', (originalFn, url, options) => { ... }) -------------------------------------------------------------------------------- /examples/cypress/cypress/support/e2e.ts: -------------------------------------------------------------------------------- 1 | // *********************************************************** 2 | // This example support/e2e.js is processed and 3 | // loaded automatically before your test files. 4 | // 5 | // This is a great place to put global configuration and 6 | // behavior that modifies Cypress. 7 | // 8 | // You can change the location of this file or turn off 9 | // automatically serving support files with the 10 | // 'supportFile' configuration option. 11 | // 12 | // You can read more here: 13 | // https://on.cypress.io/configuration 14 | // *********************************************************** 15 | 16 | // Import commands.js using ES2015 syntax: 17 | import './commands' 18 | 19 | // Alternatively you can use CommonJS syntax: 20 | // require('./commands') -------------------------------------------------------------------------------- /examples/cypress/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "cypress", 3 | "version": "1.0.0", 4 | "description": "Sample Cypress project", 5 | "main": "index.js", 6 | "type": "module", 7 | "scripts": { 8 | "clean": "rm -rf dist .run", 9 | "build": "pnpm clean && tsc", 10 | "test": "pnpm build && cypress run --browser chrome --headed", 11 | "report": "node dist/cucumber-html-report.js", 12 | "cy:run": "cypress run" 13 | }, 14 | "author": "Wasiq Bhamla", 15 | "license": "MIT", 16 | "devDependencies": { 17 | "@badeball/cypress-cucumber-preprocessor": "^22.0.1", 18 | "@bahmutov/cypress-esbuild-preprocessor": "^2.2.4", 19 | "@types/node": "^22.14.0", 20 | "cypress": "^14.0.3", 21 | "multiple-cucumber-html-reporter": "3.9.2", 22 | "typescript": "^5.8.3" 23 | }, 24 | "dependencies": { 25 | "dayjs": "^1.11.13" 26 | }, 27 | "packageManager": "pnpm@10.4.0" 28 | } 29 | -------------------------------------------------------------------------------- /examples/cypress/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "esModuleInterop": true, 4 | "module": "NodeNext", 5 | "outDir": "dist" 6 | }, 7 | "include": ["cucumber-html-report.ts"] 8 | } 9 | -------------------------------------------------------------------------------- /lib/collect-jsons.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const find = require('find'); 4 | const fs = require('fs-extra'); 5 | const jsonFile = require('jsonfile'); 6 | const path = require('node:path'); 7 | const { DateTime } = require('luxon'); 8 | 9 | /** 10 | * Formats input date to yyyy/MM/dd HH:mm:ss 11 | * 12 | * @param {Date} date 13 | * @returns {string} formatted date in ISO format local time 14 | */ 15 | function formatToLocalIso(date) { 16 | return typeof date === 'string' ? 17 | DateTime.fromISO(date).toFormat('yyyy/MM/dd HH:mm:ss') 18 | : 19 | DateTime.fromJSDate(date).toFormat('yyyy/MM/dd HH:mm:ss'); 20 | } 21 | 22 | module.exports = function collectJSONS(options) { 23 | const jsonOutput = []; 24 | let files; 25 | 26 | try { 27 | files = find.fileSync(/\.json$/, path.resolve(process.cwd(), options.jsonDir)); 28 | } catch (e) { 29 | throw new Error(`There were issues reading JSON-files from '${options.jsonDir}'.`); 30 | } 31 | 32 | if (files.length > 0) { 33 | files.map(file => { 34 | // Cucumber json can be empty, it's likely being created by another process (#47) 35 | const data = fs.readFileSync(file).toString() || "[]"; 36 | const stats = fs.statSync(file); 37 | const reportTime = stats.birthtime; 38 | 39 | JSON.parse(data).map(json => { 40 | if (options.metadata && !json.metadata) { 41 | json.metadata = options.metadata; 42 | } else { 43 | json = Object.assign({ 44 | "metadata": { 45 | "browser": { 46 | "name": "not known", 47 | "version": "not known" 48 | }, 49 | "device": "not known", 50 | "platform": { 51 | "name": "not known", 52 | "version": "not known" 53 | } 54 | } 55 | }, json); 56 | } 57 | 58 | if (json.metadata && options.displayReportTime && reportTime) { 59 | json.metadata = Object.assign({reportTime: reportTime}, json.metadata) 60 | json.metadata.reportTime = formatToLocalIso(json.metadata.reportTime); 61 | } 62 | 63 | // Only check the feature hooks if there are elements (fail-safe) 64 | const {elements} = json; 65 | 66 | if (elements) { 67 | json.elements = elements.map(scenario => { 68 | const {before, after} = scenario; 69 | 70 | if (before) { 71 | scenario.steps = parseFeatureHooks(before, 'Before').concat(scenario.steps); 72 | } 73 | if (after) { 74 | scenario.steps = scenario.steps.concat(parseFeatureHooks(after, 'After')); 75 | } 76 | 77 | return scenario 78 | }) 79 | } 80 | 81 | jsonOutput.push(json) 82 | }); 83 | }); 84 | 85 | if (options.saveCollectedJSON) { 86 | const file = path.resolve(options.reportPath, 'merged-output.json'); 87 | fs.ensureDirSync(options.reportPath); 88 | jsonFile.writeFileSync(file, jsonOutput, {spaces: 2}); 89 | } 90 | 91 | return jsonOutput; 92 | } 93 | 94 | console.log('\x1b[33m%s\x1b[0m', `WARNING: No JSON files found in '${options.jsonDir}'. NO REPORT CAN BE CREATED!`); 95 | return []; 96 | }; 97 | 98 | /** 99 | * Add the feature hooks to the steps so the report will pick them up properly 100 | * 101 | * @param {object} data 102 | * @param {string} keyword 103 | * @returns {{ 104 | * arguments: array, 105 | * keyword: string, 106 | * name: string, 107 | * result: { 108 | * status: string, 109 | * }, 110 | * line: string, 111 | * match: { 112 | * location: string 113 | * }, 114 | * embeddings: [] 115 | * }} 116 | */ 117 | function parseFeatureHooks(data, keyword) { 118 | return data.map(step => { 119 | const match = step.match && step.match.location ? step.match : {location: 'can not be determined'}; 120 | 121 | return { 122 | arguments: step.arguments || [], 123 | keyword: keyword, 124 | name: 'Hook', 125 | result: step.result, 126 | line: '', 127 | match, 128 | embeddings: step.embeddings || [] 129 | } 130 | }) 131 | } 132 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "multiple-cucumber-html-reporter", 3 | "version": "3.9.2", 4 | "description": "Generate beautiful Cucumber.js reports for multiple instances (browsers / devices)", 5 | "keywords": [ 6 | "cucumber", 7 | "html", 8 | "test report", 9 | "multiple-cucumber-html-reporter", 10 | "html report", 11 | "json to html" 12 | ], 13 | "main": "lib/generate-report.js", 14 | "license": "MIT", 15 | "scripts": { 16 | "release": "release-it", 17 | "release.ci": "pnpm release --ci", 18 | "release.patch": "pnpm release.ci patch", 19 | "release.minor": "pnpm release.ci minor", 20 | "release.major": "pnpm release.ci major", 21 | "test": "node ./test/test.js", 22 | "unit.test": "jasmine JASMINE_CONFIG_PATH=test/unit/jasmine.json", 23 | "unit.test.coverage": "JASMINE_CONFIG_PATH=test/unit/jasmine.json nyc jasmine" 24 | }, 25 | "repository": { 26 | "type": "git", 27 | "url": "git+https://github.com/WasiqB/multiple-cucumber-html-reporter.git" 28 | }, 29 | "author": "WasiqB", 30 | "bugs": { 31 | "url": "https://github.com/WasiqB/multiple-cucumber-html-reporter/issues" 32 | }, 33 | "homepage": "https://github.com/WasiqB/multiple-cucumber-html-reporter#readme", 34 | "dependencies": { 35 | "find": "^0.3.0", 36 | "fs-extra": "^11.3.0", 37 | "jsonfile": "^6.1.0", 38 | "lodash": "^4.17.21", 39 | "luxon": "^3.5.0", 40 | "open": "^8.4.2", 41 | "uuid": "^11.1.0" 42 | }, 43 | "devDependencies": { 44 | "jasmine": "^5.6.0", 45 | "nyc": "^17.1.0", 46 | "release-it": "^18.1.2" 47 | }, 48 | "packageManager": "pnpm@10.4.0" 49 | } 50 | -------------------------------------------------------------------------------- /templates/assets/css/responsive.bootstrap5.min.css: -------------------------------------------------------------------------------- 1 | table.dataTable.dtr-inline.collapsed > tbody > tr > td.child, 2 | table.dataTable.dtr-inline.collapsed > tbody > tr > th.child, 3 | table.dataTable.dtr-inline.collapsed > tbody > tr > td.dataTables_empty { 4 | cursor: default !important; 5 | } 6 | table.dataTable.dtr-inline.collapsed > tbody > tr > td.child:before, 7 | table.dataTable.dtr-inline.collapsed > tbody > tr > th.child:before, 8 | table.dataTable.dtr-inline.collapsed > tbody > tr > td.dataTables_empty:before { 9 | display: none !important; 10 | } 11 | table.dataTable.dtr-inline.collapsed > tbody > tr > td.dtr-control, 12 | table.dataTable.dtr-inline.collapsed > tbody > tr > th.dtr-control { 13 | cursor: pointer; 14 | } 15 | table.dataTable.dtr-inline.collapsed > tbody > tr > td.dtr-control:before, 16 | table.dataTable.dtr-inline.collapsed > tbody > tr > th.dtr-control:before { 17 | margin-right: 0.5em; 18 | display: inline-block; 19 | box-sizing: border-box; 20 | content: ""; 21 | border-top: 5px solid transparent; 22 | border-left: 10px solid rgba(0, 0, 0, 0.5); 23 | border-bottom: 5px solid transparent; 24 | border-right: 0px solid transparent; 25 | } 26 | table.dataTable.dtr-inline.collapsed 27 | > tbody 28 | > tr 29 | > td.dtr-control.arrow-right::before, 30 | table.dataTable.dtr-inline.collapsed 31 | > tbody 32 | > tr 33 | > th.dtr-control.arrow-right::before { 34 | border-top: 5px solid transparent; 35 | border-left: 0px solid transparent; 36 | border-bottom: 5px solid transparent; 37 | border-right: 10px solid rgba(0, 0, 0, 0.5); 38 | } 39 | table.dataTable.dtr-inline.collapsed 40 | > tbody 41 | > tr.dtr-expanded 42 | > td.dtr-control:before, 43 | table.dataTable.dtr-inline.collapsed 44 | > tbody 45 | > tr.dtr-expanded 46 | > th.dtr-control:before { 47 | border-top: 10px solid rgba(0, 0, 0, 0.5); 48 | border-left: 5px solid transparent; 49 | border-bottom: 0px solid transparent; 50 | border-right: 5px solid transparent; 51 | } 52 | table.dataTable.dtr-inline.collapsed.compact > tbody > tr > td.dtr-control, 53 | table.dataTable.dtr-inline.collapsed.compact > tbody > tr > th.dtr-control { 54 | padding-left: 0.333em; 55 | } 56 | table.dataTable.dtr-column > tbody > tr > td.dtr-control, 57 | table.dataTable.dtr-column > tbody > tr > th.dtr-control, 58 | table.dataTable.dtr-column > tbody > tr > td.control, 59 | table.dataTable.dtr-column > tbody > tr > th.control { 60 | cursor: pointer; 61 | } 62 | table.dataTable.dtr-column > tbody > tr > td.dtr-control:before, 63 | table.dataTable.dtr-column > tbody > tr > th.dtr-control:before, 64 | table.dataTable.dtr-column > tbody > tr > td.control:before, 65 | table.dataTable.dtr-column > tbody > tr > th.control:before { 66 | display: inline-block; 67 | box-sizing: border-box; 68 | content: ""; 69 | border-top: 5px solid transparent; 70 | border-left: 10px solid rgba(0, 0, 0, 0.5); 71 | border-bottom: 5px solid transparent; 72 | border-right: 0px solid transparent; 73 | } 74 | table.dataTable.dtr-column > tbody > tr > td.dtr-control.arrow-right::before, 75 | table.dataTable.dtr-column > tbody > tr > th.dtr-control.arrow-right::before, 76 | table.dataTable.dtr-column > tbody > tr > td.control.arrow-right::before, 77 | table.dataTable.dtr-column > tbody > tr > th.control.arrow-right::before { 78 | border-top: 5px solid transparent; 79 | border-left: 0px solid transparent; 80 | border-bottom: 5px solid transparent; 81 | border-right: 10px solid rgba(0, 0, 0, 0.5); 82 | } 83 | table.dataTable.dtr-column > tbody > tr.dtr-expanded td.dtr-control:before, 84 | table.dataTable.dtr-column > tbody > tr.dtr-expanded th.dtr-control:before, 85 | table.dataTable.dtr-column > tbody > tr.dtr-expanded td.control:before, 86 | table.dataTable.dtr-column > tbody > tr.dtr-expanded th.control:before { 87 | border-top: 10px solid rgba(0, 0, 0, 0.5); 88 | border-left: 5px solid transparent; 89 | border-bottom: 0px solid transparent; 90 | border-right: 5px solid transparent; 91 | } 92 | table.dataTable > tbody > tr.child { 93 | padding: 0.5em 1em; 94 | } 95 | table.dataTable > tbody > tr.child:hover { 96 | background: transparent !important; 97 | } 98 | table.dataTable > tbody > tr.child ul.dtr-details { 99 | display: inline-block; 100 | list-style-type: none; 101 | margin: 0; 102 | padding: 0; 103 | } 104 | table.dataTable > tbody > tr.child ul.dtr-details > li { 105 | border-bottom: 1px solid #efefef; 106 | padding: 0.5em 0; 107 | } 108 | table.dataTable > tbody > tr.child ul.dtr-details > li:first-child { 109 | padding-top: 0; 110 | } 111 | table.dataTable > tbody > tr.child ul.dtr-details > li:last-child { 112 | padding-bottom: 0; 113 | border-bottom: none; 114 | } 115 | table.dataTable > tbody > tr.child span.dtr-title { 116 | display: inline-block; 117 | min-width: 75px; 118 | font-weight: bold; 119 | } 120 | div.dtr-modal { 121 | position: fixed; 122 | box-sizing: border-box; 123 | top: 0; 124 | left: 0; 125 | height: 100%; 126 | width: 100%; 127 | z-index: 100; 128 | padding: 10em 1em; 129 | } 130 | div.dtr-modal div.dtr-modal-display { 131 | position: absolute; 132 | top: 0; 133 | left: 0; 134 | bottom: 0; 135 | right: 0; 136 | width: 50%; 137 | height: fit-content; 138 | max-height: 75%; 139 | overflow: auto; 140 | margin: auto; 141 | z-index: 102; 142 | overflow: auto; 143 | background-color: #f5f5f7; 144 | border: 1px solid black; 145 | border-radius: 0.5em; 146 | box-shadow: 0 12px 30px rgba(0, 0, 0, 0.6); 147 | } 148 | div.dtr-modal div.dtr-modal-content { 149 | position: relative; 150 | padding: 2.5em; 151 | } 152 | div.dtr-modal div.dtr-modal-content h2 { 153 | margin-top: 0; 154 | } 155 | div.dtr-modal div.dtr-modal-close { 156 | position: absolute; 157 | top: 6px; 158 | right: 6px; 159 | width: 22px; 160 | height: 22px; 161 | text-align: center; 162 | border-radius: 3px; 163 | cursor: pointer; 164 | z-index: 12; 165 | } 166 | div.dtr-modal div.dtr-modal-background { 167 | position: fixed; 168 | top: 0; 169 | left: 0; 170 | right: 0; 171 | bottom: 0; 172 | z-index: 101; 173 | background: rgba(0, 0, 0, 0.6); 174 | } 175 | @media screen and (max-width: 767px) { 176 | div.dtr-modal div.dtr-modal-display { 177 | width: 95%; 178 | } 179 | } 180 | html.dark table.dataTable > tbody > tr > td.dtr-control:before, 181 | html[data-bs-theme="dark"] 182 | table.dataTable 183 | > tbody 184 | > tr 185 | > td.dtr-control:before { 186 | border-left-color: rgba(255, 255, 255, 0.5) !important; 187 | } 188 | html.dark table.dataTable > tbody > tr > td.dtr-control.arrow-right::before, 189 | html[data-bs-theme="dark"] 190 | table.dataTable 191 | > tbody 192 | > tr 193 | > td.dtr-control.arrow-right::before { 194 | border-right-color: rgba(255, 255, 255, 0.5) !important; 195 | } 196 | html.dark table.dataTable > tbody > tr.dtr-expanded > td.dtr-control:before, 197 | html.dark table.dataTable > tbody > tr.dtr-expanded > th.dtr-control:before, 198 | html[data-bs-theme="dark"] 199 | table.dataTable 200 | > tbody 201 | > tr.dtr-expanded 202 | > td.dtr-control:before, 203 | html[data-bs-theme="dark"] 204 | table.dataTable 205 | > tbody 206 | > tr.dtr-expanded 207 | > th.dtr-control:before { 208 | border-top-color: rgba(255, 255, 255, 0.5) !important; 209 | border-left-color: transparent !important; 210 | border-right-color: transparent !important; 211 | } 212 | html.dark table.dataTable > tbody > tr.child ul.dtr-details > li, 213 | html[data-bs-theme="dark"] 214 | table.dataTable 215 | > tbody 216 | > tr.child 217 | ul.dtr-details 218 | > li { 219 | border-bottom-color: rgb(64, 67, 70); 220 | } 221 | html.dark div.dtr-modal div.dtr-modal-display, 222 | html[data-bs-theme="dark"] div.dtr-modal div.dtr-modal-display { 223 | background-color: rgb(33, 37, 41); 224 | border: 1px solid rgba(255, 255, 255, 0.15); 225 | } 226 | div.dtr-bs-modal table.table tr:first-child td { 227 | border-top: none; 228 | } 229 | table.dataTable.table-bordered th.dtr-control.dtr-hidden + *, 230 | table.dataTable.table-bordered td.dtr-control.dtr-hidden + * { 231 | border-left-width: 1px; 232 | } 233 | -------------------------------------------------------------------------------- /templates/assets/css/responsive.dataTables.min.css: -------------------------------------------------------------------------------- 1 | table.dataTable.dtr-inline.collapsed > tbody > tr > td.child, 2 | table.dataTable.dtr-inline.collapsed > tbody > tr > th.child, 3 | table.dataTable.dtr-inline.collapsed > tbody > tr > td.dataTables_empty { 4 | cursor: default !important; 5 | } 6 | table.dataTable.dtr-inline.collapsed > tbody > tr > td.child:before, 7 | table.dataTable.dtr-inline.collapsed > tbody > tr > th.child:before, 8 | table.dataTable.dtr-inline.collapsed > tbody > tr > td.dataTables_empty:before { 9 | display: none !important; 10 | } 11 | table.dataTable.dtr-inline.collapsed > tbody > tr > td.dtr-control, 12 | table.dataTable.dtr-inline.collapsed > tbody > tr > th.dtr-control { 13 | cursor: pointer; 14 | } 15 | table.dataTable.dtr-inline.collapsed > tbody > tr > td.dtr-control:before, 16 | table.dataTable.dtr-inline.collapsed > tbody > tr > th.dtr-control:before { 17 | margin-right: 0.5em; 18 | display: inline-block; 19 | box-sizing: border-box; 20 | content: ""; 21 | border-top: 5px solid transparent; 22 | border-left: 10px solid rgba(0, 0, 0, 0.5); 23 | border-bottom: 5px solid transparent; 24 | border-right: 0px solid transparent; 25 | } 26 | table.dataTable.dtr-inline.collapsed 27 | > tbody 28 | > tr 29 | > td.dtr-control.arrow-right::before, 30 | table.dataTable.dtr-inline.collapsed 31 | > tbody 32 | > tr 33 | > th.dtr-control.arrow-right::before { 34 | border-top: 5px solid transparent; 35 | border-left: 0px solid transparent; 36 | border-bottom: 5px solid transparent; 37 | border-right: 10px solid rgba(0, 0, 0, 0.5); 38 | } 39 | table.dataTable.dtr-inline.collapsed 40 | > tbody 41 | > tr.dtr-expanded 42 | > td.dtr-control:before, 43 | table.dataTable.dtr-inline.collapsed 44 | > tbody 45 | > tr.dtr-expanded 46 | > th.dtr-control:before { 47 | border-top: 10px solid rgba(0, 0, 0, 0.5); 48 | border-left: 5px solid transparent; 49 | border-bottom: 0px solid transparent; 50 | border-right: 5px solid transparent; 51 | } 52 | table.dataTable.dtr-inline.collapsed.compact > tbody > tr > td.dtr-control, 53 | table.dataTable.dtr-inline.collapsed.compact > tbody > tr > th.dtr-control { 54 | padding-left: 0.333em; 55 | } 56 | table.dataTable.dtr-column > tbody > tr > td.dtr-control, 57 | table.dataTable.dtr-column > tbody > tr > th.dtr-control, 58 | table.dataTable.dtr-column > tbody > tr > td.control, 59 | table.dataTable.dtr-column > tbody > tr > th.control { 60 | cursor: pointer; 61 | } 62 | table.dataTable.dtr-column > tbody > tr > td.dtr-control:before, 63 | table.dataTable.dtr-column > tbody > tr > th.dtr-control:before, 64 | table.dataTable.dtr-column > tbody > tr > td.control:before, 65 | table.dataTable.dtr-column > tbody > tr > th.control:before { 66 | display: inline-block; 67 | box-sizing: border-box; 68 | content: ""; 69 | border-top: 5px solid transparent; 70 | border-left: 10px solid rgba(0, 0, 0, 0.5); 71 | border-bottom: 5px solid transparent; 72 | border-right: 0px solid transparent; 73 | } 74 | table.dataTable.dtr-column > tbody > tr > td.dtr-control.arrow-right::before, 75 | table.dataTable.dtr-column > tbody > tr > th.dtr-control.arrow-right::before, 76 | table.dataTable.dtr-column > tbody > tr > td.control.arrow-right::before, 77 | table.dataTable.dtr-column > tbody > tr > th.control.arrow-right::before { 78 | border-top: 5px solid transparent; 79 | border-left: 0px solid transparent; 80 | border-bottom: 5px solid transparent; 81 | border-right: 10px solid rgba(0, 0, 0, 0.5); 82 | } 83 | table.dataTable.dtr-column > tbody > tr.dtr-expanded td.dtr-control:before, 84 | table.dataTable.dtr-column > tbody > tr.dtr-expanded th.dtr-control:before, 85 | table.dataTable.dtr-column > tbody > tr.dtr-expanded td.control:before, 86 | table.dataTable.dtr-column > tbody > tr.dtr-expanded th.control:before { 87 | border-top: 10px solid rgba(0, 0, 0, 0.5); 88 | border-left: 5px solid transparent; 89 | border-bottom: 0px solid transparent; 90 | border-right: 5px solid transparent; 91 | } 92 | table.dataTable > tbody > tr.child { 93 | padding: 0.5em 1em; 94 | } 95 | table.dataTable > tbody > tr.child:hover { 96 | background: transparent !important; 97 | } 98 | table.dataTable > tbody > tr.child ul.dtr-details { 99 | display: inline-block; 100 | list-style-type: none; 101 | margin: 0; 102 | padding: 0; 103 | } 104 | table.dataTable > tbody > tr.child ul.dtr-details > li { 105 | border-bottom: 1px solid #efefef; 106 | padding: 0.5em 0; 107 | } 108 | table.dataTable > tbody > tr.child ul.dtr-details > li:first-child { 109 | padding-top: 0; 110 | } 111 | table.dataTable > tbody > tr.child ul.dtr-details > li:last-child { 112 | padding-bottom: 0; 113 | border-bottom: none; 114 | } 115 | table.dataTable > tbody > tr.child span.dtr-title { 116 | display: inline-block; 117 | min-width: 75px; 118 | font-weight: bold; 119 | } 120 | div.dtr-modal { 121 | position: fixed; 122 | box-sizing: border-box; 123 | top: 0; 124 | left: 0; 125 | height: 100%; 126 | width: 100%; 127 | z-index: 100; 128 | padding: 10em 1em; 129 | } 130 | div.dtr-modal div.dtr-modal-display { 131 | position: absolute; 132 | top: 0; 133 | left: 0; 134 | bottom: 0; 135 | right: 0; 136 | width: 50%; 137 | height: fit-content; 138 | max-height: 75%; 139 | overflow: auto; 140 | margin: auto; 141 | z-index: 102; 142 | overflow: auto; 143 | background-color: #f5f5f7; 144 | border: 1px solid black; 145 | border-radius: 0.5em; 146 | box-shadow: 0 12px 30px rgba(0, 0, 0, 0.6); 147 | } 148 | div.dtr-modal div.dtr-modal-content { 149 | position: relative; 150 | padding: 2.5em; 151 | } 152 | div.dtr-modal div.dtr-modal-content h2 { 153 | margin-top: 0; 154 | } 155 | div.dtr-modal div.dtr-modal-close { 156 | position: absolute; 157 | top: 6px; 158 | right: 6px; 159 | width: 22px; 160 | height: 22px; 161 | text-align: center; 162 | border-radius: 3px; 163 | cursor: pointer; 164 | z-index: 12; 165 | } 166 | div.dtr-modal div.dtr-modal-background { 167 | position: fixed; 168 | top: 0; 169 | left: 0; 170 | right: 0; 171 | bottom: 0; 172 | z-index: 101; 173 | background: rgba(0, 0, 0, 0.6); 174 | } 175 | @media screen and (max-width: 767px) { 176 | div.dtr-modal div.dtr-modal-display { 177 | width: 95%; 178 | } 179 | } 180 | html.dark table.dataTable > tbody > tr > td.dtr-control:before, 181 | html[data-bs-theme="dark"] 182 | table.dataTable 183 | > tbody 184 | > tr 185 | > td.dtr-control:before { 186 | border-left-color: rgba(255, 255, 255, 0.5) !important; 187 | } 188 | html.dark table.dataTable > tbody > tr > td.dtr-control.arrow-right::before, 189 | html[data-bs-theme="dark"] 190 | table.dataTable 191 | > tbody 192 | > tr 193 | > td.dtr-control.arrow-right::before { 194 | border-right-color: rgba(255, 255, 255, 0.5) !important; 195 | } 196 | html.dark table.dataTable > tbody > tr.dtr-expanded > td.dtr-control:before, 197 | html.dark table.dataTable > tbody > tr.dtr-expanded > th.dtr-control:before, 198 | html[data-bs-theme="dark"] 199 | table.dataTable 200 | > tbody 201 | > tr.dtr-expanded 202 | > td.dtr-control:before, 203 | html[data-bs-theme="dark"] 204 | table.dataTable 205 | > tbody 206 | > tr.dtr-expanded 207 | > th.dtr-control:before { 208 | border-top-color: rgba(255, 255, 255, 0.5) !important; 209 | border-left-color: transparent !important; 210 | border-right-color: transparent !important; 211 | } 212 | html.dark table.dataTable > tbody > tr.child ul.dtr-details > li, 213 | html[data-bs-theme="dark"] 214 | table.dataTable 215 | > tbody 216 | > tr.child 217 | ul.dtr-details 218 | > li { 219 | border-bottom-color: rgb(64, 67, 70); 220 | } 221 | html.dark div.dtr-modal div.dtr-modal-display, 222 | html[data-bs-theme="dark"] div.dtr-modal div.dtr-modal-display { 223 | background-color: rgb(33, 37, 41); 224 | border: 1px solid rgba(255, 255, 255, 0.15); 225 | } 226 | div.dtr-bs-modal table.table tr:first-child td { 227 | border-top: none; 228 | } 229 | table.dataTable.table-bordered th.dtr-control.dtr-hidden + *, 230 | table.dataTable.table-bordered td.dtr-control.dtr-hidden + * { 231 | border-left-width: 1px; 232 | } 233 | -------------------------------------------------------------------------------- /templates/assets/fonts/FontAwesome.otf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/WasiqB/multiple-cucumber-html-reporter/937e125631970392cbff8b1237a45dc47e21f595/templates/assets/fonts/FontAwesome.otf -------------------------------------------------------------------------------- /templates/assets/fonts/fontawesome-webfont.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/WasiqB/multiple-cucumber-html-reporter/937e125631970392cbff8b1237a45dc47e21f595/templates/assets/fonts/fontawesome-webfont.eot -------------------------------------------------------------------------------- /templates/assets/fonts/fontawesome-webfont.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/WasiqB/multiple-cucumber-html-reporter/937e125631970392cbff8b1237a45dc47e21f595/templates/assets/fonts/fontawesome-webfont.ttf -------------------------------------------------------------------------------- /templates/assets/fonts/fontawesome-webfont.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/WasiqB/multiple-cucumber-html-reporter/937e125631970392cbff8b1237a45dc47e21f595/templates/assets/fonts/fontawesome-webfont.woff -------------------------------------------------------------------------------- /templates/assets/fonts/fontawesome-webfont.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/WasiqB/multiple-cucumber-html-reporter/937e125631970392cbff8b1237a45dc47e21f595/templates/assets/fonts/fontawesome-webfont.woff2 -------------------------------------------------------------------------------- /templates/assets/fonts/glyphicons-halflings-regular.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/WasiqB/multiple-cucumber-html-reporter/937e125631970392cbff8b1237a45dc47e21f595/templates/assets/fonts/glyphicons-halflings-regular.eot -------------------------------------------------------------------------------- /templates/assets/fonts/glyphicons-halflings-regular.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/WasiqB/multiple-cucumber-html-reporter/937e125631970392cbff8b1237a45dc47e21f595/templates/assets/fonts/glyphicons-halflings-regular.ttf -------------------------------------------------------------------------------- /templates/assets/fonts/glyphicons-halflings-regular.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/WasiqB/multiple-cucumber-html-reporter/937e125631970392cbff8b1237a45dc47e21f595/templates/assets/fonts/glyphicons-halflings-regular.woff -------------------------------------------------------------------------------- /templates/assets/fonts/glyphicons-halflings-regular.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/WasiqB/multiple-cucumber-html-reporter/937e125631970392cbff8b1237a45dc47e21f595/templates/assets/fonts/glyphicons-halflings-regular.woff2 -------------------------------------------------------------------------------- /templates/assets/img/ghost.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 5 | 10 | 11 | 13 | 15 | 17 | 18 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | -------------------------------------------------------------------------------- /templates/assets/js/darkmode.js: -------------------------------------------------------------------------------- 1 | var darkMode = "darkmode"; 2 | 3 | function applyDarkMode() { 4 | applyFontStyle(); 5 | document 6 | .querySelector("html") 7 | .setAttribute("data-bs-theme", isDarkModeOn() ? "dark" : "light"); 8 | document.getElementById("features-table").classList.toggle("table-striped"); 9 | } 10 | 11 | function saveState() { 12 | if (isDarkModeOn()) { 13 | window.localStorage["darkmode"] = "on"; 14 | } else { 15 | window.localStorage["darkmode"] = "off"; 16 | } 17 | } 18 | 19 | function applyFontStyle() { 20 | document.body.classList.toggle(darkMode); 21 | } 22 | 23 | function isDarkModeOn() { 24 | var toggle = document.getElementById("darkCheck"); 25 | return toggle.checked; 26 | } 27 | 28 | window.onload = function () { 29 | if (window.localStorage["darkmode"] === "on") { 30 | applyDarkMode(); 31 | document.getElementById("darkCheck").checked = true; 32 | document 33 | .querySelector("html") 34 | .setAttribute("data-bs-theme", isDarkModeOn() ? "dark" : "light"); 35 | } 36 | }; 37 | -------------------------------------------------------------------------------- /templates/assets/js/datatables.bootstrap5.min.js: -------------------------------------------------------------------------------- 1 | /*! DataTables Bootstrap 5 integration 2 | * © SpryMedia Ltd - datatables.net/license 3 | */ 4 | !(function (n) { 5 | var o, r; 6 | "function" == typeof define && define.amd 7 | ? define(["jquery", "datatables.net"], function (t) { 8 | return n(t, window, document); 9 | }) 10 | : "object" == typeof exports 11 | ? ((o = require("jquery")), 12 | (r = function (t, e) { 13 | e.fn.dataTable || require("datatables.net")(t, e); 14 | }), 15 | "undefined" == typeof window 16 | ? (module.exports = function (t, e) { 17 | return ( 18 | (t = t || window), (e = e || o(t)), r(t, e), n(e, 0, t.document) 19 | ); 20 | }) 21 | : (r(window, o), (module.exports = n(o, window, window.document)))) 22 | : n(jQuery, window, document); 23 | })(function (d, t, e) { 24 | "use strict"; 25 | var n = d.fn.dataTable; 26 | return ( 27 | d.extend(!0, n.defaults, { renderer: "bootstrap" }), 28 | d.extend(!0, n.ext.classes, { 29 | container: "dt-container dt-bootstrap5", 30 | search: { input: "form-control form-control-sm" }, 31 | length: { select: "form-select form-select-sm" }, 32 | processing: { container: "dt-processing card" }, 33 | layout: { 34 | row: "row mt-2 justify-content-between", 35 | cell: "d-md-flex justify-content-between align-items-center", 36 | tableCell: "col-12", 37 | start: "dt-layout-start col-md-auto me-auto", 38 | end: "dt-layout-end col-md-auto ms-auto", 39 | full: "dt-layout-full col-md", 40 | }, 41 | }), 42 | (n.ext.renderer.pagingButton.bootstrap = function (t, e, n, o, r) { 43 | var a = ["dt-paging-button", "page-item"], 44 | o = 45 | (o && a.push("active"), 46 | r && a.push("disabled"), 47 | d("
  • ").addClass(a.join(" "))); 48 | return { 49 | display: o, 50 | clicker: d("' + 63 | "" + 64 | '" + 66 | "" + 67 | "" 68 | ); 69 | var modal; 70 | 71 | // Note this could be undefined at the time of initialisation - the 72 | // DataTable.Responsive.bootstrap function can be used to set a different 73 | // bootstrap object 74 | var _bs = window.bootstrap; 75 | 76 | DataTable.Responsive.bootstrap = function (bs) { 77 | _bs = bs; 78 | }; 79 | 80 | // Get the Bootstrap library from locally set (legacy) or from DT. 81 | function getBs() { 82 | let dtBs = DataTable.use("bootstrap"); 83 | 84 | if (dtBs) { 85 | return dtBs; 86 | } 87 | 88 | if (_bs) { 89 | return _bs; 90 | } 91 | 92 | throw new Error( 93 | "No Bootstrap library. Set it with `DataTable.use(bootstrap);`" 94 | ); 95 | } 96 | 97 | _display.modal = function (options) { 98 | if (!modal && _bs.Modal) { 99 | let localBs = getBs(); 100 | modal = new localBs.Modal(_modal[0]); 101 | } 102 | 103 | return function (row, update, render, closeCallback) { 104 | if (!modal) { 105 | return _original(row, update, render, closeCallback); 106 | } else { 107 | var rendered = render(); 108 | 109 | if (rendered === false) { 110 | return false; 111 | } 112 | 113 | if (!update) { 114 | if (options && options.header) { 115 | var header = _modal.find("div.modal-header"); 116 | var button = header.find("button").detach(); 117 | 118 | header 119 | .empty() 120 | .append( 121 | '" 122 | ) 123 | .append(button); 124 | } 125 | 126 | _modal.find("div.modal-body").empty().append(rendered); 127 | 128 | _modal 129 | .data("dtr-row-idx", row.index()) 130 | .one("hidden.bs.modal", closeCallback) 131 | .appendTo("body"); 132 | 133 | modal.show(); 134 | } else { 135 | if ( 136 | $.contains(document, _modal[0]) && 137 | row.index() === _modal.data("dtr-row-idx") 138 | ) { 139 | _modal.find("div.modal-body").empty().append(rendered); 140 | } else { 141 | // Modal not shown for this row - do nothing 142 | return null; 143 | } 144 | } 145 | 146 | return true; 147 | } 148 | }; 149 | }; 150 | 151 | return DataTable; 152 | }); 153 | -------------------------------------------------------------------------------- /templates/components/custom-data.tmpl: -------------------------------------------------------------------------------- 1 | <% if (suite.customData) { %> 2 |
    3 |

    <% if (suite.customData.title) { %><%= suite.customData.title %><% } else { %>Custom data title<% } %>

    4 | 11 |
    12 |
    13 |
    14 |
    15 | 16 | <% if (suite.customData.data) { %> 17 | <% suite.customData.data.forEach((row)=>{ %> 18 | 19 | 20 | 21 | 22 | <% }); %> 23 | <% } else { %> 24 | 25 | 26 | 27 | 28 | <% } %> 29 |
    30 |
    31 |
    32 | <% } %> 33 | -------------------------------------------------------------------------------- /templates/components/feature-custom-metadata-overview.tmpl: -------------------------------------------------------------------------------- 1 |
    2 |

    Metadata

    3 | 10 |
    11 |
    12 |
    13 |
    14 |
      15 | <% _.each(metadata, function(metadatum, metadatumIndex) { %> 16 |
    • 17 | 18 | 19 |
    • 20 | <% }); %> 21 |
    22 |
    23 |
    24 | -------------------------------------------------------------------------------- /templates/components/feature-metadata-overview.tmpl: -------------------------------------------------------------------------------- 1 |
    2 |

    Metadata

    3 | 10 |
    11 |
    12 |
    13 |
    14 |
      15 |
    • 16 | 20 | 35 |
    • 36 |
    • 37 | 38 | 39 |
    • 40 |
    • 41 | 42 | 60 |
    • 61 | <% if (metadata.app) { %> 62 |
    • 63 | 64 | 67 |
    • 68 | <% } %> 69 | <% if (metadata.browser) { %> 70 |
    • 71 | 72 | 96 |
    • 97 | <% } %> 98 |
    99 |
    100 |
    101 | -------------------------------------------------------------------------------- /templates/components/features-overview-custom-metadata.tmpl: -------------------------------------------------------------------------------- 1 |
    2 |
    3 |
    4 |

    Features overview

    5 | 12 |
    13 |
    14 | 15 |
    16 |
    17 | 19 | 20 | 21 | 22 | 23 | 24 | <% _.each(suite.features[0].metadata, function(metadatum, metadatumIndex) { %> 25 | 26 | <%});%> 27 | <% if (suite.displayDuration) { %> 28 | 29 | <% } %> 30 | 31 | 32 | 33 | <% if(+suite.scenarios.skipped > 0) { %> 34 | 35 | <% } %> 36 | <% if(+suite.scenarios.pending > 0) { %> 37 | 38 | <% } %> 39 | <% if(+suite.scenarios.notDefined > 0) { %> 40 | 41 | <% } %> 42 | <% if(+suite.scenarios.ambiguous > 0) { %> 43 | 44 | <% } %> 45 | 46 | 47 | 48 | 49 | <% _.each(suite.features, function(feature, featureIndex) { %> 50 | 51 | 54 | 66 | 92 | <% _.each(feature.metadata, function(metadatum, metadatumIndex) { %> 93 | 96 | <% }); %> 97 | <% if (suite.displayDuration) { %> 98 | 99 | <% } %> 100 | 101 | 102 | 103 | <% if(+suite.scenarios.skipped > 0) { %> 104 | 105 | <% } %> 106 | <% if(+suite.scenarios.pending > 0) { %> 107 | 108 | <% } %> 109 | <% if(+suite.scenarios.notDefined > 0) { %> 110 | 111 | <% } %> 112 | <% if(+suite.scenarios.ambiguous > 0) { %> 113 | 114 | <% } %> 115 | 116 | <% }); %> 117 | 118 | 119 |
    Feature nameStatus<%= metadatum.name %>DurationTotalPassedFailedSkipPendingUndefinedAmbiguous
    52 | <%= feature.name %> 53 | 55 | <% if (feature.tags) { %> 56 | <% var amount = feature.tags.length; %> 57 | <% var tags = _.reduce(feature.tags, (tags, tag) => tags + tag.name + ' ', ''); %> 58 | <% if (amount > 0 ){ %> 59 | 61 | <%= tags.trim() %> 62 | 63 | <% } %> 64 | <% } %> 65 | 67 | <% var statusIcon; %> 68 | <% var status; %> 69 | <% if (feature.isFailed) { %> 70 | <% status = 'Failed'; %> 71 | <% statusIcon = 'exclamation-circle failed-color'; %> 72 | <% } else if (feature.isAmbiguous) { %> 73 | <% status = 'Ambiguous'; %> 74 | <% statusIcon = 'flash ambiguous-color'; %> 75 | <% } else if (feature.isNotdefined) { %> 76 | <% status = 'Not Defined'; %> 77 | <% statusIcon = 'question-circle not-defined-color'; %> 78 | <% } else if (feature.isPending) { %> 79 | <% status = 'Pending'; %> 80 | <% statusIcon = 'minus-circle pending-color'; %> 81 | <% } else if (feature.isSkipped) { %> 82 | <% status = 'Skipped'; %> 83 | <% statusIcon = 'arrow-circle-right skipped-color'; %> 84 | <% } else { %> 85 | <% status = 'Passed'; %> 86 | <% statusIcon = 'check-circle passed-color'; %> 87 | <% } %> 88 | 89 | <%= statusIcon %> 90 | 91 | 94 | <%=metadatum.value%> 95 | <%= feature.time %><%= feature.scenarios.total %><%= feature.scenarios.passed %><%= feature.scenarios.failed %><%= feature.scenarios.skipped %><%= feature.scenarios.pending %><%= feature.scenarios.notDefined %><%= feature.scenarios.ambiguous %>
    120 |
    121 |
    122 |
    123 |
    124 | -------------------------------------------------------------------------------- /templates/components/features-overview.chart.tmpl: -------------------------------------------------------------------------------- 1 |
    2 |

    Features

    3 | 10 |
    11 |
    12 |
    13 | 14 | 15 | 18 | 26 | 27 | 28 | 32 | 103 | 104 |
    16 |

    Chart

    17 |
    19 |
    20 |

    Status

    21 |
    22 |
    23 |

    Progress

    24 |
    25 |
    29 | 30 |
    <%= suite.featureCount.total%>
    31 |
    33 | 34 | 35 | 41 | 42 | 43 | 44 | 50 | 51 | 52 | <%if(suite.featureCount.ambiguous > 0){%> 53 | 54 | 61 | 62 | 63 | <%}%> 64 | <%if(suite.featureCount.notDefined > 0){%> 65 | 66 | 73 | 74 | 75 | <%}%> 76 | <%if(suite.featureCount.pending > 0){%> 77 | 78 | 85 | 86 | 87 | <%}%> 88 | <%if(suite.featureCount.skipped > 0){%> 89 | 90 | 97 | 98 | 99 | <%}%> 100 | 101 |
    36 |

    37 | 38 | Passed 39 |

    40 |
    <%= suite.featureCount.passedPercentage %> %
    45 |

    46 | 47 | Failed 48 |

    49 |
    <%= suite.featureCount.failedPercentage %> %
    55 |

    57 | 58 | Ambiguous 59 |

    60 |
    <%= suite.featureCount.ambiguousPercentage %> %
    67 |

    69 | 70 | Not Defined 71 |

    72 |
    <%= suite.featureCount.notDefinedPercentage %> %
    79 |

    81 | 82 | Pending 83 |

    84 |
    <%= suite.featureCount.pendingPercentage %> %
    91 |

    93 | 94 | Skipped 95 |

    96 |
    <%= suite.featureCount.skippedPercentage %> %
    102 |
    105 |
    106 | -------------------------------------------------------------------------------- /templates/components/scenarios-overview.chart.tmpl: -------------------------------------------------------------------------------- 1 |
    2 |

    Scenarios

    3 | 10 |
    11 |
    12 |
    13 | 14 | 15 | 18 | 26 | 27 | 28 | 32 | 99 | 100 | <% if (!overviewPage && suite.displayDuration) { %> 101 | 102 | 103 | 104 | 105 | <% } %> 106 |
    16 |

    Chart

    17 |
    19 |
    20 |

    Status

    21 |
    22 |
    23 |

    Progress

    24 |
    25 |
    29 | 30 |
    <%= scenarios.total%>
    31 |
    33 | 34 | 35 | 41 | 42 | 43 | 44 | 50 | 51 | <% if (scenarios.ambiguous > 0) { %> 52 | 53 | 60 | 61 | 62 | <% } %> 63 | <% if (scenarios.notDefined > 0) { %> 64 | 65 | 71 | 72 | 73 | <% } %> 74 | 75 | <% if (scenarios.pending > 0) { %> 76 | 77 | 83 | 84 | 85 | <% } %> 86 | <% if (scenarios.skipped > 0) { %> 87 | 88 | 94 | 95 | 96 | <% } %> 97 |
    36 |

    37 | 38 | Passed 39 |

    40 |
    <%= scenarios.passedPercentage %> %
    45 |

    46 | 47 | Failed 48 |

    49 |
    <%= scenarios.failedPercentage %> %
    54 |

    56 | 57 | Ambiguous 58 |

    59 |
    <%= scenarios.ambiguousPercentage %> %
    66 |

    67 | 68 | Not defined 69 |

    70 |
    <%= scenarios.notDefinedPercentage %> %
    78 |

    79 | 80 | Pending 81 |

    82 |
    <%= scenarios.pendingPercentage %> %
    89 |

    90 | 91 | Skipped 92 |

    93 |
    <%= scenarios.skippedPercentage %> %
    98 |
    Total duration:
    <%= feature.time %>
    107 |
    108 | -------------------------------------------------------------------------------- /templates/generic.js: -------------------------------------------------------------------------------- 1 | $(".x_title").on("click", function () { 2 | var $BOX_PANEL = $(this).closest(".x_panel"), 3 | $ICON = $(this).find(".collapse-link i"), 4 | $BOX_CONTENT = $BOX_PANEL.find(".x_content"); 5 | 6 | // fix for some div with hardcoded fix class 7 | if ($BOX_PANEL.attr("style")) { 8 | $BOX_CONTENT.slideToggle(200, function () { 9 | $BOX_PANEL.removeAttr("style"); 10 | }); 11 | } else { 12 | $BOX_CONTENT.slideToggle(200); 13 | $BOX_PANEL.css("height", "auto"); 14 | } 15 | 16 | $ICON.toggleClass("fa-chevron-up fa-chevron-down"); 17 | }); 18 | 19 | $("body").tooltip({ 20 | selector: '[data-bs-toggle="tooltip"]', 21 | }); 22 | 23 | hideResult = (resultId) => { 24 | $("span[class*=step]").closest("div.x_panel[style]").hide(); 25 | $("span[class*=" + resultId + "]") 26 | .closest("div.x_panel[style]") 27 | .show(); 28 | }; 29 | 30 | showAll = () => { 31 | $("span[class*=step]").closest("div.x_panel[style]").show(); 32 | }; 33 | 34 | $(document).ready(() => { 35 | const status = [ 36 | "passed", 37 | "failed", 38 | "pending", 39 | "skipped", 40 | "ambiguous", 41 | "not-defined", 42 | ]; 43 | status.forEach((value) => { 44 | var menuItem = $("span[class*=" + value + "-background]"); 45 | if (menuItem.length === 0) { 46 | $("#" + value) 47 | .parent() 48 | .addClass("disabled"); 49 | } 50 | }); 51 | }); 52 | -------------------------------------------------------------------------------- /test/custom.css: -------------------------------------------------------------------------------- 1 | body { 2 | background: rgb(58,58,58); 3 | } 4 | 5 | /* If your custom theme doesn't properly support dark mode, you can easily hide the toggle button */ 6 | .fa-toggle-off { 7 | display: none 8 | } 9 | 10 | /* This styling applies also to the doughnut charts */ 11 | .ambiguous-color { 12 | color: #AAAAAA !important; 13 | } 14 | 15 | .failed-color { 16 | color: #FF0000 !important; 17 | } 18 | 19 | .not-defined-color { 20 | color: #0000FF !important; 21 | } 22 | 23 | .passed-color { 24 | color: #00FF00 !important; 25 | } 26 | 27 | .pending-color { 28 | color: #FFAA33 !important; 29 | } 30 | 31 | .skipped-color { 32 | color: #770077 !important; 33 | } 34 | 35 | /* backgrounds */ 36 | .ambiguous-background { 37 | background: #AAAAAA !important; 38 | } 39 | 40 | .failed-background { 41 | background: #FF0000 !important; 42 | } 43 | 44 | .not-defined-background { 45 | background: #0000FF !important; 46 | } 47 | 48 | .passed-background { 49 | background: #00FF00 !important; 50 | } 51 | 52 | .pending-background { 53 | background: #FFAA33 !important; 54 | } 55 | 56 | .skipped-background { 57 | background: #770077 !important; 58 | } 59 | -------------------------------------------------------------------------------- /test/my.css: -------------------------------------------------------------------------------- 1 | /* mycss */ 2 | 3 | body { 4 | color: #358EEC; 5 | background: #A7A7A7; 6 | font-family: "Helvetica Neue", Roboto, Arial, "Droid Sans", sans-serif; 7 | font-size: 13px; 8 | font-weight: 400; 9 | line-height: 1.471; 10 | } 11 | 12 | .main_container { 13 | padding: 10px 20px 0; 14 | } 15 | 16 | i span { 17 | display: none; 18 | } 19 | 20 | /* Navigation */ 21 | nav.navbar { 22 | background: #EDEDED; 23 | border-bottom: 1px solid #D9DEE4; 24 | margin-bottom: 10px; 25 | } 26 | 27 | nav .navbar-brand { 28 | border-right: 1px solid #D9DEE4; 29 | color: #5A738E; 30 | } 31 | 32 | nav .navbar-text { 33 | font-size: 18px; 34 | height: 50px; 35 | margin-bottom: 0; 36 | margin-top: 0; 37 | padding: 15px 0; 38 | } 39 | 40 | /* Table */ 41 | table { 42 | width: 100%; 43 | } 44 | 45 | table.chart tr th:first-of-type { 46 | width: 33.333%; 47 | } 48 | 49 | .table > thead > tr > th { 50 | background: #F5F7FA; 51 | } 52 | 53 | table.tile h3, table.tile h4, table.tile span { 54 | font-weight: bold; 55 | vertical-align: middle !important; 56 | } 57 | 58 | table.tile th, table.tile td { 59 | text-align: center; 60 | } 61 | 62 | table.tile th { 63 | border-bottom: 1px solid #E6ECEE; 64 | } 65 | 66 | table.tile td { 67 | padding: 5px 0; 68 | } 69 | 70 | table.tile td ul { 71 | text-align: left; 72 | padding-left: 0; 73 | } 74 | 75 | table.tile td ul li { 76 | list-style: none; 77 | width: 100%; 78 | } 79 | 80 | table.tile td ul li a { 81 | width: 100%; 82 | } 83 | 84 | table.tile td ul li a big { 85 | right: 0; 86 | float: right; 87 | margin-right: 13px; 88 | } 89 | 90 | table.tile_info { 91 | width: 100%; 92 | } 93 | 94 | table.tile_info td { 95 | text-align: left; 96 | padding: 1px; 97 | font-size: 15px; 98 | } 99 | 100 | table.tile_info td p { 101 | white-space: nowrap; 102 | overflow: hidden; 103 | text-overflow: ellipsis; 104 | margin: 0; 105 | line-height: 28px; 106 | } 107 | 108 | table.tile_info td i { 109 | display: inline-block; 110 | margin-right: 8px; 111 | font-size: 17px; 112 | float: left; 113 | width: 18px; 114 | line-height: 28px; 115 | text-align: center; 116 | } 117 | 118 | table.tile_info td:first-child { 119 | width: 65%; 120 | } 121 | 122 | td span { 123 | line-height: 28px; 124 | } 125 | 126 | table.tile_info td.percentage { 127 | text-align: right; 128 | } 129 | 130 | /* chart in table */ 131 | table td.chart { 132 | display: inline-block; 133 | position: relative; 134 | } 135 | 136 | table td.chart #feature-chart, 137 | table td.chart #scenario-chart, 138 | table td.chart .total { 139 | height: 140px; 140 | margin: 15px 10px 10px 0; 141 | width: 140px; 142 | } 143 | 144 | table td.chart .total { 145 | display: inline-block; 146 | position: absolute; 147 | font-size: 2em; 148 | height: 50px; 149 | line-height: 50px; 150 | top: 45px; 151 | left: 45px; 152 | text-align: center; 153 | vertical-align: middle; 154 | width: 50px; 155 | } 156 | 157 | /* colors */ 158 | .ambiguous-color { 159 | color: #E74C3C !important; 160 | } 161 | 162 | .failed-color { 163 | color: #E74C3C !important; 164 | } 165 | 166 | .not-defined-color { 167 | color: #F39C12 !important; 168 | } 169 | 170 | .passed-color { 171 | color: #1ABB9C !important; 172 | } 173 | 174 | .pending-color { 175 | color: #FFD119 !important; 176 | } 177 | 178 | .skipped-color { 179 | color: #3498DB !important; 180 | } 181 | 182 | /* backgrounds */ 183 | .ambiguous-background { 184 | background: #b73122 !important; 185 | } 186 | 187 | .failed-background { 188 | background: #E74C3C !important; 189 | } 190 | 191 | .not-defined-background { 192 | background: #F39C12 !important; 193 | } 194 | 195 | .passed-background { 196 | background: #1ABB9C !important; 197 | } 198 | 199 | .pending-background { 200 | background: #FFD119 !important; 201 | } 202 | 203 | .skipped-background { 204 | background: #3498DB !important; 205 | } 206 | 207 | /* general */ 208 | .x_panel { 209 | position: relative; 210 | width: 100%; 211 | margin-bottom: 10px; 212 | padding: 10px 17px; 213 | display: inline-block; 214 | background: #fff; 215 | border: 1px solid #E6E9ED; 216 | -webkit-column-break-inside: avoid; 217 | -moz-column-break-inside: avoid; 218 | column-break-inside: avoid; 219 | opacity: 1; 220 | transition: all .2s ease; 221 | } 222 | 223 | .x_title { 224 | border-bottom: 2px solid #E6E9ED; 225 | padding: 1px 5px 6px; 226 | margin-bottom: 10px; 227 | } 228 | 229 | .x_title .filter { 230 | width: 40%; 231 | float: right; 232 | } 233 | 234 | .x_title h2 { 235 | margin: 5px 0 6px; 236 | float: left; 237 | font-size: 24px; 238 | font-weight: 400; 239 | display: block; 240 | text-overflow: ellipsis; 241 | overflow: hidden; 242 | white-space: nowrap; 243 | } 244 | 245 | .fixed_height_320 { 246 | height: 320px; 247 | } 248 | 249 | .x_title h2 small { 250 | margin-left: 10px; 251 | } 252 | 253 | .x_title span { 254 | color: #BDBDBD; 255 | } 256 | 257 | .x_content { 258 | padding: 0 5px 6px; 259 | position: relative; 260 | width: 100%; 261 | float: left; 262 | clear: both; 263 | margin-top: 5px; 264 | } 265 | 266 | .x_content h4 { 267 | font-size: 16px; 268 | font-weight: 500; 269 | } 270 | 271 | .panel_toolbox { 272 | float: right; 273 | margin: 5px 0 0; 274 | min-width: 70px; 275 | } 276 | 277 | .panel_toolbox > li { 278 | float: right; 279 | } 280 | 281 | .panel_toolbox > li > a { 282 | cursor: pointer; 283 | } 284 | 285 | .panel_toolbox > li > a { 286 | padding: 5px; 287 | color: #C5C7CB; 288 | font-size: 14px; 289 | } 290 | 291 | .panel_toolbox > li > a:hover { 292 | background: #F5F7FA; 293 | } 294 | 295 | .page-title { 296 | width: 100%; 297 | padding: 10px 0 30px 0; 298 | } 299 | 300 | .page-title { 301 | display: block; 302 | } 303 | 304 | .page-title h1 { 305 | margin: 9px 0; 306 | } 307 | 308 | .page-title .title_right { 309 | width: 55%; 310 | float: left; 311 | display: block; 312 | } 313 | 314 | .page-title .title_right .pull-right { 315 | margin: 10px 0; 316 | } 317 | 318 | .page-title p { 319 | margin-left: 15px; 320 | } 321 | 322 | .dashboard-widget-content { 323 | padding-top: 9px; 324 | } 325 | 326 | .dashboard-widget-content .sidebar-widget { 327 | width: 50%; 328 | display: inline-block; 329 | vertical-align: top; 330 | background: #fff; 331 | border: 1px solid #abd9ea; 332 | border-radius: 5px; 333 | text-align: center; 334 | float: right; 335 | padding: 2px; 336 | margin-top: 10px; 337 | } 338 | 339 | ul.quick-list { 340 | padding-left: 0; 341 | display: inline-block; 342 | } 343 | 344 | ul.quick-list li, 345 | table.quick-list tr { 346 | padding-left: 10px; 347 | list-style: none; 348 | margin: 0; 349 | padding-bottom: 6px; 350 | padding-top: 4px; 351 | white-space: nowrap; 352 | text-overflow: ellipsis; 353 | overflow: hidden; 354 | } 355 | 356 | ul.quick-list li .meta-data-title, 357 | table.quick-list td.meta-data-title { 358 | display: inline-block; 359 | min-width: 75px; 360 | font-weight: bold; 361 | } 362 | 363 | ul.quick-list li span, 364 | table.quick-list td { 365 | line-height: 28px; 366 | } 367 | 368 | ul.quick-list li, 369 | table.quick-list tr { 370 | border-bottom: 1px solid #efefef; 371 | padding: 0.5em 0; 372 | } 373 | 374 | ul.quick-list li:last-child, 375 | table.quick-list tr:last-child { 376 | border-bottom: none; 377 | } 378 | 379 | ul.quick-list li i { 380 | padding-right: 10px; 381 | color: #757679; 382 | } 383 | 384 | /* Features / Scenarios */ 385 | ul.panel_toolbox li .step { 386 | border-radius: 50%; 387 | color: #ffffff; 388 | display: block; 389 | font-size: 14px; 390 | height: 30px; 391 | margin-right: 5px; 392 | padding: 5px; 393 | text-align: center; 394 | width: 30px; 395 | } 396 | 397 | .scenario-step-container { 398 | margin-bottom: 10px; 399 | } 400 | 401 | .scenario-step-container .label { 402 | display: inline-block; 403 | text-align: center; 404 | width: 30px; 405 | } 406 | 407 | .scenario-step-container .text { 408 | display: inline; 409 | } 410 | 411 | .scenario-step-container .text .keyword.highlight { 412 | font-size: 1.2em; 413 | font-weight: 700; 414 | } 415 | 416 | .scenario-scroll-bar { 417 | overflow-x: scroll; 418 | } 419 | 420 | .scenario-step-collapse, 421 | .scenario-scroll-bar .arguments { 422 | margin-left: 30px; 423 | width: auto; 424 | } 425 | 426 | /* media */ 427 | @media (max-width: 1200px) { 428 | .x_title h2 { 429 | width: 70%; 430 | } 431 | } 432 | 433 | @media (max-width: 640px) { 434 | .x_title h2 { 435 | width: 100%; 436 | } 437 | } 438 | 439 | /* override */ 440 | table.dataTable.dtr-inline.collapsed > tbody > tr > td:first-child:before, 441 | table.dataTable.dtr-inline.collapsed > tbody > tr > th:first-child:before { 442 | background-color: #1ABB9C; 443 | } 444 | 445 | table.dataTable.dtr-inline.collapsed > tbody > tr.parent > td:first-child:before, table.dataTable.dtr-inline.collapsed > tbody > tr.parent > th:first-child:before { 446 | background-color: #E74C3C; 447 | } 448 | 449 | .created-by { 450 | padding: 50px 0; 451 | text-align: center; 452 | } 453 | -------------------------------------------------------------------------------- /test/test.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | const path = require('node:path'); 3 | 4 | const test = require('../lib/generate-report'); 5 | 6 | /** 7 | * Generate a report for browsers 8 | */ 9 | test.generate({ 10 | saveCollectedJSON: true, 11 | jsonDir: './test/unit/data/json/', 12 | reportPath: './.tmp/browsers/', 13 | reportName: 'You can adjust this report name', 14 | customMetadata: false, 15 | displayDuration: true, 16 | durationInMS: true, 17 | customData: { 18 | title: 'Run info', 19 | data: [ 20 | {label: 'Project', value: 'Custom project'}, 21 | {label: 'Release', value: '1.2.3'}, 22 | {label: 'Cycle', value: 'B11221.34321'}, 23 | {label: 'Execution Start Time', value: 'Nov 19th 2017, 02:31 PM EST'}, 24 | {label: 'Execution End Time', value: 'Nov 19th 2017, 02:56 PM EST'} 25 | ] 26 | } 27 | }); 28 | 29 | /** 30 | * Generate a report for browsers wit useCDN true 31 | */ 32 | test.generate({ 33 | saveCollectedJSON: true, 34 | jsonDir: './test/unit/data/json/', 35 | reportPath: './.tmp/browsers-with-cdn-usage/', 36 | reportName: 'Report with CDN usage', 37 | customMetadata: false, 38 | displayDuration: true, 39 | durationInMS: true, 40 | useCDN: true, 41 | customData: { 42 | title: 'Run info', 43 | data: [ 44 | {label: 'Project', value: 'Custom project'}, 45 | {label: 'Release', value: '1.2.3'}, 46 | {label: 'Cycle', value: 'B11221.34321'}, 47 | {label: 'Execution Start Time', value: 'Nov 19th 2017, 02:31 PM EST'}, 48 | {label: 'Execution End Time', value: 'Nov 19th 2017, 02:56 PM EST'} 49 | ] 50 | } 51 | }); 52 | 53 | /** 54 | * Generate a report with array of embedded data 55 | */ 56 | test.generate({ 57 | saveCollectedJSON: true, 58 | jsonDir: './test/unit/data/embedded-array-json/', 59 | reportPath: './.tmp/embedded-array/', 60 | customStyle: path.join(__dirname, './custom.css'), 61 | overrideStyle: path.join(__dirname, './my.css'), 62 | customMetadata: false, 63 | pageTitle: 'A custom page title', 64 | pageFooter: '

    Some custom footer data can be placed here

    ', 65 | plainDescription: true, 66 | customData: { 67 | title: 'Run info', 68 | data: [ 69 | {label: 'Project', value: 'Custom embedded project'}, 70 | {label: 'Release', value: '4.5.6'}, 71 | ] 72 | } 73 | }); 74 | 75 | /** 76 | * Generate a report for browsers with report time 77 | */ 78 | test.generate({ 79 | saveCollectedJSON: true, 80 | jsonDir: './test/unit/data/json/', 81 | reportPath: './.tmp/browsers-with-report-time/', 82 | reportName: 'You can adjust this report name', 83 | customMetadata: false, 84 | displayDuration: true, 85 | displayReportTime: true, 86 | hideMetadata: true, 87 | durationInMS: true, 88 | customData: { 89 | title: 'Run info', 90 | data: [ 91 | {label: 'Project', value: 'Custom project'}, 92 | {label: 'Release', value: '1.2.3'}, 93 | {label: 'Cycle', value: 'B11221.34321'}, 94 | {label: 'Execution Start Time', value: 'Nov 19th 2017, 02:31 PM EST'}, 95 | {label: 'Execution End Time', value: 'Nov 19th 2017, 02:56 PM EST'} 96 | ] 97 | } 98 | }); 99 | 100 | /** 101 | * Generate a report with custom metadata 102 | * NOTE: must be last, if you use customMetadata you cannot reuse generator 103 | */ 104 | test.generate({ 105 | saveCollectedJSON: true, 106 | jsonDir: './test/unit/data/custom-metadata-json/', 107 | reportPath: './.tmp/custom-metadata/', 108 | customMetadata: true, 109 | displayDuration: true, 110 | metadata: [ 111 | { "name": "Backend version", "value": "4.0 R11" }, 112 | { "name": "Client API version", "value": "17.10" }, 113 | { "name": "Test Configuration", "value": "Config A" }, 114 | { "name": "platform", "value": "Ubuntu" }, 115 | { "name": "platform version", "value": "16.04" } 116 | ] 117 | }); 118 | -------------------------------------------------------------------------------- /test/unit/collect-jsons.spec.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const jsonFile = require('jsonfile'); 4 | const path = require('path'); 5 | const collectJSONS = require('../../lib/collect-jsons'); 6 | const reportPath = path.resolve(process.cwd(), './.tmp/test'); 7 | 8 | describe('collect-jsons.js', () => { 9 | describe('Happy flows', () => { 10 | it('should return an output from the merged found json files', () => { 11 | expect(collectJSONS({ 12 | jsonDir: './test/unit/data/json', 13 | reportPath: reportPath 14 | })).toEqual(jsonFile.readFileSync(path.resolve(process.cwd(), './test/unit/data/output/merged-output.json'))); 15 | }); 16 | 17 | it('should return an output from the merged found json files and add the provided metadata', () => { 18 | expect(collectJSONS({ 19 | jsonDir: './test/unit/data/collect-json', 20 | reportPath: reportPath, 21 | metadata:{ 22 | browser: { 23 | name: 'chrome', 24 | version: '1' 25 | }, 26 | device: 'Local test machine', 27 | platform: { 28 | name: 'Ubuntu', 29 | version: '16.04' 30 | } 31 | } 32 | })).toEqual(jsonFile.readFileSync(path.resolve(process.cwd(), './test/unit/data/output/provided-metadata.json'))); 33 | }); 34 | 35 | it('should save an output from the merged found json files', () => { 36 | expect(collectJSONS({ 37 | jsonDir: './test/unit/data/json', 38 | reportPath: reportPath, 39 | saveCollectedJSON: true 40 | })).toEqual(jsonFile.readFileSync(path.resolve(process.cwd(), './test/unit/data/output/merged-output.json'))); 41 | }); 42 | 43 | it('should collect the creation date of json files', () => { 44 | // Given 45 | const options = { 46 | jsonDir: './test/unit/data/json', 47 | reportPath: reportPath, 48 | displayReportTime: true 49 | } 50 | 51 | // When 52 | const collectedJSONs = collectJSONS(options) 53 | 54 | // Then 55 | collectedJSONs.forEach(json => { 56 | expect(json.metadata.reportTime).toBeDefined(); 57 | expect(json.metadata.reportTime.length).toBe('YYYY/MM/DD HH:mm:ss'.length); 58 | }) 59 | }); 60 | }); 61 | 62 | describe('failures', () => { 63 | it('should throw an error when the json folder does not exist', () => { 64 | expect(() => collectJSONS({ 65 | jsonDir: './test/unit/data/bla', 66 | reportPath: reportPath 67 | })).toThrow(new Error(`There were issues reading JSON-files from './test/unit/data/bla'.`)); 68 | }); 69 | 70 | it('should print a console message when no json files could be found', () => { 71 | spyOn(console, 'log'); 72 | collectJSONS({ 73 | jsonDir: './test/unit/data/no-jsons', 74 | reportPath: reportPath 75 | }); 76 | expect(console.log).toHaveBeenCalledWith('\x1b[33m%s\x1b[0m', `WARNING: No JSON files found in './test/unit/data/no-jsons'. NO REPORT CAN BE CREATED!`) 77 | }); 78 | 79 | it('should return an empty array when no json files could be found', () => { 80 | const results = collectJSONS({ 81 | jsonDir: './test/unit/data/no-jsons', 82 | reportPath: reportPath 83 | }); 84 | expect(Array.isArray(results)).toBeTruthy(); 85 | expect(results.length).toBe(0); 86 | }); 87 | }); 88 | }); 89 | -------------------------------------------------------------------------------- /test/unit/data/collect-json/no-metadata.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "description": " No elements have been found\n So the report should hold no scenarios and steps", 4 | "id": "no_elements.1494777183356", 5 | "keyword": "Feature", 6 | "line": 1, 7 | "name": "No elements", 8 | "tags": [], 9 | "uri": "no_elements.1494777183356" 10 | } 11 | ] 12 | -------------------------------------------------------------------------------- /test/unit/data/custom-metadata-json/ambiguous_scenarios_specified_v1.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "description": " The scenarios have been scripted twice\n So the report should hold ambiguous steps", 4 | "elements": [ 5 | { 6 | "id": "ambiguous-scenarios-specified-v1;ambiguous-johnny-wants-to-see-ambiguous-scenarios", 7 | "keyword": "Scenario", 8 | "line": 5, 9 | "name": "Ambiguous Johnny wants to see ambiguous scenarios", 10 | "steps": [ 11 | { 12 | "arguments": [], 13 | "keyword": "Before ", 14 | "result": { 15 | "status": "passed", 16 | "duration": 1352242 17 | }, 18 | "hidden": true, 19 | "match": { 20 | "location": "/Users/wswebcreation/deTesters/protractor-cucumber-typescript-boilerplate/node_modules/cucumber-tsflow/src/BindingDecorator.ts:83" 21 | } 22 | }, 23 | { 24 | "arguments": [], 25 | "keyword": "Given ", 26 | "name": "Ambiguous Johnny visits the Angular homepage", 27 | "result": { 28 | "status": "passed", 29 | "duration": 1930520 30 | }, 31 | "line": 6, 32 | "match": { 33 | "location": "/Users/wswebcreation/deTesters/protractor-cucumber-typescript-boilerplate/node_modules/cucumber-tsflow/src/BindingDecorator.ts:157" 34 | } 35 | }, 36 | { 37 | "arguments": [], 38 | "keyword": "When ", 39 | "name": "he clicks on the second button", 40 | "result": { 41 | "status": "ambiguous" 42 | }, 43 | "line": 7 44 | }, 45 | { 46 | "arguments": [], 47 | "keyword": "Then ", 48 | "name": "he expects that something happens", 49 | "result": { 50 | "status": "skipped" 51 | }, 52 | "line": 8, 53 | "match": { 54 | "location": "/Users/wswebcreation/deTesters/protractor-cucumber-typescript-boilerplate/node_modules/cucumber-tsflow/src/BindingDecorator.ts:163" 55 | } 56 | }, 57 | { 58 | "arguments": [], 59 | "keyword": "After ", 60 | "result": { 61 | "status": "passed", 62 | "duration": 349939 63 | }, 64 | "hidden": true, 65 | "match": { 66 | "location": "/Users/wswebcreation/deTesters/protractor-cucumber-typescript-boilerplate/node_modules/cucumber-tsflow/src/BindingDecorator.ts:201" 67 | } 68 | }, 69 | { 70 | "arguments": [], 71 | "keyword": "After ", 72 | "result": { 73 | "status": "passed", 74 | "duration": 263324 75 | }, 76 | "hidden": true, 77 | "match": { 78 | "location": "/Users/wswebcreation/deTesters/protractor-cucumber-typescript-boilerplate/node_modules/cucumber-tsflow/src/BindingDecorator.ts:87" 79 | } 80 | } 81 | ], 82 | "tags": [], 83 | "type": "scenario" 84 | } 85 | ], 86 | "id": "ambiguous-scenarios-specified-v1", 87 | "keyword": "Feature", 88 | "line": 1, 89 | "name": "Ambiguous scenarios specified V1", 90 | "tags": [], 91 | "uri": "/Users/wswebcreation/deTesters/protractor-cucumber-typescript-boilerplate/e2e-tests/features/ambiguous.feature", 92 | "metadata": [ 93 | { "name": "Backend version", "value": "4.0 R11" }, 94 | { "name": "Client API version", "value": "17.10" }, 95 | { "name": "Test Configuration", "value": "Config A" }, 96 | { "name": "platform", "value": "Ubuntu" }, 97 | { "name": "platform version", "value": "16.04" } 98 | ] 99 | } 100 | ] 101 | -------------------------------------------------------------------------------- /test/unit/data/custom-metadata-json/ambiguous_scenarios_specified_v2.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "metadata": [ 4 | { "name": "Backend version", "value": "5.1" }, 5 | { "name": "Client API version", "value": "17.10 GA" }, 6 | { "name": "Test Configuration", "value": "Config B" }, 7 | { "name": "platform", "value": "Ubuntu" }, 8 | { "name": "platform version", "value": "16.04" } 9 | ], 10 | "description": " The scenarios have been scripted twice\n So the report should hold ambiguous steps", 11 | "keyword": "Feature", 12 | "line": 1, 13 | "name": "Ambiguous scenarios specified V2", 14 | "tags": [], 15 | "uri": "/Users/wswebcreation/deTesters/protractor-cucumber-typescript-boilerplate/e2e-tests/features/ambiguous.feature", 16 | "elements": [ 17 | { 18 | "keyword": "Scenario", 19 | "line": 5, 20 | "name": "Ambiguous Johnny wants to see ambiguous scenarios", 21 | "tags": [], 22 | "id": "ambiguous-scenarios-specified-v2;ambiguous-johnny-wants-to-see-ambiguous-scenarios", 23 | "steps": [ 24 | { 25 | "arguments": [], 26 | "keyword": "Given ", 27 | "name": "Ambiguous Johnny visits the Angular homepage", 28 | "result": { 29 | "status": "passed", 30 | "duration": 2 31 | }, 32 | "line": 6, 33 | "match": { 34 | "location": "/Users/wswebcreation/deTesters/protractor-cucumber-typescript-boilerplate/e2e-tests/features/ambiguous.steps.ts:4" 35 | } 36 | }, 37 | { 38 | "arguments": [], 39 | "keyword": "When ", 40 | "name": "he clicks on the second button", 41 | "result": { 42 | "status": "ambiguous" 43 | }, 44 | "line": 7 45 | }, 46 | { 47 | "arguments": [], 48 | "keyword": "Then ", 49 | "name": "he expects that something really ambiguous happens", 50 | "result": { 51 | "status": "ambiguous" 52 | }, 53 | "line": 8 54 | }, 55 | { 56 | "arguments": [], 57 | "keyword": "After", 58 | "result": { 59 | "status": "passed", 60 | "duration": 8 61 | }, 62 | "hidden": true, 63 | "match": { 64 | "location": "/Users/wswebcreation/deTesters/protractor-cucumber-typescript-boilerplate/e2e-tests/config/helpers/after.scenario.ts:11" 65 | } 66 | } 67 | ] 68 | } 69 | ], 70 | "id": "ambiguous-scenarios-specified-v2" 71 | } 72 | ] 73 | -------------------------------------------------------------------------------- /test/unit/data/custom-metadata-json/multiple_different_attachements.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "keyword": "Feature", 4 | "name": "Multiple json and plain text attachments", 5 | "line": 1, 6 | "id": "google.com", 7 | "tags": [], 8 | "uri": "features/google.feature", 9 | "elements": [ 10 | { 11 | "id": "google.com;i-open-google", 12 | "keyword": "Scenario", 13 | "line": 2, 14 | "name": "I open google", 15 | "tags": [], 16 | "type": "scenario", 17 | "steps": [ 18 | { 19 | "keyword": "Before", 20 | "hidden": true, 21 | "match": { 22 | "location": "src/steps.js:59" 23 | }, 24 | "result": { 25 | "status": "passed", 26 | "duration": 616 27 | } 28 | }, 29 | { 30 | "arguments": [], 31 | "keyword": "Given ", 32 | "line": 3, 33 | "name": "I open url 'https://www.google.com/?hl=en'", 34 | "match": { 35 | "location": "src/steps.js:322" 36 | }, 37 | "result": { 38 | "status": "passed", 39 | "duration": 1193 40 | } 41 | }, 42 | { 43 | "arguments": [], 44 | "keyword": "Then ", 45 | "line": 4, 46 | "name": "I see input with value 'Google Search'", 47 | "match": { 48 | "location": "src/steps.js:418" 49 | }, 50 | "result": { 51 | "status": "passed", 52 | "duration": 1214 53 | }, 54 | "embeddings": [ 55 | { 56 | "data": "eyJzb21lIjoianNvbiJ9", 57 | "media": { 58 | "type": "application/json" 59 | } 60 | }, 61 | { 62 | "data": "eyJvdGhlciI6Impzb24ifQ==", 63 | "media": { 64 | "type": "application/json" 65 | } 66 | }, 67 | { 68 | "data": "Some plain text message", 69 | "media": { 70 | "type": "text/plain" 71 | } 72 | }, 73 | { 74 | "data": "eyJ5ZXQgYW5vdGhlciBqc29uIjo1fQ==", 75 | "media": { 76 | "type": "application/json" 77 | } 78 | }, 79 | { 80 | "data": "MA==", 81 | "media": { 82 | "type": "application/json" 83 | } 84 | }, 85 | { 86 | "data": 1, 87 | "media": { 88 | "type": "text/plain" 89 | } 90 | } 91 | ] 92 | }, 93 | { 94 | "keyword": "After", 95 | "hidden": true, 96 | "match": { 97 | "location": "src/steps.js:629" 98 | }, 99 | "result": { 100 | "status": "passed", 101 | "duration": 295 102 | } 103 | } 104 | ] 105 | } 106 | ] 107 | } 108 | ] 109 | -------------------------------------------------------------------------------- /test/unit/data/custom-metadata-json/pending_scenarios_specified_v1.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "description": " The scenarios have scripted pending steps\n So the report should hold pending steps", 4 | "elements": [ 5 | { 6 | "id": "pending-scenarios-specified-v1;johnny-wants-to-see-pending-scenarios", 7 | "keyword": "Scenario", 8 | "line": 5, 9 | "name": "Johnny wants to see pending scenarios", 10 | "steps": [ 11 | { 12 | "arguments": [], 13 | "keyword": "Before ", 14 | "result": { 15 | "status": "passed", 16 | "duration": 6703453 17 | }, 18 | "hidden": true, 19 | "match": { 20 | "location": "/Users/wswebcreation/deTesters/protractor-cucumber-typescript-boilerplate/node_modules/cucumber-tsflow/src/BindingDecorator.ts:83" 21 | } 22 | }, 23 | { 24 | "arguments": [], 25 | "keyword": "Given ", 26 | "name": "Johnny visits the Angular homepage", 27 | "result": { 28 | "status": "pending" 29 | }, 30 | "line": 6, 31 | "match": { 32 | "location": "/Users/wswebcreation/deTesters/protractor-cucumber-typescript-boilerplate/node_modules/cucumber-tsflow/src/BindingDecorator.ts:157" 33 | } 34 | }, 35 | { 36 | "arguments": [], 37 | "keyword": "When ", 38 | "name": "he clicks on the first link", 39 | "result": { 40 | "status": "skipped" 41 | }, 42 | "line": 7, 43 | "match": { 44 | "location": "/Users/wswebcreation/deTesters/protractor-cucumber-typescript-boilerplate/node_modules/cucumber-tsflow/src/BindingDecorator.ts:160" 45 | } 46 | }, 47 | { 48 | "arguments": [], 49 | "keyword": "Then ", 50 | "name": "he expects that something happens", 51 | "result": { 52 | "status": "skipped" 53 | }, 54 | "line": 8, 55 | "match": { 56 | "location": "/Users/wswebcreation/deTesters/protractor-cucumber-typescript-boilerplate/node_modules/cucumber-tsflow/src/BindingDecorator.ts:163" 57 | } 58 | }, 59 | { 60 | "arguments": [], 61 | "keyword": "After ", 62 | "result": { 63 | "status": "passed", 64 | "duration": 479227 65 | }, 66 | "hidden": true, 67 | "match": { 68 | "location": "/Users/wswebcreation/deTesters/protractor-cucumber-typescript-boilerplate/node_modules/cucumber-tsflow/src/BindingDecorator.ts:201" 69 | } 70 | }, 71 | { 72 | "arguments": [], 73 | "keyword": "After ", 74 | "result": { 75 | "status": "passed", 76 | "duration": 305690 77 | }, 78 | "hidden": true, 79 | "match": { 80 | "location": "/Users/wswebcreation/deTesters/protractor-cucumber-typescript-boilerplate/node_modules/cucumber-tsflow/src/BindingDecorator.ts:87" 81 | } 82 | } 83 | ], 84 | "tags": [], 85 | "type": "scenario" 86 | } 87 | ], 88 | "id": "pending-scenarios-specified-v1", 89 | "keyword": "Feature", 90 | "line": 1, 91 | "name": "Pending scenarios specified V1", 92 | "tags": [], 93 | "uri": "/Users/wswebcreation/deTesters/protractor-cucumber-typescript-boilerplate/e2e-tests/features/pending.feature", 94 | "metadata": [ 95 | { "name": "Backend version", "value": "5.2.1" }, 96 | { "name": "Client API version", "value": "17.10" }, 97 | { "name": "Test Configuration", "value": "Config C" }, 98 | { "name": "platform", "value": "Android" }, 99 | { "name": "platform version", "value": "7.1" } 100 | ] 101 | } 102 | ] 103 | -------------------------------------------------------------------------------- /test/unit/data/custom-metadata-json/undefined_scenarios_specified_v3.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "metadata": [ 4 | { "name": "Backend version", "value": "5.1.1" }, 5 | { "name": "Client API version", "value": "17.5" }, 6 | { "name": "Test Configuration", "value": "Config D" }, 7 | { "name": "platform", "value": "iOS" }, 8 | { "name": "platform version", "value": "10.1.2" } 9 | ], 10 | "description": " No scenarios have scripted steps\n So the report should hold undefined steps", 11 | "keyword": "Feature", 12 | "name": "Undefined scenarios specified V3", 13 | "line": 2, 14 | "id": "undefined-scenarios-specified-v3", 15 | "tags": [ 16 | { 17 | "name": "@undefined", 18 | "line": 1 19 | } 20 | ], 21 | "uri": "e2e-tests/features/undefined.feature", 22 | "elements": [ 23 | { 24 | "id": "undefined-scenarios-specified-v3;timmy-wants-to-see-undefined-scenarios", 25 | "keyword": "Scenario", 26 | "line": 7, 27 | "name": "Timmy wants to see undefined scenarios", 28 | "tags": [ 29 | { 30 | "name": "@undefined", 31 | "line": 1 32 | }, 33 | { 34 | "name": "@timmy", 35 | "line": 6 36 | }, 37 | { 38 | "name": "@angular", 39 | "line": 6 40 | }, 41 | { 42 | "name": "@nothing", 43 | "line": 6 44 | } 45 | ], 46 | "type": "scenario", 47 | "steps": [ 48 | { 49 | "arguments": [], 50 | "keyword": "Given ", 51 | "line": 8, 52 | "name": "Timmy visits the Angular homepage", 53 | "result": { 54 | "status": "undefined" 55 | } 56 | }, 57 | { 58 | "arguments": [], 59 | "keyword": "When ", 60 | "line": 9, 61 | "name": "he doesn't do anything", 62 | "result": { 63 | "status": "undefined" 64 | } 65 | }, 66 | { 67 | "arguments": [], 68 | "keyword": "Then ", 69 | "line": 10, 70 | "name": "nothing has been done", 71 | "result": { 72 | "status": "undefined" 73 | } 74 | }, 75 | { 76 | "keyword": "After", 77 | "hidden": true, 78 | "match": { 79 | "location": "e2e-tests/config/helpers/after.scenario.ts:23" 80 | }, 81 | "result": { 82 | "status": "passed", 83 | "duration": 1 84 | } 85 | } 86 | ] 87 | }, 88 | { 89 | "id": "google.com;i-open-google", 90 | "keyword": "Scenario", 91 | "line": 2, 92 | "name": "I open google", 93 | "tags": [], 94 | "type": "scenario", 95 | "steps": [ 96 | { 97 | "keyword": "Before", 98 | "hidden": true, 99 | "match": { 100 | "location": "src/steps.js:59" 101 | }, 102 | "result": { 103 | "status": "passed", 104 | "duration": 616 105 | } 106 | }, 107 | { 108 | "arguments": [], 109 | "keyword": "Given ", 110 | "line": 3, 111 | "name": "I open url 'https://www.google.com/?hl=en'", 112 | "match": { 113 | "location": "src/steps.js:322" 114 | }, 115 | "result": { 116 | "status": "passed", 117 | "duration": 1193 118 | } 119 | }, 120 | { 121 | "arguments": [], 122 | "keyword": "Then ", 123 | "line": 4, 124 | "name": "I see input with value 'Google Search'", 125 | "match": { 126 | "location": "src/steps.js:418" 127 | }, 128 | "result": { 129 | "status": "passed", 130 | "duration": 1214 131 | }, 132 | "embeddings": [ 133 | { 134 | "data": "eyJzb21lIjoianNvbiJ9", 135 | "media": { 136 | "type": "application/json" 137 | } 138 | }, 139 | { 140 | "data": "eyJvdGhlciI6Impzb24ifQ==", 141 | "media": { 142 | "type": "application/json" 143 | } 144 | }, 145 | { 146 | "data": "Some plain text message", 147 | "media": { 148 | "type": "text/plain" 149 | } 150 | }, 151 | { 152 | "data": "eyJ5ZXQgYW5vdGhlciBqc29uIjo1fQ==", 153 | "media": { 154 | "type": "application/json" 155 | } 156 | } 157 | ] 158 | }, 159 | { 160 | "keyword": "After", 161 | "hidden": true, 162 | "match": { 163 | "location": "src/steps.js:629" 164 | }, 165 | "result": { 166 | "status": "passed", 167 | "duration": 295 168 | } 169 | } 170 | ] 171 | } 172 | ] 173 | } 174 | ] 175 | -------------------------------------------------------------------------------- /test/unit/data/embedded-array-json/happy_flow_v3.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "metadata": { 4 | "browser": { 5 | "name": "microsoftedge", 6 | "version": "14" 7 | }, 8 | "device": "Virtual Machine", 9 | "platform": { 10 | "name": "Windows", 11 | "version": "10" 12 | } 13 | }, 14 | "keyword": "Feature", 15 | "name": "Happy flow V3", 16 | "line": 2, 17 | "id": "happy-flow-v3", 18 | "tags": [ 19 | { 20 | "name": "@example", 21 | "line": 1 22 | }, 23 | { 24 | "name": "@happy-flow", 25 | "line": 1 26 | }, 27 | { 28 | "name": "@angular", 29 | "line": 1 30 | } 31 | ], 32 | "uri": "e2e-tests/features/example.feature", 33 | "elements": [ 34 | { 35 | "id": "happy-flow-v3;as-a-visitor-i-want-to-be-greeted", 36 | "keyword": "Scenario", 37 | "line": 8, 38 | "name": "As a visitor I want to be greeted", 39 | "tags": [ 40 | { 41 | "name": "@example", 42 | "line": 1 43 | }, 44 | { 45 | "name": "@happy-flow", 46 | "line": 1 47 | }, 48 | { 49 | "name": "@angular", 50 | "line": 1 51 | }, 52 | { 53 | "name": "@visitor", 54 | "line": 7 55 | }, 56 | { 57 | "name": "@julie", 58 | "line": 7 59 | } 60 | ], 61 | "type": "scenario", 62 | "steps": [ 63 | { 64 | "arguments": [], 65 | "keyword": "Given ", 66 | "line": 5, 67 | "name": "I visit the Angular homepage", 68 | "match": { 69 | "location": "e2e-tests/features/example.steps.ts:6" 70 | }, 71 | "result": { 72 | "status": "passed", 73 | "duration": 2230 74 | } 75 | }, 76 | { 77 | "arguments": [], 78 | "keyword": "Given ", 79 | "line": 9, 80 | "name": "I submit the name \"Julie\"", 81 | "match": { 82 | "location": "e2e-tests/features/example.steps.ts:11" 83 | }, 84 | "result": { 85 | "status": "passed", 86 | "duration": 1070 87 | } 88 | }, 89 | { 90 | "arguments": [], 91 | "keyword": "Then ", 92 | "line": 10, 93 | "name": "I am greeted as \"Hello Julie!\"", 94 | "match": { 95 | "location": "e2e-tests/features/example.steps.ts:32" 96 | }, 97 | "result": { 98 | "status": "passed", 99 | "duration": 58 100 | } 101 | }, 102 | { 103 | "keyword": "After", 104 | "hidden": true, 105 | "match": { 106 | "location": "e2e-tests/config/helpers/after.scenario.ts:23" 107 | }, 108 | "result": { 109 | "status": "passed", 110 | "duration": 1 111 | } 112 | } 113 | ] 114 | }, 115 | { 116 | "id": "happy-flow-v3;validate-todo-list", 117 | "keyword": "Scenario", 118 | "line": 13, 119 | "name": "Validate todo list", 120 | "tags": [ 121 | { 122 | "name": "@example", 123 | "line": 1 124 | }, 125 | { 126 | "name": "@happy-flow", 127 | "line": 1 128 | }, 129 | { 130 | "name": "@angular", 131 | "line": 1 132 | }, 133 | { 134 | "name": "@todo", 135 | "line": 12 136 | }, 137 | { 138 | "name": "@count", 139 | "line": 12 140 | }, 141 | { 142 | "name": "@validate", 143 | "line": 12 144 | } 145 | ], 146 | "type": "scenario", 147 | "steps": [ 148 | { 149 | "arguments": [], 150 | "keyword": "Given ", 151 | "line": 5, 152 | "name": "I visit the Angular homepage", 153 | "match": { 154 | "location": "e2e-tests/features/example.steps.ts:6" 155 | }, 156 | "result": { 157 | "status": "passed", 158 | "duration": 663 159 | } 160 | }, 161 | { 162 | "arguments": [], 163 | "keyword": "Given ", 164 | "line": 14, 165 | "name": "I count 2 todo's", 166 | "match": { 167 | "location": "e2e-tests/features/example.steps.ts:16" 168 | }, 169 | "result": { 170 | "status": "passed", 171 | "duration": 824 172 | } 173 | }, 174 | { 175 | "arguments": [], 176 | "keyword": "Then ", 177 | "line": 15, 178 | "name": "the last todo should hold \"build an AngularJS app\"", 179 | "match": { 180 | "location": "e2e-tests/features/example.steps.ts:40" 181 | }, 182 | "result": { 183 | "status": "passed", 184 | "duration": 52 185 | } 186 | }, 187 | { 188 | "keyword": "After", 189 | "hidden": true, 190 | "match": { 191 | "location": "e2e-tests/config/helpers/after.scenario.ts:23" 192 | }, 193 | "result": { 194 | "status": "passed" 195 | } 196 | } 197 | ] 198 | }, 199 | { 200 | "id": "happy-flow-v3;add-a-todo", 201 | "keyword": "Scenario", 202 | "line": 18, 203 | "name": "Add a todo", 204 | "tags": [ 205 | { 206 | "name": "@example", 207 | "line": 1 208 | }, 209 | { 210 | "name": "@happy-flow", 211 | "line": 1 212 | }, 213 | { 214 | "name": "@angular", 215 | "line": 1 216 | }, 217 | { 218 | "name": "@todo", 219 | "line": 17 220 | }, 221 | { 222 | "name": "@count", 223 | "line": 17 224 | }, 225 | { 226 | "name": "@add", 227 | "line": 17 228 | } 229 | ], 230 | "type": "scenario", 231 | "steps": [ 232 | { 233 | "arguments": [], 234 | "keyword": "Given ", 235 | "line": 5, 236 | "name": "I visit the Angular homepage", 237 | "match": { 238 | "location": "e2e-tests/features/example.steps.ts:6" 239 | }, 240 | "result": { 241 | "status": "passed", 242 | "duration": 502 243 | } 244 | }, 245 | { 246 | "arguments": [], 247 | "keyword": "Given ", 248 | "line": 19, 249 | "name": "I add the todo \"write a protractor test\"", 250 | "match": { 251 | "location": "e2e-tests/features/example.steps.ts:23" 252 | }, 253 | "result": { 254 | "status": "passed", 255 | "duration": 1029 256 | } 257 | }, 258 | { 259 | "arguments": [], 260 | "keyword": "Then ", 261 | "line": 20, 262 | "name": "I should have 3 todo's", 263 | "match": { 264 | "location": "e2e-tests/features/example.steps.ts:47" 265 | }, 266 | "result": { 267 | "status": "passed", 268 | "duration": 25 269 | } 270 | }, 271 | { 272 | "arguments": [], 273 | "keyword": "And ", 274 | "line": 21, 275 | "name": "the last todo should hold \"write a protractor test\"", 276 | "match": { 277 | "location": "e2e-tests/features/example.steps.ts:40" 278 | }, 279 | "result": { 280 | "status": "passed", 281 | "duration": 49 282 | } 283 | }, 284 | { 285 | "keyword": "After", 286 | "hidden": true, 287 | "match": { 288 | "location": "e2e-tests/config/helpers/after.scenario.ts:23" 289 | }, 290 | "result": { 291 | "status": "passed", 292 | "duration": 1 293 | } 294 | } 295 | ] 296 | } 297 | ] 298 | } 299 | ] 300 | -------------------------------------------------------------------------------- /test/unit/data/json/after.hook.error.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "keyword": "Feature", 4 | "name": "After Error", 5 | "line": 1, 6 | "id": "after-error", 7 | "tags": [], 8 | "uri": "src\\test.feature", 9 | "elements": [ 10 | { 11 | "id": "after-error;login", 12 | "keyword": "Scenario", 13 | "line": 3, 14 | "name": "Login", 15 | "tags": [ 16 | { 17 | "name": "@Test", 18 | "line": 2 19 | } 20 | ], 21 | "type": "scenario", 22 | "steps": [ 23 | { 24 | "arguments": [], 25 | "keyword": "Given ", 26 | "line": 4, 27 | "name": "i am on agendaodonto login page", 28 | "match": { 29 | "location": "src\\demo.steps.ts:26" 30 | }, 31 | "result": { 32 | "status": "passed", 33 | "duration": 2836000 34 | } 35 | }, 36 | { 37 | "arguments": [], 38 | "keyword": "When ", 39 | "line": 5, 40 | "name": "I enter with valid credentials", 41 | "match": { 42 | "location": "src\\demo.steps.ts:30" 43 | }, 44 | "result": { 45 | "status": "passed", 46 | "duration": 575000 47 | } 48 | }, 49 | { 50 | "arguments": [], 51 | "keyword": "Then ", 52 | "line": 6, 53 | "name": "I see the homepage", 54 | "match": { 55 | "location": "src\\demo.steps.ts:34" 56 | }, 57 | "result": { 58 | "status": "passed", 59 | "duration": 1000000 60 | } 61 | }, 62 | { 63 | "keyword": "After", 64 | "hidden": true, 65 | "match": { 66 | "location": "src\\demo.steps.ts:22" 67 | }, 68 | "result": { 69 | "status": "failed", 70 | "duration": 1000000, 71 | "error_message": "Error: This is a error created by miself\n at World. (E:\\Projetos\\protractor-test\\src\\demo.steps.ts:23:11)" 72 | } 73 | }, 74 | { 75 | "keyword": "After", 76 | "hidden": true, 77 | "match": { 78 | "location": "node_modules\\lib\\resultsCapturer.js:26" 79 | }, 80 | "result": { 81 | "status": "passed", 82 | "duration": 1000000 83 | } 84 | } 85 | ] 86 | } 87 | ] 88 | } 89 | ] 90 | -------------------------------------------------------------------------------- /test/unit/data/json/ambiguous_scenarios_specified_v1.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "description": " The scenarios have been scripted twice\n So the report should hold ambiguous steps", 4 | "elements": [ 5 | { 6 | "id": "ambiguous-scenarios-specified-v1;ambiguous-johnny-wants-to-see-ambiguous-scenarios", 7 | "keyword": "Scenario", 8 | "line": 5, 9 | "name": "Ambiguous Johnny wants to see ambiguous scenarios", 10 | "steps": [ 11 | { 12 | "arguments": [], 13 | "keyword": "Before ", 14 | "result": { 15 | "status": "passed", 16 | "duration": 1352242 17 | }, 18 | "hidden": true, 19 | "match": { 20 | "location": "/Users/wswebcreation/deTesters/protractor-cucumber-typescript-boilerplate/node_modules/cucumber-tsflow/src/BindingDecorator.ts:83" 21 | } 22 | }, 23 | { 24 | "arguments": [], 25 | "keyword": "Given ", 26 | "name": "Ambiguous Johnny visits the Angular homepage", 27 | "result": { 28 | "status": "passed", 29 | "duration": 1930520 30 | }, 31 | "line": 6, 32 | "match": { 33 | "location": "/Users/wswebcreation/deTesters/protractor-cucumber-typescript-boilerplate/node_modules/cucumber-tsflow/src/BindingDecorator.ts:157" 34 | } 35 | }, 36 | { 37 | "arguments": [], 38 | "keyword": "When ", 39 | "name": "he clicks on the second button", 40 | "result": { 41 | "status": "ambiguous" 42 | }, 43 | "line": 7 44 | }, 45 | { 46 | "arguments": [], 47 | "keyword": "Then ", 48 | "name": "he expects that something happens", 49 | "result": { 50 | "status": "skipped" 51 | }, 52 | "line": 8, 53 | "match": { 54 | "location": "/Users/wswebcreation/deTesters/protractor-cucumber-typescript-boilerplate/node_modules/cucumber-tsflow/src/BindingDecorator.ts:163" 55 | } 56 | }, 57 | { 58 | "arguments": [], 59 | "keyword": "After ", 60 | "result": { 61 | "status": "passed", 62 | "duration": 349939 63 | }, 64 | "hidden": true, 65 | "match": { 66 | "location": "/Users/wswebcreation/deTesters/protractor-cucumber-typescript-boilerplate/node_modules/cucumber-tsflow/src/BindingDecorator.ts:201" 67 | } 68 | }, 69 | { 70 | "arguments": [], 71 | "keyword": "After ", 72 | "result": { 73 | "status": "passed", 74 | "duration": 263324 75 | }, 76 | "hidden": true, 77 | "match": { 78 | "location": "/Users/wswebcreation/deTesters/protractor-cucumber-typescript-boilerplate/node_modules/cucumber-tsflow/src/BindingDecorator.ts:87" 79 | } 80 | } 81 | ], 82 | "tags": [], 83 | "type": "scenario" 84 | } 85 | ], 86 | "id": "ambiguous-scenarios-specified-v1", 87 | "keyword": "Feature", 88 | "line": 1, 89 | "name": "Ambiguous scenarios specified V1", 90 | "tags": [], 91 | "uri": "/Users/wswebcreation/deTesters/protractor-cucumber-typescript-boilerplate/e2e-tests/features/ambiguous.feature", 92 | "metadata": { 93 | "browser": { 94 | "name": "Firefox", 95 | "version": "53" 96 | }, 97 | "device": "Virtual Machine", 98 | "platform": { 99 | "name": "Ubuntu", 100 | "version": "16.04" 101 | } 102 | } 103 | } 104 | ] 105 | -------------------------------------------------------------------------------- /test/unit/data/json/ambiguous_scenarios_specified_v2.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "metadata": { 4 | "browser": { 5 | "name": "Firefox", 6 | "version": "53" 7 | }, 8 | "device": "Virtual Machine", 9 | "platform": { 10 | "name": "Ubuntu", 11 | "version": "16.04" 12 | } 13 | }, 14 | "description": " The scenarios have been scripted twice\n So the report should hold ambiguous steps", 15 | "keyword": "Feature", 16 | "line": 1, 17 | "name": "Ambiguous scenarios specified V2", 18 | "tags": [], 19 | "uri": "/Users/wswebcreation/deTesters/protractor-cucumber-typescript-boilerplate/e2e-tests/features/ambiguous.feature", 20 | "elements": [ 21 | { 22 | "keyword": "Scenario", 23 | "line": 5, 24 | "name": "Ambiguous Johnny wants to see ambiguous scenarios", 25 | "tags": [], 26 | "id": "ambiguous-scenarios-specified-v2;ambiguous-johnny-wants-to-see-ambiguous-scenarios", 27 | "steps": [ 28 | { 29 | "arguments": [], 30 | "keyword": "Given ", 31 | "name": "Ambiguous Johnny visits the Angular homepage", 32 | "result": { 33 | "status": "passed", 34 | "duration": 2 35 | }, 36 | "line": 6, 37 | "match": { 38 | "location": "/Users/wswebcreation/deTesters/protractor-cucumber-typescript-boilerplate/e2e-tests/features/ambiguous.steps.ts:4" 39 | } 40 | }, 41 | { 42 | "arguments": [], 43 | "keyword": "When ", 44 | "name": "he clicks on the second button", 45 | "result": { 46 | "status": "ambiguous" 47 | }, 48 | "line": 7 49 | }, 50 | { 51 | "arguments": [], 52 | "keyword": "Then ", 53 | "name": "he expects that something really ambiguous happens", 54 | "result": { 55 | "status": "ambiguous" 56 | }, 57 | "line": 8 58 | }, 59 | { 60 | "arguments": [], 61 | "keyword": "After", 62 | "result": { 63 | "status": "passed", 64 | "duration": 8 65 | }, 66 | "hidden": true, 67 | "match": { 68 | "location": "/Users/wswebcreation/deTesters/protractor-cucumber-typescript-boilerplate/e2e-tests/config/helpers/after.scenario.ts:11" 69 | } 70 | } 71 | ] 72 | } 73 | ], 74 | "id": "ambiguous-scenarios-specified-v2" 75 | } 76 | ] 77 | -------------------------------------------------------------------------------- /test/unit/data/json/ambiguous_scenarios_specified_v3.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "metadata": { 4 | "browser": { 5 | "name": "Firefox", 6 | "version": "53" 7 | }, 8 | "device": "Virtual Machine", 9 | "platform": { 10 | "name": "Ubuntu", 11 | "version": "16.04" 12 | } 13 | }, 14 | "description": " The scenarios have been scripted twice\n So the report should hold ambiguous steps", 15 | "keyword": "Feature", 16 | "name": "Ambiguous scenarios specified V3", 17 | "line": 1, 18 | "id": "ambiguous-scenarios-specified-v3", 19 | "tags": [], 20 | "uri": "e2e-tests/features/ambiguous.feature", 21 | "elements": [ 22 | { 23 | "id": "ambiguous-scenarios-specified-v3;ambiguous-johnny-wants-to-see-ambiguous-scenarios", 24 | "keyword": "Scenario", 25 | "line": 5, 26 | "name": "Ambiguous Johnny wants to see ambiguous scenarios", 27 | "tags": [], 28 | "type": "scenario", 29 | "steps": [ 30 | { 31 | "arguments": [], 32 | "keyword": "Given ", 33 | "line": 6, 34 | "name": "Ambiguous Johnny visits the Angular homepage", 35 | "match": { 36 | "location": "e2e-tests/features/ambiguous.steps.ts:4" 37 | }, 38 | "result": { 39 | "status": "passed", 40 | "duration": 3 41 | } 42 | }, 43 | { 44 | "arguments": [], 45 | "keyword": "When ", 46 | "line": 7, 47 | "name": "he clicks on the second button", 48 | "result": { 49 | "status": "ambiguous" 50 | } 51 | }, 52 | { 53 | "arguments": [], 54 | "keyword": "Then ", 55 | "line": 8, 56 | "name": "he expects that something really ambiguous happens", 57 | "result": { 58 | "status": "ambiguous" 59 | } 60 | }, 61 | { 62 | "keyword": "After", 63 | "hidden": true, 64 | "match": { 65 | "location": "e2e-tests/config/helpers/after.scenario.ts:23" 66 | }, 67 | "result": { 68 | "status": "passed", 69 | "duration": 1 70 | } 71 | } 72 | ] 73 | } 74 | ] 75 | } 76 | ] 77 | -------------------------------------------------------------------------------- /test/unit/data/json/background2.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "metadata": { 4 | "device": "Xiaomi Mi A2 Lite", 5 | "platform": { 6 | "name": "android", 7 | "version": "9" 8 | } 9 | }, 10 | "line": 2, 11 | "elements": [ 12 | { 13 | "line": 4, 14 | "name": "", 15 | "description": "", 16 | "type": "background", 17 | "keyword": "Background", 18 | "steps": [ 19 | { 20 | "result": { 21 | "duration": 110, 22 | "status": "passed" 23 | }, 24 | "line": 5, 25 | "name": "I accepted all the required app permissions", 26 | "match": { 27 | "location": "void mastestproject.steps.BasicSteps.acceptAllPermissions()" 28 | }, 29 | "keyword": "Given " 30 | }, 31 | { 32 | "result": { 33 | "duration": 9727, 34 | "status": "passed" 35 | }, 36 | "line": 6, 37 | "name": "I accepted Eula", 38 | "match": { 39 | "location": "void mastestproject.steps.AcceptEulaSteps.acceptedEula()" 40 | }, 41 | "keyword": "And " 42 | }, 43 | { 44 | "result": { 45 | "error_message": "dummy", 46 | "duration": 32309, 47 | "status": "failed" 48 | }, 49 | "line": 7, 50 | "name": "abcd", 51 | "match": { 52 | "arguments": [ 53 | { 54 | "val": "a", 55 | "offset": 5 56 | }, 57 | { 58 | "val": "b", 59 | "offset": 26 60 | }, 61 | { 62 | "val": "c", 63 | "offset": 45 64 | }, 65 | { 66 | "val": "d", 67 | "offset": 67 68 | } 69 | ], 70 | "location": "java class" 71 | }, 72 | "keyword": "And " 73 | } 74 | ] 75 | }, 76 | { 77 | "start_timestamp": "2020-07-09T06:02:45.958Z", 78 | "line": 10, 79 | "name": "Auto launch (RO)", 80 | "description": "", 81 | "id": "auto-launch;auto-launch-(ro)", 82 | "after": [ 83 | { 84 | "embeddings": [ 85 | { 86 | "data": "screenshot data here", 87 | "mime_type": "image/png", 88 | "name": "Error screenshot" 89 | } 90 | ], 91 | "result": { 92 | "duration": 3182, 93 | "status": "failed" 94 | }, 95 | "match": { 96 | "location": "void mastestproject.steps.CucumberHooks.afterScenario(io.cucumber.java.Scenario)" 97 | } 98 | } 99 | ], 100 | "before": [ 101 | { 102 | "result": { 103 | "duration": 3182, 104 | "status": "passed" 105 | }, 106 | "match": { 107 | } 108 | } 109 | ], 110 | "type": "scenario", 111 | "keyword": "Scenario", 112 | "steps": [ 113 | { 114 | "result": { 115 | "status": "skipped" 116 | }, 117 | "line": 11, 118 | "name": "I run app2app or Safari", 119 | "match": { 120 | "location": "void mastestproject.steps.App2AppSteps.runApp2App()" 121 | }, 122 | "keyword": "Given " 123 | }, 124 | { 125 | "result": { 126 | "status": "skipped" 127 | }, 128 | "line": 12, 129 | "name": "The view is app2app or Safari", 130 | "match": { 131 | "location": "void mastestproject.steps.App2AppSteps.validateIfApp2AppVisible()" 132 | }, 133 | "keyword": "And " 134 | }, 135 | { 136 | "result": { 137 | "status": "skipped" 138 | }, 139 | "line": 13, 140 | "name": "dummy abc", 141 | "match": { 142 | "arguments": [ 143 | { 144 | "val": "a", 145 | "offset": 15 146 | }, 147 | { 148 | "val": "b", 149 | "offset": 33 150 | }, 151 | { 152 | "val": "c", 153 | "offset": 59 154 | } 155 | ], 156 | "location": "void mastestproject.steps.App2AppSteps.enterScheme(java.lang.String,java.lang.String,java.lang.String)" 157 | }, 158 | "keyword": "When " 159 | }, 160 | { 161 | "result": { 162 | "status": "skipped" 163 | }, 164 | "line": 14, 165 | "name": "I execute Autolaunch command", 166 | "match": { 167 | "location": "void mastestproject.steps.App2AppSteps.executeApp2AppCommand()" 168 | }, 169 | "keyword": "And " 170 | }, 171 | { 172 | "result": { 173 | "status": "skipped" 174 | }, 175 | "line": 15, 176 | "name": "The view is OTP Result", 177 | "match": { 178 | "location": "void mastestproject.steps.OTPSteps.verifyOtpResultHeaderTitleIsShowing()" 179 | }, 180 | "keyword": "And " 181 | }, 182 | { 183 | "result": { 184 | "status": "skipped" 185 | }, 186 | "line": 16, 187 | "name": "I go back", 188 | "match": { 189 | "location": "void mastestproject.steps.BasicSteps.goBack()" 190 | }, 191 | "keyword": "And " 192 | }, 193 | { 194 | "result": { 195 | "status": "skipped" 196 | }, 197 | "line": 17, 198 | "name": "The view is Main page", 199 | "match": { 200 | "location": "void mastestproject.steps.ActivationSteps.verifyActionsViewIsShowing()" 201 | }, 202 | "keyword": "Then " 203 | } 204 | ], 205 | "tags": [ 206 | { 207 | "name": "@REGRESSION_AUTO_LAUNCH" 208 | }, 209 | { 210 | "name": "@REGRESSION_2" 211 | }, 212 | { 213 | "name": "@REGRESSION_3" 214 | }, 215 | { 216 | "name": "@AUTO_LAUNCH" 217 | } 218 | ] 219 | } 220 | ], 221 | "name": "Background Feature 2", 222 | "description": "", 223 | "id": "auto-launch", 224 | "keyword": "Feature", 225 | "uri": "classpath:features/Autolaunch.feature", 226 | "tags": [ 227 | { 228 | "name": "@REGRESSION_AUTO_LAUNCH", 229 | "location": { 230 | "line": 1, 231 | "column": 1 232 | }, 233 | "type": "Tag" 234 | } 235 | ] 236 | } 237 | ] 238 | -------------------------------------------------------------------------------- /test/unit/data/json/before.hook.error.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "keyword": "Feature", 4 | "name": "Before Error", 5 | "line": 1, 6 | "id": "before-error", 7 | "tags": [], 8 | "uri": "e2e/demo.feature", 9 | "elements": [ 10 | { 11 | "id": "before-error;demo-scenario", 12 | "keyword": "Scenario", 13 | "line": 3, 14 | "name": "demo scenario", 15 | "tags": [ 16 | { 17 | "name": "@debug", 18 | "line": 2 19 | } 20 | ], 21 | "type": "scenario", 22 | "steps": [ 23 | { 24 | "keyword": "Before", 25 | "hidden": true, 26 | "match": { 27 | "location": "e2e/steps/common.steps.ts:31" 28 | }, 29 | "result": { 30 | "status": "failed", 31 | "duration": 3, 32 | "error_message": "Error: blabla\n at World. (e2e/steps/common.steps.ts:32:10)" 33 | } 34 | }, 35 | { 36 | "arguments": [], 37 | "keyword": "Given ", 38 | "line": 4, 39 | "name": "TODO", 40 | "match": { 41 | "location": "e2e/steps/common.steps.ts:95" 42 | }, 43 | "result": { 44 | "status": "skipped" 45 | } 46 | }, 47 | { 48 | "keyword": "After", 49 | "hidden": true, 50 | "match": { 51 | "location": "e2e/steps/common.steps.ts:34" 52 | }, 53 | "result": { 54 | "status": "passed", 55 | "duration": 426 56 | }, 57 | "embeddings": [ 58 | ] 59 | }, 60 | { 61 | "keyword": "After", 62 | "hidden": true, 63 | "match": { 64 | "location": "node_modules/lib/somewhere.js:26" 65 | }, 66 | "result": { 67 | "status": "passed", 68 | "duration": 1 69 | } 70 | } 71 | ] 72 | } 73 | ] 74 | } 75 | ] 76 | -------------------------------------------------------------------------------- /test/unit/data/json/embeddings.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "metadata": { 4 | "browser": { 5 | "name": "Edge", 6 | "version": "14" 7 | }, 8 | "device": "Virtual Machine", 9 | "platform": { 10 | "name": "Windows", 11 | "version": "10" 12 | } 13 | }, 14 | "elements": [ 15 | { 16 | "id": "testing-embeddings-part-1", 17 | "keyword": "Scenario", 18 | "line": 13, 19 | "name": "Validate todo list", 20 | "steps": [ 21 | { 22 | "arguments": [], 23 | "keyword": "Before ", 24 | "name": "Prepare some data", 25 | "result": { 26 | "status": "passed", 27 | "duration": 78145 28 | }, 29 | "embeddings": [ 30 | { 31 | "data": "W3siZGF0YSI6eyJ4IjoiMSIsInkiOiIxIiwidXJsIjoiaHR0cDovL3VybC5jb20ifSwiZXZlbnQiOiJzdGFydCIsInNob3ciOjEsImlkIjo1MH0seyJkYXRhIjp7IngiOiIyIiwieSI6IjMiLCJ1cmwiOiJodHRwOi8vZXhhbXBsZS5jb20ifSwiZXZlbnQiOiJzdG9wIiwic2hvdyI6MiwiaWQiOjUxfV0=", 32 | "mime_type": "application/json" 33 | }, 34 | { 35 | "data": "orderCxl", 36 | "mime_type": "text/plain" 37 | } 38 | ], 39 | "match": { 40 | "location": "/Users/wswebcreation/protractor-cucumber-typescript-boilerplate/node_modules/cucumber-tsflow/src/BindingDecorator.ts:83" 41 | } 42 | }, 43 | { 44 | "arguments": [], 45 | "keyword": "Given ", 46 | "name": "I do nothing", 47 | "result": { 48 | "status": "passed", 49 | "duration": 779454 50 | }, 51 | "line": 5, 52 | "match": { 53 | "location": "/Users/wswebcreation/protractor-cucumber-typescript-boilerplate/node_modules/cucumber-tsflow/src/BindingDecorator.ts:155" 54 | } 55 | }, 56 | { 57 | "arguments": [], 58 | "keyword": "After ", 59 | "result": { 60 | "status": "passed", 61 | "duration": 80997 62 | }, 63 | "hidden": true, 64 | "match": { 65 | "location": "/Users/wswebcreation/protractor-cucumber-typescript-boilerplate/node_modules/cucumber-tsflow/src/BindingDecorator.ts:206" 66 | } 67 | }, 68 | { 69 | "arguments": [], 70 | "keyword": "After ", 71 | "result": { 72 | "status": "passed", 73 | "duration": 45433 74 | }, 75 | "hidden": true, 76 | "match": { 77 | "location": "/Users/wswebcreation/protractor-cucumber-typescript-boilerplate/node_modules/cucumber-tsflow/src/BindingDecorator.ts:87" 78 | } 79 | } 80 | ], 81 | "type": "scenario" 82 | }, 83 | { 84 | "id": "testing-embeddings-2", 85 | "keyword": "Scenario", 86 | "line": 18, 87 | "name": "Testing embeddings part 2", 88 | "steps": [ 89 | { 90 | "arguments": [], 91 | "keyword": "Before ", 92 | "result": { 93 | "status": "passed", 94 | "duration": 37353 95 | }, 96 | "hidden": true, 97 | "match": { 98 | "location": "/Users/wswebcreation/protractor-cucumber-typescript-boilerplate/node_modules/cucumber-tsflow/src/BindingDecorator.ts:83" 99 | } 100 | }, 101 | { 102 | "arguments": [], 103 | "keyword": "Given ", 104 | "name": "I add embeddings:", 105 | "result": { 106 | "status": "passed", 107 | "duration": 1256056 108 | }, 109 | "line": 19, 110 | "embeddings": [ 111 | { 112 | "data": "// testdata to be printed", 113 | "mime_type": "text/plain" 114 | }, 115 | { 116 | "data": "ew0KICAgICJ2YWx1ZSI6ICJ3cml0ZSBhIHByb3RyYWN0b3IgdGVzdCINCn0=", 117 | "mime_type": "text/plain" 118 | } 119 | ], 120 | "match": { 121 | "location": "/Users/wswebcreation/protractor-cucumber-typescript-boilerplate/node_modules/cucumber-tsflow/src/BindingDecorator.ts:155" 122 | } 123 | }, 124 | { 125 | "arguments": [], 126 | "keyword": "Then ", 127 | "name": "I should also have other embeddings", 128 | "result": { 129 | "status": "passed", 130 | "duration": 27341 131 | }, 132 | "line": 20, 133 | "embeddings": [ 134 | { 135 | "data": "U3VzcGVuZGlzc2UgbGVjdHVzIGxlbw==", 136 | "mime_type": "text/plain" 137 | }, 138 | { 139 | "data": "Suspendisse lectus leo", 140 | "mime_type": "text/plain" 141 | } 142 | ], 143 | "match": { 144 | "location": "/Users/wswebcreation/protractor-cucumber-typescript-boilerplate/node_modules/cucumber-tsflow/src/BindingDecorator.ts:171" 145 | } 146 | }, 147 | { 148 | "arguments": [], 149 | "keyword": "And ", 150 | "name": "I am embedded", 151 | "result": { 152 | "status": "passed", 153 | "duration": 58807 154 | }, 155 | "line": 21, 156 | "embeddings": [ 157 | { 158 | "data": "The first not encoded line", 159 | "mime_type": "text/plain" 160 | }, 161 | { 162 | "data": "And the second not encoded line\nA new line", 163 | "mime_type": "text/plain" 164 | } 165 | ], 166 | "match": { 167 | "location": "/Users/wswebcreation/protractor-cucumber-typescript-boilerplate/node_modules/cucumber-tsflow/src/BindingDecorator.ts:171" 168 | } 169 | }, 170 | { 171 | "arguments": [], 172 | "keyword": "After ", 173 | "result": { 174 | "status": "passed", 175 | "duration": 53934 176 | }, 177 | "hidden": true, 178 | "match": { 179 | "location": "/Users/wswebcreation/protractor-cucumber-typescript-boilerplate/node_modules/cucumber-tsflow/src/BindingDecorator.ts:206" 180 | } 181 | }, 182 | { 183 | "arguments": [], 184 | "keyword": "After ", 185 | "result": { 186 | "status": "passed", 187 | "duration": 30175 188 | }, 189 | "hidden": true, 190 | "match": { 191 | "location": "/Users/wswebcreation/protractor-cucumber-typescript-boilerplate/node_modules/cucumber-tsflow/src/BindingDecorator.ts:87" 192 | } 193 | } 194 | ], 195 | "type": "scenario" 196 | } 197 | ], 198 | "id": "testing_embeddings.1494823257420", 199 | "keyword": "Feature", 200 | "line": 1, 201 | "name": "Testing embeddings", 202 | "uri": "testing_embeddings.1494823257420" 203 | } 204 | ] 205 | -------------------------------------------------------------------------------- /test/unit/data/json/empty_json.json: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/WasiqB/multiple-cucumber-html-reporter/937e125631970392cbff8b1237a45dc47e21f595/test/unit/data/json/empty_json.json -------------------------------------------------------------------------------- /test/unit/data/json/happy_flow_app_android.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "metadata": { 4 | "app": { 5 | "name": "Greetings app", 6 | "version": "1.02" 7 | }, 8 | "device": "Google Pixel 2", 9 | "platform": { 10 | "name": "Android", 11 | "version": "8" 12 | } 13 | }, 14 | "keyword": "Feature", 15 | "name": "Happy flow app", 16 | "line": 2, 17 | "id": "happy-flow-app", 18 | "uri": "e2e-tests/features/app.feature", 19 | "elements": [ 20 | { 21 | "id": "happy-flow-app;as-a-visitor-i-want-to-be-greeted", 22 | "keyword": "Scenario", 23 | "line": 8, 24 | "name": "As a visitor I want to be greeted", 25 | "type": "scenario", 26 | "steps": [ 27 | { 28 | "arguments": [], 29 | "keyword": "Given ", 30 | "line": 5, 31 | "name": "I open the app", 32 | "match": { 33 | "location": "e2e-tests/features/app.steps.js:6" 34 | }, 35 | "result": { 36 | "status": "passed", 37 | "duration": 2230 38 | } 39 | }, 40 | { 41 | "arguments": [], 42 | "keyword": "Given ", 43 | "line": 9, 44 | "name": "I submit the name \"Julie\"", 45 | "match": { 46 | "location": "e2e-tests/features/app.steps.js:11" 47 | }, 48 | "result": { 49 | "status": "passed", 50 | "duration": 1070 51 | } 52 | }, 53 | { 54 | "arguments": [], 55 | "keyword": "Then ", 56 | "line": 10, 57 | "name": "I am greeted as \"Hello Julie!\"", 58 | "match": { 59 | "location": "e2e-tests/features/app.steps.js:32" 60 | }, 61 | "result": { 62 | "status": "passed", 63 | "duration": 58 64 | } 65 | }, 66 | { 67 | "keyword": "After", 68 | "hidden": true, 69 | "match": { 70 | "location": "e2e-tests/config/helpers/after.scenario.ts:23" 71 | }, 72 | "result": { 73 | "status": "passed", 74 | "duration": 1 75 | } 76 | } 77 | ] 78 | } 79 | ] 80 | } 81 | ] 82 | -------------------------------------------------------------------------------- /test/unit/data/json/happy_flow_app_ios.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "metadata": { 4 | "app": { 5 | "name": "Greetings app", 6 | "version": "1.02" 7 | }, 8 | "device": "iPhone X", 9 | "platform": { 10 | "name": "iOS", 11 | "version": "11.1" 12 | } 13 | }, 14 | "keyword": "Feature", 15 | "name": "Happy flow app", 16 | "line": 2, 17 | "id": "happy-flow-app", 18 | "uri": "e2e-tests/features/app.feature", 19 | "elements": [ 20 | { 21 | "id": "happy-flow-app;as-a-visitor-i-want-to-be-greeted", 22 | "keyword": "Scenario", 23 | "line": 8, 24 | "name": "As a visitor I want to be greeted", 25 | "type": "scenario", 26 | "steps": [ 27 | { 28 | "arguments": [], 29 | "keyword": "Given ", 30 | "line": 5, 31 | "name": "I open the app", 32 | "match": { 33 | "location": "e2e-tests/features/app.steps.js:6" 34 | }, 35 | "result": { 36 | "status": "passed", 37 | "duration": 2230 38 | } 39 | }, 40 | { 41 | "arguments": [], 42 | "keyword": "Given ", 43 | "line": 9, 44 | "name": "I submit the name \"Julie\"", 45 | "match": { 46 | "location": "e2e-tests/features/app.steps.js:11" 47 | }, 48 | "result": { 49 | "status": "passed", 50 | "duration": 1070 51 | } 52 | }, 53 | { 54 | "arguments": [], 55 | "keyword": "Then ", 56 | "line": 10, 57 | "name": "I am greeted as \"Hello Julie!\"", 58 | "match": { 59 | "location": "e2e-tests/features/app.steps.js:32" 60 | }, 61 | "result": { 62 | "status": "passed", 63 | "duration": 58 64 | } 65 | }, 66 | { 67 | "keyword": "After", 68 | "hidden": true, 69 | "match": { 70 | "location": "e2e-tests/config/helpers/after.scenario.ts:23" 71 | }, 72 | "result": { 73 | "status": "passed", 74 | "duration": 1 75 | } 76 | } 77 | ] 78 | } 79 | ] 80 | } 81 | ] 82 | -------------------------------------------------------------------------------- /test/unit/data/json/happy_flow_v3.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "metadata": { 4 | "browser": { 5 | "name": "microsoftedge", 6 | "version": "14" 7 | }, 8 | "device": "Virtual Machine", 9 | "platform": { 10 | "name": "Windows", 11 | "version": "10" 12 | } 13 | }, 14 | "keyword": "Feature", 15 | "name": "Happy flow V3", 16 | "line": 2, 17 | "id": "happy-flow-v3", 18 | "tags": [ 19 | { 20 | "name": "@example", 21 | "line": 1 22 | }, 23 | { 24 | "name": "@happy-flow", 25 | "line": 1 26 | }, 27 | { 28 | "name": "@angular", 29 | "line": 1 30 | } 31 | ], 32 | "uri": "e2e-tests/features/example.feature", 33 | "elements": [ 34 | { 35 | "id": "happy-flow-v3;as-a-visitor-i-want-to-be-greeted", 36 | "keyword": "Scenario", 37 | "line": 8, 38 | "name": "As a visitor I want to be greeted", 39 | "tags": [ 40 | { 41 | "name": "@example", 42 | "line": 1 43 | }, 44 | { 45 | "name": "@happy-flow", 46 | "line": 1 47 | }, 48 | { 49 | "name": "@angular", 50 | "line": 1 51 | }, 52 | { 53 | "name": "@visitor", 54 | "line": 7 55 | }, 56 | { 57 | "name": "@julie", 58 | "line": 7 59 | } 60 | ], 61 | "type": "scenario", 62 | "steps": [ 63 | { 64 | "arguments": [], 65 | "keyword": "Given ", 66 | "line": 5, 67 | "name": "I visit the Angular homepage", 68 | "match": { 69 | "location": "e2e-tests/features/example.steps.ts:6" 70 | }, 71 | "result": { 72 | "status": "passed", 73 | "duration": 2230 74 | } 75 | }, 76 | { 77 | "arguments": [], 78 | "keyword": "Given ", 79 | "line": 9, 80 | "name": "I submit the name \"Julie\"", 81 | "match": { 82 | "location": "e2e-tests/features/example.steps.ts:11" 83 | }, 84 | "result": { 85 | "status": "passed", 86 | "duration": 1070 87 | } 88 | }, 89 | { 90 | "arguments": [], 91 | "keyword": "Then ", 92 | "line": 10, 93 | "name": "I am greeted as \"Hello Julie!\"", 94 | "match": { 95 | "location": "e2e-tests/features/example.steps.ts:32" 96 | }, 97 | "result": { 98 | "status": "passed", 99 | "duration": 58 100 | } 101 | }, 102 | { 103 | "keyword": "After", 104 | "hidden": true, 105 | "match": { 106 | "location": "e2e-tests/config/helpers/after.scenario.ts:23" 107 | }, 108 | "result": { 109 | "status": "passed", 110 | "duration": 1 111 | } 112 | } 113 | ] 114 | }, 115 | { 116 | "id": "happy-flow-v3;validate-todo-list", 117 | "keyword": "Scenario", 118 | "line": 13, 119 | "name": "Validate todo list", 120 | "tags": [ 121 | { 122 | "name": "@example", 123 | "line": 1 124 | }, 125 | { 126 | "name": "@happy-flow", 127 | "line": 1 128 | }, 129 | { 130 | "name": "@angular", 131 | "line": 1 132 | }, 133 | { 134 | "name": "@todo", 135 | "line": 12 136 | }, 137 | { 138 | "name": "@count", 139 | "line": 12 140 | }, 141 | { 142 | "name": "@validate", 143 | "line": 12 144 | } 145 | ], 146 | "type": "scenario", 147 | "steps": [ 148 | { 149 | "arguments": [], 150 | "keyword": "Given ", 151 | "line": 5, 152 | "name": "I visit the Angular homepage", 153 | "match": { 154 | "location": "e2e-tests/features/example.steps.ts:6" 155 | }, 156 | "result": { 157 | "status": "passed", 158 | "duration": 663 159 | } 160 | }, 161 | { 162 | "arguments": [], 163 | "keyword": "Given ", 164 | "line": 14, 165 | "name": "I count 2 todo's", 166 | "match": { 167 | "location": "e2e-tests/features/example.steps.ts:16" 168 | }, 169 | "result": { 170 | "status": "passed", 171 | "duration": 824 172 | } 173 | }, 174 | { 175 | "arguments": [], 176 | "keyword": "Then ", 177 | "line": 15, 178 | "name": "the last todo should hold \"build an AngularJS app\"", 179 | "match": { 180 | "location": "e2e-tests/features/example.steps.ts:40" 181 | }, 182 | "result": { 183 | "status": "passed", 184 | "duration": 52 185 | } 186 | }, 187 | { 188 | "keyword": "After", 189 | "hidden": true, 190 | "match": { 191 | "location": "e2e-tests/config/helpers/after.scenario.ts:23" 192 | }, 193 | "result": { 194 | "status": "passed" 195 | } 196 | } 197 | ] 198 | }, 199 | { 200 | "id": "happy-flow-v3;add-a-todo", 201 | "keyword": "Scenario", 202 | "line": 18, 203 | "name": "Add a todo", 204 | "tags": [ 205 | { 206 | "name": "@example", 207 | "line": 1 208 | }, 209 | { 210 | "name": "@happy-flow", 211 | "line": 1 212 | }, 213 | { 214 | "name": "@angular", 215 | "line": 1 216 | }, 217 | { 218 | "name": "@todo", 219 | "line": 17 220 | }, 221 | { 222 | "name": "@count", 223 | "line": 17 224 | }, 225 | { 226 | "name": "@add", 227 | "line": 17 228 | } 229 | ], 230 | "type": "scenario", 231 | "steps": [ 232 | { 233 | "arguments": [], 234 | "keyword": "Given ", 235 | "line": 5, 236 | "name": "I visit the Angular homepage", 237 | "match": { 238 | "location": "e2e-tests/features/example.steps.ts:6" 239 | }, 240 | "result": { 241 | "status": "passed", 242 | "duration": 502 243 | } 244 | }, 245 | { 246 | "arguments": [], 247 | "keyword": "Given ", 248 | "line": 19, 249 | "name": "I add the todo \"write a protractor test\"", 250 | "match": { 251 | "location": "e2e-tests/features/example.steps.ts:23" 252 | }, 253 | "result": { 254 | "status": "passed", 255 | "duration": 1029 256 | } 257 | }, 258 | { 259 | "arguments": [], 260 | "keyword": "Then ", 261 | "line": 20, 262 | "name": "I should have 3 todo's", 263 | "match": { 264 | "location": "e2e-tests/features/example.steps.ts:47" 265 | }, 266 | "result": { 267 | "status": "passed", 268 | "duration": 25 269 | } 270 | }, 271 | { 272 | "arguments": [], 273 | "keyword": "And ", 274 | "line": 21, 275 | "name": "the last todo should hold \"write a protractor test\"", 276 | "match": { 277 | "location": "e2e-tests/features/example.steps.ts:40" 278 | }, 279 | "result": { 280 | "status": "passed", 281 | "duration": 49 282 | } 283 | }, 284 | { 285 | "keyword": "After", 286 | "hidden": true, 287 | "match": { 288 | "location": "e2e-tests/config/helpers/after.scenario.ts:23" 289 | }, 290 | "result": { 291 | "status": "passed", 292 | "duration": 1 293 | } 294 | } 295 | ] 296 | } 297 | ] 298 | } 299 | ] 300 | -------------------------------------------------------------------------------- /test/unit/data/json/no_elements.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "metadata": { 4 | "browser": { 5 | "name": "not known", 6 | "version": "not known" 7 | }, 8 | "device": "not known", 9 | "platform": { 10 | "name": "not known", 11 | "version": "not known" 12 | } 13 | }, 14 | "description": " No elements have been found\n So the report should hold no scenarios and steps", 15 | "id": "no_elements.1494777183356", 16 | "keyword": "Feature", 17 | "line": 1, 18 | "name": "No elements", 19 | "tags": [], 20 | "uri": "no_elements.1494777183356" 21 | } 22 | ] 23 | -------------------------------------------------------------------------------- /test/unit/data/json/no_elements_v2.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "metadata": { 4 | "browser": { 5 | "name": "Firefox Headless", 6 | "version": "58.8" 7 | }, 8 | "device": "Virtual Machine", 9 | "platform": { 10 | "name": "win32", 11 | "version": "10" 12 | } 13 | }, 14 | "description": " No elements have been found\n So the report should hold no scenarios and steps", 15 | "id": "no_elements_v2.1494777183356", 16 | "keyword": "Feature", 17 | "line": 1, 18 | "name": "No elements V2", 19 | "tags": [], 20 | "uri": "no_elements_v2.1494777183356" 21 | } 22 | ] 23 | -------------------------------------------------------------------------------- /test/unit/data/json/no_elements_v3.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "metadata": { 4 | "browser": { 5 | "name": "Chrome Headless", 6 | "version": "97.6.5.9" 7 | }, 8 | "device": "Virtual Machine", 9 | "platform": { 10 | "name": "win64", 11 | "version": "10" 12 | } 13 | }, 14 | "description": " No elements have been found\n So the report should hold no scenarios and steps", 15 | "id": "no_elements_v3.1494777183356", 16 | "keyword": "Feature", 17 | "line": 1, 18 | "name": "No elements V3", 19 | "tags": [], 20 | "uri": "no_elements_v3.1494777183356" 21 | } 22 | ] 23 | -------------------------------------------------------------------------------- /test/unit/data/json/pending_scenarios_specified_v1.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "description": " The scenarios have scripted pending steps\n So the report should hold pending steps", 4 | "elements": [ 5 | { 6 | "id": "pending-scenarios-specified-v1;johnny-wants-to-see-pending-scenarios", 7 | "keyword": "Scenario", 8 | "line": 5, 9 | "name": "Johnny wants to see pending scenarios", 10 | "steps": [ 11 | { 12 | "arguments": [], 13 | "keyword": "Before ", 14 | "result": { 15 | "status": "passed", 16 | "duration": 6703453 17 | }, 18 | "hidden": true, 19 | "match": { 20 | "location": "/Users/wswebcreation/deTesters/protractor-cucumber-typescript-boilerplate/node_modules/cucumber-tsflow/src/BindingDecorator.ts:83" 21 | } 22 | }, 23 | { 24 | "arguments": [], 25 | "keyword": "Given ", 26 | "name": "Johnny visits the Angular homepage", 27 | "result": { 28 | "status": "pending" 29 | }, 30 | "line": 6, 31 | "match": { 32 | "location": "/Users/wswebcreation/deTesters/protractor-cucumber-typescript-boilerplate/node_modules/cucumber-tsflow/src/BindingDecorator.ts:157" 33 | } 34 | }, 35 | { 36 | "arguments": [], 37 | "keyword": "When ", 38 | "name": "he clicks on the first link", 39 | "result": { 40 | "status": "skipped" 41 | }, 42 | "line": 7, 43 | "match": { 44 | "location": "/Users/wswebcreation/deTesters/protractor-cucumber-typescript-boilerplate/node_modules/cucumber-tsflow/src/BindingDecorator.ts:160" 45 | } 46 | }, 47 | { 48 | "arguments": [], 49 | "keyword": "Then ", 50 | "name": "he expects that something happens", 51 | "result": { 52 | "status": "skipped" 53 | }, 54 | "line": 8, 55 | "match": { 56 | "location": "/Users/wswebcreation/deTesters/protractor-cucumber-typescript-boilerplate/node_modules/cucumber-tsflow/src/BindingDecorator.ts:163" 57 | } 58 | }, 59 | { 60 | "arguments": [], 61 | "keyword": "After ", 62 | "result": { 63 | "status": "passed", 64 | "duration": 479227 65 | }, 66 | "hidden": true, 67 | "match": { 68 | "location": "/Users/wswebcreation/deTesters/protractor-cucumber-typescript-boilerplate/node_modules/cucumber-tsflow/src/BindingDecorator.ts:201" 69 | } 70 | }, 71 | { 72 | "arguments": [], 73 | "keyword": "After ", 74 | "result": { 75 | "status": "passed", 76 | "duration": 305690 77 | }, 78 | "hidden": true, 79 | "match": { 80 | "location": "/Users/wswebcreation/deTesters/protractor-cucumber-typescript-boilerplate/node_modules/cucumber-tsflow/src/BindingDecorator.ts:87" 81 | } 82 | } 83 | ], 84 | "tags": [], 85 | "type": "scenario" 86 | } 87 | ], 88 | "id": "pending-scenarios-specified-v1", 89 | "keyword": "Feature", 90 | "line": 1, 91 | "name": "Pending scenarios specified V1", 92 | "tags": [], 93 | "uri": "/Users/wswebcreation/deTesters/protractor-cucumber-typescript-boilerplate/e2e-tests/features/pending.feature", 94 | "metadata": { 95 | "browser": { 96 | "name": "Chrome", 97 | "version": "56" 98 | }, 99 | "device": "Samsung S7", 100 | "platform": { 101 | "name": "Android", 102 | "version": "7.1" 103 | } 104 | } 105 | } 106 | ] 107 | -------------------------------------------------------------------------------- /test/unit/data/json/pending_scenarios_specified_v2.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "metadata": { 4 | "browser": { 5 | "name": "Chrome", 6 | "version": "56" 7 | }, 8 | "device": "Samsung S7", 9 | "platform": { 10 | "name": "Android", 11 | "version": "7.1" 12 | } 13 | }, 14 | "description": " The scenarios have scripted pending steps\n So the report should hold pending steps", 15 | "keyword": "Feature", 16 | "line": 1, 17 | "name": "Pending scenarios specified V2", 18 | "tags": [], 19 | "uri": "/Users/wswebcreation/deTesters/protractor-cucumber-typescript-boilerplate/e2e-tests/features/pending.feature", 20 | "elements": [ 21 | { 22 | "keyword": "Scenario", 23 | "line": 5, 24 | "name": "Johnny wants to see pending scenarios", 25 | "tags": [], 26 | "id": "pending-scenarios-specified-v2;johnny-wants-to-see-pending-scenarios", 27 | "steps": [ 28 | { 29 | "arguments": [], 30 | "keyword": "Given ", 31 | "name": "Johnny visits the Angular homepage", 32 | "result": { 33 | "status": "pending" 34 | }, 35 | "line": 6, 36 | "match": { 37 | "location": "/Users/wswebcreation/deTesters/protractor-cucumber-typescript-boilerplate/e2e-tests/features/pending.steps.ts:4" 38 | } 39 | }, 40 | { 41 | "arguments": [], 42 | "keyword": "When ", 43 | "name": "he clicks on the first link", 44 | "result": { 45 | "status": "skipped" 46 | }, 47 | "line": 7, 48 | "match": { 49 | "location": "/Users/wswebcreation/deTesters/protractor-cucumber-typescript-boilerplate/e2e-tests/features/pending.steps.ts:9" 50 | } 51 | }, 52 | { 53 | "arguments": [], 54 | "keyword": "Then ", 55 | "name": "he expects that something happens", 56 | "result": { 57 | "status": "skipped" 58 | }, 59 | "line": 8, 60 | "match": { 61 | "location": "/Users/wswebcreation/deTesters/protractor-cucumber-typescript-boilerplate/e2e-tests/features/pending.steps.ts:14" 62 | } 63 | }, 64 | { 65 | "arguments": [], 66 | "keyword": "After", 67 | "result": { 68 | "status": "passed", 69 | "duration": 2 70 | }, 71 | "hidden": true, 72 | "match": { 73 | "location": "/Users/wswebcreation/deTesters/protractor-cucumber-typescript-boilerplate/e2e-tests/config/helpers/after.scenario.ts:11" 74 | } 75 | } 76 | ] 77 | } 78 | ], 79 | "id": "pending-scenarios-specified-v2" 80 | } 81 | ] 82 | -------------------------------------------------------------------------------- /test/unit/data/json/pending_scenarios_specified_v3.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "metadata": { 4 | "browser": { 5 | "name": "Chrome", 6 | "version": "56" 7 | }, 8 | "device": "Samsung S7", 9 | "platform": { 10 | "name": "Android", 11 | "version": "7.1" 12 | } 13 | }, 14 | "description": " The scenarios have scripted pending steps\n So the report should hold pending steps", 15 | "keyword": "Feature", 16 | "name": "Pending scenarios specified V3", 17 | "line": 1, 18 | "id": "pending-scenarios-specified-v3", 19 | "tags": [], 20 | "uri": "e2e-tests/features/pending.feature", 21 | "elements": [ 22 | { 23 | "id": "pending-scenarios-specified-v3;johnny-wants-to-see-pending-scenarios", 24 | "keyword": "Scenario", 25 | "line": 5, 26 | "name": "Johnny wants to see pending scenarios", 27 | "tags": [], 28 | "type": "scenario", 29 | "steps": [ 30 | { 31 | "arguments": [], 32 | "keyword": "Given ", 33 | "line": 6, 34 | "name": "Johnny visits the Angular homepage", 35 | "match": { 36 | "location": "e2e-tests/features/pending.steps.ts:4" 37 | }, 38 | "result": { 39 | "status": "pending", 40 | "duration": 3 41 | } 42 | }, 43 | { 44 | "arguments": [], 45 | "keyword": "When ", 46 | "line": 7, 47 | "name": "he clicks on the first link", 48 | "match": { 49 | "location": "e2e-tests/features/pending.steps.ts:9" 50 | }, 51 | "result": { 52 | "status": "skipped" 53 | } 54 | }, 55 | { 56 | "arguments": [], 57 | "keyword": "Then ", 58 | "line": 8, 59 | "name": "he expects that something happens", 60 | "match": { 61 | "location": "e2e-tests/features/pending.steps.ts:14" 62 | }, 63 | "result": { 64 | "status": "skipped" 65 | } 66 | }, 67 | { 68 | "keyword": "After", 69 | "hidden": true, 70 | "match": { 71 | "location": "e2e-tests/config/helpers/after.scenario.ts:23" 72 | }, 73 | "result": { 74 | "status": "passed" 75 | } 76 | } 77 | ] 78 | } 79 | ] 80 | } 81 | ] 82 | -------------------------------------------------------------------------------- /test/unit/data/json/undefined_scenarios_specified_v1.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "description": " No scenarios have scripted steps\n So the report should hold undefined steps", 4 | "elements": [ 5 | { 6 | "id": "undefined-scenarios-specified-v1;timmy-wants-to-see-undefined-scenarios", 7 | "keyword": "Scenario", 8 | "line": 7, 9 | "name": "Timmy wants to see undefined scenarios", 10 | "steps": [ 11 | { 12 | "arguments": [], 13 | "keyword": "Before ", 14 | "result": { 15 | "status": "passed", 16 | "duration": 664231 17 | }, 18 | "hidden": true, 19 | "match": { 20 | "location": "/Users/wswebcreation/deTesters/protractor-cucumber-typescript-boilerplate/node_modules/cucumber-tsflow/src/BindingDecorator.ts:83" 21 | } 22 | }, 23 | { 24 | "arguments": [], 25 | "keyword": "Given ", 26 | "name": "Timmy visits the Angular homepage", 27 | "result": { 28 | "status": "undefined" 29 | }, 30 | "line": 8 31 | }, 32 | { 33 | "arguments": [], 34 | "keyword": "When ", 35 | "name": "he doesn't do anything", 36 | "result": { 37 | "status": "undefined" 38 | }, 39 | "line": 9 40 | }, 41 | { 42 | "arguments": [], 43 | "keyword": "Then ", 44 | "name": "nothing has been done", 45 | "result": { 46 | "status": "undefined" 47 | }, 48 | "line": 10 49 | }, 50 | { 51 | "arguments": [], 52 | "keyword": "After ", 53 | "result": { 54 | "status": "passed", 55 | "duration": 694199 56 | }, 57 | "hidden": true, 58 | "match": { 59 | "location": "/Users/wswebcreation/deTesters/protractor-cucumber-typescript-boilerplate/node_modules/cucumber-tsflow/src/BindingDecorator.ts:201" 60 | } 61 | }, 62 | { 63 | "arguments": [], 64 | "keyword": "After ", 65 | "result": { 66 | "status": "passed", 67 | "duration": 159344 68 | }, 69 | "hidden": true, 70 | "match": { 71 | "location": "/Users/wswebcreation/deTesters/protractor-cucumber-typescript-boilerplate/node_modules/cucumber-tsflow/src/BindingDecorator.ts:87" 72 | } 73 | } 74 | ], 75 | "tags": [ 76 | { 77 | "name": "@undefined", 78 | "line": 1 79 | }, 80 | { 81 | "name": "@timmy", 82 | "line": 6 83 | }, 84 | { 85 | "name": "@angular", 86 | "line": 6 87 | }, 88 | { 89 | "name": "@nothing", 90 | "line": 6 91 | } 92 | ], 93 | "type": "scenario" 94 | } 95 | ], 96 | "id": "undefined-scenarios-specified-v1", 97 | "keyword": "Feature", 98 | "line": 2, 99 | "name": "Undefined scenarios specified V1", 100 | "tags": [ 101 | { 102 | "name": "@undefined", 103 | "line": 1 104 | } 105 | ], 106 | "uri": "/Users/wswebcreation/deTesters/protractor-cucumber-typescript-boilerplate/e2e-tests/features/undefined.feature", 107 | "metadata": { 108 | "browser": { 109 | "name": "Safari", 110 | "version": "10" 111 | }, 112 | "device": "iPhone 7", 113 | "platform": { 114 | "name": "iOS", 115 | "version": "10.1.2" 116 | } 117 | } 118 | } 119 | ] 120 | -------------------------------------------------------------------------------- /test/unit/data/json/undefined_scenarios_specified_v2.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "metadata": { 4 | "browser": { 5 | "name": "Safari", 6 | "version": "10" 7 | }, 8 | "device": "iPhone 7", 9 | "platform": { 10 | "name": "iOS", 11 | "version": "10.1.2" 12 | } 13 | }, 14 | "description": " No scenarios have scripted steps\n So the report should hold undefined steps", 15 | "keyword": "Feature", 16 | "line": 2, 17 | "name": "Undefined scenarios specified V2", 18 | "tags": [ 19 | { 20 | "line": 1, 21 | "name": "@undefined" 22 | } 23 | ], 24 | "uri": "/Users/wswebcreation/deTesters/protractor-cucumber-typescript-boilerplate/e2e-tests/features/undefined.feature", 25 | "elements": [ 26 | { 27 | "keyword": "Scenario", 28 | "line": 7, 29 | "name": "Timmy wants to see undefined scenarios", 30 | "tags": [ 31 | { 32 | "line": 1, 33 | "name": "@undefined" 34 | }, 35 | { 36 | "line": 6, 37 | "name": "@timmy" 38 | }, 39 | { 40 | "line": 6, 41 | "name": "@angular" 42 | }, 43 | { 44 | "line": 6, 45 | "name": "@nothing" 46 | } 47 | ], 48 | "id": "undefined-scenarios-specified-v2;timmy-wants-to-see-undefined-scenarios", 49 | "steps": [ 50 | { 51 | "arguments": [], 52 | "keyword": "Given ", 53 | "name": "Timmy visits the Angular homepage", 54 | "result": { 55 | "status": "undefined" 56 | }, 57 | "line": 8 58 | }, 59 | { 60 | "arguments": [], 61 | "keyword": "When ", 62 | "name": "he doesn't do anything", 63 | "result": { 64 | "status": "undefined" 65 | }, 66 | "line": 9 67 | }, 68 | { 69 | "arguments": [], 70 | "keyword": "Then ", 71 | "name": "nothing has been done", 72 | "result": { 73 | "status": "undefined" 74 | }, 75 | "line": 10 76 | }, 77 | { 78 | "arguments": [], 79 | "keyword": "After", 80 | "result": { 81 | "status": "passed", 82 | "duration": 0 83 | }, 84 | "hidden": true, 85 | "match": { 86 | "location": "/Users/wswebcreation/deTesters/protractor-cucumber-typescript-boilerplate/e2e-tests/config/helpers/after.scenario.ts:11" 87 | } 88 | } 89 | ] 90 | } 91 | ], 92 | "id": "undefined-scenarios-specified-v2" 93 | } 94 | ] 95 | -------------------------------------------------------------------------------- /test/unit/data/json/undefined_scenarios_specified_v3.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "metadata": { 4 | "browser": { 5 | "name": "Safari", 6 | "version": "10" 7 | }, 8 | "device": "iPhone 7", 9 | "platform": { 10 | "name": "iOS", 11 | "version": "10.1.2" 12 | } 13 | }, 14 | "description": " No scenarios have scripted steps\n So the report should hold undefined steps", 15 | "keyword": "Feature", 16 | "name": "Undefined scenarios specified V3", 17 | "line": 2, 18 | "id": "undefined-scenarios-specified-v3", 19 | "tags": [ 20 | { 21 | "name": "@undefined", 22 | "line": 1 23 | } 24 | ], 25 | "uri": "e2e-tests/features/undefined.feature", 26 | "elements": [ 27 | { 28 | "id": "undefined-scenarios-specified-v3;timmy-wants-to-see-undefined-scenarios", 29 | "keyword": "Scenario", 30 | "line": 7, 31 | "name": "Timmy wants to see undefined scenarios", 32 | "tags": [ 33 | { 34 | "name": "@undefined", 35 | "line": 1 36 | }, 37 | { 38 | "name": "@timmy", 39 | "line": 6 40 | }, 41 | { 42 | "name": "@angular", 43 | "line": 6 44 | }, 45 | { 46 | "name": "@nothing", 47 | "line": 6 48 | } 49 | ], 50 | "type": "scenario", 51 | "steps": [ 52 | { 53 | "arguments": [], 54 | "keyword": "Given ", 55 | "line": 8, 56 | "name": "Timmy visits the Angular homepage", 57 | "result": { 58 | "status": "undefined" 59 | } 60 | }, 61 | { 62 | "arguments": [], 63 | "keyword": "When ", 64 | "line": 9, 65 | "name": "he doesn't do anything", 66 | "result": { 67 | "status": "undefined" 68 | } 69 | }, 70 | { 71 | "arguments": [], 72 | "keyword": "Then ", 73 | "line": 10, 74 | "name": "nothing has been done", 75 | "result": { 76 | "status": "undefined" 77 | } 78 | }, 79 | { 80 | "keyword": "After", 81 | "hidden": true, 82 | "match": { 83 | "location": "e2e-tests/config/helpers/after.scenario.ts:23" 84 | }, 85 | "result": { 86 | "status": "passed", 87 | "duration": 1 88 | } 89 | } 90 | ] 91 | } 92 | ] 93 | } 94 | ] 95 | -------------------------------------------------------------------------------- /test/unit/data/json/undefined_succeeded_pending_scenarios_specified_v3.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "metadata": { 4 | "browser": { 5 | "name": "Safari", 6 | "version": "10" 7 | }, 8 | "device": "iPhone 7", 9 | "platform": { 10 | "name": "iOS", 11 | "version": "10.1.2" 12 | } 13 | }, 14 | "description": " Scenarios have undefined, pending and passed steps.\n So the report should hold all steps and status should be undefined", 15 | "keyword": "Feature", 16 | "name": "Undefined, pending, passed scenarios specified V3", 17 | "line": 2, 18 | "id": "undefined-pending-passed-scenarios-specified-v3", 19 | "tags": [ 20 | { 21 | "name": "@undefined", 22 | "line": 1 23 | }, 24 | { 25 | "name": "@pending", 26 | "line": 1 27 | }, 28 | { 29 | "name": "@happy-flow", 30 | "line": 1 31 | } 32 | ], 33 | "uri": "e2e-tests/features/undefined.feature", 34 | "elements": [ 35 | { 36 | "id": "undefined-pending-passed-scenarios-specified-v3;timmy-wants-to-see-undefined-scenarios", 37 | "keyword": "Scenario", 38 | "line": 7, 39 | "name": "Timmy wants to see undefined scenarios", 40 | "tags": [ 41 | { 42 | "name": "@undefined", 43 | "line": 1 44 | } 45 | ], 46 | "type": "scenario", 47 | "steps": [ 48 | { 49 | "arguments": [], 50 | "keyword": "Given ", 51 | "line": 8, 52 | "name": "Timmy visits the Angular homepage", 53 | "result": { 54 | "status": "undefined" 55 | } 56 | }, 57 | { 58 | "arguments": [], 59 | "keyword": "When ", 60 | "line": 9, 61 | "name": "he doesn't do anything", 62 | "result": { 63 | "status": "undefined" 64 | } 65 | }, 66 | { 67 | "arguments": [], 68 | "keyword": "Then ", 69 | "line": 10, 70 | "name": "nothing has been done", 71 | "result": { 72 | "status": "undefined" 73 | } 74 | }, 75 | { 76 | "keyword": "After", 77 | "hidden": true, 78 | "match": { 79 | "location": "e2e-tests/config/helpers/after.scenario.ts:23" 80 | }, 81 | "result": { 82 | "status": "passed", 83 | "duration": 1 84 | } 85 | } 86 | ] 87 | }, 88 | { 89 | "id": "undefined-pending-passed-scenarios-specified-v3;johnny-wants-to-see-pending-scenarios", 90 | "keyword": "Scenario", 91 | "line": 5, 92 | "name": "Johnny wants to see pending scenarios", 93 | "tags": [ 94 | { 95 | "name": "@pending", 96 | "line": 1 97 | }], 98 | "type": "scenario", 99 | "steps": [ 100 | { 101 | "arguments": [], 102 | "keyword": "Given ", 103 | "line": 6, 104 | "name": "Johnny visits the Angular homepage", 105 | "match": { 106 | "location": "e2e-tests/features/pending.steps.ts:4" 107 | }, 108 | "result": { 109 | "status": "pending", 110 | "duration": 3 111 | } 112 | }, 113 | { 114 | "arguments": [], 115 | "keyword": "When ", 116 | "line": 7, 117 | "name": "he clicks on the first link", 118 | "match": { 119 | "location": "e2e-tests/features/pending.steps.ts:9" 120 | }, 121 | "result": { 122 | "status": "skipped" 123 | } 124 | }, 125 | { 126 | "arguments": [], 127 | "keyword": "Then ", 128 | "line": 8, 129 | "name": "he expects that something happens", 130 | "match": { 131 | "location": "e2e-tests/features/pending.steps.ts:14" 132 | }, 133 | "result": { 134 | "status": "skipped" 135 | } 136 | }, 137 | { 138 | "keyword": "After", 139 | "hidden": true, 140 | "match": { 141 | "location": "e2e-tests/config/helpers/after.scenario.ts:23" 142 | }, 143 | "result": { 144 | "status": "passed" 145 | } 146 | } 147 | ] 148 | }, 149 | { 150 | "id": "undefined-pending-passed-scenarios-specified-v3;add-a-todo", 151 | "keyword": "Scenario", 152 | "line": 18, 153 | "name": "Add a todo", 154 | "tags": [ 155 | { 156 | "name": "@happy-flow", 157 | "line": 1 158 | } 159 | ], 160 | "type": "scenario", 161 | "steps": [ 162 | { 163 | "arguments": [], 164 | "keyword": "Given ", 165 | "line": 5, 166 | "name": "I visit the Angular homepage", 167 | "match": { 168 | "location": "e2e-tests/features/example.steps.ts:6" 169 | }, 170 | "result": { 171 | "status": "passed", 172 | "duration": 502 173 | } 174 | }, 175 | { 176 | "arguments": [], 177 | "keyword": "Given ", 178 | "line": 19, 179 | "name": "I add the todo \"write a protractor test\"", 180 | "match": { 181 | "location": "e2e-tests/features/example.steps.ts:23" 182 | }, 183 | "result": { 184 | "status": "passed", 185 | "duration": 1029 186 | } 187 | }, 188 | { 189 | "arguments": [], 190 | "keyword": "Then ", 191 | "line": 20, 192 | "name": "I should have 3 todo's", 193 | "match": { 194 | "location": "e2e-tests/features/example.steps.ts:47" 195 | }, 196 | "result": { 197 | "status": "passed", 198 | "duration": 25 199 | } 200 | }, 201 | { 202 | "arguments": [], 203 | "keyword": "And ", 204 | "line": 21, 205 | "name": "the last todo should hold \"write a protractor test\"", 206 | "match": { 207 | "location": "e2e-tests/features/example.steps.ts:40" 208 | }, 209 | "result": { 210 | "status": "passed", 211 | "duration": 49 212 | } 213 | }, 214 | { 215 | "keyword": "After", 216 | "hidden": true, 217 | "match": { 218 | "location": "e2e-tests/config/helpers/after.scenario.ts:23" 219 | }, 220 | "result": { 221 | "status": "passed", 222 | "duration": 1 223 | } 224 | } 225 | ] 226 | } 227 | ] 228 | } 229 | ] 230 | -------------------------------------------------------------------------------- /test/unit/data/no-jsons/empty.md: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/WasiqB/multiple-cucumber-html-reporter/937e125631970392cbff8b1237a45dc47e21f595/test/unit/data/no-jsons/empty.md -------------------------------------------------------------------------------- /test/unit/data/output/provided-metadata.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "metadata":{ 4 | "browser": { 5 | "name": "chrome", 6 | "version": "1" 7 | }, 8 | "device": "Local test machine", 9 | "platform": { 10 | "name": "Ubuntu", 11 | "version": "16.04" 12 | } 13 | }, 14 | "description": " No elements have been found\n So the report should hold no scenarios and steps", 15 | "id": "no_elements.1494777183356", 16 | "keyword": "Feature", 17 | "line": 1, 18 | "name": "No elements", 19 | "tags": [], 20 | "uri": "no_elements.1494777183356" 21 | } 22 | ] 23 | -------------------------------------------------------------------------------- /test/unit/generate-json.spec.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const fs = require('fs-extra'); 4 | const path = require('path'); 5 | const multiCucumberHTMLReporter = require('../../lib/generate-report'); 6 | const REPORT_PATH = './.tmp/'; 7 | 8 | describe('generate-report.js', () => { 9 | describe('Happy flows', () => { 10 | it('should create a report from the merged found json files without provided custom data', () => { 11 | fs.removeSync(REPORT_PATH); 12 | multiCucumberHTMLReporter.generate({ 13 | jsonDir: './test/unit/data/json', 14 | reportPath: REPORT_PATH, 15 | saveCollectedJSON: true, 16 | displayDuration: true 17 | }); 18 | 19 | expect(fs.statSync(`${path.join(process.cwd(), REPORT_PATH, 'index.html')}`).isFile()) 20 | .toEqual(true, 'Index file exists'); 21 | expect(function() { fs.statSync(`${path.join(process.cwd(), REPORT_PATH, 'features/happy-flow-v2.html')}`); }) 22 | .toThrow(); 23 | expect(fs.statSync(`${path.join(process.cwd(), REPORT_PATH, 'merged-output.json')}`).isFile()) 24 | .toEqual(true, 'merged-output.json file exists'); 25 | expect(fs.statSync(`${path.join(process.cwd(), REPORT_PATH, 'enriched-output.json')}`).isFile()) 26 | .toEqual(true, 'temp-output.json file exists'); 27 | }); 28 | it('should create a report with the report time', () => { 29 | fs.removeSync(REPORT_PATH); 30 | multiCucumberHTMLReporter.generate({ 31 | jsonDir: './test/unit/data/json', 32 | reportPath: REPORT_PATH, 33 | saveCollectedJSON: true, 34 | displayDuration: true, 35 | displayReportTime: true 36 | }); 37 | 38 | expect(fs.statSync(`${path.join(process.cwd(), REPORT_PATH, 'index.html')}`).isFile()) 39 | .toEqual(true, 'Index file exists'); 40 | expect(fs.readFileSync(`${path.join(process.cwd(), REPORT_PATH, 'index.html')}`, 'utf8')).toContain('>Date'); 41 | }); 42 | it('should create a report from the merged found json files with custom data with static file paths', () => { 43 | fs.removeSync(REPORT_PATH); 44 | multiCucumberHTMLReporter.generate({ 45 | jsonDir: './test/unit/data/json', 46 | reportPath: REPORT_PATH, 47 | staticFilePath: true, 48 | saveCollectedJSON: true, 49 | reportName: 'You can adjust this report name', 50 | customData: { 51 | title: 'Run info', 52 | data: [ 53 | {label: 'Project', value: 'Custom project'}, 54 | {label: 'Release', value: '1.2.3'}, 55 | {label: 'Cycle', value: 'B11221.34321'}, 56 | {label: 'Execution Start Time', value: 'Nov 19th 2017, 02:31 PM EST'}, 57 | {label: 'Execution End Time', value: 'Nov 19th 2017, 02:56 PM EST'} 58 | ] 59 | }, 60 | displayDuration: true, 61 | durationInMS: true 62 | }); 63 | 64 | expect(fs.statSync(`${path.join(process.cwd(), REPORT_PATH, 'index.html')}`).isFile()) 65 | .toEqual(true, 'Index file exists'); 66 | expect(fs.statSync(`${path.join(process.cwd(), REPORT_PATH, 'features/happy-flow-v2.html')}`).isFile()) 67 | .toEqual(true, 'uuid free feature exists'); 68 | expect(fs.statSync(`${path.join(process.cwd(), REPORT_PATH, 'merged-output.json')}`).isFile()) 69 | .toEqual(true, 'merged-output.json file exists'); 70 | expect(fs.statSync(`${path.join(process.cwd(), REPORT_PATH, 'enriched-output.json')}`).isFile()) 71 | .toEqual(true, 'temp-output.json file exists'); 72 | }); 73 | it('should create a report from the merged found json files with custom metadata', () => { 74 | fs.removeSync(REPORT_PATH); 75 | multiCucumberHTMLReporter.generate({ 76 | jsonDir: './test/unit/data/custom-metadata-json/', 77 | reportPath: REPORT_PATH, 78 | customMetadata: true 79 | }); 80 | 81 | expect(fs.statSync(`${path.join(process.cwd(), REPORT_PATH, 'index.html')}`).isFile()) 82 | .toEqual(true, 'Index file exists'); 83 | }); 84 | 85 | it('should create a report from the merged found json files and with array of embedded items', () => { 86 | fs.removeSync(REPORT_PATH); 87 | multiCucumberHTMLReporter.generate({ 88 | jsonDir: './test/unit/data/embedded-array-json/', 89 | reportName: 'Embedded array of various mimeType', 90 | reportPath: REPORT_PATH, 91 | customStyle: path.join(__dirname, '../my.css'), 92 | customMetadata: false 93 | }); 94 | 95 | expect(fs.statSync(`${path.join(process.cwd(), REPORT_PATH, 'index.html')}`).isFile()) 96 | .toEqual(true, 'Index file exists'); 97 | }); 98 | }); 99 | 100 | describe('failures', () => { 101 | it('should throw an error when no options are provided', () => { 102 | expect(() => multiCucumberHTMLReporter.generate()).toThrowError('Options need to be provided.'); 103 | }); 104 | 105 | it('should throw an error when the json folder does not exist', () => { 106 | expect(() => multiCucumberHTMLReporter.generate({})).toThrowError(`A path which holds the JSON files should be provided.`); 107 | }); 108 | 109 | it('should throw an error when the report folder is not provided', () => { 110 | expect(() => multiCucumberHTMLReporter.generate({ 111 | jsonDir: './test/unit/data/json' 112 | })).toThrowError(`An output path for the reports should be defined, no path was provided.`); 113 | }); 114 | }); 115 | }); 116 | -------------------------------------------------------------------------------- /test/unit/jasmine.json: -------------------------------------------------------------------------------- 1 | { 2 | "spec_dir": "test/", 3 | "spec_files": [ 4 | "unit/*.spec.js" 5 | ], 6 | "stopSpecOnExpectationFailure": false, 7 | "random": false 8 | } 9 | --------------------------------------------------------------------------------