Skip to content

Commit

Permalink
Merge pull request #219 from TheBrainFamily/pr/217
Browse files Browse the repository at this point in the history
Add Ability to combine multiple feature files into one spec by Darrinholst
  • Loading branch information
lgandecki authored Aug 26, 2019
2 parents 4527d7a + c3db0c7 commit c08fb58
Show file tree
Hide file tree
Showing 13 changed files with 157 additions and 49 deletions.
3 changes: 3 additions & 0 deletions circle.yml
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,9 @@ jobs:
- run:
name: run tests
command: npm test
- run:
name: slow test run, for sanity
command: npm run test:each
- run:
name: release
command: npm run semantic-release || true
Empty file.
6 changes: 3 additions & 3 deletions cypress/integration/AndAndButSteps.feature
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
Feature: Using And and but
Scenario: With an And everything is find
Feature: Using And and But
Scenario: With an And everything is fine
Given I do something
And Something else
Then I happily work

Scenario: With a But also the step definition
Scenario: With a But
Given I dont do something
And it is sunday
Then I stream on twitch
Expand Down
5 changes: 0 additions & 5 deletions cypress/integration/UsingAnd.feature

This file was deleted.

15 changes: 0 additions & 15 deletions cypress/support/step_definitions/usingAnd.js

This file was deleted.

24 changes: 24 additions & 0 deletions lib/cucumberTemplate.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
exports.cucumberTemplate = `
const {
resolveAndRunStepDefinition,
defineParameterType,
given,
when,
then,
and,
but,
Before,
After,
defineStep
} = require("cypress-cucumber-preprocessor/lib/resolveStepDefinition");
const Given = (window.Given = window.given = given);
const When = (window.When = window.when = when);
const Then = (window.Then = window.then = then);
const And = (window.And = window.and = and);
const But = (window.But = window.but = but);
window.defineParameterType = defineParameterType;
window.defineStep = defineStep;
const {
createTestsFromFeature
} = require("cypress-cucumber-preprocessor/lib/createTestsFromFeature");
`;
90 changes: 90 additions & 0 deletions lib/featuresLoader.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
const glob = require("glob");
const path = require("path");
const fs = require("fs");
const { Parser } = require("gherkin");
const log = require("debug")("cypress:cucumber");

const { getStepDefinitionsPaths } = require("./getStepDefinitionsPaths");
const { cucumberTemplate } = require("./cucumberTemplate");
const { getCucumberJsonConfig } = require("./getCucumberJsonConfig");
const {
isNonGlobalStepDefinitionsMode
} = require("./isNonGlobalStepDefinitionsMode");

const createCucumber = (
specs,
globalToRequire,
nonGlobalToRequire,
cucumberJson
) =>
`
${cucumberTemplate}
window.cucumberJson = ${JSON.stringify(cucumberJson)};
${globalToRequire.join("\n")}
${specs
.map(
({ spec, filePath, name }) => `
describe(\`${name}\`, function() {
${nonGlobalToRequire &&
nonGlobalToRequire
.find(fileSteps => fileSteps[filePath])
[filePath].join("\n")}
createTestsFromFeature('${path.basename(filePath)}', \`${spec}\`);
})
`
)
.join("\n")}
`;

module.exports = function(_, filePath) {
log("compiling", filePath);

const features = glob.sync(`${path.dirname(filePath)}/**/*.feature`);

let globalStepDefinitionsToRequire = [];
let nonGlobalStepDefinitionsToRequire;

if (isNonGlobalStepDefinitionsMode()) {
nonGlobalStepDefinitionsToRequire = features.map(featurePath => ({
[featurePath]: getStepDefinitionsPaths(featurePath).map(
sdPath => `require('${sdPath}')`
)
}));
} else {
globalStepDefinitionsToRequire = [
...new Set(
features.reduce(
requires =>
requires.concat(
getStepDefinitionsPaths(filePath).map(
sdPath => `require('${sdPath}')`
)
),
[]
)
)
];
}

const specs = features
.map(featurePath => ({
spec: fs.readFileSync(featurePath).toString(),
filePath: featurePath
}))
.map(feature =>
Object.assign({}, feature, {
name: new Parser().parse(feature.spec.toString()).feature.name
})
);

return createCucumber(
specs,
globalStepDefinitionsToRequire,
nonGlobalStepDefinitionsToRequire,
getCucumberJsonConfig()
);
};
15 changes: 15 additions & 0 deletions lib/getCucumberJsonConfig.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
const cosmiconfig = require("cosmiconfig");
const log = require("debug")("cypress:cucumber");

exports.getCucumberJsonConfig = () => {
const explorer = cosmiconfig("cypress-cucumber-preprocessor", { sync: true });
const loaded = explorer.load();

const cucumberJson =
loaded && loaded.config && loaded.config.cucumberJson
? loaded.config.cucumberJson
: { generate: false };
log("cucumber.json", JSON.stringify(cucumberJson));

return cucumberJson;
};
1 change: 0 additions & 1 deletion lib/getStepDefinitionsPaths.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,6 @@ const getStepDefinitionsPaths = filePath => {
const nonGlobalPattern = `${getStepDefinitionPathsFrom(
filePath
)}/**/*.+(js|ts)`;

const commonPath =
loaded.config.commonPath || `${stepDefinitionPath()}/common/`;
const commonDefinitionsPattern = `${commonPath}**/*.+(js|ts)`;
Expand Down
6 changes: 5 additions & 1 deletion lib/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ const browserify = require("@cypress/browserify-preprocessor");
const log = require("debug")("cypress:cucumber");
const chokidar = require("chokidar");
const compile = require("./loader.js");
const compileFeatures = require("./featuresLoader.js");
const stepDefinitionPath = require("./stepDefinitionPath.js");

const transform = file => {
Expand All @@ -15,7 +16,10 @@ const transform = file => {
}

function end() {
if (file.match(".feature$")) {
if (file.match(".features$")) {
log("compiling features ", file);
this.queue(compileFeatures(data, file));
} else if (file.match(".feature$")) {
log("compiling feature ", file);
this.queue(compile(data, file));
} else {
Expand Down
7 changes: 7 additions & 0 deletions lib/isNonGlobalStepDefinitionsMode.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
const cosmiconfig = require("cosmiconfig");

exports.isNonGlobalStepDefinitionsMode = () => {
const explorer = cosmiconfig("cypress-cucumber-preprocessor", { sync: true });
const loaded = explorer.load();
return loaded && loaded.config && loaded.config.nonGlobalStepDefinitions;
};
30 changes: 7 additions & 23 deletions lib/loader.js
Original file line number Diff line number Diff line change
@@ -1,49 +1,33 @@
/* eslint-disable no-eval */
const log = require("debug")("cypress:cucumber");
const path = require("path");
const cosmiconfig = require("cosmiconfig");
const { Parser } = require("gherkin");
const { getStepDefinitionsPaths } = require("./getStepDefinitionsPaths");

const { cucumberTemplate } = require("./cucumberTemplate");
const { getCucumberJsonConfig } = require("./getCucumberJsonConfig");
// This is the template for the file that we will send back to cypress instead of the text of a
// feature file
const createCucumber = (filePath, cucumberJson, spec, toRequire, name) =>
`
const {resolveAndRunStepDefinition, defineParameterType, given, when, then, and, but, Before, After, defineStep} = require('cypress-cucumber-preprocessor/lib/resolveStepDefinition');
const Given = window.Given = window.given = given;
const When = window.When = window.when = when;
const Then = window.Then = window.then = then;
const And = window.And = window.and = and;
const But = window.But = window.but = but;
window.defineParameterType = defineParameterType;
window.defineStep = defineStep;
window.cucumberJson = ${JSON.stringify(cucumberJson)};
const { createTestsFromFeature } = require('cypress-cucumber-preprocessor/lib/createTestsFromFeature');
${cucumberTemplate}
window.cucumberJson = ${JSON.stringify(cucumberJson)};
describe(\`${name}\`, function() {
${eval(toRequire).join("\n")}
${toRequire.join("\n")}
createTestsFromFeature('${filePath}', \`${spec}\`);
});
`;

// eslint-disable-next-line func-names
module.exports = function(spec, filePath = this.resourcePath) {
const explorer = cosmiconfig("cypress-cucumber-preprocessor", { sync: true });
const loaded = explorer.load();
const cucumberJson =
loaded && loaded.config && loaded.config.cucumberJson
? loaded.config.cucumberJson
: { generate: false };

log("compiling", spec);
log("cucumber.json", JSON.stringify(cucumberJson));
const stepDefinitionsToRequire = getStepDefinitionsPaths(filePath).map(
sdPath => `require('${sdPath}')`
);

const { name } = new Parser().parse(spec.toString()).feature;
return createCucumber(
path.basename(filePath),
cucumberJson,
getCucumberJsonConfig(),
spec,
stepDefinitionsToRequire,
name
Expand Down
4 changes: 3 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,9 @@
"description": "Run gherkin-syntaxed specs with cypress.io",
"main": "lib/index.js",
"scripts": {
"test": "eslint . && jest && cypress run",
"test": "eslint . && jest && npm run test:all",
"test:all": "cypress run --spec \"**/*.features\"",
"test:each": "cypress run --spec \"**/*.feature\"",
"test:debug": "jest && DEBUG=cypress:* cypress open\n",
"fixStyle": "eslint . --fix",
"semantic-release": "semantic-release"
Expand Down

0 comments on commit c08fb58

Please sign in to comment.