Repository: TheBrainFamily/cypress-cucumber-preprocessor
Pull request #329 (2020-02-29 18:05:46)

Skipped Scenarios missing from cucumber.json files #288
Addresses https://github.com/TheBrainFamily/cypress-cucumber-preprocessor/issues/288

Currently skipped tests, those not matching the tags used for the run, are not added to the cucumber.json files generated during that run.

This PR adds them back into the generated files.

Changes:
In `createTestsFromFeature` pass all the scenarios into `createTestFromScenarios` but mark the subset that should actually be executed with a `shouldRun` flag.

In `createTestFromScenarios` check if the scenario should be executed and if not just add it to the `cucumberDataCollector` and tell `mocha` that we skipped this test.

Fix the `cucumberDataCollector` not to fail the scenario if `mocha` tells it that the test was skipped.



Patch:
diff --git a/README.md b/README.md
index 7ac0c15..0ae679b 100644
--- a/README.md
+++ b/README.md
@@ -28,6 +28,7 @@ You can follow the documentation below, or if you prefer to hack on a working ex
   * [Smart tagging](#smart-tagging)
 * [How to run the tests](#excluding-tests)
   * [Running tagged tests](#running-tagged-tests)
+  * [Ignoring specific scenarios using tags when executing test runner](#ignoring-specific-scenarios-using-tags-when-executing-test-runner)
   * [Output](#output)
 * [IDE support](#ide-support)
   * [Webstorm](#webstorm)
@@ -327,8 +328,28 @@ Example:
 ```
 
 Please note - we use our own cypress-tags wrapper to speed things up.
+This wrapper calls the cypress executable from local modules and if not found it falls back to the globally installed one.
 For more details and examples please take a look to the [example repo](https://github.com/TheBrainFamily/cypress-cucumber-example).
 
+### Ignoring specific scenarios using tags when executing test runner
+You can also use tags to skip or ignore specific tests/scenarios when running cypress test runner (where you don't have the abilitiy to pass parameters like in the examples above for the execution)
+
+The trick consists in adding the "env" property with the "TAGS" subproperty in the cypress.json configuration file. It would look something like this:
+
+```javascript
+{
+    "env": {
+        "TAGS": "not @ignore"
+    },
+    //rest of configuration options
+    "baseUrl": "yourBaseUrl",       
+    "ignoreTestFiles": "*.js",
+    //etc
+}
+```
+
+Then, any scenarios tagged with @ignore will be skipped when running the tests using the cypress test runner
+
 ### Limiting to a subset of feature files
 You can use a glob expression to select which feature files should be included.
 
@@ -388,6 +409,14 @@ Note, that unlike WebStorm which will correctly identify multiple implementation
 
 ## TypeScript Support
 
+### Install
+
+Install the plug-in type definitions:
+
+```shell
+npm install --save-dev @types/cypress-cucumber-preprocessor
+```
+
 ### With Webpack
 You can also use a Webpack loader to process feature files (TypeScript supported). To see how it is done please take 
 a look here: [cypress-cucumber-webpack-typescript-example](https://github.com/TheBrainFamily/cypress-cucumber-webpack-typescript-example)
diff --git a/cypress-tags.js b/cypress-tags.js
index 60445f0..0846d12 100755
--- a/cypress-tags.js
+++ b/cypress-tags.js
@@ -31,13 +31,18 @@ const featuresToRun = [];
 
 const found = process.argv.slice(2).find(arg => arg.indexOf("TAGS=") === 0);
 
-const envTags = found.replace(/.*=/, "");
+const envTags = found ? found.replace(/.*=/, "") : "";
 debug("Found tag expression", envTags);
 
 paths.forEach(featurePath => {
   const spec = `${fs.readFileSync(featurePath)}`;
   const parsedFeature = new Parser().parse(spec);
 
+  if (!parsedFeature.feature) {
+    debug(`Feature: ${featurePath} is empty`);
+    return;
+  }
+
   const featureTags = parsedFeature.feature.tags;
   const featureShouldRun = shouldProceedCurrentStep(featureTags, envTags);
   const taggedScenarioShouldRun = parsedFeature.feature.children.some(
@@ -54,12 +59,20 @@ paths.forEach(featurePath => {
   }
 });
 
+function getOsSpecificExecutable(command) {
+  return process.platform === "win32" ? `${command}.cmd` : command;
+}
+
+function getCypressExecutable() {
+  const command = getOsSpecificExecutable(`${__dirname}/../.bin/cypress`);
+  // fallback to the globally installed cypress instead
+  return fs.existsSync(command) ? command : getOsSpecificExecutable("cypress");
+}
+
 try {
   if (featuresToRun.length || envTags === "") {
     execFileSync(
-      process.platform === "win32"
-        ? `${__dirname}/../.bin/cypress.cmd`
-        : `${__dirname}/../.bin/cypress`,
+      getCypressExecutable(),
       [...process.argv.slice(2), "--spec", featuresToRun.join(",")],
       {
         stdio: [process.stdin, process.stdout, process.stderr]
diff --git a/lib/cukejson/cucumberDataCollector.js b/lib/cukejson/cucumberDataCollector.js
index 55570a3..002621a 100644
--- a/lib/cukejson/cucumberDataCollector.js
+++ b/lib/cukejson/cucumberDataCollector.js
@@ -1,5 +1,5 @@
 const { Parser } = require("gherkin");
-const statuses = require("cucumber").Status;
+const statuses = require("cucumber/lib/status").default;
 
 class CucumberDataCollector {
   constructor(uri, spec) {
@@ -72,6 +72,14 @@ class CucumberDataCollector {
           status: statuses.UNDEFINED,
           duration: this.timeTaken()
         };
+      } else if (err.constructor.name === "Pending") {
+        // cypress marks skipped mocha tests as pending
+        // https://github.com/cypress-io/cypress/issues/3092
+        // don't record this error and mark the step as skipped
+        this.stepResults[this.currentStep] = {
+          status: statuses.SKIPPED,
+          duration: this.timeTaken()
+        };
       } else {
         this.stepResults[this.currentStep] = {
           status: statuses.FAILED,
@@ -122,9 +130,13 @@ class CucumberDataCollector {
       });
     };
     this.recordScenarioResult = scenario => {
-      this.runTests[scenario.name].result = this.anyStepsHaveFailed(scenario)
-        ? statuses.FAILED
-        : statuses.PASSED;
+      const allSkipped = this.areAllStepsSkipped(scenario.name);
+      const anyFailed = this.anyStepsHaveFailed(scenario.name);
+      if (allSkipped) this.runTests[scenario.name].result = statuses.SKIPPED;
+      else
+        this.runTests[scenario.name].result = anyFailed
+          ? statuses.FAILED
+          : statuses.PASSED;
     };
 
     this.setStepToPending = step => {
@@ -138,8 +150,11 @@ class CucumberDataCollector {
       };
     };
 
-    this.anyStepsHaveFailed = () =>
-      Object.values(this.stepResults).find(e => e.status !== statuses.PASSED);
+    this.areAllStepsSkipped = name =>
+      this.runTests[name].every(e => e.status === statuses.SKIPPED);
+
+    this.anyStepsHaveFailed = name =>
+      this.runTests[name].find(e => e.status === statuses.FAILED) !== undefined;
   }
 }
 
diff --git a/lib/cukejson/generateCucumberJson.js b/lib/cukejson/generateCucumberJson.js
index f77de96..ad55bc7 100644
--- a/lib/cukejson/generateCucumberJson.js
+++ b/lib/cukejson/generateCucumberJson.js
@@ -1,6 +1,7 @@
 const { EventEmitter } = require("events");
 const { generateEvents } = require("gherkin");
-const { JsonFormatter, formatterHelpers } = require("cucumber");
+const JsonFormatter = require("cucumber/lib/formatter/json_formatter").default;
+const formatterHelpers = require("cucumber/lib/formatter/helpers");
 
 function generateCucumberJson(state) {
   let output = "";
diff --git a/lib/getConfig.js b/lib/getConfig.js
new file mode 100644
index 0000000..e3f5ca7
--- /dev/null
+++ b/lib/getConfig.js
@@ -0,0 +1,15 @@
+const cosmiconfig = require("cosmiconfig");
+
+let explorer;
+
+exports.getConfig = () => {
+  if (!explorer) {
+    explorer = cosmiconfig("cypress-cucumber-preprocessor", {
+      sync: true,
+      rcExtensions: true
+    });
+  }
+
+  const loaded = explorer.load();
+  return loaded && loaded.config;
+};
diff --git a/lib/getCucumberJsonConfig.js b/lib/getCucumberJsonConfig.js
index 4d27235..93e9226 100644
--- a/lib/getCucumberJsonConfig.js
+++ b/lib/getCucumberJsonConfig.js
@@ -1,14 +1,10 @@
-const cosmiconfig = require("cosmiconfig");
 const log = require("debug")("cypress:cucumber");
+const { getConfig } = require("./getConfig");
 
 exports.getCucumberJsonConfig = () => {
-  const explorer = cosmiconfig("cypress-cucumber-preprocessor", { sync: true });
-  const loaded = explorer.load();
-
+  const config = getConfig();
   const cucumberJson =
-    loaded && loaded.config && loaded.config.cucumberJson
-      ? loaded.config.cucumberJson
-      : { generate: false };
+    config && config.cucumberJson ? config.cucumberJson : { generate: false };
   log("cucumber.json", JSON.stringify(cucumberJson));
 
   return cucumberJson;
diff --git a/lib/getStepDefinitionsPaths.js b/lib/getStepDefinitionsPaths.js
index d35401d..3744fd6 100644
--- a/lib/getStepDefinitionsPaths.js
+++ b/lib/getStepDefinitionsPaths.js
@@ -1,18 +1,23 @@
+const path = require("path");
 const glob = require("glob");
-const cosmiconfig = require("cosmiconfig");
+const { getConfig } = require("./getConfig");
 const stepDefinitionPath = require("./stepDefinitionPath.js");
 const { getStepDefinitionPathsFrom } = require("./getStepDefinitionPathsFrom");
 
 const getStepDefinitionsPaths = filePath => {
+  const appRoot = process.cwd();
   let paths = [];
-  const explorer = cosmiconfig("cypress-cucumber-preprocessor", { sync: true });
-  const loaded = explorer.load();
-  if (loaded && loaded.config && loaded.config.nonGlobalStepDefinitions) {
+  const config = getConfig();
+  if (config && config.nonGlobalStepDefinitions) {
     const nonGlobalPattern = `${getStepDefinitionPathsFrom(
       filePath
     )}/**/*.+(js|ts)`;
-    const commonPath =
-      loaded.config.commonPath || `${stepDefinitionPath()}/common/`;
+
+    let commonPath = config.commonPath || `${stepDefinitionPath()}/common/`;
+
+    if (config.commonPath) {
+      commonPath = path.resolve(appRoot, commonPath);
+    }
     const commonDefinitionsPattern = `${commonPath}**/*.+(js|ts)`;
     paths = paths.concat(glob.sync(nonGlobalPattern));
     paths = paths.concat(glob.sync(commonDefinitionsPattern));
diff --git a/lib/index.js b/lib/index.js
index 76ffb4e..0e55ada 100644
--- a/lib/index.js
+++ b/lib/index.js
@@ -37,24 +37,51 @@ const touch = filename => {
 
 let watcher;
 
-const preprocessor = (options = browserify.defaultOptions) => async file => {
-  if (options.browserifyOptions.transform.indexOf(transform) === -1) {
-    options.browserifyOptions.transform.unshift(transform);
+// Include our transform in Browserify options
+const wrapOptions = options => {
+  let wrappedTransform;
+  if (
+    !options.browserifyOptions ||
+    !Array.isArray(options.browserifyOptions.transform)
+  ) {
+    wrappedTransform = [transform];
+  } else if (!options.browserifyOptions.transform.includes(transform)) {
+    wrappedTransform = [transform, ...options.browserifyOptions.transform];
+  } else {
+    wrappedTransform = options.browserifyOptions.transform;
   }
 
-  if (file.shouldWatch) {
-    if (watcher) {
-      watcher.close();
+  return {
+    ...options,
+    browserifyOptions: {
+      ...(options.browserifyOptions || {}),
+      transform: wrappedTransform
     }
-    watcher = chokidar
-      .watch([`${stepDefinitionPath()}*.js`, `${stepDefinitionPath()}*.ts`], {
-        ignoreInitial: true
-      })
-      .on("all", () => {
-        touch(file.filePath);
-      });
+  };
+};
+
+const preprocessor = (options = browserify.defaultOptions) => {
+  if (typeof options !== "object") {
+    throw new Error("Preprocessor options must be an object");
   }
-  return browserify(options)(file);
+
+  const wrappedOptions = wrapOptions(options);
+
+  return async file => {
+    if (file.shouldWatch) {
+      if (watcher) {
+        watcher.close();
+      }
+      watcher = chokidar
+        .watch([`${stepDefinitionPath()}*.js`, `${stepDefinitionPath()}*.ts`], {
+          ignoreInitial: true
+        })
+        .on("all", () => {
+          touch(file.filePath);
+        });
+    }
+    return browserify(wrappedOptions)(file);
+  };
 };
 
 module.exports = {
diff --git a/lib/isNonGlobalStepDefinitionsMode.js b/lib/isNonGlobalStepDefinitionsMode.js
index 11173c5..d7a53a7 100644
--- a/lib/isNonGlobalStepDefinitionsMode.js
+++ b/lib/isNonGlobalStepDefinitionsMode.js
@@ -1,7 +1,6 @@
-const cosmiconfig = require("cosmiconfig");
+const { getConfig } = require("./getConfig");
 
 exports.isNonGlobalStepDefinitionsMode = () => {
-  const explorer = cosmiconfig("cypress-cucumber-preprocessor", { sync: true });
-  const loaded = explorer.load();
-  return loaded && loaded.config && loaded.config.nonGlobalStepDefinitions;
+  const config = getConfig();
+  return config && config.nonGlobalStepDefinitions;
 };
diff --git a/lib/stepDefinitionPath.js b/lib/stepDefinitionPath.js
index ffa9382..fb4a790 100644
--- a/lib/stepDefinitionPath.js
+++ b/lib/stepDefinitionPath.js
@@ -1,15 +1,11 @@
 const path = require("path");
-const cosmiconfig = require("cosmiconfig");
 const fs = require("fs");
+const { getConfig } = require("./getConfig");
 
 module.exports = () => {
   const appRoot = process.cwd();
-
-  const explorer = cosmiconfig("cypress-cucumber-preprocessor", { sync: true });
-  const loaded = explorer.load();
-  if (loaded && loaded.config) {
-    const { config, filepath } = loaded;
-
+  const config = getConfig();
+  if (config) {
     // left for backward compability, but we need the consistency with other configuration options
     const confStepDefinitions = config.step_definitions
       ? config.step_definitions
@@ -21,7 +17,7 @@ module.exports = () => {
 
       if (!fs.existsSync(stepsPath)) {
         throw new Error(
-          `We've tried to resolve your step definitions at ${relativePath}, but that doesn't seem to exist. As of version 2.0.0 it's required to set step_definitions in your cypress-cucumber-preprocessor configuration. Currently your configuration is in: ${filepath}. Look for nonGlobalStepDefinitions and add stepDefinitions right next to it. It should match your cypress configuration has set for integrationFolder. We no longer rely on getting information from that file as it was unreliable and problematic across Linux/MacOS/Windows especially since the config file could have been passed as an argument to cypress.`
+          `We've tried to resolve your step definitions at ${relativePath}, but that doesn't seem to exist. As of version 2.0.0 it's required to set step_definitions in your cypress-cucumber-preprocessor configuration. Look for nonGlobalStepDefinitions and add stepDefinitions right next to it. It should match your cypress configuration has set for integrationFolder. We no longer rely on getting information from that file as it was unreliable and problematic across Linux/MacOS/Windows especially since the config file could have been passed as an argument to cypress.`
         );
       }
       return stepsPath;
diff --git a/package-lock.json b/package-lock.json
index 24892de..c452b2a 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -3144,24 +3144,6 @@
       "integrity": "sha1-YXmX/F9gV2iUxDX5QNgZ4TW4B2I=",
       "dev": true
     },
-    "align-text": {
-      "version": "0.1.4",
-      "resolved": "https://registry.npmjs.org/align-text/-/align-text-0.1.4.tgz",
-      "integrity": "sha1-DNkKVhCT810KmSVsIrcGlDP60Rc=",
-      "dev": true,
-      "optional": true,
-      "requires": {
-        "kind-of": "^3.0.2",
-        "longest": "^1.0.1",
-        "repeat-string": "^1.5.2"
-      }
-    },
-    "amdefine": {
-      "version": "1.0.1",
-      "resolved": "https://registry.npmjs.org/amdefine/-/amdefine-1.0.1.tgz",
-      "integrity": "sha1-SlKCrBZHKek2Gbz9OtFR+BfOkfU=",
-      "dev": true
-    },
     "ansi-escapes": {
       "version": "1.4.0",
       "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-1.4.0.tgz",
@@ -4019,13 +4001,6 @@
       "integrity": "sha1-r6uWJikQp/M8GaV3WCXGnzTjUMo=",
       "dev": true
     },
-    "camelcase": {
-      "version": "1.2.1",
-      "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-1.2.1.tgz",
-      "integrity": "sha1-m7UwTS4LVmmLLHWLCKPqqdqlijk=",
-      "dev": true,
-      "optional": true
-    },
     "camelcase-keys": {
       "version": "4.2.0",
       "resolved": "https://registry.npmjs.org/camelcase-keys/-/camelcase-keys-4.2.0.tgz",
@@ -4075,17 +4050,6 @@
       "integrity": "sha1-G2gcIf+EAzyCZUMJBolCDRhxUdw=",
       "dev": true
     },
-    "center-align": {
-      "version": "0.1.3",
-      "resolved": "https://registry.npmjs.org/center-align/-/center-align-0.1.3.tgz",
-      "integrity": "sha1-qg0yYptu6XIgBBHL1EYckHvCt60=",
-      "dev": true,
-      "optional": true,
-      "requires": {
-        "align-text": "^0.1.3",
-        "lazy-cache": "^1.0.3"
-      }
-    },
     "chai": {
       "version": "4.1.2",
       "resolved": "https://registry.npmjs.org/chai/-/chai-4.1.2.tgz",
@@ -4547,27 +4511,6 @@
       "integrity": "sha1-/xnt6Kml5XkyQUewwR8PvLq+1jk=",
       "dev": true
     },
-    "cliui": {
-      "version": "2.1.0",
-      "resolved": "https://registry.npmjs.org/cliui/-/cliui-2.1.0.tgz",
-      "integrity": "sha1-S0dXYP+AJkx2LDoXGQMukcf+oNE=",
-      "dev": true,
-      "optional": true,
-      "requires": {
-        "center-align": "^0.1.1",
-        "right-align": "^0.1.1",
-        "wordwrap": "0.0.2"
-      },
-      "dependencies": {
-        "wordwrap": {
-          "version": "0.0.2",
-          "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-0.0.2.tgz",
-          "integrity": "sha1-t5Zpu0LstAn4PVg8rVLKF+qhZD8=",
-          "dev": true,
-          "optional": true
-        }
-      }
-    },
     "co": {
       "version": "4.6.0",
       "resolved": "https://registry.npmjs.org/co/-/co-4.6.0.tgz",
@@ -8288,30 +8231,39 @@
       "dev": true
     },
     "handlebars": {
-      "version": "4.0.11",
-      "resolved": "https://registry.npmjs.org/handlebars/-/handlebars-4.0.11.tgz",
-      "integrity": "sha1-Ywo13+ApS8KB7a5v/F0yn8eYLcw=",
+      "version": "4.7.3",
+      "resolved": "https://registry.npmjs.org/handlebars/-/handlebars-4.7.3.tgz",
+      "integrity": "sha512-SRGwSYuNfx8DwHD/6InAPzD6RgeruWLT+B8e8a7gGs8FWgHzlExpTFMEq2IA6QpAfOClpKHy6+8IqTjeBCu6Kg==",
       "dev": true,
       "requires": {
-        "async": "^1.4.0",
+        "neo-async": "^2.6.0",
         "optimist": "^0.6.1",
-        "source-map": "^0.4.4",
-        "uglify-js": "^2.6"
+        "source-map": "^0.6.1",
+        "uglify-js": "^3.1.4"
       },
       "dependencies": {
-        "async": {
-          "version": "1.5.2",
-          "resolved": "https://registry.npmjs.org/async/-/async-1.5.2.tgz",
-          "integrity": "sha1-7GphrlZIDAw8skHJVhjiCJL5Zyo=",
-          "dev": true
+        "commander": {
+          "version": "2.20.3",
+          "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz",
+          "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==",
+          "dev": true,
+          "optional": true
         },
         "source-map": {
-          "version": "0.4.4",
-          "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.4.4.tgz",
-          "integrity": "sha1-66T12pwNyZneaAMti092FzZSA2s=",
+          "version": "0.6.1",
+          "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
+          "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==",
+          "dev": true
+        },
+        "uglify-js": {
+          "version": "3.7.7",
+          "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.7.7.tgz",
+          "integrity": "sha512-FeSU+hi7ULYy6mn8PKio/tXsdSXN35lm4KgV2asx00kzrLU9Pi3oAslcJT70Jdj7PHX29gGUPOT6+lXGBbemhA==",
           "dev": true,
+          "optional": true,
           "requires": {
-            "amdefine": ">=0.0.4"
+            "commander": "~2.20.3",
+            "source-map": "~0.6.1"
           }
         }
       }
@@ -9553,33 +9505,17 @@
           "version": "2.20.0",
           "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.0.tgz",
           "integrity": "sha512-7j2y+40w61zy6YC2iRNpUe/NwhNyoXrYpHMrSunaMG64nRnaf96zO/KMQR4OyN/UnE5KLyEBnKHd4aG3rskjpQ==",
-          "dev": true,
           "optional": true
         },
-        "handlebars": {
-          "version": "4.1.2",
-          "resolved": "https://registry.npmjs.org/handlebars/-/handlebars-4.1.2.tgz",
-          "integrity": "sha512-nvfrjqvt9xQ8Z/w0ijewdD/vvWDTOweBUm96NTr66Wfvo1mJenBLwcYmPs3TIBP5ruzYGD7Hx/DaM9RmhroGPw==",
-          "dev": true,
-          "requires": {
-            "neo-async": "^2.6.0",
-            "optimist": "^0.6.1",
-            "source-map": "^0.6.1",
-            "uglify-js": "^3.1.4"
-          }
-        },
         "source-map": {
           "version": "0.6.1",
           "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
-          "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==",
-          "dev": true
+          "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g=="
         },
         "uglify-js": {
           "version": "3.6.0",
           "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.6.0.tgz",
           "integrity": "sha512-W+jrUHJr3DXKhrsS7NUVxn3zqMOFn0hL/Ei6v0anCIMoKC93TjcflTagwIHLW7SfMFfiQuktQyFVCFHGUE0+yg==",
-          "dev": true,
-          "optional": true,
           "requires": {
             "commander": "~2.20.0",
             "source-map": "~0.6.1"
@@ -12323,13 +12259,6 @@
       "integrity": "sha1-eZllXoZGwX8In90YfRUNMyTVRRM=",
       "dev": true
     },
-    "lazy-cache": {
-      "version": "1.0.4",
-      "resolved": "https://registry.npmjs.org/lazy-cache/-/lazy-cache-1.0.4.tgz",
-      "integrity": "sha1-odePw6UEdMuAhF07O24dpJpEbo4=",
-      "dev": true,
-      "optional": true
-    },
     "left-pad": {
       "version": "1.3.0",
       "resolved": "https://registry.npmjs.org/left-pad/-/left-pad-1.3.0.tgz",
@@ -13175,13 +13104,6 @@
         "cli-cursor": "^1.0.2"
       }
     },
-    "longest": {
-      "version": "1.0.1",
-      "resolved": "https://registry.npmjs.org/longest/-/longest-1.0.1.tgz",
-      "integrity": "sha1-MKCy2jj3N3DoKUoNIuZiXtd9AJc=",
-      "dev": true,
-      "optional": true
-    },
     "loose-envify": {
       "version": "1.4.0",
       "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz",
@@ -13524,9 +13446,9 @@
       }
     },
     "minimist": {
-      "version": "1.2.0",
-      "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz",
-      "integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ="
+      "version": "1.2.3",
+      "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.3.tgz",
+      "integrity": "sha512-+bMdgqjMN/Z77a6NlY/I3U5LlRDbnmaAk6lDveAPKwSpcPM4tKAuYsvYF8xjhOPXhOYGe/73vVLVez5PW+jqhw=="
     },
     "minimist-options": {
       "version": "3.0.2",
@@ -18444,16 +18366,6 @@
       "integrity": "sha1-G0KmJmoh8HQh0bC1S33BZ7AcATs=",
       "dev": true
     },
-    "right-align": {
-      "version": "0.1.3",
-      "resolved": "https://registry.npmjs.org/right-align/-/right-align-0.1.3.tgz",
-      "integrity": "sha1-YTObci/mo1FWiSENJOFMlhSGE+8=",
-      "dev": true,
-      "optional": true,
-      "requires": {
-        "align-text": "^0.1.1"
-      }
-    },
     "right-pad": {
       "version": "1.0.1",
       "resolved": "https://registry.npmjs.org/right-pad/-/right-pad-1.0.1.tgz",
@@ -20422,40 +20334,6 @@
       "resolved": "https://registry.npmjs.org/typedarray/-/typedarray-0.0.6.tgz",
       "integrity": "sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c="
     },
-    "uglify-js": {
-      "version": "2.8.29",
-      "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-2.8.29.tgz",
-      "integrity": "sha1-KcVzMUgFe7Th913zW3qcty5qWd0=",
-      "dev": true,
-      "optional": true,
-      "requires": {
-        "source-map": "~0.5.1",
-        "uglify-to-browserify": "~1.0.0",
-        "yargs": "~3.10.0"
-      },
-      "dependencies": {
-        "yargs": {
-          "version": "3.10.0",
-          "resolved": "https://registry.npmjs.org/yargs/-/yargs-3.10.0.tgz",
-          "integrity": "sha1-9+572FfdfB0tOMDnTvvWgdFDH9E=",
-          "dev": true,
-          "optional": true,
-          "requires": {
-            "camelcase": "^1.0.2",
-            "cliui": "^2.1.0",
-            "decamelize": "^1.0.0",
-            "window-size": "0.1.0"
-          }
-        }
-      }
-    },
-    "uglify-to-browserify": {
-      "version": "1.0.2",
-      "resolved": "https://registry.npmjs.org/uglify-to-browserify/-/uglify-to-browserify-1.0.2.tgz",
-      "integrity": "sha1-bgkk1r2mta/jSeOabWMoUKD4grc=",
-      "dev": true,
-      "optional": true
-    },
     "umd": {
       "version": "3.0.3",
       "resolved": "https://registry.npmjs.org/umd/-/umd-3.0.3.tgz",
@@ -21336,13 +21214,6 @@
       "integrity": "sha1-2e8H3Od7mQK4o6j6SzHD4/fm6Ho=",
       "dev": true
     },
-    "window-size": {
-      "version": "0.1.0",
-      "resolved": "https://registry.npmjs.org/window-size/-/window-size-0.1.0.tgz",
-      "integrity": "sha1-VDjNLqk7IC76Ohn+iIeu58lPnJ0=",
-      "dev": true,
-      "optional": true
-    },
     "windows-release": {
       "version": "3.1.0",
       "resolved": "https://registry.npmjs.org/windows-release/-/windows-release-3.1.0.tgz",


Test patch:
diff --git a/lib/createTestFromScenario.js b/lib/createTestFromScenario.js
index 6086a4a..ceeba49 100644
--- a/lib/createTestFromScenario.js
+++ b/lib/createTestFromScenario.js
@@ -1,5 +1,5 @@
 /* eslint-disable prefer-template */
-const statuses = require("cucumber").Status;
+const statuses = require("cucumber/lib/status").default;
 const {
   resolveAndRunStepDefinition,
   resolveAndRunBeforeHooks,
@@ -33,22 +33,42 @@ const runTest = (scenario, stepsToRun, rowData) => {
     Object.assign({}, step, { index })
   );
 
-  // eslint-disable-next-line func-names
-  it(scenario.name, function() {
-    const state = window.testState;
-    return cy
-      .then(() => state.onStartScenario(scenario, indexedSteps))
-      .then(() =>
-        resolveAndRunBeforeHooks.call(this, scenario.tags, state.feature.name)
-      )
-      .then(() =>
-        indexedSteps.forEach(step => stepTest.call(this, state, step, rowData))
-      )
-      .then(() =>
-        resolveAndRunAfterHooks.call(this, scenario.tags, state.feature.name)
-      )
-      .then(() => state.onFinishScenario(scenario));
-  });
+  // should we actually run this scenario
+  // or just mark it as skipped
+  if (scenario.shouldRun) {
+    // eslint-disable-next-line func-names
+    it(scenario.name, function() {
+      const state = window.testState;
+      return cy
+        .then(() => state.onStartScenario(scenario, indexedSteps))
+        .then(() =>
+          resolveAndRunBeforeHooks.call(this, scenario.tags, state.feature.name)
+        )
+        .then(() =>
+          indexedSteps.forEach(step =>
+            stepTest.call(this, state, step, rowData)
+          )
+        )
+        .then(() =>
+          resolveAndRunAfterHooks.call(this, scenario.tags, state.feature.name)
+        )
+        .then(() => state.onFinishScenario(scenario));
+    });
+  } else {
+    // eslint-disable-next-line func-names,prefer-arrow-callback
+    it(scenario.name, function() {
+      // register this scenario with the cucumber data collector
+      // but don't run it
+      // Tell mocha this is a skipped test so it also shows correctly in Cypress
+      const state = window.testState;
+      cy.then(() => state.onStartScenario(scenario, indexedSteps))
+        .then(() => state.onFinishScenario(scenario))
+        // eslint-disable-next-line func-names
+        .then(function() {
+          return this.skip();
+        });
+    });
+  }
 };
 
 const cleanupFilename = s => s.split(".")[0];
@@ -64,7 +84,7 @@ const writeCucumberJsonFile = json => {
 };
 
 const createTestFromScenarios = (
-  scenariosToRun,
+  allScenarios,
   backgroundSection,
   testState
 ) => {
@@ -87,7 +107,7 @@ const createTestFromScenarios = (
     Cypress.on("fail", failHandler);
   });
 
-  scenariosToRun.forEach(section => {
+  allScenarios.forEach(section => {
     if (section.examples) {
       section.examples.forEach(example => {
         const exampleValues = [];
diff --git a/lib/createTestsFromFeature.js b/lib/createTestsFromFeature.js
index a5bc69a..caa4272 100644
--- a/lib/createTestsFromFeature.js
+++ b/lib/createTestsFromFeature.js
@@ -6,58 +6,37 @@ const createTestsFromFeature = (filePath, spec) => {
   const testState = new CucumberDataCollector(filePath, spec);
   const featureTags = testState.feature.tags;
   const hasEnvTags = !!getEnvTags();
-  const sectionsWithTags = testState.feature.children.filter(
-    section => section.tags && section.tags.length
+  const anyFocused =
+    testState.feature.children.filter(
+      section => section.tags && section.tags.find(t => t.name === "@focus")
+    ).length > 0;
+  const backgroundSection = testState.feature.children.find(
+    section => section.type === "Background"
+  );
+  const allScenarios = testState.feature.children.filter(
+    section => section.type !== "Background"
   );
 
-  const sectionsWithTagsExist = sectionsWithTags.length > 0;
-
-  let everythingShouldRun = false;
-  let featureShouldRun = false;
-  let taggedScenarioShouldRun = false;
-  let anyFocused = false;
-  if (hasEnvTags) {
-    featureShouldRun = shouldProceedCurrentStep(featureTags);
-    taggedScenarioShouldRun = testState.feature.children.some(
-      section =>
-        section.tags &&
-        section.tags.length &&
-        shouldProceedCurrentStep(section.tags.concat(featureTags))
-    );
-  } else if (!sectionsWithTagsExist) {
-    everythingShouldRun = true;
-  } else {
-    anyFocused = sectionsWithTags.some(section =>
-      section.tags.find(t => t.name === "@focus")
-    );
-    if (anyFocused) {
-      taggedScenarioShouldRun = true;
+  const scenariosToRun = allScenarios.filter(section => {
+    let shouldRun;
+    // only just run focused if no env tags set
+    // https://github.com/TheBrainFamily/cypress-cucumber-example#smart-tagging
+    if (!hasEnvTags && anyFocused) {
+      shouldRun = section.tags.find(t => t.name === "@focus");
     } else {
-      everythingShouldRun = true;
+      shouldRun =
+        !hasEnvTags ||
+        shouldProceedCurrentStep(section.tags.concat(featureTags)); // Concat handles inheritance of tags from feature
     }
-  }
-
-  // eslint-disable-next-line prefer-arrow-callback
-  if (everythingShouldRun || featureShouldRun || taggedScenarioShouldRun) {
-    const backgroundSection = testState.feature.children.find(
-      section => section.type === "Background"
-    );
-    const otherSections = testState.feature.children.filter(
-      section => section.type !== "Background"
-    );
-    const scenariosToRun = otherSections.filter(section => {
-      let shouldRun;
-      if (anyFocused) {
-        shouldRun = section.tags.find(t => t.name === "@focus");
-      } else {
-        shouldRun =
-          everythingShouldRun ||
-          shouldProceedCurrentStep(section.tags.concat(featureTags)); // Concat handles inheritance of tags from feature
-      }
-      return shouldRun;
-    });
-    createTestFromScenarios(scenariosToRun, backgroundSection, testState);
-  }
+    return shouldRun;
+  });
+  // create tests for all the scenarios
+  // but flag only the ones that should be run
+  scenariosToRun.forEach(section => {
+    // eslint-disable-next-line no-param-reassign
+    section.shouldRun = true;
+  });
+  createTestFromScenarios(allScenarios, backgroundSection, testState);
 };
 
 module.exports = {
diff --git a/lib/cukejson/cucumberDataCollector.test.js b/lib/cukejson/cucumberDataCollector.test.js
index 272f837..7f68f64 100644
--- a/lib/cukejson/cucumberDataCollector.test.js
+++ b/lib/cukejson/cucumberDataCollector.test.js
@@ -1,5 +1,5 @@
 const fs = require("fs");
-const statuses = require("cucumber").Status;
+const statuses = require("cucumber/lib/status").default;
 const { CucumberDataCollector } = require("./cucumberDataCollector");
 const { generateCucumberJson } = require("./generateCucumberJson");
 
diff --git a/lib/getStepDefinitionsPaths.test.js b/lib/getStepDefinitionsPaths.test.js
index 34c3e70..212cb09 100644
--- a/lib/getStepDefinitionsPaths.test.js
+++ b/lib/getStepDefinitionsPaths.test.js
@@ -1,3 +1,4 @@
+/* eslint-disable global-require */
 jest.mock("./stepDefinitionPath.js", () => () => "stepDefinitionPath");
 jest.mock("glob", () => ({
   sync(pattern) {
@@ -5,17 +6,19 @@ jest.mock("glob", () => ({
   }
 }));
 
+let getConfig;
+
 describe("getStepDefinitionsPaths", () => {
-  it("should return the default common folder", () => {
+  beforeEach(() => {
     jest.resetModules();
-    jest.mock("cosmiconfig", () => () => ({
-      load: () => ({
-        config: {
-          nonGlobalStepDefinitions: true
-        }
-      })
-    }));
-    // eslint-disable-next-line global-require
+    ({ getConfig } = require("./getConfig"));
+    jest.mock("./getConfig");
+  });
+  it("should return the default common folder", () => {
+    getConfig.mockReturnValue({
+      nonGlobalStepDefinitions: true
+    });
+
     const { getStepDefinitionsPaths } = require("./getStepDefinitionsPaths");
 
     const actual = getStepDefinitionsPaths("/path");
@@ -23,22 +26,29 @@ describe("getStepDefinitionsPaths", () => {
 
     expect(actual).to.include(expected);
   });
-  it("should return the common folder defined by the developper", () => {
-    jest.resetModules();
-    jest.mock("cosmiconfig", () => () => ({
-      load: () => ({
-        config: {
-          nonGlobalStepDefinitions: true,
-          commonPath: "myPath/"
-        }
-      })
+
+  it("should return the common folder defined by the developer", () => {
+    jest.mock("path", () => ({
+      resolve(appRoot, commonPath) {
+        return `./${appRoot}/${commonPath}`;
+      },
+      extname() {
+        return ".js";
+      }
     }));
-    // eslint-disable-next-line global-require
+
+    jest.mock("fs");
+    jest.spyOn(process, "cwd").mockImplementation(() => "cwd");
+
+    getConfig.mockReturnValue({
+      nonGlobalStepDefinitions: true,
+      commonPath: "myPath/"
+    });
+
     const { getStepDefinitionsPaths } = require("./getStepDefinitionsPaths");
 
     const actual = getStepDefinitionsPaths("/path");
-    const expected = "myPath/**/*.+(js|ts)";
-
+    const expected = "./cwd/myPath/**/*.+(js|ts)";
     expect(actual).to.include(expected);
   });
 });
diff --git a/lib/index.test.js b/lib/index.test.js
new file mode 100644
index 0000000..e5d8013
--- /dev/null
+++ b/lib/index.test.js
@@ -0,0 +1,98 @@
+const browserify = require("@cypress/browserify-preprocessor");
+const { default: preprocessor, transform } = require(".");
+
+jest.mock("@cypress/browserify-preprocessor");
+const { defaultOptions } = jest.requireActual(
+  "@cypress/browserify-preprocessor"
+);
+
+describe("Preprocessor", () => {
+  beforeEach(() => {
+    browserify.defaultOptions = defaultOptions;
+    browserify.mockReturnValue(() => {});
+  });
+
+  afterEach(() => {
+    browserify.mockClear();
+  });
+
+  it("should use Cypress browserify options by default", async () => {
+    await preprocessor()({ shouldWatch: false });
+
+    global.jestExpect(browserify).toHaveBeenCalledWith({
+      ...defaultOptions,
+      browserifyOptions: {
+        ...defaultOptions.browserifyOptions,
+        transform: [transform, ...defaultOptions.browserifyOptions.transform]
+      }
+    });
+  });
+
+  it("should add transform when other transforms are defined in options", async () => {
+    await preprocessor({
+      browserifyOptions: {
+        transform: ["babelify"]
+      }
+    })({ shouldWatch: false });
+
+    global.jestExpect(browserify).toHaveBeenCalledWith({
+      browserifyOptions: {
+        transform: [transform, "babelify"]
+      }
+    });
+  });
+
+  it("should preserve transforms in options when our transform is already included", async () => {
+    const options = {
+      browserifyOptions: {
+        extensions: ["js", "ts"],
+        plugins: [["tsify"]],
+        transform: ["aliasify", transform, "babelify"]
+      }
+    };
+
+    await preprocessor(options)({ shouldWatch: false });
+
+    global.jestExpect(browserify).toHaveBeenCalledWith(options);
+  });
+
+  it("should add our transform when no other transforms are defined in options", async () => {
+    const options = {
+      browserifyOptions: {
+        extensions: ["js", "ts"],
+        plugins: [["tsify"]]
+      }
+    };
+
+    await preprocessor(options)({ shouldWatch: false });
+
+    global.jestExpect(browserify).toHaveBeenCalledWith({
+      ...options,
+      browserifyOptions: {
+        ...options.browserifyOptions,
+        transform: [transform]
+      }
+    });
+  });
+
+  it("should add our transform when browserifyOptions property is not passed to options", async () => {
+    const options = { unsupported: true };
+
+    await preprocessor(options)({ shouldWatch: false });
+
+    global.jestExpect(browserify).toHaveBeenCalledWith({
+      ...options,
+      browserifyOptions: {
+        transform: [transform]
+      }
+    });
+  });
+
+  it("should fail gracefully when options is not an object", () => {
+    const err = new Error("Preprocessor options must be an object");
+
+    global.jestExpect(() => preprocessor("options")).toThrow(err);
+    global.jestExpect(() => preprocessor(1)).toThrow(err);
+    global.jestExpect(() => preprocessor(false)).toThrow(err);
+  });
+});
diff --git a/lib/stepDefinitionPath.test.js b/lib/stepDefinitionPath.test.js
index f4402a0..f88cd5c 100644
--- a/lib/stepDefinitionPath.test.js
+++ b/lib/stepDefinitionPath.test.js
@@ -1,66 +1,46 @@
 const fs = require("fs");
-const cosmiconfig = require("cosmiconfig");
-const stepDefinitionPath = require("./stepDefinitionPath.js");
+const { getConfig } = require("./getConfig");
+const stepDefinitionPath = require("./stepDefinitionPath");
 
+jest.mock("./getConfig");
 jest.mock("fs", () => ({
   existsSync: jest.fn()
 }));
 
-jest.mock("cosmiconfig");
-
 const defaultNonGlobalStepDefinitionsPath = "cypress/integration";
 
 describe("load path from step definitions", () => {
   beforeEach(() => {
-    cosmiconfig.mockReset();
+    getConfig.mockReset();
     fs.existsSync.mockReset();
   });
 
   test("Should throw an error if nonGlobalStepDefinitions and stepDefinitions are not set and the default is wrong", () => {
-    const COMICONFIG_FILEPATH = "package.json";
-    const loadMock = jest.fn().mockReturnValue({
-      config: {
-        nonGlobalStepDefinitions: true
-      },
-      filepath: COMICONFIG_FILEPATH
-    });
-    cosmiconfig.mockReturnValue({
-      load: loadMock
+    getConfig.mockReturnValue({
+      nonGlobalStepDefinitions: true
     });
     fs.existsSync.mockReturnValue(false);
 
-    const errorMessage = `We've tried to resolve your step definitions at ${defaultNonGlobalStepDefinitionsPath}, but that doesn't seem to exist. As of version 2.0.0 it's required to set step_definitions in your cypress-cucumber-preprocessor configuration. Currently your configuration is in: ${COMICONFIG_FILEPATH}. Look for nonGlobalStepDefinitions and add stepDefinitions right next to it. It should match your cypress configuration has set for integrationFolder. We no longer rely on getting information from that file as it was unreliable and problematic across Linux/MacOS/Windows especially since the config file could have been passed as an argument to cypress.`;
+    const errorMessage = `We've tried to resolve your step definitions at ${defaultNonGlobalStepDefinitionsPath}, but that doesn't seem to exist. As of version 2.0.0 it's required to set step_definitions in your cypress-cucumber-preprocessor configuration. Look for nonGlobalStepDefinitions and add stepDefinitions right next to it. It should match your cypress configuration has set for integrationFolder. We no longer rely on getting information from that file as it was unreliable and problematic across Linux/MacOS/Windows especially since the config file could have been passed as an argument to cypress.`;
     expect(stepDefinitionPath).throw(errorMessage);
   });
 
   test("Should throw an error if nonGlobalStepDefinitions and stepDefinitions are set but the folder doesn't exist", () => {
-    const COMICONFIG_FILEPATH = "package.json";
     const stepDefinitions = "cypress/stepDefinitions";
-    const loadMock = jest.fn().mockReturnValue({
-      config: {
-        nonGlobalStepDefinitions: true,
-        stepDefinitions
-      },
-      filepath: COMICONFIG_FILEPATH
-    });
-    cosmiconfig.mockReturnValue({
-      load: loadMock
+    getConfig.mockReturnValue({
+      nonGlobalStepDefinitions: true,
+      stepDefinitions
     });
     fs.existsSync.mockReturnValue(false);
 
-    const errorMessage = `We've tried to resolve your step definitions at ${stepDefinitions}, but that doesn't seem to exist. As of version 2.0.0 it's required to set step_definitions in your cypress-cucumber-preprocessor configuration. Currently your configuration is in: ${COMICONFIG_FILEPATH}. Look for nonGlobalStepDefinitions and add stepDefinitions right next to it. It should match your cypress configuration has set for integrationFolder. We no longer rely on getting information from that file as it was unreliable and problematic across Linux/MacOS/Windows especially since the config file could have been passed as an argument to cypress.`;
+    const errorMessage = `We've tried to resolve your step definitions at ${stepDefinitions}, but that doesn't seem to exist. As of version 2.0.0 it's required to set step_definitions in your cypress-cucumber-preprocessor configuration. Look for nonGlobalStepDefinitions and add stepDefinitions right next to it. It should match your cypress configuration has set for integrationFolder. We no longer rely on getting information from that file as it was unreliable and problematic across Linux/MacOS/Windows especially since the config file could have been passed as an argument to cypress.`;
     expect(stepDefinitionPath).throw(errorMessage);
   });
 
   test("should use the default stepDefinitions path for nonGlobalStepDefinitions", () => {
     const appRoot = process.cwd();
-    const loadMock = jest.fn().mockReturnValue({
-      config: {
-        nonGlobalStepDefinitions: true
-      }
-    });
-    cosmiconfig.mockReturnValue({
-      load: loadMock
+    getConfig.mockReturnValue({
+      nonGlobalStepDefinitions: true
     });
     fs.existsSync.mockReturnValue(true);
 
@@ -71,16 +51,9 @@ describe("load path from step definitions", () => {
 
   test("should use the stepDefinitions path for nonGlobalStepDefinitions", () => {
     const appRoot = process.cwd();
-    const loadMock = jest.fn().mockReturnValue({
-      config: {
-        stepDefinitions: "./e2e/support/step-definitions",
-        nonGlobalStepDefinitions: true
-      }
-    });
-    cosmiconfig.mockReturnValue({
-      load: loadMock
+    getConfig.mockReturnValue({
+      step_definitions: "./e2e/support/step-definitions"
     });
-    fs.existsSync.mockReturnValue(true);
 
     expect(stepDefinitionPath()).to.equal(
       `${appRoot}/e2e/support/step-definitions`
@@ -89,15 +62,9 @@ describe("load path from step definitions", () => {
 
   test("should use the stepDefinitions path", () => {
     const appRoot = process.cwd();
-    const loadMock = jest.fn().mockReturnValue({
-      config: {
-        stepDefinitions: "./e2e/support/step-definitions"
-      }
+    getConfig.mockReturnValue({
+      step_definitions: "./e2e/support/step-definitions"
     });
-    cosmiconfig.mockReturnValue({
-      load: loadMock
-    });
-    fs.existsSync.mockReturnValue(true);
 
     expect(stepDefinitionPath()).to.equal(
       `${appRoot}/e2e/support/step-definitions`
@@ -106,10 +73,6 @@ describe("load path from step definitions", () => {
 
   test("should return default path if stepDefinition are not configured and nonGlobalStepDefinitions are not set", () => {
     const appRoot = process.cwd();
-    cosmiconfig.mockReturnValue({
-      load: jest.fn()
-    });
-
     expect(stepDefinitionPath()).to.equal(
       `${appRoot}/cypress/support/step_definitions`
     );
@@ -117,15 +80,9 @@ describe("load path from step definitions", () => {
 
   test("should allow the backward compatible use of step_definitions in cosmiconfig", () => {
     const appRoot = process.cwd();
-    const loadMock = jest.fn().mockReturnValue({
-      config: {
-        step_definitions: "./e2e/support/step-definitions"
-      }
-    });
-    cosmiconfig.mockReturnValue({
-      load: loadMock
+    getConfig.mockReturnValue({
+      step_definitions: "./e2e/support/step-definitions"
     });
-    fs.existsSync.mockReturnValue(true);
 
     expect(stepDefinitionPath()).to.equal(
       `${appRoot}/e2e/support/step-definitions`
diff --git a/lib/testHelpers/setupTestFramework.js b/lib/testHelpers/setupTestFramework.js
index a636dbb..d25311a 100644
--- a/lib/testHelpers/setupTestFramework.js
+++ b/lib/testHelpers/setupTestFramework.js
@@ -3,6 +3,7 @@ global.expect = require("chai").expect;
 
 global.before = jest.fn();
 global.after = jest.fn();
+global.skip = jest.fn();
 
 window.Cypress = {
   env: jest.fn(),