@kronoslive/codeceptjs
Version:
Supercharged End 2 End Testing Framework for NodeJS
236 lines (198 loc) • 7.2 kB
JavaScript
const colors = require('chalk');
const fs = require('fs');
const inquirer = require('inquirer');
const mkdirp = require('mkdirp');
const path = require('path');
const { inspect } = require('util');
const { print, success, error } = require('../output');
const { fileExists, beautify } = require('../utils');
const { getTestRoot } = require('./utils');
const generateDefinitions = require('./definitions');
const { test: generateTest } = require('./generate');
const isLocal = require('../utils').installedLocally();
const defaultConfig = {
tests: './*_test.js',
output: '',
helpers: {},
include: {},
bootstrap: null,
mocha: {},
};
const helpers = ['Playwright', 'WebDriver', 'Puppeteer', 'TestCafe', 'Protractor', 'Nightmare', 'Appium'];
const translations = Object.keys(require('../../translations'));
const noTranslation = 'English (no localization)';
translations.unshift(noTranslation);
let packages;
const configHeader = `const { setHeadlessWhen } = require('@codeceptjs/configure');
// turn on headless mode when running with HEADLESS=true environment variable
// export HEADLESS=true && npx codeceptjs run
setHeadlessWhen(process.env.HEADLESS);
`;
const defaultActor = `// in this file you can append custom step methods to 'I' object
module.exports = function() {
return actor({
// Define custom steps here, use 'this' to access default methods of I.
// It is recommended to place a general 'login' function here.
});
}
`;
module.exports = function (initPath) {
const testsPath = getTestRoot(initPath);
print();
print(` Welcome to ${colors.magenta.bold('CodeceptJS')} initialization tool`);
print(' It will prepare and configure a test environment for you');
print();
if (!path) {
print('No test root specified.');
print(`Test root is assumed to be ${colors.yellow.bold(testsPath)}`);
print('----------------------------------');
} else {
print(`Installing to ${colors.bold(testsPath)}`);
}
if (!fileExists(testsPath)) {
print(`Directory ${testsPath} does not exist, creating...`);
mkdirp.sync(testsPath);
}
const configFile = path.join(testsPath, 'codecept.conf.js');
if (fileExists(configFile)) {
error(`Config is already created at ${configFile}`);
return;
}
inquirer.prompt([
{
name: 'tests',
type: 'input',
default: './*_test.js',
message: 'Where are your tests located?',
},
{
name: 'helper',
type: 'list',
choices: helpers,
message: 'What helpers do you want to use?',
},
{
name: 'output',
default: './output',
message: 'Where should logs, screenshots, and reports to be stored?',
},
{
name: 'translation',
type: 'list',
message: 'Do you want localization for tests? (See https://codecept.io/translation/)',
choices: translations,
},
]).then((result) => {
const config = defaultConfig;
config.name = testsPath.split(path.sep).pop();
config.output = result.output;
config.tests = result.tests;
// create a directory tests if it is included in tests path
const matchResults = config.tests.match(/[^*.]+/);
if (matchResults) {
mkdirp.sync(path.join(testsPath, matchResults[0]));
}
// append file mask to the end of tests
if (!config.tests.match(/\*(.*?)$/)) {
config.tests = `${config.tests.replace(/\/+$/, '')}/*_test.js`;
print(`Adding default test mask: ${config.tests}`);
}
if (result.translation !== noTranslation) config.translation = result.translation;
const helperName = result.helper;
config.helpers[helperName] = {};
let helperConfigs = [];
try {
const Helper = require(`../helper/${helperName}`);
if (Helper._checkRequirements) {
packages = Helper._checkRequirements();
}
if (!Helper._config()) return;
helperConfigs = helperConfigs.concat(Helper._config().map((config) => {
config.message = `[${helperName}] ${config.message}`;
config.name = `${helperName}_${config.name}`;
config.type = config.type || 'input';
return config;
}));
} catch (err) {
error(err);
}
const finish = () => {
// create steps file by default
const stepFile = './steps_file.js';
fs.writeFileSync(path.join(testsPath, stepFile), defaultActor);
config.include.I = stepFile;
print(`Steps file created at ${stepFile}`);
config.plugins = {
pauseOnFail: {},
retryFailedStep: {
enabled: true,
},
tryTo: {
enabled: true,
},
screenshotOnFail: {
enabled: true,
},
};
let configSource = beautify(`exports.config = ${inspect(config, false, 4, false)}`);
if (require.resolve('@codeceptjs/configure') && isLocal && !initPath) {
// prepend @codeceptjs/configure only when this module can be required in config
configSource = configHeader + configSource;
}
fs.writeFileSync(configFile, configSource, 'utf-8');
print(`Config created at ${configFile}`);
if (config.output) {
if (!fileExists(config.output)) {
mkdirp.sync(path.join(testsPath, config.output));
print(`Directory for temporary output files created at '${config.output}'`);
} else {
print(`Directory for temporary output files is already created at '${config.output}'`);
}
}
const jsconfig = {
compilerOptions: {
allowJs: true,
},
};
const jsconfigJson = beautify(JSON.stringify(jsconfig));
const jsconfigFile = path.join(testsPath, 'jsconfig.json');
if (fileExists(jsconfigFile)) {
print(`jsconfig.json already exists at ${jsconfigFile}`);
} else {
fs.writeFileSync(jsconfigFile, jsconfigJson);
print(`Intellisense enabled in ${jsconfigFile}`);
}
generateDefinitions(testsPath, {});
print('');
success(' Almost ready... Next step:');
const generatedTest = generateTest(testsPath);
if (!generatedTest) return;
generatedTest.then(() => {
print('\n--');
print(colors.bold.green('CodeceptJS Installed! Enjoy supercharged testing! 🤩'));
print(colors.bold.magenta('Find more information at https://codecept.io'));
print();
if (packages) {
print('\n--');
if (isLocal) {
success(`Please install dependent packages locally: ${colors.bold(`npm install --save-dev ${packages.join(' ')}`)}`);
} else {
success(`Please install dependent packages globally: [sudo] ${colors.bold(`npm install -g ${packages.join(' ')}`)}`);
}
}
});
};
print('Configure helpers...');
inquirer.prompt(helperConfigs).then((helperResult) => {
Object.keys(helperResult).forEach((key) => {
const parts = key.split('_');
const helperName = parts[0];
const configName = parts[1];
if (!configName) return;
config.helpers[helperName][configName] = helperResult[key];
});
print('');
finish();
});
});
};