Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

hooks: Add BeforeStep and AfterStep Hooks #1416

Merged
merged 123 commits into from
Nov 16, 2020
Merged
Show file tree
Hide file tree
Changes from 120 commits
Commits
Show all changes
123 commits
Select commit Hold shift + click to select a range
56ddf0c
Initial promote of code
Aug 31, 2020
d279f71
don't promote local test files
Aug 31, 2020
4fa8161
fix TS compile issue
Aug 31, 2020
baf4dcf
get feature files syntatically correct
Aug 31, 2020
a7989c9
feature file updates
Aug 31, 2020
694ba63
final feature file updates
Aug 31, 2020
e0a9547
update documentation
Aug 31, 2020
e30513c
fix typo in docs
Aug 31, 2020
2d0f256
update api docs
Aug 31, 2020
d207dcd
fix existing errors
Aug 31, 2020
0381924
fix eol issue
Aug 31, 2020
8436b72
addressing code review comments
Sep 9, 2020
6fe2412
update md file
Sep 10, 2020
e0dd4b9
add and fix unit tests
Sep 11, 2020
7bce91e
progress on stepResult passed to AfterStep hook
Sep 11, 2020
f56bb07
Merge branch 'master' into addBeforeAfterStepHooks
Adam-ARK Sep 11, 2020
7e70b49
fix broken unit tests from merge with master
Sep 14, 2020
12dca9d
Fixing failing feature files
Sep 14, 2020
8a1aaf8
get all features passing
Sep 14, 2020
a080569
Add unit tests for all new code
Sep 14, 2020
808af69
Updates to address code review comment
Oct 13, 2020
dfc9657
remove bad unit test / import
Oct 13, 2020
fbe4c33
fix unit test issues & add testStepId
Oct 13, 2020
e44dcf5
have stepResult sent to afterStep hook
Oct 14, 2020
fe877b2
remove class var, and null checks
Oct 14, 2020
49984f1
Pin dependencies (#1417)
renovate[bot] Sep 1, 2020
689fce6
Update dependency @types/lodash to v4.14.161 (#1418)
renovate[bot] Sep 1, 2020
9dee48c
Update dependency @types/semver to v7.3.3 (#1419)
renovate[bot] Sep 1, 2020
04ff4a0
Update dependency browserify to v16.5.2 (#1420)
renovate[bot] Sep 1, 2020
04c3efb
Update unit test packages (#1422)
renovate[bot] Sep 1, 2020
116193a
Upgrade dependencies. Notably @cucumber/create-meta which fixes a sec…
aslakhellesoy Sep 4, 2020
b53043c
Update eslint packages (#1421)
renovate[bot] Sep 4, 2020
19247fe
build: scoped package name (#1411)
davidjgoss Sep 5, 2020
4f3fabd
docs: add changelog note about typescript (#1414)
davidjgoss Sep 6, 2020
3aa6a44
Update installation instructions. Closes #1428.
aslakhellesoy Sep 8, 2020
c974511
update to fix security issue (#1427)
charlierudolph Sep 10, 2020
c74bfb5
Publish option (#1424)
aslakhellesoy Sep 10, 2020
fcab4d3
Compute timestamp Using Date.now (without deriving from duration) (#1…
aslakhellesoy Sep 11, 2020
95d3979
Updated CONTRIBUTING.md
Sep 14, 2020
a93e487
remove travis deploy target
vincentcapicotto Sep 14, 2020
2b959b5
Revert to using lib
Sep 14, 2020
b425af1
Update contributors list, add extra steps in releasse process
vincent-psarga Sep 14, 2020
455e814
Update dependecies
vincent-psarga Sep 14, 2020
d472ae8
add makefile
vincentcapicotto Sep 14, 2020
512d270
Update release process
Sep 14, 2020
364985c
Make Changelog more consistent with other implementations
vincent-psarga Sep 14, 2020
2e0db31
Bring back yarn.lock
vincent-psarga Sep 14, 2020
cd95a88
Add s cripts to update changelog automatically
vincent-psarga Sep 14, 2020
7cf5d1a
reformat CHANGELOG
vincentcapicotto Sep 14, 2020
a98534b
start to move versions at the CHANGELOG bottom
vincentcapicotto Sep 14, 2020
657c4d1
Add links
Sep 14, 2020
1fd860b
Remove links
Sep 14, 2020
05b1e05
Remove -E flag from sed so it works in docker
Sep 14, 2020
9a95009
Release 7.0.0-rc.0
Sep 14, 2020
36c97f7
Prepublish with npm rather than yarn (cucumber-build only has npm)
Sep 14, 2020
aad15e7
7.0.0-rc.0
Sep 14, 2020
3cca2e9
Load npm credentials
Sep 14, 2020
6dd3002
Update changelog
Sep 14, 2020
d9cda3a
Update CHANGELOG.md
aslakhellesoy Sep 17, 2020
5ecce60
Update changelog. Closes #1423
Sep 17, 2020
f4549a2
grab own package version from regular require (#1442)
davidjgoss Sep 30, 2020
6869ba2
Pin dependencies (#1449)
renovate[bot] Oct 1, 2020
3ad0aa9
Update dependency @types/node to v14.11.2 (#1450)
renovate[bot] Oct 1, 2020
f07596f
Update dependency typescript to v4.0.3 (#1452)
renovate[bot] Oct 1, 2020
ed6f297
Update dependency @types/semver to v7.3.4 (#1451)
renovate[bot] Oct 2, 2020
63595ac
formatters: include html-formatter as a built-in option (#1432)
davidjgoss Oct 2, 2020
edc4f82
Fix link
aslakhellesoy Oct 6, 2020
9f95dea
Unit test for CLI flag --name + doc update (#1445)
kamolins Oct 8, 2020
b4079d4
publish features use stubbed reports server (#1456)
charlierudolph Oct 21, 2020
fdf58b6
cleanup pickle runner (#1455)
charlierudolph Oct 21, 2020
1e946bf
Merge branch 'master' into addBeforeAfterStepHooks
Oct 21, 2020
ec6dd1f
rebase to latest master
Oct 21, 2020
1530e5f
fix unti test compile issue
Oct 21, 2020
904b882
attempt to get things working with latest code
Oct 21, 2020
59d365c
revert merge with master
Oct 21, 2020
0bb9d9d
Initial promote of code
Aug 31, 2020
cdb3c4e
don't promote local test files
Aug 31, 2020
ffffdc4
fix TS compile issue
Aug 31, 2020
38aa598
get feature files syntatically correct
Aug 31, 2020
78ea343
feature file updates
Aug 31, 2020
052e79e
final feature file updates
Aug 31, 2020
f8652b1
update documentation
Aug 31, 2020
586e074
fix typo in docs
Aug 31, 2020
5f0976c
update api docs
Aug 31, 2020
669abc2
fix existing errors
Aug 31, 2020
109d5e9
fix eol issue
Aug 31, 2020
e310bc3
addressing code review comments
Sep 9, 2020
78dc786
update md file
Sep 10, 2020
b3bd2e5
add and fix unit tests
Sep 11, 2020
97f4a04
progress on stepResult passed to AfterStep hook
Sep 11, 2020
4d7dd54
Update unit test packages (#1422)
renovate[bot] Sep 1, 2020
e7aa122
Update eslint packages (#1421)
renovate[bot] Sep 4, 2020
6d3a7b6
build: scoped package name (#1411)
davidjgoss Sep 5, 2020
6cacb13
Update installation instructions. Closes #1428.
aslakhellesoy Sep 8, 2020
963e5f4
Publish option (#1424)
aslakhellesoy Sep 10, 2020
f1492a2
fix broken unit tests from merge with master
Sep 14, 2020
27f0277
Fixing failing feature files
Sep 14, 2020
c07311b
get all features passing
Sep 14, 2020
1d0ad2c
Add unit tests for all new code
Sep 14, 2020
fda4a60
Updates to address code review comment
Oct 13, 2020
383da79
remove bad unit test / import
Oct 13, 2020
b9758e3
fix unit test issues & add testStepId
Oct 13, 2020
5a4cfe1
have stepResult sent to afterStep hook
Oct 14, 2020
6502106
remove class var, and null checks
Oct 14, 2020
7ea414c
rebase to latest master
Oct 21, 2020
9528f4b
fix unti test compile issue
Oct 21, 2020
c39287e
attempt to get things working with latest code
Oct 21, 2020
609308f
revert merge with master
Oct 21, 2020
c028521
fix yarn lock
Oct 21, 2020
43636f9
finish fixing the bad merge with master
Oct 21, 2020
5034a29
Merge branch 'addBeforeAfterStepHooks' of https://github.com/Adam-ARK…
Oct 21, 2020
0469165
Merge branch 'master' into addBeforeAfterStepHooks
Adam-ARK Oct 21, 2020
d039f7f
fix yarn file
Oct 21, 2020
7f4e35d
fix yarn file
Oct 21, 2020
2ffd11d
fix newline
Oct 21, 2020
27982b6
finish address refactor comments
Oct 21, 2020
d2ae318
fix lint issues
Oct 21, 2020
6601597
quick cleanup
Oct 26, 2020
ac0ef0b
final cleanup fix
Oct 26, 2020
2f51a0f
always run afterStepHooks
Nov 2, 2020
e95fa70
ensure worst result is returned, not just status
Nov 4, 2020
a2aa0be
fix lint issue
Nov 4, 2020
2e6f378
add unit test, fix concat result issue
Nov 13, 2020
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
30 changes: 28 additions & 2 deletions docs/support_files/api_reference.md
Original file line number Diff line number Diff line change
Expand Up @@ -36,8 +36,8 @@ Defines a hook which is run after each scenario.
* `tags`: String tag expression used to apply this hook to only specific scenarios. See [cucumber-tag-expressions](https://docs.cucumber.io/tag-expressions/) for more information.
* `timeout`: A hook-specific timeout, to override the default timeout.
* `fn`: A function, defined as follows:
* The first argument will be an object of the form `{sourceLocation: {line, uri}, result: {duration, status, exception?}, pickle}`
* The pickle object comes from the [gherkin](https://github.com/cucumber/cucumber/tree/gherkin-v4.1.3/gherkin) library. See `testdata/good/*.pickles.ndjson` for examples of its structure.
* The first argument will be an object of the form `{pickle, gherkinDocument, result, testCaseStartedId}`
* The pickle object comes from the [gherkin](https://github.com/cucumber/cucumber/tree/gherkin/v15.0.2/gherkin) library. See `testdata/good/*.pickles.ndjson` for examples of its structure.
* When using the asynchronous callback interface, have one final argument for the callback function.

`options` can also be a string as a shorthand for specifying `tags`.
Expand All @@ -59,6 +59,24 @@ Multiple `AfterAll` hooks are executed in the **reverse** order that they are de

---

#### `AfterStep([options,] fn)`

Defines a hook which is run after each step.

* `options`: An object with the following keys:
* `tags`: String tag expression used to apply this hook to only specific scenarios. See [cucumber-tag-expressions](https://docs.cucumber.io/tag-expressions/) for more information.
* `timeout`: A hook-specific timeout, to override the default timeout.
* `fn`: A function, defined as follows:
* The first argument will be an object of the form `{pickle, gherkinDocument, result, testCaseStartedId, testStepId}`
* The pickle object comes from the [gherkin](https://github.com/cucumber/cucumber/tree/gherkin/v15.0.2/gherkin) library. See `testdata/good/*.pickles.ndjson` for examples of its structure.
* When using the asynchronous callback interface, have one final argument for the callback function.

`options` can also be a string as a shorthand for specifying `tags`.

Multiple `AfterStep` hooks are executed in the **reverse** order that they are defined.

---

#### `Before([options,] fn)`

Defines a hook which is run before each scenario. Same interface as `After` except the first argument passed to `fn` will not have the `result` property.
Expand All @@ -75,6 +93,14 @@ Multiple `BeforeAll` hooks are executed in the order that they are defined.

---

#### `BeforeStep([options,] fn)`

Defines a hook which is run before each step. Same interface as `AfterStep` except the first argument passed to `fn` will not have the `result` property.

Multiple `BeforeStep` hooks are executed in the order that they are defined.

---

#### `defineStep(pattern[, options], fn)`

Defines a step.
Expand Down
20 changes: 20 additions & 0 deletions docs/support_files/hooks.md
Original file line number Diff line number Diff line change
Expand Up @@ -102,3 +102,23 @@ AfterAll(function () {
return Promise.resolve()
});
```

## BeforeStep / AfterStep

If you have some code execution that needs to be done before or after all steps, use `BeforeStep` / `AfterStep`. Like the `Before` / `After` hooks, these also have a world instance as 'this', and can be conditionally selected for execution based on the tags of the scenario.

```javascript
var {AfterStep, BeforeStep} = require('cucumber');

BeforeStep({tags: "@foo"}, function () {
// This hook will be executed before all steps in a scenario with tag @foo
});

AfterStep( function ({result}) {
// This hook will be executed after all steps, and take a screenshot on step failure
if (result.status === Status.FAILED) {
this.driver.takeScreenshot();
}
});
```

95 changes: 95 additions & 0 deletions features/before_after_step_hooks.feature
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
Feature: Before and After Step Hooks

Background:
Given a file named "features/a.feature" with:
"""
Feature: some feature
@this-tag
Scenario: some scenario
Given a step
"""
And a file named "features/step_definitions/cucumber_steps.js" with:
"""
const {Given} = require('@cucumber/cucumber')
Given(/^a step$/, function() {})
"""

Scenario: Before and After Hooks work correctly
Given a file named "features/support/hooks.js" with:
"""
const {BeforeStep, AfterStep, BeforeAll, AfterAll} = require('@cucumber/cucumber')
const {expect} = require('chai')

let counter = 1

BeforeStep(function() {
counter = counter + 1
})

AfterStep(function() {
expect(counter).to.eql(2)
counter = counter + 1
})

AfterAll(function() {
expect(counter).to.eql(3)
})
"""
When I run cucumber-js
Then it passes

Scenario: Failing before step fails the scenario
Given a file named "features/support/hooks.js" with:
"""
const {BeforeStep} = require('@cucumber/cucumber')
BeforeStep(function() { throw 'Fail' })
"""
When I run cucumber-js
Then it fails

Scenario: Failing after step fails the scenario
Given a file named "features/support/hooks.js" with:
"""
const {AfterStep} = require('@cucumber/cucumber')
AfterStep(function() { throw 'Fail' })
"""
When I run cucumber-js
Then it fails

Scenario: Only run BeforeStep hooks with appropriate tags
Given a file named "features/support/hooks.js" with:
"""
const { BeforeStep } = require('@cucumber/cucumber')
BeforeStep({tags: "@any-tag"}, function() {
throw Error("Would fail if ran")
})
"""
When I run cucumber-js
Then it passes

Scenario: Only run BeforeStep hooks with appropriate tags
Given a file named "features/support/hooks.js" with:
"""
const { AfterStep } = require('@cucumber/cucumber')
AfterStep({tags: "@this-tag"}, function() {
throw Error("Would fail if ran")
})
"""
When I run cucumber-js
Then it fails

Scenario: after hook parameter can access result status of step
Given a file named "features/support/hooks.js" with:
"""
const { AfterStep, Status } = require('@cucumber/cucumber')

AfterStep(function({result}) {
if (result.status === Status.PASSED) {
return
} else {
throw Error("Result object did not get passed properly to AfterStep Hook.")
}
})
"""
When I run cucumber-js
Then it passes
68 changes: 68 additions & 0 deletions features/world_in_hooks.feature
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
Feature: World in Hooks

Background:
Given a file named "features/a.feature" with:
"""
Feature: some feature
Scenario: some scenario
Given a step
"""
And a file named "features/step_definitions/cucumber_steps.js" with:
"""
const {Given} = require('@cucumber/cucumber')
Given(/^a step$/, function() {})
"""
And a file named "features/support/world.js" with:
"""
const {setWorldConstructor} = require('@cucumber/cucumber')
function WorldConstructor() {
return {
isWorld: function() { return true }
}
}
setWorldConstructor(WorldConstructor)
"""

Scenario: World is this in hooks
Given a file named "features/support/hooks.js" with:
"""
const {After, Before } = require('@cucumber/cucumber')
Before(function() {
if (!this.isWorld()) {
throw Error("Expected this to be world")
}
})
After(function() {
if (!this.isWorld()) {
throw Error("Expected this to be world")
}
})
"""
When I run cucumber-js
Then it passes

Scenario: World is this in BeforeStep hooks
Given a file named "features/support/hooks.js" with:
"""
const {BeforeStep } = require('@cucumber/cucumber')
BeforeStep(function() {
if (!this.isWorld()) {
throw Error("Expected this to be world")
}
})
"""
When I run cucumber-js
Then it passes

Scenario: World is this in AfterStep hooks
Given a file named "features/support/hooks.js" with:
"""
const {AfterStep } = require('@cucumber/cucumber')
AfterStep(function() {
if (!this.isWorld()) {
throw Error("Expected this to be world")
}
})
"""
When I run cucumber-js
Then it passes
2 changes: 2 additions & 0 deletions src/cli/helpers_spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -69,8 +69,10 @@ function testEmitSupportCodeMessages(
stepDefinitions: [],
beforeTestRunHookDefinitions: [],
beforeTestCaseHookDefinitions: [],
beforeTestStepHookDefinitions: [],
afterTestRunHookDefinitions: [],
afterTestCaseHookDefinitions: [],
afterTestStepHookDefinitions: [],
defaultTimeout: 0,
parameterTypeRegistry: new ParameterTypeRegistry(),
undefinedParameterTypes: [],
Expand Down
4 changes: 3 additions & 1 deletion src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,12 +22,14 @@ export { default as UsageFormatter } from './formatter/usage_formatter'
export { default as UsageJsonFormatter } from './formatter/usage_json_formatter'
export { formatterHelpers }

// Support Code Fuctions
// Support Code Functions
const { methods } = supportCodeLibraryBuilder
export const After = methods.After
export const AfterAll = methods.AfterAll
export const AfterStep = methods.AfterStep
export const Before = methods.Before
export const BeforeAll = methods.BeforeAll
export const BeforeStep = methods.BeforeStep
export const defineParameterType = methods.defineParameterType
export const defineStep = methods.defineStep
export const Given = methods.Given
Expand Down
37 changes: 37 additions & 0 deletions src/models/test_step_hook_definition.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
import { PickleTagFilter } from '../pickle_filter'
import Definition, {
IDefinition,
IGetInvocationDataResponse,
IGetInvocationDataRequest,
IDefinitionParameters,
IHookDefinitionOptions,
} from './definition'
import { messages } from '@cucumber/messages'

export default class TestStepHookDefinition
extends Definition
implements IDefinition {
public readonly tagExpression: string
private readonly pickleTagFilter: PickleTagFilter

constructor(data: IDefinitionParameters<IHookDefinitionOptions>) {
super(data)
this.tagExpression = data.options.tags
this.pickleTagFilter = new PickleTagFilter(data.options.tags)
}

appliesToTestCase(pickle: messages.IPickle): boolean {
return this.pickleTagFilter.matchesAllTagExpressions(pickle)
}

async getInvocationParameters({
hookParameter,
}: IGetInvocationDataRequest): Promise<IGetInvocationDataResponse> {
return await Promise.resolve({
getInvalidCodeLengthMessage: () =>
this.buildInvalidCodeLengthMessage('0 or 1', '2'),
parameters: [hookParameter],
validCodeLengths: [0, 1, 2],
})
}
}
68 changes: 68 additions & 0 deletions src/models/test_step_hook_definition_spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
import { describe, it } from 'mocha'
import { expect } from 'chai'
import TestStepHookDefinition from './test_step_hook_definition'
import { getPickleWithTags } from '../../test/gherkin_helpers'

describe('TestStepHookDefinition', () => {
describe('appliesToTestCase', () => {
describe('no tags', () => {
it('returns true', async () => {
// Arrange
const pickle = await getPickleWithTags([])
const testStepHookDefinition = new TestStepHookDefinition({
code: undefined,
id: '',
line: 0,
uri: '',
options: {},
})

// Act
const result = testStepHookDefinition.appliesToTestCase(pickle)

// Assert
expect(result).to.eql(true)
})
})

describe('tags match', () => {
it('returns true', async () => {
// Arrange
const pickle = await getPickleWithTags(['@tagA'])
const testStepHookDefinition = new TestStepHookDefinition({
code: undefined,
id: '',
line: 0,
uri: '',
options: { tags: '@tagA' },
})

// Act
const result = testStepHookDefinition.appliesToTestCase(pickle)

// Assert
expect(result).to.eql(true)
})
})

describe('tags do not match', () => {
it('returns false', async () => {
// Arrange
const pickle = await getPickleWithTags([])
const testStepHookDefinition = new TestStepHookDefinition({
code: undefined,
id: '',
line: 0,
uri: '',
options: { tags: '@tagA' },
})

// Act
const result = testStepHookDefinition.appliesToTestCase(pickle)

// Assert
expect(result).to.eql(false)
})
})
})
})
Loading