@flakiness/cucumberjs 1.0.0-alpha.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (38) hide show
  1. package/.github/workflows/flakiness-upload-fork-prs.yml +30 -0
  2. package/.github/workflows/publish-npm.yml +41 -0
  3. package/.github/workflows/tests.yml +45 -0
  4. package/CONTRIBUTING.md +58 -0
  5. package/LICENSE +21 -0
  6. package/agenda.md +2 -0
  7. package/build.mts +34 -0
  8. package/cucumber.mjs +6 -0
  9. package/features/attachments.feature +32 -0
  10. package/features/basic.feature +27 -0
  11. package/features/data_tables.feature +45 -0
  12. package/features/description.feature +49 -0
  13. package/features/errors.feature +28 -0
  14. package/features/hooks_named.feature +32 -0
  15. package/features/hooks_unnamed.feature +33 -0
  16. package/features/locations.feature +37 -0
  17. package/features/retries.feature +30 -0
  18. package/features/rules.feature +25 -0
  19. package/features/scenario_outlines.feature +57 -0
  20. package/features/scenario_outlines_multiple.feature +44 -0
  21. package/features/statuses.feature +70 -0
  22. package/features/stdio.feature +29 -0
  23. package/features/steps.feature +24 -0
  24. package/features/support/attachments_steps.ts +32 -0
  25. package/features/support/basic_steps.ts +235 -0
  26. package/features/support/description_steps.ts +37 -0
  27. package/features/support/errors_steps.ts +48 -0
  28. package/features/support/harness.ts +196 -0
  29. package/features/support/project_steps.ts +24 -0
  30. package/features/support/stdio_steps.ts +21 -0
  31. package/features/support/tags_steps.ts +10 -0
  32. package/features/tags.feature +19 -0
  33. package/features/tags_hierarchy.feature +37 -0
  34. package/package.json +37 -0
  35. package/plan.md +59 -0
  36. package/pnpm-workspace.yaml +2 -0
  37. package/src/formatter.ts +635 -0
  38. package/tsconfig.json +24 -0
@@ -0,0 +1,30 @@
1
+ name: Upload Flakiness.io report (fork PRs)
2
+ on:
3
+ workflow_run:
4
+ # Must match the name(s) of workflows that produce flakiness-report artifacts
5
+ workflows: ["Tests"]
6
+ types: [completed]
7
+
8
+ jobs:
9
+ upload-flakiness-report:
10
+ runs-on: ubuntu-latest
11
+ if: >-
12
+ (github.event.workflow_run.conclusion == 'success' || github.event.workflow_run.conclusion == 'failure')
13
+ && github.event.workflow_run.event == 'pull_request'
14
+ && github.event.workflow_run.head_repository.full_name != github.event.workflow_run.repository.full_name
15
+ permissions:
16
+ actions: read
17
+ contents: read
18
+ id-token: write
19
+ steps:
20
+ - name: Install Flakiness CLI
21
+ run: curl -LsSf https://cli.flakiness.io/install.sh | sh
22
+
23
+ - name: Download flakiness-report artifacts
24
+ env:
25
+ GH_TOKEN: ${{ github.token }}
26
+ RUN_ID: ${{ github.event.workflow_run.id }}
27
+ run: gh run download "$RUN_ID" --repo "$GITHUB_REPOSITORY" --pattern 'flakiness-report-*' --dir .
28
+
29
+ - name: Upload to Flakiness.io
30
+ run: find . -path '*/flakiness-report-*/report.json' -exec flakiness upload {} \;
@@ -0,0 +1,41 @@
1
+ name: Publish to NPM
2
+
3
+ on:
4
+ release:
5
+ types: [published]
6
+
7
+ permissions:
8
+ id-token: write # Required for OIDC
9
+ contents: read
10
+
11
+ jobs:
12
+ publish-to-npm:
13
+ runs-on: ubuntu-latest
14
+
15
+ steps:
16
+ - uses: actions/checkout@v4
17
+
18
+ - name: Setup Node.js
19
+ uses: actions/setup-node@v4
20
+ with:
21
+ node-version: 24
22
+
23
+ - name: Setup PNPM
24
+ uses: pnpm/action-setup@v4
25
+ with:
26
+ version: 10
27
+
28
+ - name: Build & Publish
29
+ run: |
30
+ pnpm i --frozen-lockfile
31
+ pnpm build
32
+
33
+ VERSION=${GITHUB_REF_NAME#v}
34
+
35
+ if [[ "$VERSION" == *"-"* ]]; then
36
+ echo "Publishing prerelease to @next"
37
+ pnpm publish --access=public --no-git-checks --tag next
38
+ else
39
+ echo "Publishing stable to @latest"
40
+ pnpm publish --access=public --no-git-checks
41
+ fi
@@ -0,0 +1,45 @@
1
+ name: Tests
2
+
3
+ on:
4
+ push:
5
+ branches: [main]
6
+ pull_request:
7
+ branches: [main]
8
+
9
+ permissions:
10
+ contents: read
11
+ id-token: write
12
+
13
+ jobs:
14
+ test:
15
+ strategy:
16
+ fail-fast: false
17
+ matrix:
18
+ os: [ubuntu-latest, macos-latest, windows-latest]
19
+ runs-on: ${{ matrix.os }}
20
+ steps:
21
+ - uses: actions/checkout@v4
22
+
23
+ - name: Setup pnpm
24
+ uses: pnpm/action-setup@v4
25
+ with:
26
+ version: 10
27
+
28
+ - uses: actions/setup-node@v4
29
+ with:
30
+ node-version: lts/*
31
+ cache: pnpm
32
+
33
+ # Since tests initialize git repositories, we have
34
+ # to configure git to avoid warnings.
35
+ - run: git config --global init.defaultBranch main
36
+ - run: pnpm install --frozen-lockfile
37
+ - run: pnpm build
38
+ - run: pnpm test
39
+ - name: Upload Flakiness report artifact (for Pull Requests from forks)
40
+ if: always() && github.event_name == 'pull_request' && github.event.pull_request.head.repo.fork
41
+ uses: actions/upload-artifact@v4
42
+ with:
43
+ name: flakiness-report-${{ github.job }}-${{ strategy.job-index }}
44
+ path: flakiness-report/
45
+ retention-days: 1
@@ -0,0 +1,58 @@
1
+ # Contributing
2
+
3
+ ## Prerequisites
4
+
5
+ - Node.js 22+
6
+ - [pnpm](https://pnpm.io/)
7
+
8
+ ## Getting Started
9
+
10
+ Clone the repo and install dependencies:
11
+
12
+ ```bash
13
+ git clone https://github.com/flakiness/cucumberjs.git fk-cucumber
14
+ cd fk-cucumber
15
+ pnpm install
16
+ ```
17
+
18
+ ## Building
19
+
20
+ This project uses [Kubik](https://github.com/flakiness/kubik) as its build system. The build script is defined in `build.mts`.
21
+
22
+ To build:
23
+
24
+ ```bash
25
+ pnpm build
26
+ ```
27
+
28
+ To watch:
29
+
30
+ ```bash
31
+ pnpm build -w
32
+ ```
33
+
34
+ This will bundle the source with esbuild and generate TypeScript declarations.
35
+
36
+ ## Releasing
37
+
38
+ To release a new version:
39
+
40
+ 1. Bump the version:
41
+
42
+ ```bash
43
+ # For a stable minor release
44
+ pnpm version minor
45
+
46
+ # For an alpha pre-release
47
+ pnpm version preminor --preid=alpha
48
+ ```
49
+
50
+ 2. Push the commit and tag:
51
+
52
+ ```bash
53
+ git push --follow-tags
54
+ ```
55
+
56
+ 3. [Create a GitHub Release](https://github.com/flakiness/cucumberjs/releases/new) for the new tag and publish it.
57
+
58
+ CI will handle publishing to npm. Pre-releases are published under @next tag.
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2023-2026 Degu Labs, Inc
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.
package/agenda.md ADDED
@@ -0,0 +1,2 @@
1
+ - descriptions as annotations
2
+
package/build.mts ADDED
@@ -0,0 +1,34 @@
1
+ #!/usr/bin/env pnpm kubik
2
+
3
+ import esbuild from 'esbuild';
4
+ import fs from 'fs';
5
+ import { Task } from 'kubik';
6
+ import path from 'path';
7
+
8
+ const { __dirname, $ } = Task.init(import.meta, {
9
+ name: 'cucubmer',
10
+ watch: [ './src' ],
11
+ });
12
+
13
+ const outDir = path.join(__dirname, 'lib');
14
+ const typesDir = path.join(__dirname, 'types');
15
+ const srcDir = path.join(__dirname, 'src');
16
+ await fs.promises.rm(outDir, { recursive: true, force: true });
17
+ await fs.promises.rm(typesDir, { recursive: true, force: true });
18
+
19
+ const { errors } = await esbuild.build({
20
+ color: true,
21
+ entryPoints: [
22
+ path.join(srcDir, 'formatter.ts'),
23
+ ],
24
+ outdir: outDir,
25
+ format: 'esm',
26
+ platform: 'node',
27
+ target: ['node22'],
28
+ sourcemap: true,
29
+ bundle: false,
30
+ minify: false,
31
+ });
32
+
33
+ if (!errors.length)
34
+ await $`tsc --pretty -p .`;
package/cucumber.mjs ADDED
@@ -0,0 +1,6 @@
1
+ export default {
2
+ paths: ['features/**/*.feature'],
3
+ import: ['tsx/esm', 'features/support/**/*.ts'],
4
+ format: ['@cucumber/pretty-formatter'],
5
+ parallel: 0,
6
+ };
@@ -0,0 +1,32 @@
1
+ Feature: Attachments
2
+ Scenario: captures Cucumber attachments
3
+ Given the project file "features/attachments.feature":
4
+ """
5
+ Feature: Attachments
6
+ Scenario: it attaches data
7
+ Given a step with attachments
8
+ """
9
+ And the project file "features/support/steps.js":
10
+ """
11
+ const { Given } = require('@cucumber/cucumber');
12
+ Given('a step with attachments', function() {
13
+ this.attach('hello attachment', { mediaType: 'text/plain', fileName: 'note.txt' });
14
+ this.attach(Buffer.from('{"ok":true}', 'utf8'), { mediaType: 'application/json', fileName: 'data.json' });
15
+ });
16
+ """
17
+ When I generate the Flakiness report for "scenario with attachments"
18
+ When I look at the test named "it attaches data"
19
+ Then the test contains 1 attempt
20
+ When I look at the attempt #1
21
+ Then the attempt contains 2 attachments
22
+ And the report contains 0 missing attachments
23
+
24
+ When I look at the attachment #1
25
+ Then the attachment is called "note.txt"
26
+ And the attachment has content type "text/plain"
27
+ And the stored attachment has text "hello attachment"
28
+
29
+ When I look at the attachment #2
30
+ Then the attachment is called "data.json"
31
+ And the attachment has content type "application/json"
32
+ And the stored attachment has text "{\"ok\":true}"
@@ -0,0 +1,27 @@
1
+ Feature: Basic Functionality
2
+ Scenario: generates a basic report
3
+ Given the project file "features/passing.feature":
4
+ """
5
+ Feature: Passing Test Suite
6
+ Scenario: it passes
7
+ Given a passing step
8
+ """
9
+ And the project file "features/support/steps.js":
10
+ """
11
+ const { Given } = require('@cucumber/cucumber');
12
+ Given('a passing step', function() {});
13
+ """
14
+ And the environment variable "BUILD_URL" is "https://ci.example.test/build/123"
15
+ When I generate the Flakiness report for "passing scenario"
16
+ Then the report should contain the basic metadata
17
+ And the report hierarchy is:
18
+ """
19
+ `- file passing.feature
20
+ `- suite Passing Test Suite
21
+ `- test it passes
22
+ `- attempt #1 passed
23
+ `- step Given a passing step
24
+ """
25
+
26
+ When I look at the test named "it passes"
27
+ Then the test is in file "features/passing.feature" at line 2
@@ -0,0 +1,45 @@
1
+ Feature: Data Tables
2
+ Scenario: keeps step titles single-line for data tables
3
+ Given the project file "features/data-table.feature":
4
+ """
5
+ Feature: Data table
6
+
7
+ Scenario: addition
8
+ Given a table
9
+ | a | b | result |
10
+ | 2 | 2 | 4 |
11
+ | 1 | 7 | 8 |
12
+ When I add the values
13
+ Then I get the expected results
14
+ """
15
+ And the project file "features/support/steps.js":
16
+ """
17
+ const assert = require('node:assert/strict');
18
+ const { Given, When, Then } = require('@cucumber/cucumber');
19
+
20
+ Given('a table', function(dataTable) {
21
+ this.rows = dataTable.hashes().map(row => ({
22
+ a: Number(row.a),
23
+ b: Number(row.b),
24
+ result: Number(row.result),
25
+ }));
26
+ });
27
+
28
+ When('I add the values', function() {
29
+ this.results = this.rows.map(row => row.a + row.b);
30
+ });
31
+
32
+ Then('I get the expected results', function() {
33
+ assert.deepEqual(this.results, this.rows.map(row => row.result));
34
+ });
35
+ """
36
+ When I generate the Flakiness report for "scenario with a data table"
37
+ When I look at the test named "addition"
38
+ Then the test contains 1 attempt
39
+ When I look at the attempt #1
40
+ Then the attempt contains 3 steps:
41
+ """
42
+ Given a table
43
+ When I add the values
44
+ Then I get the expected results
45
+ """
@@ -0,0 +1,49 @@
1
+ Feature: Descriptions
2
+
3
+ Scenario: captures native Gherkin descriptions as annotations
4
+ Given the project file "features/help-center.feature":
5
+ """
6
+ Feature: Help center
7
+ These scenarios describe what support agents can read.
8
+ They should stay visible in the report.
9
+
10
+ Scenario: opens a pinned article
11
+ Support agents can inspect a pinned article before replying.
12
+ Given a pinned article exists
13
+
14
+ Scenario: lists pinned articles
15
+ Given several pinned articles exist
16
+ """
17
+ And the project file "features/support/steps.js":
18
+ """
19
+ const { Given } = require('@cucumber/cucumber');
20
+
21
+ Given('a pinned article exists', function() {});
22
+ Given('several pinned articles exist', function() {});
23
+ """
24
+ When I generate the Flakiness report for "descriptions"
25
+
26
+ When I look at the test named "opens a pinned article"
27
+ Then the test contains 1 attempt
28
+ When I look at the attempt #1
29
+ Then the attempt contains 2 annotations
30
+ And the attempt has an annotation "feature" with description:
31
+ """
32
+ These scenarios describe what support agents can read.
33
+ They should stay visible in the report.
34
+ """
35
+ And the attempt has an annotation "scenario" with description:
36
+ """
37
+ Support agents can inspect a pinned article before replying.
38
+ """
39
+
40
+ When I look at the test named "lists pinned articles"
41
+ Then the test contains 1 attempt
42
+ When I look at the attempt #1
43
+ Then the attempt contains 1 annotation
44
+ And the attempt has an annotation "feature" with description:
45
+ """
46
+ These scenarios describe what support agents can read.
47
+ They should stay visible in the report.
48
+ """
49
+ And the attempt has no annotation "scenario"
@@ -0,0 +1,28 @@
1
+ Feature: Errors
2
+ Scenario: captures thrown errors
3
+ Given the project file "features/failing.feature":
4
+ """
5
+ Feature: Failing
6
+ Scenario: it fails
7
+ Given a step that throws an error
8
+ """
9
+ And the project file "features/support/steps.js":
10
+ """
11
+ const { Given } = require('@cucumber/cucumber');
12
+ Given('a step that throws an error', function() {
13
+ throw new Error('intentional failure');
14
+ });
15
+ """
16
+ When I generate the Flakiness report for "failing scenario"
17
+ When I look at the test named "it fails"
18
+ Then the test contains 1 attempt
19
+ When I look at the "failed" attempt
20
+ # Make sure the attempt has error
21
+ Then the attempt contains 1 error
22
+ And the attempt error #1 has message "intentional failure"
23
+ And the attempt error #1 has a stack trace
24
+
25
+ # Make sure the same error exists in step too
26
+ Then the attempt contains 1 step
27
+ When I look at the step named "Given a step that throws an error"
28
+ And the step has an error with message "intentional failure"
@@ -0,0 +1,32 @@
1
+ Feature: Named Hooks
2
+ Scenario: captures named hooks
3
+ Given the project file "features/passing.feature":
4
+ """
5
+ Feature: Hooks
6
+ Scenario: it runs named hooks
7
+ Given a passing step
8
+ """
9
+ And the project file "features/support/steps.js":
10
+ """
11
+ const { Before, After, Given } = require('@cucumber/cucumber');
12
+ Before({ name: 'database setup' }, function() {});
13
+ After({ name: 'database cleanup' }, function() {});
14
+ Given('a passing step', function() {});
15
+ """
16
+ When I generate the Flakiness report for "scenario with named hooks"
17
+ When I look at the test named "it runs named hooks"
18
+ And the test contains 1 attempt
19
+ When I look at the attempt #1
20
+ And the attempt contains 3 steps
21
+
22
+ When I look at the step #1
23
+ Then the step is called "Before (database setup)"
24
+ And the step is in file "features/support/steps.js" at line 2
25
+
26
+ When I look at the step #2
27
+ Then the step is called "Given a passing step"
28
+ And the step is in file "features/passing.feature" at line 3
29
+
30
+ When I look at the step #3
31
+ Then the step is called "After (database cleanup)"
32
+ And the step is in file "features/support/steps.js" at line 3
@@ -0,0 +1,33 @@
1
+ Feature: Unnamed Hooks
2
+ Scenario: captures before and after hooks
3
+ Given the project file "features/passing.feature":
4
+ """
5
+ Feature: Hooks
6
+ Scenario: it runs hooks
7
+ Given a passing step
8
+ """
9
+ And the project file "features/support/steps.js":
10
+ """
11
+ const { Before, After, Given } = require('@cucumber/cucumber');
12
+ Before(function() {});
13
+ After(function() {});
14
+ Given('a passing step', function() {});
15
+ """
16
+ And the environment variable "BUILD_URL" is "https://ci.example.test/build/123"
17
+ When I generate the Flakiness report for "scenario with hooks"
18
+ When I look at the test named "it runs hooks"
19
+ And the test contains 1 attempt
20
+ When I look at the attempt #1
21
+ And the attempt contains 3 steps
22
+
23
+ When I look at the step #1
24
+ Then the step is called "Before"
25
+ And the step is in file "features/support/steps.js" at line 2
26
+
27
+ When I look at the step #2
28
+ Then the step is called "Given a passing step"
29
+ And the step is in file "features/passing.feature" at line 3
30
+
31
+ When I look at the step #3
32
+ Then the step is called "After"
33
+ And the step is in file "features/support/steps.js" at line 3
@@ -0,0 +1,37 @@
1
+ Feature: Locations
2
+ Scenario: computes locations relative to the git root
3
+ Given the project file "features/orders/nested/checkout.feature":
4
+ """
5
+ Feature: Nested checkout
6
+
7
+ Scenario: computes locations from git root
8
+ Given a passing step
9
+ """
10
+ And the project file "features/support/nested/steps.js":
11
+ """
12
+ const { Before, Given } = require('@cucumber/cucumber');
13
+
14
+ Before(function() {});
15
+ Given('a passing step', function() {});
16
+ """
17
+ When I generate the Flakiness report for "nested locations"
18
+ When I look at the suite named "checkout.feature"
19
+ Then the suite is in file "features/orders/nested/checkout.feature" at line 0
20
+ When I look at the suite named "Nested checkout"
21
+ Then the suite is in file "features/orders/nested/checkout.feature" at line 1
22
+ When I look at the test named "computes locations from git root"
23
+ Then the test is in file "features/orders/nested/checkout.feature" at line 3
24
+ And the test contains 1 attempt
25
+
26
+ When I look at the attempt #1
27
+ Then the attempt contains 2 steps:
28
+ """
29
+ Before
30
+ Given a passing step
31
+ """
32
+
33
+ When I look at the step #1
34
+ Then the step is in file "features/support/nested/steps.js" at line 3
35
+
36
+ When I look at the step #2
37
+ Then the step is in file "features/orders/nested/checkout.feature" at line 4
@@ -0,0 +1,30 @@
1
+ Feature: Retries
2
+ Scenario: captures a scenario that succeeds on retry
3
+ Given the project file "features/eventually-passing.feature":
4
+ """
5
+ Feature: Eventually passing
6
+ Scenario: it succeeds on retry
7
+ Given a step that succeeds on retry
8
+ """
9
+ And the project file "features/support/steps.js":
10
+ """
11
+ const { Given } = require('@cucumber/cucumber');
12
+ let hasFailedOnce = false;
13
+
14
+ Given('a step that succeeds on retry', function() {
15
+ if (hasFailedOnce)
16
+ return;
17
+ hasFailedOnce = true;
18
+ throw new Error('intentional first-attempt failure');
19
+ });
20
+ """
21
+ And the Cucumber arguments are:
22
+ | --retry |
23
+ | 1 |
24
+ When I generate the Flakiness report for "flaky scenario"
25
+ When I look at the test named "it succeeds on retry"
26
+ Then the test contains 2 attempts
27
+ When I look at the "failed" attempt
28
+ Then the attempt contains 1 step
29
+ When I look at the "passed" attempt
30
+ Then the attempt contains 1 step
@@ -0,0 +1,25 @@
1
+ Feature: Rules
2
+ Scenario: captures rules as suites
3
+ Given the project file "features/rules.feature":
4
+ """
5
+ Feature: Wallet
6
+
7
+ Rule: Balance cannot go negative
8
+ Scenario: rejects overspending
9
+ Given an account with a balance
10
+ """
11
+ And the project file "features/support/steps.js":
12
+ """
13
+ const { Given } = require('@cucumber/cucumber');
14
+ Given('an account with a balance', function() {});
15
+ """
16
+ When I generate the Flakiness report for "rules"
17
+ Then the report hierarchy is:
18
+ """
19
+ `- file rules.feature
20
+ `- suite Wallet
21
+ `- suite Balance cannot go negative
22
+ `- test rejects overspending
23
+ `- attempt #1 passed
24
+ `- step Given an account with a balance
25
+ """
@@ -0,0 +1,57 @@
1
+ Feature: Scenario Outlines
2
+ Scenario: captures one reported test per example row
3
+ Given the project file "features/addition.feature":
4
+ """
5
+ Feature: Addition
6
+
7
+ Scenario Outline: addition
8
+ Given a calculator
9
+ When I add <a> to <b>
10
+ Then I get <result>
11
+
12
+ Examples:
13
+ | a | b | result |
14
+ | 2 | 2 | 4 |
15
+ | 1 | 7 | 8 |
16
+ """
17
+ And the project file "features/support/steps.js":
18
+ """
19
+ const assert = require('node:assert/strict');
20
+ const { Given, When, Then } = require('@cucumber/cucumber');
21
+
22
+ Given('a calculator', function() {});
23
+
24
+ When('I add {int} to {int}', function(a, b) {
25
+ this.result = a + b;
26
+ });
27
+
28
+ Then('I get {int}', function(expected) {
29
+ assert.equal(this.result, expected);
30
+ });
31
+ """
32
+ When I generate the Flakiness report for "scenario outline"
33
+ Then the report contains 2 tests
34
+
35
+ When I look at the test named "addition [a=2, b=2, result=4]"
36
+ And the test is in file "features/addition.feature" at line 10
37
+ And the test contains 1 attempt
38
+
39
+ When I look at the attempt #1
40
+ Then the attempt contains 3 steps:
41
+ """
42
+ Given a calculator
43
+ When I add 2 to 2
44
+ Then I get 4
45
+ """
46
+
47
+ When I look at the test named "addition [a=1, b=7, result=8]"
48
+ And the test is in file "features/addition.feature" at line 11
49
+ And the test contains 1 attempt
50
+
51
+ When I look at the attempt #1
52
+ Then the attempt contains 3 steps:
53
+ """
54
+ Given a calculator
55
+ When I add 1 to 7
56
+ Then I get 8
57
+ """