Skip to content

Commit e1263f6

Browse files
Evgeniy LukoyanovDavertMik
authored andcommitted
Fix/bootstrap starting for def (#1270)
* Updated typescipt definitions. * Fixed test runner for nested test directories * Fixed test runner for nested test directories * Remove bootstrap call for list and definition * remove teardown from list and def * remove bootstrap from gherkin commands * remove callback from codeceptjs.init()
1 parent 6c39874 commit e1263f6

File tree

7 files changed

+168
-174
lines changed

7 files changed

+168
-174
lines changed

lib/codecept.js

Lines changed: 14 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -28,20 +28,19 @@ class Codecept {
2828
* Initialize CodeceptJS at specific directory.
2929
* If async initialization is required, pass callback as second parameter.
3030
*
31-
* @param {*} dir
32-
* @param {*} callback
31+
* @param {string} dir
3332
*/
34-
init(dir, callback) {
33+
init(dir) {
3534
this.initGlobals(dir);
3635
// initializing listeners
3736
Container.create(this.config, this.opts);
38-
this.bootstrap(callback);
37+
this.runHooks();
3938
}
4039

4140
/**
4241
* Creates global variables
4342
*
44-
* @param {*} dir
43+
* @param {string} dir
4544
*/
4645
initGlobals(dir) {
4746
global.codecept_dir = dir;
@@ -66,12 +65,9 @@ class Codecept {
6665
}
6766

6867
/**
69-
* Executes hooks and bootstrap.
70-
* If bootstrap is async, second parameter is required.
71-
*
72-
* @param {*} done
68+
* Executes hooks.
7369
*/
74-
bootstrap(done) {
70+
runHooks() {
7571
// default hooks
7672
runHook(require('./listener/steps'));
7773
runHook(require('./listener/config'));
@@ -81,8 +77,15 @@ class Codecept {
8177

8278
// custom hooks
8379
this.config.hooks.forEach(hook => runHook(hook));
80+
}
8481

85-
// bootstrap
82+
/**
83+
* Executes bootstrap.
84+
* If bootstrap is async, second parameter is required.
85+
*
86+
* @param {() => any} [done]
87+
*/
88+
runBootstrap(done) {
8689
runHook(this.config.bootstrap, done, 'bootstrap');
8790
}
8891

lib/command/definitions.js

Lines changed: 40 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -160,55 +160,48 @@ module.exports = function (genPath, options) {
160160
if (!config) return;
161161

162162
const codecept = new Codecept(config, {});
163-
codecept.init(testsPath, (err) => {
164-
if (err) {
165-
output.error(`Error while running bootstrap file :${err}`);
166-
return;
167-
}
168-
169-
const helpers = container.helpers();
170-
const suppportI = container.support('I');
171-
const translations = container.translation();
172-
let methods = [];
173-
const actions = [];
174-
for (const name in helpers) {
175-
const helper = helpers[name];
176-
methods = addAllMethodsInObject(helper, actions, methods, translations);
177-
}
178-
methods = addAllNamesInObject(suppportI, actions, methods);
179-
180-
const supports = container.support(); // return all support objects
181-
const exportPageObjects = [];
182-
const callbackParams = [];
183-
for (const name in supports) {
184-
if (name === 'I') continue;
185-
callbackParams.push(`${name}:any`);
186-
const pageObject = supports[name];
187-
const pageMethods = addAllMethodsInObject(pageObject, {}, []);
188-
let pageObjectExport = pageObjectTemplate.replace('{{methods}}', pageMethods.join(''));
189-
pageObjectExport = pageObjectExport.replace('{{name}}', name);
190-
exportPageObjects.push(pageObjectExport);
191-
}
192-
193-
let definitionsTemplate = template.replace('{{methods}}', methods.join(''));
194-
definitionsTemplate = definitionsTemplate.replace('{{exportPageObjects}}', exportPageObjects.join('\n'));
195-
if (callbackParams.length > 0) {
196-
definitionsTemplate = definitionsTemplate.replace('{{callbackParams}}', `, ${callbackParams.join(', ')}`);
197-
} else {
198-
definitionsTemplate = definitionsTemplate.replace('{{callbackParams}}', '');
199-
}
200-
if (translations) {
201-
definitionsTemplate = definitionsTemplate.replace(/\{\{I\}\}/g, translations.I);
202-
}
163+
codecept.init(testsPath);
164+
165+
const helpers = container.helpers();
166+
const suppportI = container.support('I');
167+
const translations = container.translation();
168+
let methods = [];
169+
const actions = [];
170+
for (const name in helpers) {
171+
const helper = helpers[name];
172+
methods = addAllMethodsInObject(helper, actions, methods, translations);
173+
}
174+
methods = addAllNamesInObject(suppportI, actions, methods);
175+
176+
const supports = container.support(); // return all support objects
177+
const exportPageObjects = [];
178+
const callbackParams = [];
179+
for (const name in supports) {
180+
if (name === 'I') continue;
181+
callbackParams.push(`${name}:any`);
182+
const pageObject = supports[name];
183+
const pageMethods = addAllMethodsInObject(pageObject, {}, []);
184+
let pageObjectExport = pageObjectTemplate.replace('{{methods}}', pageMethods.join(''));
185+
pageObjectExport = pageObjectExport.replace('{{name}}', name);
186+
exportPageObjects.push(pageObjectExport);
187+
}
203188

204-
fs.writeFileSync(path.join(testsPath, 'steps.d.ts'), definitionsTemplate);
205-
output.print('TypeScript Definitions provide autocompletion in Visual Studio Code and other IDEs');
206-
output.print('Definitions were generated in steps.d.ts');
207-
output.print('Load them by adding at the top of a test file:');
208-
output.print(output.colors.grey('\n/// <reference path="./steps.d.ts" />'));
189+
let definitionsTemplate = template.replace('{{methods}}', methods.join(''));
190+
definitionsTemplate = definitionsTemplate.replace('{{exportPageObjects}}', exportPageObjects.join('\n'));
191+
if (callbackParams.length > 0) {
192+
definitionsTemplate = definitionsTemplate.replace('{{callbackParams}}', `, ${callbackParams.join(', ')}`);
193+
} else {
194+
definitionsTemplate = definitionsTemplate.replace('{{callbackParams}}', '');
195+
}
196+
if (translations) {
197+
definitionsTemplate = definitionsTemplate.replace(/\{\{I\}\}/g, translations.I);
198+
}
209199

210-
codecept.teardown();
211-
});
200+
fs.writeFileSync(path.join(testsPath, 'steps.d.ts'), definitionsTemplate);
201+
output.print('TypeScript Definitions provide autocompletion in Visual Studio Code and other IDEs');
202+
output.print('Definitions were generated in steps.d.ts');
203+
output.print('Load them by adding at the top of a test file:');
204+
output.print(output.colors.grey('\n/// <reference path="./steps.d.ts" />'));
212205
};
213206

214207
function addAllMethodsInObject(supportObj, actions, methods, translations) {

lib/command/gherkin/snippets.js

Lines changed: 73 additions & 73 deletions
Original file line numberDiff line numberDiff line change
@@ -20,91 +20,91 @@ module.exports = function (genPath, options) {
2020
if (!config) return;
2121

2222
const codecept = new Codecept(config, {});
23-
codecept.init(testsPath, (err) => {
24-
if (!config.gherkin) {
25-
output.error('Gherkin is not enabled in config. Run `codecept gherkin:init` to enable it');
26-
process.exit(1);
27-
}
28-
if (!config.gherkin.steps || !config.gherkin.steps[0]) {
29-
output.error('No gherkin steps defined in config. Exiting');
30-
process.exit(1);
31-
}
32-
if (!config.gherkin.features) {
33-
output.error('No gherkin features defined in config. Exiting');
34-
process.exit(1);
35-
}
23+
codecept.init(testsPath);
3624

37-
const files = [];
38-
glob.sync(config.gherkin.features, { cwd: global.codecept_dir }).forEach((file) => {
39-
if (!fsPath.isAbsolute(file)) {
40-
file = fsPath.join(global.codecept_dir, file);
41-
}
42-
files.push(fsPath.resolve(file));
43-
});
44-
output.print(`Loaded ${files.length} files`);
25+
if (!config.gherkin) {
26+
output.error('Gherkin is not enabled in config. Run `codecept gherkin:init` to enable it');
27+
process.exit(1);
28+
}
29+
if (!config.gherkin.steps || !config.gherkin.steps[0]) {
30+
output.error('No gherkin steps defined in config. Exiting');
31+
process.exit(1);
32+
}
33+
if (!config.gherkin.features) {
34+
output.error('No gherkin features defined in config. Exiting');
35+
process.exit(1);
36+
}
4537

46-
let newSteps = [];
38+
const files = [];
39+
glob.sync(config.gherkin.features, { cwd: global.codecept_dir }).forEach((file) => {
40+
if (!fsPath.isAbsolute(file)) {
41+
file = fsPath.join(global.codecept_dir, file);
42+
}
43+
files.push(fsPath.resolve(file));
44+
});
45+
output.print(`Loaded ${files.length} files`);
4746

48-
const parseSteps = (steps) => {
49-
const newSteps = [];
50-
let currentKeyword = '';
51-
for (const step of steps) {
52-
if (step.keyword.trim() === 'And') {
53-
if (!currentKeyword) throw new Error(`There is no active keyword for step '${step.text}'`);
54-
step.keyword = currentKeyword;
55-
}
56-
currentKeyword = step.keyword;
57-
try {
58-
matchStep(step.text);
59-
} catch (err) {
60-
let stepLine = step.text
61-
.replace(/\"(.*?)\"/g, '{string}')
62-
.replace(/(\d+\.\d+)/, '{float}')
63-
.replace(/ (\d+) /, ' {int} ');
64-
stepLine = Object.assign(stepLine, { type: step.keyword.trim(), location: step.location });
65-
newSteps.push(stepLine);
66-
}
67-
}
68-
return newSteps;
69-
};
47+
let newSteps = [];
7048

71-
const parseFile = (file) => {
72-
const ast = parser.parse(fs.readFileSync(file).toString());
73-
for (const child of ast.feature.children) {
74-
if (child.type === 'ScenarioOutline') continue; // skip scenario outline
75-
newSteps = newSteps.concat(parseSteps(child.steps).map((step) => {
76-
return Object.assign(step, { file: file.replace(global.codecept_dir, '').slice(1) });
77-
}));
49+
const parseSteps = (steps) => {
50+
const newSteps = [];
51+
let currentKeyword = '';
52+
for (const step of steps) {
53+
if (step.keyword.trim() === 'And') {
54+
if (!currentKeyword) throw new Error(`There is no active keyword for step '${step.text}'`);
55+
step.keyword = currentKeyword;
7856
}
79-
};
80-
81-
files.forEach(file => parseFile(file));
57+
currentKeyword = step.keyword;
58+
try {
59+
matchStep(step.text);
60+
} catch (err) {
61+
let stepLine = step.text
62+
.replace(/\"(.*?)\"/g, '{string}')
63+
.replace(/(\d+\.\d+)/, '{float}')
64+
.replace(/ (\d+) /, ' {int} ');
65+
stepLine = Object.assign(stepLine, { type: step.keyword.trim(), location: step.location });
66+
newSteps.push(stepLine);
67+
}
68+
}
69+
return newSteps;
70+
};
8271

83-
let stepFile = config.gherkin.steps[0];
84-
if (!fsPath.isAbsolute(stepFile)) {
85-
stepFile = fsPath.join(global.codecept_dir, stepFile);
72+
const parseFile = (file) => {
73+
const ast = parser.parse(fs.readFileSync(file).toString());
74+
for (const child of ast.feature.children) {
75+
if (child.type === 'ScenarioOutline') continue; // skip scenario outline
76+
newSteps = newSteps.concat(parseSteps(child.steps).map((step) => {
77+
return Object.assign(step, { file: file.replace(global.codecept_dir, '').slice(1) });
78+
}));
8679
}
80+
};
81+
82+
files.forEach(file => parseFile(file));
83+
84+
let stepFile = config.gherkin.steps[0];
85+
if (!fsPath.isAbsolute(stepFile)) {
86+
stepFile = fsPath.join(global.codecept_dir, stepFile);
87+
}
8788

88-
const snippets = newSteps
89-
.filter((value, index, self) => self.indexOf(value) === index)
90-
.map((step) => {
91-
return `
89+
const snippets = newSteps
90+
.filter((value, index, self) => self.indexOf(value) === index)
91+
.map((step) => {
92+
return `
9293
${step.type}('${step}', () => {
9394
// From "${step.file}" ${JSON.stringify(step.location)}
9495
throw new Error('Not implemented yet');
9596
});`;
96-
});
97+
});
9798

98-
if (!snippets.length) {
99-
output.print('No new snippets found');
100-
return;
101-
}
102-
output.success(`Snippets generated: ${snippets.length}`);
103-
output.print(snippets.join('\n'));
99+
if (!snippets.length) {
100+
output.print('No new snippets found');
101+
return;
102+
}
103+
output.success(`Snippets generated: ${snippets.length}`);
104+
output.print(snippets.join('\n'));
104105

105-
if (!options.dryRun) {
106-
output.success(`Snippets added to ${output.colors.bold(stepFile)}`);
107-
fs.writeFileSync(stepFile, fs.readFileSync(stepFile).toString() + snippets.join('\n') + '\n'); // eslint-disable-line
108-
}
109-
});
106+
if (!options.dryRun) {
107+
output.success(`Snippets added to ${output.colors.bold(stepFile)}`);
108+
fs.writeFileSync(stepFile, fs.readFileSync(stepFile).toString() + snippets.join('\n') + '\n'); // eslint-disable-line
109+
}
110110
};

lib/command/gherkin/steps.js

Lines changed: 12 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -12,17 +12,17 @@ module.exports = function (genPath, options) {
1212
if (!config) return;
1313

1414
const codecept = new Codecept(config, {});
15-
codecept.init(testsPath, (err) => {
16-
output.print('Gherkin Step definitions:');
17-
output.print();
18-
const steps = getSteps();
19-
for (const step of Object.keys(steps)) {
20-
output.print(` ${output.colors.bold(step)} \n => ${output.colors.green(steps[step].line || '')}`);
21-
}
22-
output.print();
23-
if (!Object.keys(steps).length) {
24-
output.error('No Gherkin steps defined');
25-
}
26-
});
15+
codecept.init(testsPath);
16+
17+
output.print('Gherkin Step definitions:');
18+
output.print();
19+
const steps = getSteps();
20+
for (const step of Object.keys(steps)) {
21+
output.print(` ${output.colors.bold(step)} \n => ${output.colors.green(steps[step].line || '')}`);
22+
}
23+
output.print();
24+
if (!Object.keys(steps).length) {
25+
output.error('No Gherkin steps defined');
26+
}
2727
};
2828

lib/command/interactive.js

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,9 @@ module.exports = function (path, options) {
1111
const testsPath = getTestRoot(path);
1212
const config = getConfig(testsPath);
1313
const codecept = new Codecept(config, options);
14-
codecept.init(testsPath, (err) => {
14+
codecept.init(testsPath);
15+
16+
codecept.runBootstrap((err) => {
1517
if (err) {
1618
output.error(`Error while running bootstrap file :${err}`);
1719
return;

0 commit comments

Comments
 (0)