## 1. Project Overview & Quickstart (cucumber/cucumber-js) ## File: README.md

Cucumber

[Cucumber](https://github.com/cucumber) is a tool for running automated tests written in plain language. Because they're written in plain language, they can be read by anyone on your team. Because they can be read by anyone, you can use them to help improve communication, collaboration and trust on your team. This is the JavaScript implementation of Cucumber. It runs on [maintained versions](https://github.com/nodejs/Release) of Node.js. You can [quickly try it via CodeSandbox](https://codesandbox.io/s/cucumber-js-demo-2p3vrl?file=/features/greeting.feature), or read on to get started locally in a couple of minutes. Looking to contribute? Read our [code of conduct](https://github.com/cucumber/.github/blob/main/CODE_OF_CONDUCT.md) first, then check the [contributing guide](./CONTRIBUTING.md) to get up and running. ## Install Cucumber is [available on npm](https://www.npmjs.com/package/@cucumber/cucumber): ```shell npm install @cucumber/cucumber ``` ## Get Started Let's take this example of something to test: First, write your main code in `src/index.js`: ```js class Greeter { sayHello() { return 'hello' } } module.exports = { Greeter } ``` Then, write your feature in `features/greeting.feature`: ```gherkin Feature: Greeting Scenario: Say hello When the greeter says hello Then I should have heard "hello" ``` Next, implement your steps in `features/support/steps.js`: ```js const assert = require('assert') const { When, Then } = require('@cucumber/cucumber') const { Greeter } = require('../../src') When('the greeter says hello', function () { this.whatIHeard = new Greeter().sayHello() }) Then('I should have heard {string}', function (expectedResponse) { assert.equal(this.whatIHeard, expectedResponse) }) ``` Finally, run Cucumber: ```shell npx cucumber-js ``` And see the output: If you learn best by example, we have [a repo with several example projects](https://github.com/cucumber/cucumber-js-examples), that might help you get going. ## Documentation The following documentation is for `main`, which might contain some unreleased features. See [documentation for older versions](./docs/older_versions.md) if you need it. * [Installation](./docs/installation.md) * [CLI](./docs/cli.md) * [Configuration](./docs/configuration.md) * Support Code * [API Reference](./docs/support_files/api_reference.md) * [Attachments](./docs/support_files/attachments.md) * [Data Tables](./docs/support_files/data_table_interface.md) * [Hooks](./docs/support_files/hooks.md) * [Step Definitions](./docs/support_files/step_definitions.md) * [Timeouts](./docs/support_files/timeouts.md) * [World](./docs/support_files/world.md) * Guides * [Debugging](./docs/debugging.md) * [Dry run](./docs/dry_run.md) * [ES Modules](./docs/esm.md) * [Failing fast](./docs/fail_fast.md) * [Filtering which scenarios run](./docs/filtering.md) * [Formatters for feedback and reporting](./docs/formatters.md) * [Parallel running for speed](./docs/parallel.md) * [Plugins for extending functionality](./docs/plugins.md) * [Profiles for composable configuration](./docs/profiles.md) * [Rerunning just failures](./docs/rerun.md) * [Retrying flaky scenarios](./docs/retry.md) * [Sharding to split tests across machines](./docs/sharding.md) * [JavaScript API for running programmatically](./docs/javascript_api.md) * [Snippets for undefined steps](./docs/snippets.md) * [Transpiling (from TypeScript etc)](./docs/transpiling.md) * [FAQ](./docs/faq.md) ## Support Support is [available from the community](https://cucumber.io/tools/cucumber-open/support/) if you need it. --- ## File: docs/retro/2022/01/14.md # What we liked: * small pieces / small steps * screensharing - made it easy to switch over, like when Demi left * liked talking about cars! - personal connection is really important. * liked the explanation of the World. Hugely valuable for Blaise! Cucumber ecosystem struggles defining these concepts for newcomers. * A clear objective with many similar, small examples of it. Easy to shift context from one problem to the next, even though they were all slightly different. * we just kept rolling when Blaise arrived and Demi left. * Kate coming along even though she didn't feel she had a lot to contribute # Anything we would do differently? Puzzles? * Took half an hour to get started - setting up the environment, etc. A script to get everything set up beforehand? GitPod? 15 minutes was just spent chatting but we value that! * Deciding what to work on took some time too - could we do that in Slack beforehand? * We could log how time is spent. Blaise has acted as "the scribe" beforehand. * VS Code linting was broken on Matt's machine at least. * Matt's GPG keychain * Tests run slowly # Actions * Kate: be the scribe next week, and spot how we spend our time * Matt: Fix GPG key and linting thing * Blaise: Come along next week! --- ## File: docs/retro/2022/01/21.md # 2022-01-21 ## Who * Kate Dames * Blaise Pabon * Matt Wynne ## What happened * Kate and Blaise showed up 2 weeks in a row! * We talked about the docs, and their shortcomings * We decided we wanted a real example of Cucumber used in practice * We looked at https://github.com/gothinkster/realworld * We decided to start work on the thingy to retire inactive contributors (https://github.com/cucumber/commitbit/issues/3) * We made an example map: https://miro.com/app/board/uXjVOVYhVEw=/?moveToWidget=3458764517125398282&cot=14 * We had this retro ## Insights * Kate liked that we're using a real-world example. * Kate liked going through the example mapping looking at actual data in the examples - e.g. Greg, Julien etc. * Kate liked that we used Miro for collaboration to pin-down our thoughts - park some things for later etc. * Blaise liked doing example mapping in action, so concretely. Learning the distinctions between a rule / example and a story. * Blaise: Materials used to teach cucumber don't emphasise the end result (e.g. the intermediate progress towards a goal). Also don't emphasis how you can use a single test to run against multiple implementations. * Matt: Our docs don't give people enough context as they arrive - straight into the guts of installation without any signposts * Matt: How much we discovered about a problem I thought was simple. I love example mapping! * Kate: I wanted to be more actively participating in the miro board. More hands on. Was it a lack of familiarity? Should we have mad more formal mob roles? What are the rules of the example mapping game? * Kate: It was fun, thank you! ## Actions * Matt: figure out where we should implement this - in the existing commitbit repo or somewhere else? * Kate: think about the process and formalising example mapping --- ## File: docs/retro/2021/08/06.md # 2021-08-06 This session was streamed live and recorded. Watch the video [here](https://youtu.be/EiqLzBBpjxM). ## Who * [@artismarti] * [@16sheep] * [@mattwynne] (taking these notes) ## What happened * Discussed what to do. * Worked from Marju's fork, using our own local machines and VSCode each time we switched driver. * Re-did the work we did last time on the mobbing machine, using Marju's machine to submit a new PR ([#1764]) cleanly. We learned about `git cherry-pick`. * Started working through the pre-prepared issue [#1136] in a [branch on Marju's fork](https://github.com/16sheep/cucumber-js/tree/opt-out-print-attachments-1136) * Arti had to leave a bit early ## Insights * Preferred working on own machine, not using the live share / mobbing machine. * Good to prepare. But, wonder if you over-prepare, it doesn't leave space for people to explore and learn. * Good to have an issue to focus on, and perhaps a failing test (but we could also write this ourselves). * It would help to share what we're goin g to work on beforehand so people watching can also know ahead of time. Decide now for next session? * Matt didn't like being in charge of the timer. * Could use different mechanism for changing turns, like when we make a commit, or when a test is passing? * More people would be nice. Should we make it easier for people to join? How? How often can we run it? ### Actions / decisions * Next time, continue with fixing [#1136] [@artismarti]: https://github.com/artismarti [@mattwynne]: https://github.com/mattwynne [@16sheep]: https://github.com/16sheep [#1764]: https://github.com/cucumber/cucumber-js/pull/1764 [#1136]: https://github.com/cucumber/cucumber-js/issues/1136 --- ## File: docs/retro/2021/07/17.md # 2021-07-17 This retrospective was from the first [new contributors mob/ensemble]. Sadly [@mattwynne] failed to record the stream, so there's no video from this session. ## Who * [@artismarti] * [@16sheep] * [@eoola] * [@mattwynne] (taking these notes) ## What happened? * We read the issues at https://github.com/cucumber/cucumber-js/contribute * We decided to tackle one * We discussed how contributing to the repo worked * We changed what to tackle and tackled something simpler * We made a [PR](https://github.com/cucumber/cucumber-js/pull/1734). ## What worked? What do you appreciate? * I learned general concepts that I could use in other open source projects * The issues were good for first timers. I'm keen to explore more now I've looked through them! * It was so much fun! All the intimidation I had about starting to do this vanished. Low stakes way of learning new things. A nice way of learning new stuff! * The team gelled almost instantly ## What was puzzling or frustrating? * Why did the tests pass on your device but not mine? * Live Share vs Screen Share it was a bit confusing * Would it be easier to just run it locally on one of our machines? More realistic. * Twitch setup (Matt heard his own voice the whole way through, and we started late) ## What should we decide / change for next time? * Do this regularly but on any codebase, not just Cucumber * Would it be easier to just run it locally on one of our machines? * Matt do more homework on twitch setup * Let's do this again! * Update CONTRIBUTING.md guide to include pointers to basics about forking, how to name your branch etc., and some words of comfort and encouragement to make mistakes, ask for help etc. [@artismarti]: https://github.com/artismarti [@mattwynne]: https://github.com/mattwynne [@16sheep]: https://github.com/16sheep [@eoola]: https://github.com/eoola [new contributors mob/ensemble]: https://cucumber.io/blog/open-source/tackling-structural-racism-(and-sexism)-in-open-so/ --- ## File: docs/retro/README.md # Retrospective notes From time to time, we run a mobbing/ensemble session on this codebase. At the end of each session., we run a brief [retrospective] conversation in an effort to "turn up the good" – Woody Zuill. Each retro we run is recorded in this folder as a dated Markdown file. e.g. To find the retrospective for 23rd May 2020 you would look in the file `2020/05/23.md` We use [retro-tools] to help manage these files. [retrospective]: https://www.agilealliance.org/glossary/heartbeatretro/ [retro-tools]: https://github.com/tooky/retro-tools --- ## File: docs/support_files/api_reference.md # Support Files ## API Reference Each method can be destructed from the object returned by `require('@cucumber/cucumber')`. --- #### `defineParameterType({name, preferForRegexpMatch, regexp, transformer, useForSnippets})` Define a new parameter type and optionally convert an output parameter into something else. * `name`: String used to refer to this type in cucumber expressions. * `regexp`: A regular expression (or array of regular expressions) that match the parameter. * `transformer`: An optional function which transforms the captured argument from a string into what is passed to the step definition. If no transform function is specified, the captured argument is left as a string. The function can be synchronous or return a `Promise` of the transformed value. The value of `this` is the current world, so the function can delegate to world functions. Note that your transformer functions cannot reference the [world](./world.md) as `this` if you use arrow functions. See [FAQ](../faq.md) for details. * `useForSnippets`: Defaults to `true`. That means this parameter type will be used to generate snippets for undefined steps. If the `regexp` frequently matches text you don't intend to be used as arguments, disable its use for snippets with `false`. * `preferForRegexpMatch`: Defaults to `false`. Set to `true` if you use regular expressions and you want this parameter type's `regexp` to take precedence over others during a match. The built in parameter types are: * `int` * `float` * `string` * contained in single or double quotes * the transformer removes the quotes * `word` --- #### `After([options,] fn)` Defines a hook which is run after each scenario. * `options`: An object with the following keys: * `name`: An optional name for this hook * `tags`: String tag expression used to apply this hook to only specific scenarios. See [cucumber-tag-expressions](https://github.com/cucumber/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, error, willBeRetried, 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`. Multiple `After` hooks are executed in the **reverse** order that they are defined. --- #### `AfterAll([options,] fn)` Defines a hook which is run after all scenarios have completed. * `options`: An object with the following keys: * `name`: An optional name for this hook * `on`: In [parallel mode](../parallel.md), where the hook should run - `HookTarget.WORKER` (the default) to run once per worker, or `HookTarget.COORDINATOR` to run just once on the coordinator. Has no effect in serial mode. * `timeout`: A hook-specific timeout, to override the default timeout. * `fn`: A function, defined as follows: * When using the asynchronous callback interface, have one argument for the callback function. Multiple `AfterAll` hooks are executed in the **reverse** order that they are defined. --- #### `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://github.com/cucumber/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, pickleStep, 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. * The `pickleStep` is the step in the `pickle` that this hook has been invoked for * 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. Multiple `Before` hooks are executed in the order that they are defined. --- #### `BeforeAll([options,] fn)` Defines a hook which is run before all scenarios. Same interface as `AfterAll`. 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. --- #### `Given(pattern[, options], fn)` Define a "Given" step. Aliases: `defineStep` (deprecated and will be removed in a future release; use the appropriate Given/When/Then keyword to define your step). * `pattern`: A regex or string pattern to match against a gherkin step. * `options`: An object with the following keys: - `timeout`: A step-specific timeout, to override the default timeout. - `wrapperOptions`: Step-specific options that are passed to the definition function wrapper. * `fn`: A function, which should be defined as follows: - Should have one argument for each capture in the regular expression. - May have an additional argument if the gherkin step has a docstring or data table. - When using the asynchronous callback interface, have one final argument for the callback function. --- #### `setDefaultTimeout(milliseconds)` Set the default timeout for asynchronous steps. Defaults to `5000` milliseconds. --- #### `setDefinitionFunctionWrapper(wrapper)` _Note: the usage of `setDefinitionFunctionWrapper` is discouraged in favor of [BeforeStep](#beforestepoptions-fn) and [AfterStep](#afterstepoptions-fn) hooks._ Set a function used to wrap step / hook definitions. The `wrapper` function is expected to take 2 arguments: - `fn` is the original function defined for the step - needs to be called in order for the step to be run. - `options` is the step specific `wrapperOptions` and may be undefined. Example: ```javascript setDefinitionFunctionWrapper(function(fn, options) { return function(...args) { // call original function with correct `this` and arguments // ensure return value of function is returned return fn.apply(this, args) .catch(error => { // rethrow error to avoid swallowing failure throw error; }); } }) ``` When used, the result is wrapped again to ensure it has the same length of the original step / hook definition. --- ### setParallelCanAssign(canAssignFn) Set the function used to determine if a pickle can be executed based on currently executing pickles. The `canAssignFn` function is expected to take 2 arguments: - `pickleInQuestion` is the a pickle we are checking if its okay to run - `picklesInProgress` is an array of pickles currently being executed And returns true if the pickle can be executed, false otherwise. See examples in our [parallel](../parallel.md) documentation. --- #### `setWorldConstructor(constructor)` Set a custom world constructor, to override the default world constructor: ```javascript function World({attach, parameters}) { this.attach = attach; this.parameters = parameters; } ``` * `attach` - a function hooks / steps can use to add [attachments](./attachments.md) * `parameters` - world parameters passed in through the [cli](../cli.md#world-parameters) **Note:** The World constructor was made strictly synchronous in *[v0.8.0](https://github.com/cucumber/cucumber-js/releases/tag/v0.8.0)*. --- #### `Then(pattern[, options], fn)` Define a "Then" step. Same interface as `Given` --- #### `When(pattern[, options], fn)` Define a "When" step. Same interface as `Given` --- ## File: docs/support_files/attachments.md # Attachments Text, images and other data can be added to the output of the messages and JSON formatters with attachments. The world constructor is passed an `attach` function, which the default world constructor assigns to `this.attach`. If using a custom world constructor, you need to do this as well if you want to add attachments. ```javascript var {After} = require('@cucumber/cucumber'); After(function () { this.attach('Some text'); }); ``` By default, text is saved with a MIME type of `text/plain`. You can also specify a different MIME type as part of a second argument: ```javascript var {After} = require('@cucumber/cucumber'); After(function () { this.attach('{"name": "some JSON"}', { mediaType: 'application/json' }); }); ``` If you'd like, you can also specify a filename to be used if the attachment is made available to download as a file via a formatter: ```javascript var {After} = require('@cucumber/cucumber'); After(function () { this.attach('{"name": "some JSON"}', { mediaType: 'application/json', fileName: 'results.json' }); }); ``` Images and other binary data can be attached using a [stream.Readable](https://nodejs.org/api/stream.html). The data will be `base64` encoded in the output. You should wait for the stream to be read before continuing by awaiting the returned promise or providing a callback. ```javascript var {After, Status} = require('@cucumber/cucumber'); // Awaiting the promise After(async function (testCase) { if (testCase.result.status === Status.FAILED) { var stream = getScreenshotOfError(); await this.attach(stream, { mediaType: 'image/png' }); } }); // Passing a callback After(function (testCase, callback) { if (testCase.result.status === Status.FAILED) { var stream = getScreenshotOfError(); this.attach(stream, { mediaType: 'image/png' }, callback); } else { callback(); } }); ``` Images and binary data can also be attached using a [Buffer](https://nodejs.org/api/buffer.html). The data will be `base64` encoded in the output. ```javascript var {After, Status} = require('@cucumber/cucumber'); After(function (testCase) { if (testCase.result.status === Status.FAILED) { var buffer = getScreenshotOfError(); this.attach(buffer, { mediaType: 'image/png' }); } }); ``` If you've already got a base64-encoded string, you can prefix your mime type with `base64:` to indicate this. Here's an example of saving a screenshot using [Selenium WebDriver](https://www.npmjs.com/package/selenium-webdriver) when a scenario fails: ```javascript var {After, Status} = require('@cucumber/cucumber'); After(async function (testCase) { if (testCase.result.status === Status.FAILED) { const screenshot = await driver.takeScreenshot() this.attach(screenshot, { mediaType: 'base64:image/png' }) } }) ``` Attachments are also printed by the progress, progress-bar and summary formatters. They appear right after the step and only `text/plain` content is visible. It can be used to debug scenarios, especially in parallel mode. ```javascript // Step definition Given('a basic step', async function() { this.attach('Some info.') this.attach('{"some": "JSON"}}', { mediaType: 'application/json' }) this.attach((await driver.takeScreenshot()), { mediaType: 'base64:image/png', fileName: 'screenshot.png' }) }) // Result format // ✔ Given a basic step # path:line // Attachment (text/plain): Some info. // Attachment (application/json) // Attachment (image/png): screenshot.png ``` ## Logging You can log useful information from your support code with the simple `log` function: ```javascript var {After} = require('@cucumber/cucumber'); After(function () { this.log('Something interesting happened!'); }); ``` Anything you log will be attached as a string with a MIME type of `text/x.cucumber.log+plain` ## Links You can attach one or more links from your support code with the `link` function: ```javascript var {Before, After} = require('@cucumber/cucumber'); Before(function () { this.link('https://cucumber.io'); }); After(function () { this.link( 'https://github.com/cucumber/cucumber-js', 'https://github.com/cucumber/cucumber-jvm', 'https://github.com/cucumber/cucumber-ruby' ); }); ``` Links will be attached as a string with a MIME type of `text/uri-list` --- ## File: docs/support_files/data_table_interface.md # Data tables When steps have a data table, they are passed an object with methods that can be used to access the data. - with column headers - `hashes`: returns an array of objects where each row is converted to an object (column header is the key) - `rows`: returns the table as a 2-D array, without the first row - without column headers - `raw`: returns the table as a 2-D array - `rowsHash`: returns an object where each row corresponds to an entry (first column is the key, second column is the value) - `transpose`: returns a new instance with the data transposed See this [feature](/features/data_tables.feature) for examples --- ## File: docs/support_files/hooks.md # Hooks Hooks are used for setup and teardown the environment before and after each scenario. See the [API reference](./api_reference.md) for the specification of the first argument passed to hooks. Multiple *Before* hooks are executed in the order that they were defined. Multiple *After* hooks are executed in the **reverse** order that they were defined. Note that your hook functions cannot reference the [world](./world.md) as `this` if you use arrow functions. See [FAQ](../faq.md) for details. ```javascript const {After, Before} = require('@cucumber/cucumber'); // Synchronous Before(function () { this.count = 0; }); // Asynchronous Callback Before(function (testCase, callback) { var world = this; tmp.dir({unsafeCleanup: true}, function(error, dir) { if (error) { callback(error); } else { world.tmpDir = dir; callback(); } }); }); // Asynchronous Promise After(function () { // Assuming this.driver is a selenium webdriver return this.driver.quit(); }); ``` ## Named hooks ℹ️ Added in v8.1.0 Hooks can optionally be named: ```javascript const {Before} = require('@cucumber/cucumber'); Before({name: "Set up some test state"}, function () { // do stuff here }); ``` Such hooks will then be referenced by name in [formatter](../formatters.md) output, which can be useful to help you understand what's happening with your tests. ## Tagged hooks Hooks can be conditionally selected for execution based on the tags of the scenario. ```javascript const {After, Before} = require('@cucumber/cucumber'); Before(function () { // This hook will be executed before all scenarios }); Before({tags: "@foo"}, function () { // This hook will be executed before scenarios tagged with @foo }); Before({tags: "@foo and @bar"}, function () { // This hook will be executed before scenarios tagged with @foo and @bar }); Before({tags: "@foo or @bar"}, function () { // This hook will be executed before scenarios tagged with @foo or @bar }); // You can use the following shorthand when only specifying tags Before("@foo", function () { // This hook will be executed before scenarios tagged with @foo }); ``` See more documentation on [tag expressions](https://docs.cucumber.io/cucumber/api/#tag-expressions) ## Skipping in a Before Hook If you need to imperatively skip a test using a `Before` hook, this can be done using any of the constructs defined in [skipped steps](./step_definitions.md) This includes using: a synchronous return, an asynchronous callback, or an asynchronous promise ```javascript // Synchronous Before(function() { // perform some runtime check to decide whether to skip the proceeding scenario return 'skipped' }); ``` ## BeforeAll / AfterAll If you have some setup / teardown that needs to be done before or after all scenarios, use `BeforeAll` / `AfterAll`. Like hooks and steps, these can be synchronous, accept a callback, or return a promise. ```javascript const {AfterAll, BeforeAll} = require('@cucumber/cucumber'); // Synchronous BeforeAll(function () { // perform some shared setup }); // Asynchronous Callback BeforeAll(function (callback) { // perform some shared setup // execute the callback (optionally passing an error when done) }); // Asynchronous Promise AfterAll(function () { // perform some shared teardown return Promise.resolve() }); ``` ### World parameters in BeforeAll/AfterAll ℹ️ Added in v10.1.0 `BeforeAll`/`AfterAll` hooks aren't given a World instance bound to `this` like other hooks and steps. But they can access [World parameters](./world.md#world-parameters) via `this.parameters` in order to: - Use the parameters as configuration to drive automation - Update the parameters with extra context which will then be available to other hooks and steps Here's a fictional example of obtaining an auth token that can then be used by all tests: ```javascript const {AfterAll, BeforeAll} = require('@cucumber/cucumber'); BeforeAll(async function () { this.parameters.accessToken = await getAccessToken(this.parameters.oauth) }); ``` ### Running on the coordinator in parallel mode ℹ️ Added in v13.2.0 In [parallel mode](../parallel.md), `BeforeAll`/`AfterAll` hooks run _once per worker_ by default. This is helpful where each worker needs its own separate setup (e.g. its own browser instance). Sometimes though, you have setup/teardown that should happen just once, centrally - like starting a shared server or seeding a database. For those, set the `on` option to `HookTarget.COORDINATOR` so the hook runs a single time on the coordinator process instead of on each worker: ```javascript const {AfterAll, BeforeAll, HookTarget} = require('@cucumber/cucumber'); BeforeAll({on: HookTarget.COORDINATOR}, async function () { // runs once, before any workers start their scenarios await startSharedServer(); }); AfterAll({on: HookTarget.COORDINATOR}, async function () { // runs once, after all workers have finished await stopSharedServer(); }); ``` Coordinator `BeforeAll` hooks run before the workers begin, and coordinator `AfterAll` hooks run after all workers have finished, so they wrap the entire run. The default is `HookTarget.WORKER`, which you can also specify explicitly. In serial (non-parallel) mode there's a single process, so all `BeforeAll`/`AfterAll` hooks run once regardless of the `on` option. ## 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 const {AfterStep, BeforeStep} = require('@cucumber/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(); } }); ``` ## 2. Official Technical Reference & Guides (cucumber/docs) [](https://vshymanskyy.github.io/StandWithUkraine) # Cucumber Documentation 🚨 This repository is archived. the Cucumber documentation is now at https://github.com/cucumber/website. 🚨 ## Contributing Content For small edits, the recommended way to contribute is via GitHub. Each page in the documentation will have an **Edit** link for this purpose. ## Working locally For bigger modifications, the recommended workflow is to edit the documentation locally on your machine, and seeing the results in a web browser. This involves getting the source code and building the documentation yourself. # Get the source code git clone https://github.com/cucumber/docs cd docs ### View the site The website is built with several tools that are distributed as a Docker image. Install [Docker](https://docs.docker.com/install/) if you don't already have it on your machine. Build and serve the website: ./docker_shell.sh Open a browser: http://localhost:1313 Changes to the contents will be automatically updated in the browser. Try editing one of the pages under `content` and see for yourself! ### Build the site and check links When you are done editing, build the site and check links: ./docker_shell.sh make clean ./docker_shell.sh make ./docker_shell.sh make htmlproofer By default, external links are only checked in CI (because it takes a while). To check external links locally: CI=true ./docker_shell.sh make htmlproofer Links that are broken should be removed or replaced, even if they are only temporarily broken. The reason for this is that broken links negatively affect search engine rankings. ### Modify theme See the [theme README](themes/cucumber-hugo/README.md) ## Architecture The site is built with [Hugo](https://gohugo.io), a fast static site generator. We have a custom-built theme for the site in `themes/cucumber-hugo`. This theme is based on [Bulma](https://bulma.io/) - a lightweight CSS framework. The online site is rebuild automatically whenever the git repository receives new commits, either via a `git push`, or by modifying contents in the CMS. The [Netlify CMS](https://www.netlifycms.org/) saves contents straight to GitHub using the GitHub API. ## Continuous Deployment GitHub will notify Netlify for every `git push` thanks to a [webhook](https://github.com/cucumber/docs.cucumber.io/settings/hooks). Netlify will then build the site and [deploy](https://app.netlify.com/sites/cucumber/deploys) it if the build is successful. The build will check for broken links and other problems. Internal and external links are checked by [HTMLProofer](https://github.com/gjtorikian/html-proofer). Occasionally, the build will fail due to external links being unavailable or giving a timeout. When that happens, please check if these external links are available and if so, 'Retry build' on Netlify. You can discuss the documentation in the [Community Discord](https://cucumber.io/community#discord).