A Cypress plugin to skip tests on first failure.
TypeScript
122
682 commits
updated Sep 22, 2026
Skip the rest of your Cypress tests after the first failure.
With Cypress Fail Fast, you can:
Add the plugin to your devDependencies:
npm install --save-dev cypress-fail-fast
Then register the plugin in your Cypress configuration (cypress.config.ts or cypress.config.js):
// cypress.config.ts
import { defineConfig } from "cypress";
import cypressFailFast from "cypress-fail-fast/plugin";
export default defineConfig({
e2e: {
setupNodeEvents(on, config) {
cypressFailFast(on, config);
return config;
},
},
});
At the top of your support file (for example cypress/support/e2e.ts or cypress/support/e2e.js):
import "cypress-fail-fast";
From now on, once a test fails (after its last retry), the plugin will enter "fail-fast" mode and start skipping the remaining tests according to the configured strategy. By default, the strategy is to skip all remaining tests in the entire run, but you can customize this behavior as explained in the Configuration section.
Cypress Fail Fast tracks when a test failure should trigger "fail-fast" mode and then uses Mocha's this.skip() to skip subsequent tests and hooks. Skipped tests will appear as pending in the Cypress results, which is the expected behavior when tests are skipped programmatically through Mocha.
The plugin uses a beforeEach hook to decide whether the current test should run or be skipped. This allows it to stop execution not only within the current spec file but also across the rest of the spec files in the run when the chosen strategy requires it.
All plugin configuration is provided through the Cypress configuration file using the expose option.
The following properties are supported:
failFastStrategy: "spec" | "run" | "describe" (default: "run")
"spec": Skip remaining tests only in the current spec file."run" (default): Skip remaining tests in all spec files for the current run."describe": Skip remaining tests only in the describe block where the failure happened. The skipped scope is resolved as follows:
failFastBail. Use unique describe titles within a spec file when using this strategy.failFastEnabled: boolean (default: true)
Enable or disable the fail-fast behavior globally. When set to false, fail-fast can still be enabled for specific tests or suites using per-test configuration.
failFastBail: number (default: 1)
Number of failed tests required before entering fail-fast mode. For example, failFastBail: 2 will start skipping tests once two failures have been counted. Where those failures are counted depends on the strategy:
"run": Failures are tracked across the entire run, so fail-fast mode will be triggered after the configured number of failures regardless of which spec files they occur in."spec": Failures are reset at the beginning of each spec file, so fail-fast mode will be triggered after the configured number of failures within the same spec."describe": Failures are also reset at the beginning of each spec file, but they are counted separately for each describe block, so the limit applies to every block independently. A block is skipped once it accumulates the configured number of failures on its own, and failures in other blocks never count towards it. The block a failure is counted for is the failed test's immediate parent describe, the same one used as skip scope, so a failure inside a nested describe counts for that nested block and not for its ancestors.failFastIgnorePerTestConfig: boolean (default: false)
When true, the plugin ignores any per-test or per-suite failFast configuration and only uses the global options exposed through expose. This is useful when you want to control fail-fast exclusively at a global level (for example, disabling it completely or enabling it for the entire run) and avoid any accidental overrides in tests or suites.
To configure the plugin options, use the expose property in your Cypress configuration:
// cypress.config.ts
import { defineConfig } from "cypress";
import cypressFailFast from "cypress-fail-fast/plugin";
export default defineConfig({
e2e: {
setupNodeEvents(on, config) {
cypressFailFast(on, config);
return config;
},
expose: {
failFastStrategy: "run",
failFastEnabled: true,
failFastBail: 1,
failFastIgnorePerTestConfig: false,
},
},
});
You can configure fail-fast behavior at the test or suite level using the failFast property in Cypress test configuration. The plugin supports:
failFast.enabled: booleanfailFastEnabled option, unless failFastIgnorePerTestConfig is set to true.Example:
describe(
"All tests",
{
failFast: {
enabled: false, // Children tests and suites will inherit this configuration
},
},
() => {
it(
"sanity test",
{
failFast: {
enabled: true, // Overrides configuration defined in parents
},
},
() => {
// If this test fails, remaining tests (and specs) will be skipped
expect(true).to.be.true;
},
);
it("second test", () => {
// If this test fails, fail-fast will not be applied
expect(true).to.be.true;
});
},
);
// cypress.config.ts
export default defineConfig({
e2e: {
expose: {
failFastStrategy: "run",
failFastEnabled: false,
},
},
});
Then enable fail-fast in specific suites:
describe(
"Critical tests",
{
failFast: { enabled: true },
},
() => {
// If any test in this suite fails, remaining tests and specs will be skipped
},
);
// cypress.config.ts
export default defineConfig({
e2e: {
expose: {
failFastEnabled: false,
failFastIgnorePerTestConfig: true,
},
},
});
With this configuration, fail-fast is disabled regardless of any failFast configuration defined in tests or suites.
Hooks allow you to run custom logic when fail-fast mode is triggered or to trigger fail-fast mode based on custom conditions. This can be useful for various purposes, such as coordinating multiple parallel runs with the mechanism that best fits your environment (for example, using a shared file, a database, or an API) as long as it can be accessed by all parallel runs.
Supported hooks:
onFailFastTriggered: Run custom logic when fail-fast mode is triggered. For example, you can use this hook to log additional information or to notify an external system. The hook receives an object with the following properties:
strategy: The fail-fast strategy that is being applied ("spec", "run" or "describe").test: The failed test that triggered fail-fast mode, with the following properties:
name: The title of the test that failed.fullTitle: The full title of the test that failed, including the titles of its parent suites.shouldTriggerFailFast: Trigger fail-fast mode at any moment based on custom logic. For example, you can use this hook to trigger fail-fast mode when a certain threshold of failures is reached across parallel runs. The hook should return true to trigger fail-fast mode or false to continue without triggering it. This hook is called before each test execution, so be careful with the performance of the logic implemented here. Note that fail-fast mode triggered from this hook has no failed test attached, so, when the "describe" strategy is used, there is no describe block to scope the skipped tests to: every remaining test is skipped, as with the "spec" strategy.Both onFailFastTriggered and shouldTriggerFailFast support returning a Promise, allowing you to execute asynchronous operations like API calls or database queries inside the hooks. If a Promise is returned, the plugin will wait for it to resolve before continuing. If a hook throws an error or returns a rejected Promise, the error will be caught, a warning will be logged, and execution will continue normally (in the case of shouldTriggerFailFast, it will assume false).
Here you have an example of how to use these hooks to coordinate multiple parallel runs using a shared file as a flag:
// cypress.config.ts
import { defineConfig } from "cypress";
import fs from "node:fs";
import path from "node:path";
import cypressFailFast from "cypress-fail-fast/plugin";
const testsSkippedFlagFile = path.resolve(__dirname, ".tests_skipped");
export default defineConfig({
e2e: {
setupNodeEvents(on, config) {
cypressFailFast(on, config, {
hooks: {
onFailFastTriggered: ({ strategy, test }) => {
// Create flag file when the plugin starts skipping tests
// You can also use the spec and test information to implement more complex coordination logic if needed
fs.writeFileSync(testsSkippedFlagFile, "");
},
shouldTriggerFailFast: () => {
// If any other run has created the file, start skipping tests
return fs.existsSync(testsSkippedFlagFile);
},
},
});
return config;
},
expose: {
failFastStrategy: "run",
},
},
});
this.skip() is used internally instead of Cypress.stop(). This is intentional, because using Cypress.stop() would mark the first test of each spec file as failed instead of pending, which is not the expected behavior for the plugin.Cypress Fail Fast stopped using Cypress.env() for configuration in version 8.0.0, so, from this version onwards, the plugin is only compatible with Cypress >= 15.10.0, which introduced the expose configuration option. If you are using an older version of Cypress, you can use the last compatible plugin version according to next compatibility table:
| Cypress version | Compatible plugin version |
|---|---|
| >=15.10.0 | 8.x |
| 9.x to 14.x | 7.x |
| 7.x | 6.x |
| 6.x | 5.x |
| 5.x or lower | <= 4.x |
Contributions are welcome. Please read the contributing guidelines and code of conduct before opening an issue or pull request.
MIT, see LICENSE for details.
TypeScript
81.4%
JavaScript
18.4%
A Cypress plugin to skip tests on first failure.
TypeScript
122
682 commits
updated Sep 22, 2026
Skip the rest of your Cypress tests after the first failure.
With Cypress Fail Fast, you can:
Add the plugin to your devDependencies:
npm install --save-dev cypress-fail-fast
Then register the plugin in your Cypress configuration (cypress.config.ts or cypress.config.js):
// cypress.config.ts
import { defineConfig } from "cypress";
import cypressFailFast from "cypress-fail-fast/plugin";
export default defineConfig({
e2e: {
setupNodeEvents(on, config) {
cypressFailFast(on, config);
return config;
},
},
});
At the top of your support file (for example cypress/support/e2e.ts or cypress/support/e2e.js):
import "cypress-fail-fast";
From now on, once a test fails (after its last retry), the plugin will enter "fail-fast" mode and start skipping the remaining tests according to the configured strategy. By default, the strategy is to skip all remaining tests in the entire run, but you can customize this behavior as explained in the Configuration section.
Cypress Fail Fast tracks when a test failure should trigger "fail-fast" mode and then uses Mocha's this.skip() to skip subsequent tests and hooks. Skipped tests will appear as pending in the Cypress results, which is the expected behavior when tests are skipped programmatically through Mocha.
The plugin uses a beforeEach hook to decide whether the current test should run or be skipped. This allows it to stop execution not only within the current spec file but also across the rest of the spec files in the run when the chosen strategy requires it.
All plugin configuration is provided through the Cypress configuration file using the expose option.
The following properties are supported:
failFastStrategy: "spec" | "run" | "describe" (default: "run")
"spec": Skip remaining tests only in the current spec file."run" (default): Skip remaining tests in all spec files for the current run."describe": Skip remaining tests only in the describe block where the failure happened. The skipped scope is resolved as follows:
failFastBail. Use unique describe titles within a spec file when using this strategy.failFastEnabled: boolean (default: true)
Enable or disable the fail-fast behavior globally. When set to false, fail-fast can still be enabled for specific tests or suites using per-test configuration.
failFastBail: number (default: 1)
Number of failed tests required before entering fail-fast mode. For example, failFastBail: 2 will start skipping tests once two failures have been counted. Where those failures are counted depends on the strategy:
"run": Failures are tracked across the entire run, so fail-fast mode will be triggered after the configured number of failures regardless of which spec files they occur in."spec": Failures are reset at the beginning of each spec file, so fail-fast mode will be triggered after the configured number of failures within the same spec."describe": Failures are also reset at the beginning of each spec file, but they are counted separately for each describe block, so the limit applies to every block independently. A block is skipped once it accumulates the configured number of failures on its own, and failures in other blocks never count towards it. The block a failure is counted for is the failed test's immediate parent describe, the same one used as skip scope, so a failure inside a nested describe counts for that nested block and not for its ancestors.failFastIgnorePerTestConfig: boolean (default: false)
When true, the plugin ignores any per-test or per-suite failFast configuration and only uses the global options exposed through expose. This is useful when you want to control fail-fast exclusively at a global level (for example, disabling it completely or enabling it for the entire run) and avoid any accidental overrides in tests or suites.
To configure the plugin options, use the expose property in your Cypress configuration:
// cypress.config.ts
import { defineConfig } from "cypress";
import cypressFailFast from "cypress-fail-fast/plugin";
export default defineConfig({
e2e: {
setupNodeEvents(on, config) {
cypressFailFast(on, config);
return config;
},
expose: {
failFastStrategy: "run",
failFastEnabled: true,
failFastBail: 1,
failFastIgnorePerTestConfig: false,
},
},
});
You can configure fail-fast behavior at the test or suite level using the failFast property in Cypress test configuration. The plugin supports:
failFast.enabled: booleanfailFastEnabled option, unless failFastIgnorePerTestConfig is set to true.Example:
describe(
"All tests",
{
failFast: {
enabled: false, // Children tests and suites will inherit this configuration
},
},
() => {
it(
"sanity test",
{
failFast: {
enabled: true, // Overrides configuration defined in parents
},
},
() => {
// If this test fails, remaining tests (and specs) will be skipped
expect(true).to.be.true;
},
);
it("second test", () => {
// If this test fails, fail-fast will not be applied
expect(true).to.be.true;
});
},
);
// cypress.config.ts
export default defineConfig({
e2e: {
expose: {
failFastStrategy: "run",
failFastEnabled: false,
},
},
});
Then enable fail-fast in specific suites:
describe(
"Critical tests",
{
failFast: { enabled: true },
},
() => {
// If any test in this suite fails, remaining tests and specs will be skipped
},
);
// cypress.config.ts
export default defineConfig({
e2e: {
expose: {
failFastEnabled: false,
failFastIgnorePerTestConfig: true,
},
},
});
With this configuration, fail-fast is disabled regardless of any failFast configuration defined in tests or suites.
Hooks allow you to run custom logic when fail-fast mode is triggered or to trigger fail-fast mode based on custom conditions. This can be useful for various purposes, such as coordinating multiple parallel runs with the mechanism that best fits your environment (for example, using a shared file, a database, or an API) as long as it can be accessed by all parallel runs.
Supported hooks:
onFailFastTriggered: Run custom logic when fail-fast mode is triggered. For example, you can use this hook to log additional information or to notify an external system. The hook receives an object with the following properties:
strategy: The fail-fast strategy that is being applied ("spec", "run" or "describe").test: The failed test that triggered fail-fast mode, with the following properties:
name: The title of the test that failed.fullTitle: The full title of the test that failed, including the titles of its parent suites.shouldTriggerFailFast: Trigger fail-fast mode at any moment based on custom logic. For example, you can use this hook to trigger fail-fast mode when a certain threshold of failures is reached across parallel runs. The hook should return true to trigger fail-fast mode or false to continue without triggering it. This hook is called before each test execution, so be careful with the performance of the logic implemented here. Note that fail-fast mode triggered from this hook has no failed test attached, so, when the "describe" strategy is used, there is no describe block to scope the skipped tests to: every remaining test is skipped, as with the "spec" strategy.Both onFailFastTriggered and shouldTriggerFailFast support returning a Promise, allowing you to execute asynchronous operations like API calls or database queries inside the hooks. If a Promise is returned, the plugin will wait for it to resolve before continuing. If a hook throws an error or returns a rejected Promise, the error will be caught, a warning will be logged, and execution will continue normally (in the case of shouldTriggerFailFast, it will assume false).
Here you have an example of how to use these hooks to coordinate multiple parallel runs using a shared file as a flag:
// cypress.config.ts
import { defineConfig } from "cypress";
import fs from "node:fs";
import path from "node:path";
import cypressFailFast from "cypress-fail-fast/plugin";
const testsSkippedFlagFile = path.resolve(__dirname, ".tests_skipped");
export default defineConfig({
e2e: {
setupNodeEvents(on, config) {
cypressFailFast(on, config, {
hooks: {
onFailFastTriggered: ({ strategy, test }) => {
// Create flag file when the plugin starts skipping tests
// You can also use the spec and test information to implement more complex coordination logic if needed
fs.writeFileSync(testsSkippedFlagFile, "");
},
shouldTriggerFailFast: () => {
// If any other run has created the file, start skipping tests
return fs.existsSync(testsSkippedFlagFile);
},
},
});
return config;
},
expose: {
failFastStrategy: "run",
},
},
});
this.skip() is used internally instead of Cypress.stop(). This is intentional, because using Cypress.stop() would mark the first test of each spec file as failed instead of pending, which is not the expected behavior for the plugin.Cypress Fail Fast stopped using Cypress.env() for configuration in version 8.0.0, so, from this version onwards, the plugin is only compatible with Cypress >= 15.10.0, which introduced the expose configuration option. If you are using an older version of Cypress, you can use the last compatible plugin version according to next compatibility table:
| Cypress version | Compatible plugin version |
|---|---|
| >=15.10.0 | 8.x |
| 9.x to 14.x | 7.x |
| 7.x | 6.x |
| 6.x | 5.x |
| 5.x or lower | <= 4.x |
Contributions are welcome. Please read the contributing guidelines and code of conduct before opening an issue or pull request.
MIT, see LICENSE for details.
TypeScript
81.4%
JavaScript
18.4%