225 lines
41 KiB
JavaScript
225 lines
41 KiB
JavaScript
|
"use strict";
|
||
|
var __importDefault = (this && this.__importDefault) || function (mod) {
|
||
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
||
|
};
|
||
|
Object.defineProperty(exports, "__esModule", { value: true });
|
||
|
const configuration_base_1 = __importDefault(require("./configuration-base"));
|
||
|
const lodash_1 = require("lodash");
|
||
|
const get_options_1 = require("../utils/get-options");
|
||
|
const option_names_1 = __importDefault(require("./option-names"));
|
||
|
const get_filter_fn_1 = __importDefault(require("../utils/get-filter-fn"));
|
||
|
const prepare_reporters_1 = __importDefault(require("../utils/prepare-reporters"));
|
||
|
const string_1 = require("../utils/string");
|
||
|
const render_template_1 = __importDefault(require("../utils/render-template"));
|
||
|
const warning_message_1 = __importDefault(require("../notifications/warning-message"));
|
||
|
const resolve_path_relatively_cwd_1 = __importDefault(require("../utils/resolve-path-relatively-cwd"));
|
||
|
const default_values_1 = require("./default-values");
|
||
|
const option_source_1 = __importDefault(require("./option-source"));
|
||
|
const customizable_compilers_1 = __importDefault(require("./customizable-compilers"));
|
||
|
const deprecated_1 = require("../notifications/deprecated");
|
||
|
const pool_1 = __importDefault(require("../browser/provider/pool"));
|
||
|
const formats_1 = require("./formats");
|
||
|
const runtime_1 = require("../errors/runtime");
|
||
|
const types_1 = require("../errors/types");
|
||
|
const localhost_names_1 = require("../utils/localhost-names");
|
||
|
const BASE_CONFIGURATION_FILENAME = '.testcaferc';
|
||
|
const CONFIGURATION_FILENAMES = formats_1.CONFIGURATION_EXTENSIONS.map(ext => `${BASE_CONFIGURATION_FILENAME}${ext}`);
|
||
|
const DEFAULT_SCREENSHOTS_DIRECTORY = 'screenshots';
|
||
|
const OPTION_FLAG_NAMES = [
|
||
|
option_names_1.default.debugMode,
|
||
|
option_names_1.default.debugOnFail,
|
||
|
option_names_1.default.skipUncaughtErrors,
|
||
|
option_names_1.default.stopOnFirstFail,
|
||
|
option_names_1.default.takeScreenshotsOnFails,
|
||
|
option_names_1.default.disablePageCaching,
|
||
|
option_names_1.default.disablePageReloads,
|
||
|
option_names_1.default.disableScreenshots,
|
||
|
option_names_1.default.disableMultipleWindows,
|
||
|
];
|
||
|
const OPTION_INIT_FLAG_NAMES = [
|
||
|
option_names_1.default.developmentMode,
|
||
|
option_names_1.default.retryTestPages,
|
||
|
option_names_1.default.cache,
|
||
|
option_names_1.default.disableHttp2,
|
||
|
option_names_1.default.experimentalProxyless,
|
||
|
option_names_1.default.disableCrossDomain,
|
||
|
];
|
||
|
class TestCafeConfiguration extends configuration_base_1.default {
|
||
|
constructor(configFile = '') {
|
||
|
super(configFile || CONFIGURATION_FILENAMES);
|
||
|
this._isExplicitConfig = !!configFile;
|
||
|
}
|
||
|
async init(options) {
|
||
|
await super.init();
|
||
|
const opts = await this._load();
|
||
|
this._checkUnsecureDataInJSONConfiguration(opts);
|
||
|
if (opts) {
|
||
|
this._options = configuration_base_1.default._fromObj(opts);
|
||
|
await this._normalizeOptionsAfterLoad();
|
||
|
}
|
||
|
await this.asyncMergeOptions(options);
|
||
|
const proxyless = this.getOption(option_names_1.default.experimentalProxyless);
|
||
|
if (proxyless)
|
||
|
this._ensureOptionWithValue(option_names_1.default.hostname, localhost_names_1.LOCALHOST_NAMES.LOCALHOST, option_source_1.default.Input);
|
||
|
}
|
||
|
async asyncMergeOptions(options) {
|
||
|
options = options || {};
|
||
|
super.mergeOptions(options);
|
||
|
if (!options.isCli && this._options.browsers)
|
||
|
this._options.browsers.value = await this._getBrowserInfo();
|
||
|
}
|
||
|
prepare() {
|
||
|
this._prepareFlags();
|
||
|
this._setDefaultValues();
|
||
|
this._prepareCompilerOptions();
|
||
|
}
|
||
|
notifyAboutOverriddenOptions(warningLog) {
|
||
|
if (!this._overriddenOptions.length)
|
||
|
return;
|
||
|
const optionsStr = (0, string_1.getConcatenatedValuesString)(this._overriddenOptions);
|
||
|
const optionsSuffix = (0, string_1.getPluralSuffix)(this._overriddenOptions);
|
||
|
const renderedMessage = (0, render_template_1.default)(warning_message_1.default.configOptionsWereOverridden, optionsStr, optionsSuffix);
|
||
|
configuration_base_1.default._showConsoleWarning(renderedMessage);
|
||
|
if (warningLog)
|
||
|
warningLog.addWarning(renderedMessage);
|
||
|
this._overriddenOptions = [];
|
||
|
}
|
||
|
notifyAboutDeprecatedOptions(warningLog) {
|
||
|
const deprecatedOptions = this.getOptions((name, option) => name in deprecated_1.DEPRECATED && option.value !== void 0);
|
||
|
for (const optionName in deprecatedOptions)
|
||
|
warningLog.addWarning((0, deprecated_1.getDeprecationMessage)(deprecated_1.DEPRECATED[optionName]));
|
||
|
}
|
||
|
get startOptions() {
|
||
|
const proxyless = this.getOption(option_names_1.default.experimentalProxyless);
|
||
|
let hostname = this.getOption(option_names_1.default.hostname);
|
||
|
if (!hostname && proxyless)
|
||
|
hostname = localhost_names_1.LOCALHOST_NAMES.LOCALHOST;
|
||
|
const result = {
|
||
|
hostname,
|
||
|
port1: this.getOption(option_names_1.default.port1),
|
||
|
port2: this.getOption(option_names_1.default.port2),
|
||
|
options: {
|
||
|
ssl: this.getOption(option_names_1.default.ssl),
|
||
|
developmentMode: this.getOption(option_names_1.default.developmentMode),
|
||
|
retryTestPages: this.getOption(option_names_1.default.retryTestPages),
|
||
|
cache: this.getOption(option_names_1.default.cache),
|
||
|
disableHttp2: this.getOption(option_names_1.default.disableHttp2),
|
||
|
disableCrossDomain: this.getOption(option_names_1.default.disableCrossDomain),
|
||
|
proxyless,
|
||
|
},
|
||
|
};
|
||
|
return result;
|
||
|
}
|
||
|
_checkUnsecureDataInJSONConfiguration(opts) {
|
||
|
var _a;
|
||
|
if (!this._isJSONConfiguration())
|
||
|
return;
|
||
|
if ((_a = opts === null || opts === void 0 ? void 0 : opts[option_names_1.default.dashboard]) === null || _a === void 0 ? void 0 : _a.token)
|
||
|
throw new runtime_1.GeneralError(types_1.RUNTIME_ERRORS.dashboardTokenInJSON);
|
||
|
}
|
||
|
_prepareFlag(name, source = option_source_1.default.Configuration) {
|
||
|
const option = this._ensureOption(name, void 0, source);
|
||
|
option.value = !!option.value;
|
||
|
}
|
||
|
_prepareFlags() {
|
||
|
OPTION_FLAG_NAMES.forEach(name => this._prepareFlag(name));
|
||
|
}
|
||
|
_prepareInitFlags() {
|
||
|
OPTION_INIT_FLAG_NAMES.forEach(name => this._prepareFlag(name, option_source_1.default.Default));
|
||
|
}
|
||
|
async _normalizeOptionsAfterLoad() {
|
||
|
await this._prepareSslOptions();
|
||
|
this._prepareInitFlags();
|
||
|
this._prepareFilterFn();
|
||
|
this._ensureArrayOption(option_names_1.default.src);
|
||
|
this._ensureArrayOption(option_names_1.default.browsers);
|
||
|
this._ensureArrayOption(option_names_1.default.clientScripts);
|
||
|
this._prepareReporters();
|
||
|
}
|
||
|
_prepareFilterFn() {
|
||
|
const filterOption = this._ensureOption(option_names_1.default.filter, default_values_1.DEFAULT_FILTER_FN, option_source_1.default.Default);
|
||
|
if (!filterOption.value)
|
||
|
return;
|
||
|
const filterOptionValue = filterOption.value;
|
||
|
if (filterOptionValue.testGrep)
|
||
|
filterOptionValue.testGrep = (0, get_options_1.getGrepOptions)(option_names_1.default.filterTestGrep, filterOptionValue.testGrep);
|
||
|
if (filterOptionValue.fixtureGrep)
|
||
|
filterOptionValue.fixtureGrep = (0, get_options_1.getGrepOptions)(option_names_1.default.filterFixtureGrep, filterOptionValue.fixtureGrep);
|
||
|
filterOption.value = (0, get_filter_fn_1.default)(filterOption.value);
|
||
|
filterOption.source = option_source_1.default.Configuration;
|
||
|
}
|
||
|
_ensureScreenshotOptions() {
|
||
|
const path = (0, resolve_path_relatively_cwd_1.default)(DEFAULT_SCREENSHOTS_DIRECTORY);
|
||
|
const screenshots = this._ensureOption(option_names_1.default.screenshots, {}, option_source_1.default.Configuration).value;
|
||
|
if (!screenshots.path)
|
||
|
screenshots.path = path;
|
||
|
if (screenshots.thumbnails === void 0)
|
||
|
screenshots.thumbnails = default_values_1.DEFAULT_SCREENSHOT_THUMBNAILS;
|
||
|
}
|
||
|
_ensureSkipJsOptions() {
|
||
|
const option = this._ensureOption(option_names_1.default.skipJsErrors, void 0, option_source_1.default.Configuration);
|
||
|
if (option.value === void 0)
|
||
|
option.value = !!option.value;
|
||
|
}
|
||
|
_prepareReporters() {
|
||
|
const reporterOption = this._options[option_names_1.default.reporter];
|
||
|
if (!reporterOption)
|
||
|
return;
|
||
|
const optionValue = (0, lodash_1.castArray)(reporterOption.value);
|
||
|
reporterOption.value = (0, prepare_reporters_1.default)(optionValue);
|
||
|
}
|
||
|
async _prepareSslOptions() {
|
||
|
const sslOptions = this._options[option_names_1.default.ssl];
|
||
|
if (!sslOptions)
|
||
|
return;
|
||
|
sslOptions.value = await (0, get_options_1.getSSLOptions)(sslOptions.value);
|
||
|
}
|
||
|
_setDefaultValues() {
|
||
|
this._ensureOptionWithValue(option_names_1.default.selectorTimeout, default_values_1.DEFAULT_TIMEOUT.selector, option_source_1.default.Configuration);
|
||
|
this._ensureOptionWithValue(option_names_1.default.assertionTimeout, default_values_1.DEFAULT_TIMEOUT.assertion, option_source_1.default.Configuration);
|
||
|
this._ensureOptionWithValue(option_names_1.default.pageLoadTimeout, default_values_1.DEFAULT_TIMEOUT.pageLoad, option_source_1.default.Configuration);
|
||
|
this._ensureOptionWithValue(option_names_1.default.speed, default_values_1.DEFAULT_SPEED_VALUE, option_source_1.default.Configuration);
|
||
|
this._ensureOptionWithValue(option_names_1.default.appInitDelay, default_values_1.DEFAULT_APP_INIT_DELAY, option_source_1.default.Configuration);
|
||
|
this._ensureOptionWithValue(option_names_1.default.concurrency, default_values_1.DEFAULT_CONCURRENCY_VALUE, option_source_1.default.Configuration);
|
||
|
this._ensureOptionWithValue(option_names_1.default.src, default_values_1.DEFAULT_SOURCE_DIRECTORIES, option_source_1.default.Configuration);
|
||
|
this._ensureOptionWithValue(option_names_1.default.developmentMode, default_values_1.DEFAULT_DEVELOPMENT_MODE, option_source_1.default.Configuration);
|
||
|
this._ensureOptionWithValue(option_names_1.default.retryTestPages, default_values_1.DEFAULT_RETRY_TEST_PAGES, option_source_1.default.Configuration);
|
||
|
this._ensureOptionWithValue(option_names_1.default.disableHttp2, default_values_1.DEFAULT_DISABLE_HTTP2, option_source_1.default.Configuration);
|
||
|
this._ensureOptionWithValue(option_names_1.default.experimentalProxyless, default_values_1.DEFAULT_PROXYLESS, option_source_1.default.Configuration);
|
||
|
this._ensureOptionWithValue(option_names_1.default.disableCrossDomain, default_values_1.DEFAULT_DISABLE_CROSS_DOMAIN, option_source_1.default.Configuration);
|
||
|
this._ensureScreenshotOptions();
|
||
|
this._ensureSkipJsOptions();
|
||
|
}
|
||
|
_prepareCompilerOptions() {
|
||
|
const compilerOptions = this._ensureOption(option_names_1.default.compilerOptions, (0, default_values_1.getDefaultCompilerOptions)(), option_source_1.default.Configuration);
|
||
|
compilerOptions.value = compilerOptions.value || (0, default_values_1.getDefaultCompilerOptions)();
|
||
|
const tsConfigPath = this.getOption(option_names_1.default.tsConfigPath);
|
||
|
if (tsConfigPath) {
|
||
|
const compilerOptionValue = compilerOptions.value;
|
||
|
let typeScriptCompilerOptions = compilerOptionValue[customizable_compilers_1.default.typescript];
|
||
|
typeScriptCompilerOptions = Object.assign({
|
||
|
configPath: tsConfigPath,
|
||
|
}, typeScriptCompilerOptions);
|
||
|
compilerOptions.value[customizable_compilers_1.default.typescript] = typeScriptCompilerOptions;
|
||
|
}
|
||
|
}
|
||
|
async _getBrowserInfo() {
|
||
|
if (!this._options.browsers.value)
|
||
|
return [];
|
||
|
const browsers = Array.isArray(this._options.browsers.value) ? [...this._options.browsers.value] : [this._options.browsers.value];
|
||
|
const browserInfo = await Promise.all(browsers.map(browser => pool_1.default.getBrowserInfo(browser)));
|
||
|
return (0, lodash_1.flatten)(browserInfo);
|
||
|
}
|
||
|
async _isConfigurationFileExists(filePath = this.filePath) {
|
||
|
const fileExists = await super._isConfigurationFileExists(filePath);
|
||
|
if (!fileExists && this._isExplicitConfig)
|
||
|
throw new runtime_1.GeneralError(types_1.RUNTIME_ERRORS.cannotFindTestcafeConfigurationFile, filePath);
|
||
|
return fileExists;
|
||
|
}
|
||
|
static get FILENAMES() {
|
||
|
return CONFIGURATION_FILENAMES;
|
||
|
}
|
||
|
}
|
||
|
exports.default = TestCafeConfiguration;
|
||
|
module.exports = exports.default;
|
||
|
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoidGVzdGNhZmUtY29uZmlndXJhdGlvbi5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uL3NyYy9jb25maWd1cmF0aW9uL3Rlc3RjYWZlLWNvbmZpZ3VyYXRpb24udHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6Ijs7Ozs7QUFBQSw4RUFBaUQ7QUFDakQsbUNBQTRDO0FBQzVDLHNEQUFxRTtBQUNyRSxrRUFBMEM7QUFDMUMsMkVBQWlEO0FBQ2pELG1GQUEwRDtBQUMxRCw0Q0FBK0U7QUFDL0UsK0VBQXNEO0FBQ3RELHVGQUFnRTtBQUNoRSx1R0FBNEU7QUFFNUUscURBYzBCO0FBRTFCLG9FQUEyQztBQVMzQyxzRkFBNkQ7QUFDN0QsNERBQWdGO0FBRWhGLG9FQUEyRDtBQUUzRCx1Q0FBcUQ7QUFDckQsK0NBQWlEO0FBQ2pELDJDQUFpRDtBQUNqRCw4REFBMkQ7QUFFM0QsTUFBTSwyQkFBMkIsR0FBRyxhQUFhLENBQUM7QUFDbEQsTUFBTSx1QkFBdUIsR0FBTyxrQ0FBd0IsQ0FBQyxHQUFHLENBQUMsR0FBRyxDQUFDLEVBQUUsQ0FBQyxHQUFHLDJCQUEyQixHQUFHLEdBQUcsRUFBRSxDQUFDLENBQUM7QUFFaEgsTUFBTSw2QkFBNkIsR0FBRyxhQUFhLENBQUM7QUFFcEQsTUFBTSxpQkFBaUIsR0FBRztJQUN0QixzQkFBWSxDQUFDLFNBQVM7SUFDdEIsc0JBQVksQ0FBQyxXQUFXO0lBQ3hCLHNCQUFZLENBQUMsa0JBQWtCO0lBQy9CLHNCQUFZLENBQUMsZUFBZTtJQUM1QixzQkFBWSxDQUFDLHNCQUFzQjtJQUNuQyxzQkFBWSxDQUFDLGtCQUFrQjtJQUMvQixzQkFBWSxDQUFDLGtCQUFrQjtJQUMvQixzQkFBWSxDQUFDLGtCQUFrQjtJQUMvQixzQkFBWSxDQUFDLHNCQUFzQjtDQUN0QyxDQUFDO0FBRUYsTUFBTSxzQkFBc0IsR0FBRztJQUMzQixzQkFBWSxDQUFDLGVBQWU7SUFDNUIsc0JBQVksQ0FBQyxjQUFjO0lBQzNCLHNCQUFZLENBQUMsS0FB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
|