From 1119d240263c82e3f4674327dde63c1c2b3e2e58 Mon Sep 17 00:00:00 2001 From: Jonas Amundsen Date: Sun, 19 Mar 2023 13:06:25 +0100 Subject: [PATCH] Replace cucumber-json-formatter with native components This relates to #795, #827, #870, #966, #967. --- .github/workflows/build.yml | 13 +- CHANGELOG.md | 2 + docs/configuration.md | 2 - docs/faq.md | 5 - docs/json-report.md | 14 --- features/attachments.feature | 10 +- features/fixtures/attachments/screenshot.json | 3 + .../fixtures/attachments/screenshot.ndjson | 2 +- .../{string.json => string-base64.json} | 3 + .../string-literal.json} | 33 ++--- features/fixtures/failing-after.json | 13 +- features/fixtures/failing-before.json | 18 +-- features/fixtures/failing-step.json | 8 +- features/fixtures/multiple-features.json | 6 + .../fixtures/parameterized-scenario-name.json | 37 ++++++ features/fixtures/passed-example.json | 3 + features/fixtures/passed-outline.json | 9 +- features/fixtures/pending-steps.json | 11 +- features/fixtures/rescued-error.json | 10 +- features/fixtures/undefined-steps.json | 10 +- features/issues/736.feature | 1 - features/issues/795.feature | 30 +++++ features/json_config.feature | 64 ---------- features/reporters/json.feature | 4 +- features/step_definitions/json_steps.ts | 19 +-- lib/add-cucumber-preprocessor-plugin.ts | 113 +++++++++++++----- lib/constants.ts | 3 + lib/create-tests.ts | 6 + lib/preprocessor-configuration.ts | 64 ---------- package.json | 2 +- 30 files changed, 252 insertions(+), 266 deletions(-) rename features/fixtures/attachments/{string.json => string-base64.json} (92%) rename features/fixtures/{retried.json => attachments/string-literal.json} (56%) create mode 100644 features/fixtures/parameterized-scenario-name.json create mode 100644 features/issues/795.feature delete mode 100644 features/json_config.feature diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index a290f512..be4efd00 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -41,13 +41,10 @@ jobs: - name: Dependencies env: CYPRESS_INSTALL_BINARY: "0" - CUCUMBER_JSON_FORMATTER_URL: https://github.com/cucumber/common/releases/download/json-formatter%2Fgo%2Fv19.0.0/cucumber-json-formatter-linux-amd64 run: | npm install --force && \ npm install --no-save cypress@${{ matrix.cypress-version }} && \ - env -u CYPRESS_INSTALL_BINARY npx cypress install && \ - curl -L $CUCUMBER_JSON_FORMATTER_URL > /usr/bin/cucumber-json-formatter && \ - chmod +x /usr/bin/cucumber-json-formatter + env -u CYPRESS_INSTALL_BINARY npx cypress install - name: Build run: npm run build - name: Test @@ -75,13 +72,7 @@ jobs: key: cypress-windows - name: Dependencies shell: bash - env: - CUCUMBER_JSON_FORMATTER_URL: https://github.com/cucumber/common/releases/download/json-formatter%2Fgo%2Fv19.0.0/cucumber-json-formatter-windows-amd64 - run: | - npm install - curl -L $CUCUMBER_JSON_FORMATTER_URL > cucumber-json-formatter.exe - chmod +x ./cucumber-json-formatter.exe - echo $GITHUB_WORKSPACE >> $GITHUB_PATH + run: npm install - name: Build run: npm run build # https://github.com/webpack/webpack/issues/12759 diff --git a/CHANGELOG.md b/CHANGELOG.md index 4b4600e8..a3ac4712 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,8 @@ All notable changes to this project will be documented in this file. - Correctly set `willBeRetried` in messages reports, fixes [#849](https://github.com/badeball/cypress-cucumber-preprocessor/issues/849). +- Replace [cucumber-json-formatter](https://github.com/cucumber/json-formatter) with native components, relates to [#795](https://github.com/badeball/cypress-cucumber-preprocessor/issues/795), [#827](https://github.com/badeball/cypress-cucumber-preprocessor/issues/827), [#870](https://github.com/badeball/cypress-cucumber-preprocessor/issues/870), [#966](https://github.com/badeball/cypress-cucumber-preprocessor/issues/966) and [#967](https://github.com/badeball/cypress-cucumber-preprocessor/issues/967). + ## v15.1.5 - Correctly escape injected values to glob patterns, fixes [#946](https://github.com/badeball/cypress-cucumber-preprocessor/issues/946). diff --git a/docs/configuration.md b/docs/configuration.md index 4811bdbe..e665bd06 100644 --- a/docs/configuration.md +++ b/docs/configuration.md @@ -59,8 +59,6 @@ Every configuration option has a similar key which can be use to override it, sh | `messages.enabled` | `messagesEnabled` | `true`, `false` | | `messages.output` | `messagesOutput` | `cucumber-messages.ndjson` | | `json.enabled` | `jsonEnabled` | `true`, `false` | -| `json.formatter` | `jsonFormatter` | `/usr/bin/cucumber-json-formatter` | -| `json.args ` | `jsonArgs ` | `["custom-json-formatter.js"]` | | `json.output` | `jsonOutput` | `cucumber-report.json` | | `html.enabled` | `htmlEnabled` | `true`, `false` | | `html.output` | `htmlOutput` | `cucumber-report.html` | diff --git a/docs/faq.md b/docs/faq.md index 73a88cd1..7b3b8ecb 100644 --- a/docs/faq.md +++ b/docs/faq.md @@ -2,7 +2,6 @@ * [`--env` / `tags` isn't picked up](#--env--tags-isnt-picked-up) * [I get `fs_1.promises.rm is not a function`](#i-get-fs_1promisesrm-is-not-a-function) -* [I get `spawn cucumber-json-formatter ENOENT`](#i-get-spawn-cucumber-json-formatter-enoent) * [Why is `cypress-tags` missing?](#why-is-cypress-tags-missing) * [My JSON report isn't generated](#my-json-report-isnt-generated) * [JSON reports aren't generated in open / interactive mode](#json-reports-arent-generated-in-open--interactive-mode) @@ -18,10 +17,6 @@ This might be because you're trying to specify `-e / --env` multiple times, but Upgrade your node version to at least [v14.14.0](https://nodejs.org/api/fs.html#fspromisesrmpath-options). -## I get `spawn cucumber-json-formatter ENOENT` - -You need to install `cucumber-json-formatter` **yourself**, as per [documentation](json-report.md). - ## Why is `cypress-tags` missing? The `cypress-tags` executable has been removed and made redundant. Specs containing no matching scenarios are [automatically filtered](https://github.com/badeball/cypress-cucumber-preprocessor/blob/master/docs/tags.md#running-a-subset-of-scenarios), provided that `filterSpecs` is set to true. diff --git a/docs/json-report.md b/docs/json-report.md index 33d5a2a1..046e23ca 100644 --- a/docs/json-report.md +++ b/docs/json-report.md @@ -10,10 +10,6 @@ JSON reports can be enabled using the `json.enabled` property. The preprocessor } ``` -This **requires** you to have downloaded and installed the [cucumber-json-formatter](https://github.com/cucumber/json-formatter) **yourself**. Arch Linux users can install it from [AUR](https://aur.archlinux.org/packages/cucumber-json-formatter). - -The location of the executable is configurable through the `json.formatter` property, but it will by default search for `cucumber-json-formatter` in your `PATH`. - The report is outputted to `cucumber-report.json` in the project directory, but can be configured through the `json.output` property. ## Screenshots @@ -61,13 +57,3 @@ Given("a step", function() { attach("Zm9vYmFy", "base64:text/plain"); }); ``` - -## Known issues - -Some issues with `cucumber-json-formatter` are known and don't need to be reported again. - -- Inaccurate output of `match` on undefined step definition (https://github.com/cucumber/json-formatter/issues/2) - -- Inaccurate output of retried scenarios (https://github.com/cucumber/json-formatter/issues/4) - -- Inaccurate output of scenario outlined with parameterized name (https://github.com/cucumber/json-formatter/issues/25) diff --git a/features/attachments.feature b/features/attachments.feature index cd7b9070..6767927d 100644 --- a/features/attachments.feature +++ b/features/attachments.feature @@ -9,8 +9,10 @@ Feature: attachments } } """ - And I've ensured cucumber-json-formatter is installed + # This test's result will likely change in the future, see + # https://github.com/cucumber/cucumber-js/issues/2260 and + # https://github.com/cucumber/cucumber-js/pull/2261 Scenario: string identity Given a file named "cypress/e2e/a.feature" with: """ @@ -27,7 +29,7 @@ Feature: attachments """ When I run cypress Then it passes - And there should be a JSON output similar to "fixtures/attachments/string.json" + And there should be a JSON output similar to "fixtures/attachments/string-literal.json" Scenario: array buffer Given a file named "cypress/e2e/a.feature" with: @@ -45,7 +47,7 @@ Feature: attachments """ When I run cypress Then it passes - And there should be a JSON output similar to "fixtures/attachments/string.json" + And there should be a JSON output similar to "fixtures/attachments/string-base64.json" Scenario: string encoded Given a file named "cypress/e2e/a.feature" with: @@ -64,4 +66,4 @@ Feature: attachments """ When I run cypress Then it passes - And there should be a JSON output similar to "fixtures/attachments/string.json" + And there should be a JSON output similar to "fixtures/attachments/string-base64.json" diff --git a/features/fixtures/attachments/screenshot.json b/features/fixtures/attachments/screenshot.json index 0fc89bec..b3d598f7 100644 --- a/features/fixtures/attachments/screenshot.json +++ b/features/fixtures/attachments/screenshot.json @@ -10,6 +10,7 @@ "name": "a scenario", "steps": [ { + "arguments": [], "keyword": "Given ", "line": 3, "name": "a step", @@ -28,6 +29,7 @@ ] } ], + "tags": [], "type": "scenario" } ], @@ -35,6 +37,7 @@ "keyword": "Feature", "line": 1, "name": "a feature", + "tags": [], "uri": "cypress/e2e/a.feature" } ] diff --git a/features/fixtures/attachments/screenshot.ndjson b/features/fixtures/attachments/screenshot.ndjson index 6b3a2aaa..77cf9061 100644 --- a/features/fixtures/attachments/screenshot.ndjson +++ b/features/fixtures/attachments/screenshot.ndjson @@ -5,7 +5,7 @@ {"stepDefinition":{"id":"id","pattern":{"source":"a step","type":"CUCUMBER_EXPRESSION"},"sourceReference":{"uri":"not available","location":{"line":0}}}} {"testCase":{"id":"id","pickleId":"id","testSteps":[{"id":"id","pickleStepId":"id","stepDefinitionIds":["id"]}]}} {"testCaseStarted":{"id":"id","testCaseId":"id","attempt":0,"timestamp":{"seconds":0,"nanos":0}}} -{"attachment":{"testStepId":"id","body":"iVBORw0KGgoAAAANSUhEUgAAABQAAAAUCAYAAACNiR0NAAAAM0lEQVR4Aa3BAQEAAAiDMKR/51uC7QYjJDGJSUxiEpOYxCQmMYlJTGISk5jEJCYxiUnsARwEAibDACoRAAAAAElFTkSuQmCC","mediaType":"image/png","contentEncoding":"BASE64"}} +{"attachment":{"testCaseStartedId": "id", "testStepId":"id","body":"iVBORw0KGgoAAAANSUhEUgAAABQAAAAUCAYAAACNiR0NAAAAM0lEQVR4Aa3BAQEAAAiDMKR/51uC7QYjJDGJSUxiEpOYxCQmMYlJTGISk5jEJCYxiUnsARwEAibDACoRAAAAAElFTkSuQmCC","mediaType":"image/png","contentEncoding":"BASE64"}} {"testStepStarted":{"testStepId":"id","testCaseStartedId":"id","timestamp":{"seconds":0,"nanos":0}}} {"testStepFinished":{"testStepId":"id","testCaseStartedId":"id","testStepResult":{"status":"PASSED","duration":0},"timestamp":{"seconds":0,"nanos":0}}} {"testCaseFinished":{"testCaseStartedId":"id","timestamp":{"seconds":0,"nanos":0},"willBeRetried":false}} diff --git a/features/fixtures/attachments/string.json b/features/fixtures/attachments/string-base64.json similarity index 92% rename from features/fixtures/attachments/string.json rename to features/fixtures/attachments/string-base64.json index 713ca830..bb27e9f6 100644 --- a/features/fixtures/attachments/string.json +++ b/features/fixtures/attachments/string-base64.json @@ -10,6 +10,7 @@ "name": "a scenario", "steps": [ { + "arguments": [], "keyword": "Given ", "line": 3, "name": "a step", @@ -28,6 +29,7 @@ ] } ], + "tags": [], "type": "scenario" } ], @@ -35,6 +37,7 @@ "keyword": "Feature", "line": 1, "name": "a feature", + "tags": [], "uri": "cypress/e2e/a.feature" } ] diff --git a/features/fixtures/retried.json b/features/fixtures/attachments/string-literal.json similarity index 56% rename from features/fixtures/retried.json rename to features/fixtures/attachments/string-literal.json index 7fa6b498..ceed5637 100644 --- a/features/fixtures/retried.json +++ b/features/fixtures/attachments/string-literal.json @@ -10,28 +10,7 @@ "name": "a scenario", "steps": [ { - "keyword": "Given ", - "line": 3, - "name": "a step", - "result": { - "status": "failed", - "error_message": "some error" - }, - "match": { - "location": "not available:0" - } - } - ], - "type": "scenario" - }, - { - "description": "", - "id": "a-feature;a-scenario", - "keyword": "Scenario", - "line": 2, - "name": "a scenario", - "steps": [ - { + "arguments": [], "keyword": "Given ", "line": 3, "name": "a step", @@ -41,9 +20,16 @@ }, "match": { "location": "not available:0" - } + }, + "embeddings": [ + { + "data": "foobar", + "mime_type": "text/plain" + } + ] } ], + "tags": [], "type": "scenario" } ], @@ -51,6 +37,7 @@ "keyword": "Feature", "line": 1, "name": "a feature", + "tags": [], "uri": "cypress/e2e/a.feature" } ] diff --git a/features/fixtures/failing-after.json b/features/fixtures/failing-after.json index 6fab6aad..55ef5c94 100644 --- a/features/fixtures/failing-after.json +++ b/features/fixtures/failing-after.json @@ -10,6 +10,7 @@ "name": "a scenario", "steps": [ { + "arguments": [], "keyword": "Given ", "line": 3, "name": "a step", @@ -20,19 +21,18 @@ "match": { "location": "not available:0" } - } - ], - "after": [ + }, { + "keyword": "After", + "hidden": true, "result": { "status": "failed", + "duration": 0, "error_message": "some error" - }, - "match": { - "location": "not available:0" } } ], + "tags": [], "type": "scenario" } ], @@ -40,6 +40,7 @@ "keyword": "Feature", "line": 1, "name": "a feature", + "tags": [], "uri": "cypress/e2e/a.feature" } ] diff --git a/features/fixtures/failing-before.json b/features/fixtures/failing-before.json index 5859904d..f070a211 100644 --- a/features/fixtures/failing-before.json +++ b/features/fixtures/failing-before.json @@ -8,30 +8,31 @@ "keyword": "Scenario", "line": 2, "name": "a scenario", - "before": [ + "steps": [ { + "keyword": "Before", + "hidden": true, "result": { "status": "failed", + "duration": 0, "error_message": "some error" - }, - "match": { - "location": "not available:0" } - } - ], - "steps": [ + }, { + "arguments": [], "keyword": "Given ", "line": 3, "name": "a step", "result": { - "status": "skipped" + "status": "skipped", + "duration": 0 }, "match": { "location": "not available:0" } } ], + "tags": [], "type": "scenario" } ], @@ -39,6 +40,7 @@ "keyword": "Feature", "line": 1, "name": "a feature", + "tags": [], "uri": "cypress/e2e/a.feature" } ] diff --git a/features/fixtures/failing-step.json b/features/fixtures/failing-step.json index 44bd84f4..091f9f8c 100644 --- a/features/fixtures/failing-step.json +++ b/features/fixtures/failing-step.json @@ -10,11 +10,13 @@ "name": "a scenario", "steps": [ { + "arguments": [], "keyword": "Given ", "line": 3, "name": "a failing step", "result": { "status": "failed", + "duration": 0, "error_message": "some error" }, "match": { @@ -22,17 +24,20 @@ } }, { + "arguments": [], "keyword": "And ", "line": 4, "name": "another step", "result": { - "status": "skipped" + "status": "skipped", + "duration": 0 }, "match": { "location": "not available:0" } } ], + "tags": [], "type": "scenario" } ], @@ -40,6 +45,7 @@ "keyword": "Feature", "line": 1, "name": "a feature", + "tags": [], "uri": "cypress/e2e/a.feature" } ] diff --git a/features/fixtures/multiple-features.json b/features/fixtures/multiple-features.json index 2be251ae..9fa58fc7 100644 --- a/features/fixtures/multiple-features.json +++ b/features/fixtures/multiple-features.json @@ -10,6 +10,7 @@ "name": "a scenario", "steps": [ { + "arguments": [], "keyword": "Given ", "line": 3, "name": "a step", @@ -22,6 +23,7 @@ } } ], + "tags": [], "type": "scenario" } ], @@ -29,6 +31,7 @@ "keyword": "Feature", "line": 1, "name": "a feature", + "tags": [], "uri": "cypress/e2e/a.feature" }, { @@ -42,6 +45,7 @@ "name": "another scenario", "steps": [ { + "arguments": [], "keyword": "Given ", "line": 3, "name": "a step", @@ -54,6 +58,7 @@ } } ], + "tags": [], "type": "scenario" } ], @@ -61,6 +66,7 @@ "keyword": "Feature", "line": 1, "name": "another feature", + "tags": [], "uri": "cypress/e2e/b.feature" } ] diff --git a/features/fixtures/parameterized-scenario-name.json b/features/fixtures/parameterized-scenario-name.json new file mode 100644 index 00000000..d38a1de6 --- /dev/null +++ b/features/fixtures/parameterized-scenario-name.json @@ -0,0 +1,37 @@ +[ + { + "description": "", + "elements": [ + { + "description": "", + "id": "a-feature;foo", + "keyword": "Scenario Outline", + "line": 7, + "name": "foo", + "steps": [ + { + "arguments": [], + "keyword": "Given ", + "line": 3, + "name": "a step", + "match": { + "location": "not available:0" + }, + "result": { + "status": "passed", + "duration": 0 + } + } + ], + "tags": [], + "type": "scenario" + } + ], + "id": "a-feature", + "line": 1, + "keyword": "Feature", + "name": "a feature", + "tags": [], + "uri": "cypress/e2e/a.feature" + } +] diff --git a/features/fixtures/passed-example.json b/features/fixtures/passed-example.json index 6583582d..ff7eff8b 100644 --- a/features/fixtures/passed-example.json +++ b/features/fixtures/passed-example.json @@ -10,6 +10,7 @@ "name": "a scenario", "steps": [ { + "arguments": [], "keyword": "Given ", "line": 3, "name": "a step", @@ -22,6 +23,7 @@ } } ], + "tags": [], "type": "scenario" } ], @@ -29,6 +31,7 @@ "keyword": "Feature", "line": 1, "name": "a feature", + "tags": [], "uri": "cypress/e2e/a.feature" } ] diff --git a/features/fixtures/passed-outline.json b/features/fixtures/passed-outline.json index 429c4c75..4c2ca419 100644 --- a/features/fixtures/passed-outline.json +++ b/features/fixtures/passed-outline.json @@ -4,12 +4,13 @@ "elements": [ { "description": "", - "id": "a-feature;a-scenario;;2", + "id": "a-feature;a-scenario", "keyword": "Scenario Outline", "line": 6, "name": "a scenario", "steps": [ { + "arguments": [], "keyword": "Given ", "line": 3, "name": "a step", @@ -22,16 +23,18 @@ } } ], + "tags": [], "type": "scenario" }, { "description": "", - "id": "a-feature;a-scenario;;3", + "id": "a-feature;a-scenario", "keyword": "Scenario Outline", "line": 7, "name": "a scenario", "steps": [ { + "arguments": [], "keyword": "Given ", "line": 3, "name": "a step", @@ -44,6 +47,7 @@ } } ], + "tags": [], "type": "scenario" } ], @@ -51,6 +55,7 @@ "keyword": "Feature", "line": 1, "name": "a feature", + "tags": [], "uri": "cypress/e2e/a.feature" } ] diff --git a/features/fixtures/pending-steps.json b/features/fixtures/pending-steps.json index ca076344..a05007ab 100644 --- a/features/fixtures/pending-steps.json +++ b/features/fixtures/pending-steps.json @@ -10,6 +10,7 @@ "name": "a scenario", "steps": [ { + "arguments": [], "keyword": "Given ", "line": 3, "name": "a pending step", @@ -22,28 +23,33 @@ } }, { + "arguments": [], "keyword": "And ", "line": 4, "name": "another pending step", "result": { - "status": "skipped" + "status": "skipped", + "duration": 0 }, "match": { "location": "not available:0" } }, { + "arguments": [], "keyword": "And ", "line": 5, "name": "an implemented step", "result": { - "status": "skipped" + "status": "skipped", + "duration": 0 }, "match": { "location": "not available:0" } } ], + "tags": [], "type": "scenario" } ], @@ -51,6 +57,7 @@ "keyword": "Feature", "line": 1, "name": "a feature", + "tags": [], "uri": "cypress/e2e/a.feature" } ] diff --git a/features/fixtures/rescued-error.json b/features/fixtures/rescued-error.json index 8c69a6f4..a49c1d07 100644 --- a/features/fixtures/rescued-error.json +++ b/features/fixtures/rescued-error.json @@ -10,28 +10,33 @@ "name": "a scenario", "steps": [ { + "arguments": [], "keyword": "Given ", "line": 3, "name": "a failing step", "result": { - "status": "unknown" + "status": "unknown", + "duration": 0 }, "match": { "location": "not available:0" } }, { + "arguments": [], "keyword": "And ", "line": 4, "name": "an unimplemented step", "result": { - "status": "unknown" + "status": "unknown", + "duration": 0 }, "match": { "location": "not available:0" } } ], + "tags": [], "type": "scenario" } ], @@ -39,6 +44,7 @@ "keyword": "Feature", "line": 1, "name": "a feature", + "tags": [], "uri": "cypress/e2e/a.feature" } ] diff --git a/features/fixtures/undefined-steps.json b/features/fixtures/undefined-steps.json index fdf51a31..8969fb3b 100644 --- a/features/fixtures/undefined-steps.json +++ b/features/fixtures/undefined-steps.json @@ -10,28 +10,33 @@ "name": "a scenario", "steps": [ { + "arguments": [], "keyword": "Given ", "line": 3, "name": "an undefined step", "result": { - "status": "undefined" + "status": "undefined", + "duration": 0 }, "match": { "location": "not available:0" } }, { + "arguments": [], "keyword": "And ", "line": 4, "name": "another step", "result": { - "status": "skipped" + "status": "skipped", + "duration": 0 }, "match": { "location": "not available:0" } } ], + "tags": [], "type": "scenario" } ], @@ -39,6 +44,7 @@ "keyword": "Feature", "line": 1, "name": "a feature", + "tags": [], "uri": "cypress/e2e/a.feature" } ] diff --git a/features/issues/736.feature b/features/issues/736.feature index 6c78d880..4f5c2a45 100644 --- a/features/issues/736.feature +++ b/features/issues/736.feature @@ -15,7 +15,6 @@ Feature: create output directories } } """ - And I've ensured cucumber-json-formatter is installed Scenario: Given a file named "cypress/e2e/a.feature" with: diff --git a/features/issues/795.feature b/features/issues/795.feature new file mode 100644 index 00000000..68f19202 --- /dev/null +++ b/features/issues/795.feature @@ -0,0 +1,30 @@ +# https://github.com/badeball/cypress-cucumber-preprocessor/issues/795 + +Feature: scenario outline with parameterized name + Scenario: scenario outline with parameterized name + Given additional preprocessor configuration + """ + { + "json": { + "enabled": true + } + } + """ + And a file named "cypress/e2e/a.feature" with: + """ + Feature: a feature + Scenario Outline: + Given a step + + Examples: + | value | + | foo | + """ + And a file named "cypress/support/step_definitions/steps.js" with: + """ + const { Given } = require("@badeball/cypress-cucumber-preprocessor"); + Given("a step", () => {}) + """ + When I run cypress + Then it passes + And there should be a JSON output similar to "fixtures/parameterized-scenario-name.json" diff --git a/features/json_config.feature b/features/json_config.feature deleted file mode 100644 index dd0850ed..00000000 --- a/features/json_config.feature +++ /dev/null @@ -1,64 +0,0 @@ -Feature: JSON config - - Background: - Given I've ensured cucumber-json-formatter is installed - And a file named "assert_args_and_exec.js" with: - """ - const assert = require("assert/strict"); - assert.match(process.argv[0], /node(:?\.exe)?$/); - assert.match(process.argv[1], /assert_args_and_exec\.js$/); - assert.equal(process.argv[2], "cucumber-json-formatter"); - require("child_process").execSync(process.argv[2], { stdio: "inherit" }); - """ - - Scenario: passed example with config args - Given a file named "cypress/e2e/a.feature" with: - """ - Feature: a feature - Scenario: a scenario - Given a step - """ - And a file named "cypress/support/step_definitions/steps.js" with: - """ - const { Given } = require("@badeball/cypress-cucumber-preprocessor"); - Given("a step", function() {}) - """ - And additional preprocessor configuration - """ - { - "json": { - "args": ["assert_args_and_exec.js", "cucumber-json-formatter"], - "enabled": true, - "formatter": "node" - } - } - """ - When I run cypress - Then it passes - And there should be a JSON output similar to "fixtures/passed-example.json" - - Scenario: passed example with jsonArgs through -e - Given a file named "cypress/e2e/a.feature" with: - """ - Feature: a feature - Scenario: a scenario - Given a step - """ - And a file named "cypress/support/step_definitions/steps.js" with: - """ - const { Given } = require("@badeball/cypress-cucumber-preprocessor"); - Given("a step", function() {}) - """ - And additional preprocessor configuration - """ - { - "json": { - "enabled": true, - "formatter": "node" - } - } - """ - When I run cypress with "-e jsonArgs=[\"assert_args_and_exec.js\",\"cucumber-json-formatter\"]" - Then it passes - And there should be a JSON output similar to "fixtures/passed-example.json" - diff --git a/features/reporters/json.feature b/features/reporters/json.feature index 3efe9275..05292d34 100644 --- a/features/reporters/json.feature +++ b/features/reporters/json.feature @@ -9,7 +9,6 @@ Feature: JSON formatter } } """ - And I've ensured cucumber-json-formatter is installed Scenario: passed example Given a file named "cypress/e2e/a.feature" with: @@ -223,8 +222,7 @@ Feature: JSON formatter """ When I run cypress Then it passes - And there should be a JSON output similar to "fixtures/retried.json" - # And there should be a JSON output similar to "fixtures/passed-example.json" + And there should be a JSON output similar to "fixtures/passed-example.json" Scenario: rescued error Given a file named "cypress/e2e/a.feature" with: diff --git a/features/step_definitions/json_steps.ts b/features/step_definitions/json_steps.ts index 8b8d06d8..83976d48 100644 --- a/features/step_definitions/json_steps.ts +++ b/features/step_definitions/json_steps.ts @@ -1,8 +1,7 @@ -import { Given, Then } from "@cucumber/cucumber"; +import { Then } from "@cucumber/cucumber"; import path from "path"; import { promises as fs } from "fs"; import assert from "assert"; -import child_process from "child_process"; import { PNG } from "pngjs"; import { version as cypressVersion } from "cypress/package.json"; @@ -44,22 +43,6 @@ function prepareJsonReport(tree: any) { return tree; } -Given("I've ensured cucumber-json-formatter is installed", async () => { - const child = child_process.spawn("which", ["cucumber-json-formatter"], { - stdio: "ignore", - }); - - await new Promise((resolve, reject) => { - child.on("exit", (code) => { - if (code === 0) { - resolve(); - } else { - reject(new Error("cucumber-json-formatter must be installed")); - } - }); - }); -}); - Then("there should be no JSON output", async function () { await assert.rejects( () => fs.readFile(path.join(this.tmpDir, "cucumber-report.json")), diff --git a/lib/add-cucumber-preprocessor-plugin.ts b/lib/add-cucumber-preprocessor-plugin.ts index 355ff502..e70defd1 100644 --- a/lib/add-cucumber-preprocessor-plugin.ts +++ b/lib/add-cucumber-preprocessor-plugin.ts @@ -2,14 +2,16 @@ import syncFs, { promises as fs, constants as fsConstants } from "fs"; import path from "path"; -import child_process from "child_process"; - import stream from "stream"; +import { EventEmitter } from "events"; + import chalk from "chalk"; import resolvePkg from "resolve-pkg"; +import { formatterHelpers, JsonFormatter } from "@cucumber/cucumber"; + import { NdjsonToMessageStream } from "@cucumber/message-streams"; import CucumberHtmlStream from "@cucumber/html-formatter"; @@ -31,6 +33,7 @@ import { INTERNAL_SUITE_PROPERTIES, TASK_APPEND_MESSAGES, TASK_CREATE_STRING_ATTACHMENT, + TASK_TEST_CASE_STARTED, TASK_TEST_STEP_STARTED, } from "./constants"; @@ -70,8 +73,13 @@ function memoize any>( }; } +function notEmpty(value: TValue | null | undefined): value is TValue { + return value !== null && value !== undefined; +} + const resolve = memoize(origResolve); +let currentTestCaseStartedId: string; let currentTestStepStartedId: string; let currentSpecMessages: messages.Envelope[]; @@ -146,38 +154,67 @@ export async function afterRunHandler(config: Cypress.PluginConfigOptions) { await fs.mkdir(path.dirname(jsonPath), { recursive: true }); - const messages = await fs.open(messagesPath, "r"); - - try { - const json = await fs.open(jsonPath, "w"); - - try { - const { formatter, args } = preprocessor.json; - const child = child_process.spawn(formatter, args, { - stdio: [messages.fd, json.fd, "inherit"], - }); - - await new Promise((resolve, reject) => { - child.on("exit", (code) => { - if (code === 0) { - resolve(); - } else { - reject( - new Error( - `${preprocessor.json.formatter} exited non-successfully` - ) - ); - } - }); - - child.on("error", reject); - }); - } finally { - await json.close(); + const messages = (await fs.readFile(messagesPath)) + .toString() + .trim() + .split("\n") + .map((line) => JSON.parse(line)); + + let jsonOutput: string | undefined; + + const log = (output: string | Uint8Array) => { + if (typeof output !== "string") { + throw new Error( + "Expected a JSON output of string, but got " + typeof output + ); + } else { + jsonOutput = output; } - } finally { - await messages.close(); + }; + + const eventBroadcaster = new EventEmitter(); + + const eventDataCollector = new formatterHelpers.EventDataCollector( + eventBroadcaster + ); + + const stepDefinitions = messages + .map((m) => m.stepDefinition) + .filter(notEmpty) + .map((s) => { + return { + id: s.id, + uri: "not available", + line: 0, + }; + }); + + new JsonFormatter({ + eventBroadcaster, + eventDataCollector, + log, + supportCodeLibrary: { + stepDefinitions, + } as any, + colorFns: null as any, + cwd: null as any, + parsedArgvOptions: {}, + snippetBuilder: null as any, + stream: null as any, + cleanup: null as any, + }); + + for (const message of messages) { + eventBroadcaster.emit("envelope", message); } + + if (typeof jsonOutput !== "string") { + throw new Error( + "Expected JSON formatter to have finished, but it never returned" + ); + } + + await fs.writeFile(jsonPath, jsonOutput); } if (preprocessor.html.enabled) { @@ -278,6 +315,7 @@ export async function afterScreenshotHandler( const message: messages.Envelope = { attachment: { + testCaseStartedId: currentTestCaseStartedId, testStepId: currentTestStepStartedId, body: buffer.toString("base64"), mediaType: "image/png", @@ -366,6 +404,16 @@ export default async function addCucumberPreprocessorPlugin( return true; }, + [TASK_TEST_CASE_STARTED]: (testCaseStartedId) => { + if (!currentSpecMessages) { + return true; + } + + currentTestCaseStartedId = testCaseStartedId; + + return true; + }, + [TASK_TEST_STEP_STARTED]: (testStepStartedId) => { if (!currentSpecMessages) { return true; @@ -383,6 +431,7 @@ export default async function addCucumberPreprocessorPlugin( const message: messages.Envelope = { attachment: { + testCaseStartedId: currentTestCaseStartedId, testStepId: currentTestStepStartedId, body: data, mediaType: mediaType, diff --git a/lib/constants.ts b/lib/constants.ts index 00cc18d1..cb239c55 100644 --- a/lib/constants.ts +++ b/lib/constants.ts @@ -1,6 +1,9 @@ export const TASK_APPEND_MESSAGES = "cypress-cucumber-preprocessor:append-messages"; +export const TASK_TEST_CASE_STARTED = + "cypress-cucumber-preprocessor:test-case-started"; + export const TASK_TEST_STEP_STARTED = "cypress-cucumber-preprocessor:test-step-started"; diff --git a/lib/create-tests.ts b/lib/create-tests.ts index 518ccd9b..eac3e604 100644 --- a/lib/create-tests.ts +++ b/lib/create-tests.ts @@ -33,6 +33,7 @@ import { INTERNAL_SPEC_PROPERTIES, INTERNAL_SUITE_PROPERTIES, TASK_APPEND_MESSAGES, + TASK_TEST_CASE_STARTED, TASK_TEST_STEP_STARTED, } from "./constants"; @@ -272,6 +273,7 @@ function createPickle( ...afterHooks.map((hook) => ({ hook })), ]; + // TODO: Why am I doing this? For example, an undefined step should not resolve to a step definition with location "not available:0". for (const id of definitionIds) { messages.stack.push({ stepDefinition: { @@ -369,6 +371,10 @@ function createPickle( }, }); + if (messages.enabled) { + cy.task(TASK_TEST_CASE_STARTED, testCaseStartedId, { log: false }); + } + flushMessages(context.messages); window.testState = { diff --git a/lib/preprocessor-configuration.ts b/lib/preprocessor-configuration.ts index 38722025..65774561 100644 --- a/lib/preprocessor-configuration.ts +++ b/lib/preprocessor-configuration.ts @@ -71,18 +71,6 @@ function validateConfigurationEntry( `Expected an object (json), but got ${util.inspect(value)}` ); } - let args: string[] | undefined; - if (hasOwnProperty(value, "args")) { - if (Array.isArray(value.args) && value.args.every(isString)) { - args = value.args; - } else { - throw new Error( - `Expected an array of strings (json.args), but got ${util.inspect( - value - )}` - ); - } - } if ( !hasOwnProperty(value, "enabled") || typeof value.enabled !== "boolean" @@ -91,16 +79,6 @@ function validateConfigurationEntry( `Expected a boolean (json.enabled), but got ${util.inspect(value)}` ); } - let formatter: string | undefined; - if (hasOwnProperty(value, "formatter")) { - if (isString(value.formatter)) { - formatter = value.formatter; - } else { - throw new Error( - `Expected a string (json.formatter), but got ${util.inspect(value)}` - ); - } - } let output: string | undefined; if (hasOwnProperty(value, "output")) { if (isString(value.output)) { @@ -112,9 +90,7 @@ function validateConfigurationEntry( } } const messagesConfig = { - args, enabled: value.enabled, - formatter, output, }; return { [key]: messagesConfig }; @@ -219,20 +195,6 @@ function validateEnvironmentOverrides( } } - if (hasOwnProperty(environment, "jsonArgs")) { - const { jsonArgs } = environment; - if (isString(jsonArgs)) { - overrides.jsonArgs = [jsonArgs]; - } - if (Array.isArray(jsonArgs) && jsonArgs.every(isString)) { - overrides.jsonArgs = jsonArgs; - } else { - throw new Error( - `Expected a string array (jsonArgs), but got ${util.inspect(jsonArgs)}` - ); - } - } - if (hasOwnProperty(environment, "jsonEnabled")) { const { jsonEnabled } = environment; @@ -247,20 +209,6 @@ function validateEnvironmentOverrides( } } - if (hasOwnProperty(environment, "jsonFormatter")) { - const { jsonFormatter } = environment; - - if (isString(jsonFormatter)) { - overrides.jsonFormatter = jsonFormatter; - } else { - throw new Error( - `Expected a string (jsonFormatter), but got ${util.inspect( - jsonFormatter - )}` - ); - } - } - if (hasOwnProperty(environment, "jsonOutput")) { const { jsonOutput } = environment; @@ -353,9 +301,7 @@ export interface IPreprocessorConfiguration { output?: string; }; readonly json?: { - args?: string[]; enabled: boolean; - formatter?: string; output?: string; }; readonly html?: { @@ -371,9 +317,7 @@ export interface IEnvironmentOverrides { stepDefinitions?: string | string[]; messagesEnabled?: boolean; messagesOutput?: string; - jsonArgs?: string[]; jsonEnabled?: boolean; - jsonFormatter?: string; jsonOutput?: string; htmlEnabled?: boolean; htmlOutput?: string; @@ -436,18 +380,10 @@ export class PreprocessorConfiguration implements IPreprocessorConfiguration { get json() { return { - args: - this.environmentOverrides.jsonArgs ?? - this.explicitValues.json?.args ?? - [], enabled: this.environmentOverrides.jsonEnabled ?? this.explicitValues.json?.enabled ?? false, - formatter: - this.environmentOverrides.jsonFormatter ?? - this.explicitValues.json?.formatter ?? - "cucumber-json-formatter", output: this.environmentOverrides.jsonOutput ?? (this.explicitValues.json?.output || "cucumber-report.json"), diff --git a/package.json b/package.json index 63eebf02..ebb054a9 100644 --- a/package.json +++ b/package.json @@ -47,6 +47,7 @@ }, "dependencies": { "@badeball/cypress-configuration": "^4.0.0", + "@cucumber/cucumber": "^8.11.1", "@cucumber/cucumber-expressions": "^16.0.0", "@cucumber/gherkin": "^24.0.0", "@cucumber/html-formatter": "^19.2.0", @@ -73,7 +74,6 @@ "@babel/parser": "^7.20.7", "@babel/types": "^7.20.7", "@bahmutov/cypress-esbuild-preprocessor": "^2.2.0", - "@cucumber/cucumber": "^8.0.0", "@cucumber/pretty-formatter": "^1.0.0-alpha.0", "@cypress/browserify-preprocessor": "^3.0.2", "@cypress/webpack-preprocessor": "^5.11.1",