UNPKG

nightwatch

Version:

Easy to use Node.js based end-to-end testing solution for web applications using the W3C WebDriver API.

464 lines (373 loc) 12.9 kB
const dotenv = require('dotenv'); const path = require('path'); const defaultsDeep = require('lodash.defaultsdeep'); const lodashClone = require('lodash.clone'); const lodashMerge = require('lodash.merge'); const CI_Info = require('ci-info'); const Defaults = require('./defaults.js'); const Utils = require('../utils'); const {isObject, isUndefined, isDefined} = Utils; class Settings { static get DEFAULT_ENV() { return 'default'; } static get DEFAULTS() { return Defaults; } /** * Looks for pattern ${VAR_NAME} in settings * @param {Object} [target] */ static replaceEnvVariables(target) { for (const key in target) { switch (typeof target[key]) { case 'object': Settings.replaceEnvVariables(target[key]); break; case 'string': target[key] = target[key].replace(/\$\{(\w+)\}/g, function(match, varName) { return process.env[varName] || '${' + varName + '}'; }); break; } } return this; } static getDefaults() { return lodashClone(Settings.DEFAULTS, true); } /** * Called from the cli runner with data from config file * * @param {Object} [settings] additional settings which can be passed when called programmatically * @param {Object} [baseSettings] settings data from nightwatch config file * @param {Object} [argv] cli arguments object * @param {String} [testEnv] current test environment * @returns {Object} */ static parse(settings = {}, baseSettings = {}, argv = {}, testEnv = '') { const instance = new Settings(argv, testEnv); instance.fromConfigFile(baseSettings); instance.inheritFromDefaultEnv(); instance.init(settings); return instance.settings; } /** * Called from Nightwatch main client instance containing either an existing settings object or a new one * * @param {Object} userSettings * @param {Object} argv * @returns {Object} */ static fromClient(userSettings = {}, argv = {}) { const instance = new Settings(argv); instance.init(userSettings); return instance.settings; } static isNightwatchObject(settings) { return isDefined(settings['[[@nightwatch_createdAt]]']); } /** * @deprecated * @param settings */ static setDefaults(settings) { defaultsDeep(settings, Defaults); if (settings.unit_testing_mode) { settings.unit_tests_mode = true; } if (!settings.unit_tests_mode) { settings.skip_testcases_on_fail = settings.skip_testcases_on_fail || isUndefined(settings.skip_testcases_on_fail); } } static isUsingSeleniumServer(settings) { return settings.selenium && settings.selenium.start_process; } get testWorkersEnabled() { const {test_workers} = this.settings; return this.argv.parallel === true || test_workers === true || isObject(test_workers) && test_workers.enabled; } /** * @param {Object} [argv] the cli arguments object * @param {String} [testEnv] the current test env */ constructor(argv = {}, testEnv = '') { this.baseSettings = null; this.argv = argv; this.testEnv = testEnv || ''; this.initSettingsObject(); } /** * @param {Object|null} [baseSettings] the raw nightwatch config object * @param baseSettings */ fromConfigFile(baseSettings) { this.baseSettings = baseSettings || {}; this.copyGenericProperties(); } /** * Initialize a new settings object based on the defaults */ initSettingsObject() { this.settings = Settings.getDefaults(); } /** * Copy all properties from the config file to this.settings that are located outside any test environment * defined as part of the "test_settings" dictionary; * * This allows to define non-standard properties to the Nightwatch settings object */ copyGenericProperties() { Object.keys(this.baseSettings).forEach(key => { if (key === 'test_settings') { return; } const copyVal = lodashClone(this.baseSettings[key], true); if (isObject(this.settings[key])) { Object.assign(this.settings[key], copyVal); } else { this.settings[key] = copyVal; } }); } isSettingsDefined(settingName) { const {webdriver} = this.settings; if (isObject(webdriver[settingName])) { const values = Object.values(webdriver[settingName]); return values.every(item => isDefined(item)); } return isDefined(webdriver[settingName]); } /** * Tries to set a webdriver setting from a several legacy places if the value is not already set * * @param {String} newSetting the new property name * @param {String|Array} [oldSetting] * @param {Object} [opts] * @returns {Settings} for chaining */ setWebdriverHttpOption(newSetting, oldSetting, opts = {}) { let webdriverOpts = this.settings.webdriver; if (this.isSettingsDefined(newSetting)) { return this; } if (oldSetting === undefined) { oldSetting = [newSetting]; } else if (!Array.isArray(oldSetting)) { oldSetting = [oldSetting]; } for (let i = 0; i < oldSetting.length; i++) { let item = oldSetting[i]; if (isDefined(this.settings[item])) { webdriverOpts[newSetting] = this.settings[item]; return this; } } if (isDefined(opts.defaultValue)) { webdriverOpts[newSetting] = opts.defaultValue; } return this; } isUsingSelenium() { const {selenium} = this.settings; return isObject(selenium); } isSeleniumServerManaged() { return this.isUsingSelenium() && this.settings.selenium.start_process; } /** * Set the connection settings to the Webdriver server and any networking options */ setWebdriverSettings() { // if using selenium server, we read settings from the selenium dictionary if (this.isSeleniumServerManaged()) { lodashMerge(this.settings.webdriver, this.settings.selenium); } else if (this.isUsingSelenium()) { defaultsDeep(this.settings.webdriver, this.settings.selenium); } this .setWebdriverHttpOption('host', ['seleniumHost', 'selenium_host'], {defaultValue: 'localhost'}) .setWebdriverHttpOption('port', ['seleniumPort', 'selenium_port']) .setWebdriverHttpOption('ssl', ['useSsl', 'use_ssl']) .setWebdriverHttpOption('proxy') .setWebdriverHttpOption('start_session') .setWebdriverHttpOption('timeout_options', 'request_timeout_options') .setWebdriverHttpOption('default_path_prefix') .setWebdriverHttpOption('username') .setWebdriverHttpOption('access_key', ['accessKey', 'access_key', 'password']); if (isUndefined(this.settings.webdriver.ssl)) { this.settings.webdriver.ssl = this.settings.webdriver.port === 443; } if (!this.settings.webdriver.host) { this.settings.webdriver.host = this.settings.selenium_host || 'localhost'; } this.setServerUrl(); } /** * Set the webdriver server url which will be used in case the service is not mananged by Nightwatch */ setServerUrl() { const protocol = this.settings.webdriver.ssl ? 'https' : 'http'; const {port, host, default_path_prefix = ''} = this.settings.webdriver; this.settings.webdriver.url = `${protocol}://${host}:${port}${default_path_prefix}`; if (isObject(this.settings.selenium)) { this.settings.selenium.url = this.settings.webdriver.url; } } mergeOntoExisting(userSettings = {}) { lodashMerge(this.settings, userSettings); return this; } /** * @returns {Settings} */ adaptSettings() { this.setCliOptions(); this.setScreenshotsOptions(); this.setUnitTestsMode(); this.setParallelMode(); this.setTestRunner(); if (typeof this.settings.src_folders == 'string') { this.settings.src_folders = [this.settings.src_folders]; } if (typeof this.settings.skipgroup == 'string' && this.settings.skipgroup.length > 0) { this.settings.skipgroup = this.settings.skipgroup.split(','); } return this; } setParallelMode() { const Concurrency = require('../runner/concurrency'); if (Concurrency.isChildProcess()) { this.settings.parallel_mode = true; } if (this.argv.parallel === true && !this.settings.test_workers) { this.settings.test_workers = true; } this.settings.testWorkersEnabled = this.testWorkersEnabled; return this; } setTestRunner() { if (Utils.isString(this.settings.test_runner)) { this.settings.test_runner = { type: this.settings.test_runner, options: {} }; } if (!Utils.isObject(this.settings.test_runner)) { throw new Error(`Invalid "test_runner" settings specified; received: ${this.settings.test_runner}`); } return this; } setUnitTestsMode() { const unitTesting = this.settings.unit_tests_mode || this.settings.unit_testing_mode; this.settings.unit_testing_mode = this.settings.unit_tests_mode = unitTesting; if (unitTesting) { this.settings.webdriver.start_process = false; this.settings.webdriver.start_session = false; this.settings.start_session = false; this.settings.detailed_output = false; this.settings.output_timestamp = false; } else { this.settings.skip_testcases_on_fail = this.settings.skip_testcases_on_fail || isUndefined(this.settings.skip_testcases_on_fail); } return this; } inheritFromDefaultEnv() { if (!this.baseSettings.test_settings) { return this; } const defaultEnvSettings = this.baseSettings.test_settings[Settings.DEFAULT_ENV] || {}; lodashMerge(this.settings, defaultEnvSettings); if (!this.testEnv || this.testEnv === Settings.DEFAULT_ENV) { return this; } const testEnvSettings = this.baseSettings.test_settings[this.testEnv] || {}; this.inheritFromSuperEnv(testEnvSettings); defaultsDeep(testEnvSettings, defaultEnvSettings); lodashMerge(this.settings, testEnvSettings); return this; } inheritFromSuperEnv(testEnvSettings) { if (testEnvSettings.extends) { const superEnv = this.baseSettings.test_settings[testEnvSettings.extends] || {}; delete testEnvSettings.extends; defaultsDeep(testEnvSettings, superEnv); return this.inheritFromSuperEnv(testEnvSettings); } return testEnvSettings; } /** * @param settings */ persistGlobals(settings) { if (this.settings.persist_globals === true && isObject(settings.globals)) { defaultsDeep(settings.globals, this.settings.globals); this.settings.globals = settings.globals; } } setScreenshotsOptions() { if (isObject(this.settings.screenshots)) { this.settings.screenshots.path = this.settings.screenshots.path ? path.resolve(this.settings.screenshots.path) : ''; } else { const enabled = this.settings.screenshots === true; this.settings.screenshots = Object.assign({}, Defaults.screenshots, {enabled}); } this.settings.screenshotsPath = this.settings.screenshots.path; return this; } setCliOptions() { if (this.argv.verbose) { this.settings.silent = false; } const cliOverwrites = { output_folder: this.argv.output, filename_filter: this.argv.filter, tag_filter: this.argv.tag, skipgroup: this.argv.skipgroup, skiptags: this.argv.skiptags, enable_fail_fast: this.argv['fail-fast'] }; Object.keys(cliOverwrites).forEach(key => { if (isDefined(cliOverwrites[key]) && cliOverwrites[key] !== null) { this.settings[key] = cliOverwrites[key]; } }); // TODO: add support for overwriting any setting return this; } sortSettings() { const sortedSettings = {}; Object.keys(this.settings).sort().forEach(key => { sortedSettings[key] = this.settings[key]; }); this.settings = sortedSettings; Object.defineProperty(this.settings, '[[@nightwatch_createdAt]]', { value: new Date().valueOf(), enumerable: false, configurable: false, writable: false }); } setColorOutput() { const {isCI, CIRCLE, JENKINS, NETLIFY, TRAVIS, GITLAB, BUILDKITE} = CI_Info; let coloringSupport = CIRCLE || JENKINS || NETLIFY || TRAVIS || GITLAB || BUILDKITE; if (isCI && !coloringSupport) { this.settings.disable_colors = true; } } /** * Validates and parses the test settings * * @param {Object} [userSettings] */ init(userSettings = {}) { this.mergeOntoExisting(userSettings); this.setWebdriverSettings(); this.adaptSettings(); this.sortSettings(); this.persistGlobals(userSettings); dotenv.config(this.settings.dotenv); Settings.replaceEnvVariables(this.settings); this.setColorOutput(); } } module.exports = Settings;