Skip to content

Commit 0160151

Browse files
committed
fix(stepdefinitionpath): do not rely on cypress configuration for determining the stepDefinitionPath
Relying on any parts of the cypress configuration has been problematic since the beginning, it was time to use only the configuration that we own. This is a breaking change but it should be relatively easy to adjust to it. And for people using defaults - nothing changes. Should improve performance especially on Windows. BREAKING CHANGE: You need to configure the stepDefinitions if you use the nonGlobalStepDefinitions option. re #295 , fix #283
1 parent f963a2e commit 0160151

File tree

7 files changed

+126
-246
lines changed

7 files changed

+126
-246
lines changed

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -86,7 +86,7 @@ Option | Default value | Description
8686
------------ | ------------- | -------------
8787
commonPath | `cypress/integration/common` when `nonGlobalStepDefinitions` is true <br> `cypress/support/step_definitions` when `nonGlobalStepDefinitions` is false | Define the path to a folder containing all common step definitions of your tests
8888
nonGlobalStepDefinitions | false | If true use the Cypress Cucumber Preprocessor Style pattern for placing step definitions files. If false, we will use the "oldschool" (everything is global) Cucumber style
89-
step_definitions | `cypress/support/step_definitions` | Path to the folder containing our step definitions
89+
stepDefinitions | `cypress/integration` when `nonGlobalStepDefinitions` is true <br> `cypress/support/step_definitions` when `nonGlobalStepDefinitions` is false | Path to the folder containing our step definitions.
9090

9191
## How to organize the tests
9292

lib/cypressExecutionInstance.js

Lines changed: 0 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -1,31 +0,0 @@
1-
const ps = require("ps-node");
2-
const minimist = require("minimist");
3-
4-
let cypressExecArgs;
5-
const lookupCypressQuery = {
6-
command: "cypress",
7-
arguments: "--config-file"
8-
};
9-
10-
module.exports = {
11-
load: async () =>
12-
new Promise(resolve => {
13-
ps.lookup(lookupCypressQuery, (err, resultList) => {
14-
if (err) {
15-
resolve();
16-
}
17-
cypressExecArgs = resultList
18-
.filter(process => process && process.arguments.length)
19-
.map(process => minimist(process.arguments))
20-
.reduce(
21-
(processArgs, finalProcessArgs) => ({
22-
...finalProcessArgs,
23-
...processArgs
24-
}),
25-
{}
26-
);
27-
resolve();
28-
});
29-
}),
30-
getArguments: () => cypressExecArgs
31-
};

lib/index.js

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,6 @@ const chokidar = require("chokidar");
77
const compile = require("./loader.js");
88
const compileFeatures = require("./featuresLoader.js");
99
const stepDefinitionPath = require("./stepDefinitionPath.js");
10-
const cypressExecutionInstance = require("./cypressExecutionInstance");
1110

1211
const transform = file => {
1312
let data = "";
@@ -42,8 +41,6 @@ const preprocessor = (options = browserify.defaultOptions) => async file => {
4241
if (options.browserifyOptions.transform.indexOf(transform) === -1) {
4342
options.browserifyOptions.transform.unshift(transform);
4443
}
45-
// load arguments from running Cypress instance
46-
await cypressExecutionInstance.load();
4744

4845
if (file.shouldWatch) {
4946
if (watcher) {

lib/stepDefinitionPath.js

Lines changed: 19 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -1,44 +1,35 @@
1-
const fs = require("fs");
21
const path = require("path");
32
const cosmiconfig = require("cosmiconfig");
4-
const cypressExecutionInstance = require("./cypressExecutionInstance");
3+
const fs = require("fs");
54

65
module.exports = () => {
76
const appRoot = process.cwd();
8-
const cypressExecutionArguments = cypressExecutionInstance.getArguments();
9-
const cypressConfigPath =
10-
cypressExecutionArguments && cypressExecutionArguments["config-file"]
11-
? path.resolve(appRoot, cypressExecutionArguments["config-file"])
12-
: `${appRoot}/cypress.json`;
13-
const cypressOptions = JSON.parse(
14-
fs.readFileSync(cypressConfigPath, "utf-8")
15-
);
167

178
const explorer = cosmiconfig("cypress-cucumber-preprocessor", { sync: true });
189
const loaded = explorer.load();
1910
if (loaded && loaded.config) {
20-
const { config } = loaded;
21-
if (config.nonGlobalStepDefinitions && config.step_definitions) {
22-
throw new Error(
23-
"Error! You can't have both step_definitions folder and nonGlobalStepDefinitions setup in cypress-cucumber-preprocessor configuration"
24-
);
25-
}
11+
const { config, filepath } = loaded;
12+
13+
// left for backward compability, but we need the consistency with other configuration options
14+
const confStepDefinitions = config.step_definitions
15+
? config.step_definitions
16+
: config.stepDefinitions;
17+
2618
if (config.nonGlobalStepDefinitions) {
27-
return path.resolve(
28-
appRoot,
29-
cypressOptions.integrationFolder || "cypress/integration"
30-
);
19+
const relativePath = confStepDefinitions || "cypress/integration";
20+
const stepsPath = path.resolve(appRoot, relativePath);
21+
22+
if (!fs.existsSync(stepsPath)) {
23+
throw new Error(
24+
`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.`
25+
);
26+
}
27+
return stepsPath;
3128
}
32-
if (config.step_definitions) {
33-
return path.resolve(appRoot, config.step_definitions);
29+
if (confStepDefinitions) {
30+
return path.resolve(appRoot, confStepDefinitions);
3431
}
3532
}
3633

37-
// XXX Deprecated, left here for backward compability
38-
39-
if (cypressOptions && cypressOptions.fileServerFolder) {
40-
return `${cypressOptions.fileServerFolder}/support/step_definitions`;
41-
}
42-
4334
return `${appRoot}/cypress/support/step_definitions`;
4435
};

lib/stepDefinitionPath.test.js

Lines changed: 59 additions & 55 deletions
Original file line numberDiff line numberDiff line change
@@ -1,114 +1,121 @@
11
const fs = require("fs");
22
const cosmiconfig = require("cosmiconfig");
33
const stepDefinitionPath = require("./stepDefinitionPath.js");
4-
const cypressExecutionInstance = require("./cypressExecutionInstance");
5-
6-
jest.mock("./cypressExecutionInstance", () => ({
7-
getArguments: jest.fn()
8-
}));
94

105
jest.mock("fs", () => ({
11-
readFileSync: jest.fn()
6+
existsSync: jest.fn()
127
}));
138

149
jest.mock("cosmiconfig");
1510

11+
const defaultNonGlobalStepDefinitionsPath = "cypress/integration";
12+
1613
describe("load path from step definitions", () => {
1714
beforeEach(() => {
1815
cosmiconfig.mockReset();
19-
fs.readFileSync.mockReset();
20-
cypressExecutionInstance.getArguments.mockReset();
16+
fs.existsSync.mockReset();
2117
});
2218

23-
test("Should throw error if both nonGlobalStepDefinitions and step_definitions are set in cosmic config", () => {
19+
test("Should throw an error if nonGlobalStepDefinitions and stepDefinitions are not set and the default is wrong", () => {
20+
const COMICONFIG_FILEPATH = "package.json";
2421
const loadMock = jest.fn().mockReturnValue({
2522
config: {
26-
nonGlobalStepDefinitions: true,
27-
step_definitions: "e2e/step_definitions"
28-
}
23+
nonGlobalStepDefinitions: true
24+
},
25+
filepath: COMICONFIG_FILEPATH
2926
});
3027
cosmiconfig.mockReturnValue({
3128
load: loadMock
3229
});
33-
fs.readFileSync.mockReturnValue("{}");
30+
fs.existsSync.mockReturnValue(false);
3431

35-
const errorMessage =
36-
"Error! You can't have both step_definitions folder and nonGlobalStepDefinitions setup in cypress-cucumber-preprocessor configuration";
32+
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.`;
3733
expect(stepDefinitionPath).throw(errorMessage);
3834
});
3935

40-
test("should return default path if coscmicconfig and cypress.json has not config properties", () => {
41-
const appRoot = process.cwd();
36+
test("Should throw an error if nonGlobalStepDefinitions and stepDefinitions are set but the folder doesn't exist", () => {
37+
const COMICONFIG_FILEPATH = "package.json";
38+
const stepDefinitions = "cypress/stepDefinitions";
39+
const loadMock = jest.fn().mockReturnValue({
40+
config: {
41+
nonGlobalStepDefinitions: true,
42+
stepDefinitions
43+
},
44+
filepath: COMICONFIG_FILEPATH
45+
});
4246
cosmiconfig.mockReturnValue({
43-
load: jest.fn()
47+
load: loadMock
4448
});
45-
fs.readFileSync.mockReturnValue("{}");
49+
fs.existsSync.mockReturnValue(false);
4650

47-
expect(stepDefinitionPath()).to.equal(
48-
`${appRoot}/cypress/support/step_definitions`
49-
);
50-
expect(fs.readFileSync.mock.calls[0][0]).to.equal(
51-
`${appRoot}/cypress.json`
52-
);
51+
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.`;
52+
expect(stepDefinitionPath).throw(errorMessage);
5353
});
5454

55-
test("should use fileServerFolder from cypress options for backward compability", () => {
55+
test("should use the default stepDefinitions path for nonGlobalStepDefinitions", () => {
5656
const appRoot = process.cwd();
5757
const loadMock = jest.fn().mockReturnValue({
5858
config: {
59-
commonPath: "./e2e/step-definitions/common"
59+
nonGlobalStepDefinitions: true
6060
}
6161
});
6262
cosmiconfig.mockReturnValue({
6363
load: loadMock
6464
});
65-
fs.readFileSync.mockReturnValue(`{
66-
"fileServerFolder": "./e2e"
67-
}`);
65+
fs.existsSync.mockReturnValue(true);
6866

69-
expect(stepDefinitionPath()).to.equal(`./e2e/support/step_definitions`);
70-
expect(fs.readFileSync.mock.calls[0][0]).to.equal(
71-
`${appRoot}/cypress.json`
67+
expect(stepDefinitionPath()).to.equal(
68+
`${appRoot}/${defaultNonGlobalStepDefinitionsPath}`
7269
);
7370
});
7471

75-
test("should use config file for cypress when cypress is running with option --config-file", () => {
72+
test("should use the stepDefinitions path for nonGlobalStepDefinitions", () => {
7673
const appRoot = process.cwd();
77-
cosmiconfig.mockReturnValue({
78-
load: jest.fn()
74+
const loadMock = jest.fn().mockReturnValue({
75+
config: {
76+
stepDefinitions: "./e2e/support/step-definitions",
77+
nonGlobalStepDefinitions: true
78+
}
7979
});
80-
fs.readFileSync.mockReturnValue(`{
81-
"fileServerFolder": "./e2e"
82-
}`);
83-
cypressExecutionInstance.getArguments.mockReturnValue({
84-
"config-file": "./e2e/cypress.json"
80+
cosmiconfig.mockReturnValue({
81+
load: loadMock
8582
});
83+
fs.existsSync.mockReturnValue(true);
8684

87-
expect(stepDefinitionPath()).to.equal(`./e2e/support/step_definitions`);
88-
expect(fs.readFileSync.mock.calls[0][0]).to.equal(
89-
`${appRoot}/e2e/cypress.json`
85+
expect(stepDefinitionPath()).to.equal(
86+
`${appRoot}/e2e/support/step-definitions`
9087
);
9188
});
9289

93-
test("should use nonGlobalStepDefinitions from cosmiconfig", () => {
90+
test("should use the stepDefinitions path", () => {
9491
const appRoot = process.cwd();
9592
const loadMock = jest.fn().mockReturnValue({
9693
config: {
97-
nonGlobalStepDefinitions: true
94+
stepDefinitions: "./e2e/support/step-definitions"
9895
}
9996
});
10097
cosmiconfig.mockReturnValue({
10198
load: loadMock
10299
});
103-
fs.readFileSync.mockReturnValue(`{}`);
100+
fs.existsSync.mockReturnValue(true);
101+
102+
expect(stepDefinitionPath()).to.equal(
103+
`${appRoot}/e2e/support/step-definitions`
104+
);
105+
});
106+
107+
test("should return default path if stepDefinition are not configured and nonGlobalStepDefinitions are not set", () => {
108+
const appRoot = process.cwd();
109+
cosmiconfig.mockReturnValue({
110+
load: jest.fn()
111+
});
104112

105-
expect(stepDefinitionPath()).to.equal(`${appRoot}/cypress/integration`);
106-
expect(fs.readFileSync.mock.calls[0][0]).to.equal(
107-
`${appRoot}/cypress.json`
113+
expect(stepDefinitionPath()).to.equal(
114+
`${appRoot}/cypress/support/step_definitions`
108115
);
109116
});
110117

111-
test("should use step_definitions from cosmiconfig", () => {
118+
test("should allow the backward compatible use of step_definitions in cosmiconfig", () => {
112119
const appRoot = process.cwd();
113120
const loadMock = jest.fn().mockReturnValue({
114121
config: {
@@ -118,13 +125,10 @@ describe("load path from step definitions", () => {
118125
cosmiconfig.mockReturnValue({
119126
load: loadMock
120127
});
121-
fs.readFileSync.mockReturnValue(`{}`);
128+
fs.existsSync.mockReturnValue(true);
122129

123130
expect(stepDefinitionPath()).to.equal(
124131
`${appRoot}/e2e/support/step-definitions`
125132
);
126-
expect(fs.readFileSync.mock.calls[0][0]).to.equal(
127-
`${appRoot}/cypress.json`
128-
);
129133
});
130134
});

0 commit comments

Comments
 (0)