Skip to content
New issue

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

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

Already on GitHub? Sign in to your account

Add Ability to combine multiple feature files into one spec by Darrinholst #219

Merged
merged 4 commits into from
Aug 26, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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