diff --git a/lib/codecept.js b/lib/codecept.js
index 76c9a4f8d..22b769ebc 100644
--- a/lib/codecept.js
+++ b/lib/codecept.js
@@ -28,20 +28,19 @@ class Codecept {
* Initialize CodeceptJS at specific directory.
* If async initialization is required, pass callback as second parameter.
*
- * @param {*} dir
- * @param {*} callback
+ * @param {string} dir
*/
- init(dir, callback) {
+ init(dir) {
this.initGlobals(dir);
// initializing listeners
Container.create(this.config, this.opts);
- this.bootstrap(callback);
+ this.runHooks();
}
/**
* Creates global variables
*
- * @param {*} dir
+ * @param {string} dir
*/
initGlobals(dir) {
global.codecept_dir = dir;
@@ -66,12 +65,9 @@ class Codecept {
}
/**
- * Executes hooks and bootstrap.
- * If bootstrap is async, second parameter is required.
- *
- * @param {*} done
+ * Executes hooks.
*/
- bootstrap(done) {
+ runHooks() {
// default hooks
runHook(require('./listener/steps'));
runHook(require('./listener/config'));
@@ -81,8 +77,15 @@ class Codecept {
// custom hooks
this.config.hooks.forEach(hook => runHook(hook));
+ }
- // bootstrap
+ /**
+ * Executes bootstrap.
+ * If bootstrap is async, second parameter is required.
+ *
+ * @param {() => any} [done]
+ */
+ runBootstrap(done) {
runHook(this.config.bootstrap, done, 'bootstrap');
}
diff --git a/lib/command/definitions.js b/lib/command/definitions.js
index 557b3cab1..501fdfa07 100644
--- a/lib/command/definitions.js
+++ b/lib/command/definitions.js
@@ -160,55 +160,48 @@ module.exports = function (genPath, options) {
if (!config) return;
const codecept = new Codecept(config, {});
- codecept.init(testsPath, (err) => {
- if (err) {
- output.error(`Error while running bootstrap file :${err}`);
- return;
- }
-
- const helpers = container.helpers();
- const suppportI = container.support('I');
- const translations = container.translation();
- let methods = [];
- const actions = [];
- for (const name in helpers) {
- const helper = helpers[name];
- methods = addAllMethodsInObject(helper, actions, methods, translations);
- }
- methods = addAllNamesInObject(suppportI, actions, methods);
-
- const supports = container.support(); // return all support objects
- const exportPageObjects = [];
- const callbackParams = [];
- for (const name in supports) {
- if (name === 'I') continue;
- callbackParams.push(`${name}:any`);
- const pageObject = supports[name];
- const pageMethods = addAllMethodsInObject(pageObject, {}, []);
- let pageObjectExport = pageObjectTemplate.replace('{{methods}}', pageMethods.join(''));
- pageObjectExport = pageObjectExport.replace('{{name}}', name);
- exportPageObjects.push(pageObjectExport);
- }
-
- let definitionsTemplate = template.replace('{{methods}}', methods.join(''));
- definitionsTemplate = definitionsTemplate.replace('{{exportPageObjects}}', exportPageObjects.join('\n'));
- if (callbackParams.length > 0) {
- definitionsTemplate = definitionsTemplate.replace('{{callbackParams}}', `, ${callbackParams.join(', ')}`);
- } else {
- definitionsTemplate = definitionsTemplate.replace('{{callbackParams}}', '');
- }
- if (translations) {
- definitionsTemplate = definitionsTemplate.replace(/\{\{I\}\}/g, translations.I);
- }
+ codecept.init(testsPath);
+
+ const helpers = container.helpers();
+ const suppportI = container.support('I');
+ const translations = container.translation();
+ let methods = [];
+ const actions = [];
+ for (const name in helpers) {
+ const helper = helpers[name];
+ methods = addAllMethodsInObject(helper, actions, methods, translations);
+ }
+ methods = addAllNamesInObject(suppportI, actions, methods);
+
+ const supports = container.support(); // return all support objects
+ const exportPageObjects = [];
+ const callbackParams = [];
+ for (const name in supports) {
+ if (name === 'I') continue;
+ callbackParams.push(`${name}:any`);
+ const pageObject = supports[name];
+ const pageMethods = addAllMethodsInObject(pageObject, {}, []);
+ let pageObjectExport = pageObjectTemplate.replace('{{methods}}', pageMethods.join(''));
+ pageObjectExport = pageObjectExport.replace('{{name}}', name);
+ exportPageObjects.push(pageObjectExport);
+ }
- fs.writeFileSync(path.join(testsPath, 'steps.d.ts'), definitionsTemplate);
- output.print('TypeScript Definitions provide autocompletion in Visual Studio Code and other IDEs');
- output.print('Definitions were generated in steps.d.ts');
- output.print('Load them by adding at the top of a test file:');
- output.print(output.colors.grey('\n/// '));
+ let definitionsTemplate = template.replace('{{methods}}', methods.join(''));
+ definitionsTemplate = definitionsTemplate.replace('{{exportPageObjects}}', exportPageObjects.join('\n'));
+ if (callbackParams.length > 0) {
+ definitionsTemplate = definitionsTemplate.replace('{{callbackParams}}', `, ${callbackParams.join(', ')}`);
+ } else {
+ definitionsTemplate = definitionsTemplate.replace('{{callbackParams}}', '');
+ }
+ if (translations) {
+ definitionsTemplate = definitionsTemplate.replace(/\{\{I\}\}/g, translations.I);
+ }
- codecept.teardown();
- });
+ fs.writeFileSync(path.join(testsPath, 'steps.d.ts'), definitionsTemplate);
+ output.print('TypeScript Definitions provide autocompletion in Visual Studio Code and other IDEs');
+ output.print('Definitions were generated in steps.d.ts');
+ output.print('Load them by adding at the top of a test file:');
+ output.print(output.colors.grey('\n/// '));
};
function addAllMethodsInObject(supportObj, actions, methods, translations) {
diff --git a/lib/command/gherkin/snippets.js b/lib/command/gherkin/snippets.js
index 93fa8c138..6b735c4b8 100644
--- a/lib/command/gherkin/snippets.js
+++ b/lib/command/gherkin/snippets.js
@@ -20,91 +20,91 @@ module.exports = function (genPath, options) {
if (!config) return;
const codecept = new Codecept(config, {});
- codecept.init(testsPath, (err) => {
- if (!config.gherkin) {
- output.error('Gherkin is not enabled in config. Run `codecept gherkin:init` to enable it');
- process.exit(1);
- }
- if (!config.gherkin.steps || !config.gherkin.steps[0]) {
- output.error('No gherkin steps defined in config. Exiting');
- process.exit(1);
- }
- if (!config.gherkin.features) {
- output.error('No gherkin features defined in config. Exiting');
- process.exit(1);
- }
+ codecept.init(testsPath);
- const files = [];
- glob.sync(config.gherkin.features, { cwd: global.codecept_dir }).forEach((file) => {
- if (!fsPath.isAbsolute(file)) {
- file = fsPath.join(global.codecept_dir, file);
- }
- files.push(fsPath.resolve(file));
- });
- output.print(`Loaded ${files.length} files`);
+ if (!config.gherkin) {
+ output.error('Gherkin is not enabled in config. Run `codecept gherkin:init` to enable it');
+ process.exit(1);
+ }
+ if (!config.gherkin.steps || !config.gherkin.steps[0]) {
+ output.error('No gherkin steps defined in config. Exiting');
+ process.exit(1);
+ }
+ if (!config.gherkin.features) {
+ output.error('No gherkin features defined in config. Exiting');
+ process.exit(1);
+ }
- let newSteps = [];
+ const files = [];
+ glob.sync(config.gherkin.features, { cwd: global.codecept_dir }).forEach((file) => {
+ if (!fsPath.isAbsolute(file)) {
+ file = fsPath.join(global.codecept_dir, file);
+ }
+ files.push(fsPath.resolve(file));
+ });
+ output.print(`Loaded ${files.length} files`);
- const parseSteps = (steps) => {
- const newSteps = [];
- let currentKeyword = '';
- for (const step of steps) {
- if (step.keyword.trim() === 'And') {
- if (!currentKeyword) throw new Error(`There is no active keyword for step '${step.text}'`);
- step.keyword = currentKeyword;
- }
- currentKeyword = step.keyword;
- try {
- matchStep(step.text);
- } catch (err) {
- let stepLine = step.text
- .replace(/\"(.*?)\"/g, '{string}')
- .replace(/(\d+\.\d+)/, '{float}')
- .replace(/ (\d+) /, ' {int} ');
- stepLine = Object.assign(stepLine, { type: step.keyword.trim(), location: step.location });
- newSteps.push(stepLine);
- }
- }
- return newSteps;
- };
+ let newSteps = [];
- const parseFile = (file) => {
- const ast = parser.parse(fs.readFileSync(file).toString());
- for (const child of ast.feature.children) {
- if (child.type === 'ScenarioOutline') continue; // skip scenario outline
- newSteps = newSteps.concat(parseSteps(child.steps).map((step) => {
- return Object.assign(step, { file: file.replace(global.codecept_dir, '').slice(1) });
- }));
+ const parseSteps = (steps) => {
+ const newSteps = [];
+ let currentKeyword = '';
+ for (const step of steps) {
+ if (step.keyword.trim() === 'And') {
+ if (!currentKeyword) throw new Error(`There is no active keyword for step '${step.text}'`);
+ step.keyword = currentKeyword;
}
- };
-
- files.forEach(file => parseFile(file));
+ currentKeyword = step.keyword;
+ try {
+ matchStep(step.text);
+ } catch (err) {
+ let stepLine = step.text
+ .replace(/\"(.*?)\"/g, '{string}')
+ .replace(/(\d+\.\d+)/, '{float}')
+ .replace(/ (\d+) /, ' {int} ');
+ stepLine = Object.assign(stepLine, { type: step.keyword.trim(), location: step.location });
+ newSteps.push(stepLine);
+ }
+ }
+ return newSteps;
+ };
- let stepFile = config.gherkin.steps[0];
- if (!fsPath.isAbsolute(stepFile)) {
- stepFile = fsPath.join(global.codecept_dir, stepFile);
+ const parseFile = (file) => {
+ const ast = parser.parse(fs.readFileSync(file).toString());
+ for (const child of ast.feature.children) {
+ if (child.type === 'ScenarioOutline') continue; // skip scenario outline
+ newSteps = newSteps.concat(parseSteps(child.steps).map((step) => {
+ return Object.assign(step, { file: file.replace(global.codecept_dir, '').slice(1) });
+ }));
}
+ };
+
+ files.forEach(file => parseFile(file));
+
+ let stepFile = config.gherkin.steps[0];
+ if (!fsPath.isAbsolute(stepFile)) {
+ stepFile = fsPath.join(global.codecept_dir, stepFile);
+ }
- const snippets = newSteps
- .filter((value, index, self) => self.indexOf(value) === index)
- .map((step) => {
- return `
+ const snippets = newSteps
+ .filter((value, index, self) => self.indexOf(value) === index)
+ .map((step) => {
+ return `
${step.type}('${step}', () => {
// From "${step.file}" ${JSON.stringify(step.location)}
throw new Error('Not implemented yet');
});`;
- });
+ });
- if (!snippets.length) {
- output.print('No new snippets found');
- return;
- }
- output.success(`Snippets generated: ${snippets.length}`);
- output.print(snippets.join('\n'));
+ if (!snippets.length) {
+ output.print('No new snippets found');
+ return;
+ }
+ output.success(`Snippets generated: ${snippets.length}`);
+ output.print(snippets.join('\n'));
- if (!options.dryRun) {
- output.success(`Snippets added to ${output.colors.bold(stepFile)}`);
- fs.writeFileSync(stepFile, fs.readFileSync(stepFile).toString() + snippets.join('\n') + '\n'); // eslint-disable-line
- }
- });
+ if (!options.dryRun) {
+ output.success(`Snippets added to ${output.colors.bold(stepFile)}`);
+ fs.writeFileSync(stepFile, fs.readFileSync(stepFile).toString() + snippets.join('\n') + '\n'); // eslint-disable-line
+ }
};
diff --git a/lib/command/gherkin/steps.js b/lib/command/gherkin/steps.js
index 12904c411..0a9f8173a 100644
--- a/lib/command/gherkin/steps.js
+++ b/lib/command/gherkin/steps.js
@@ -12,17 +12,17 @@ module.exports = function (genPath, options) {
if (!config) return;
const codecept = new Codecept(config, {});
- codecept.init(testsPath, (err) => {
- output.print('Gherkin Step definitions:');
- output.print();
- const steps = getSteps();
- for (const step of Object.keys(steps)) {
- output.print(` ${output.colors.bold(step)} \n => ${output.colors.green(steps[step].line || '')}`);
- }
- output.print();
- if (!Object.keys(steps).length) {
- output.error('No Gherkin steps defined');
- }
- });
+ codecept.init(testsPath);
+
+ output.print('Gherkin Step definitions:');
+ output.print();
+ const steps = getSteps();
+ for (const step of Object.keys(steps)) {
+ output.print(` ${output.colors.bold(step)} \n => ${output.colors.green(steps[step].line || '')}`);
+ }
+ output.print();
+ if (!Object.keys(steps).length) {
+ output.error('No Gherkin steps defined');
+ }
};
diff --git a/lib/command/interactive.js b/lib/command/interactive.js
index 9c6363427..5f3215255 100644
--- a/lib/command/interactive.js
+++ b/lib/command/interactive.js
@@ -11,7 +11,9 @@ module.exports = function (path, options) {
const testsPath = getTestRoot(path);
const config = getConfig(testsPath);
const codecept = new Codecept(config, options);
- codecept.init(testsPath, (err) => {
+ codecept.init(testsPath);
+
+ codecept.runBootstrap((err) => {
if (err) {
output.error(`Error while running bootstrap file :${err}`);
return;
diff --git a/lib/command/list.js b/lib/command/list.js
index ca5c877c6..cd488fc29 100644
--- a/lib/command/list.js
+++ b/lib/command/list.js
@@ -10,35 +10,28 @@ module.exports = function (path) {
const testsPath = getTestRoot(path);
const config = getConfig(testsPath);
const codecept = new Codecept(config, {});
- codecept.init(testsPath, (err) => {
- if (err) {
- output.error(`Error while running bootstrap file :${err}`);
- return;
- }
+ codecept.init(testsPath);
- output.print('List of test actions: -- ');
- const helpers = container.helpers();
- const supportI = container.support('I');
- const actions = [];
- for (const name in helpers) {
- const helper = helpers[name];
- methodsOfObject(helper).forEach((action) => {
- const params = getParamsToString(helper[action]);
- actions[action] = 1;
- output.print(` ${output.colors.grey(name)} I.${output.colors.bold(action)}(${params})`);
- });
- }
- for (const name in supportI) {
- if (actions[name]) {
- continue;
- }
- const actor = supportI[name];
- const params = getParamsToString(actor);
- output.print(` I.${output.colors.bold(name)}(${params})`);
+ output.print('List of test actions: -- ');
+ const helpers = container.helpers();
+ const supportI = container.support('I');
+ const actions = [];
+ for (const name in helpers) {
+ const helper = helpers[name];
+ methodsOfObject(helper).forEach((action) => {
+ const params = getParamsToString(helper[action]);
+ actions[action] = 1;
+ output.print(` ${output.colors.grey(name)} I.${output.colors.bold(action)}(${params})`);
+ });
+ }
+ for (const name in supportI) {
+ if (actions[name]) {
+ continue;
}
- output.print('PS: Actions are retrieved from enabled helpers. ');
- output.print('Implement custom actions in your helper classes.');
-
- codecept.teardown();
- });
+ const actor = supportI[name];
+ const params = getParamsToString(actor);
+ output.print(` I.${output.colors.bold(name)}(${params})`);
+ }
+ output.print('PS: Actions are retrieved from enabled helpers. ');
+ output.print('Implement custom actions in your helper classes.');
};
diff --git a/lib/command/run.js b/lib/command/run.js
index bca890c06..51a51a892 100644
--- a/lib/command/run.js
+++ b/lib/command/run.js
@@ -32,8 +32,11 @@ module.exports = function (test, options) {
try {
codecept = new Codecept(config, options);
- codecept.init(testRoot, (err) => {
+ codecept.init(testRoot);
+
+ codecept.runBootstrap((err) => {
if (err) throw new Error(`Error while running bootstrap file :${err}`);
+
codecept.loadTests();
codecept.run(test);
});