Skip to content

Fix/bootstrap starting for def #1270

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

Merged
merged 12 commits into from
Oct 23, 2018
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
25 changes: 14 additions & 11 deletions lib/codecept.js
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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'));
Expand All @@ -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');
}

Expand Down
87 changes: 40 additions & 47 deletions lib/command/definitions.js
Original file line number Diff line number Diff line change
Expand Up @@ -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/// <reference path="./steps.d.ts" />'));
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/// <reference path="./steps.d.ts" />'));
};

function addAllMethodsInObject(supportObj, actions, methods, translations) {
Expand Down
146 changes: 73 additions & 73 deletions lib/command/gherkin/snippets.js
Original file line number Diff line number Diff line change
Expand Up @@ -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
}
};
24 changes: 12 additions & 12 deletions lib/command/gherkin/steps.js
Original file line number Diff line number Diff line change
Expand Up @@ -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');
}
};

4 changes: 3 additions & 1 deletion lib/command/interactive.js
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
Loading