diff --git a/src/dashboard/config-storage.ts b/src/dashboard/config-storage.ts index f82f2b9806..187832040a 100644 --- a/src/dashboard/config-storage.ts +++ b/src/dashboard/config-storage.ts @@ -1,13 +1,7 @@ import { SafeStorage } from 'testcafe-safe-storage'; +import { DasboardOptions } from './interfaces'; - -// TODO: make this properties required -export interface DashboardConfigOptions { - token?: string; - sendReport?: boolean; -} - -const DEFAULT_DASHBOARD_OPTIONS: DashboardConfigOptions = { +const DEFAULT_DASHBOARD_OPTIONS: DasboardOptions = { token: '', // NOTE: we should send reports to the dashboard until it is disabled explicitly @@ -15,19 +9,19 @@ const DEFAULT_DASHBOARD_OPTIONS: DashboardConfigOptions = { }; export default class DashboardConfigStorage { - public options: DashboardConfigOptions; - private _storage: SafeStorage; + public options: DasboardOptions; + private _storage: SafeStorage; public constructor () { this.options = {}; - this._storage = new SafeStorage(); + this._storage = new SafeStorage(); } public async load (): Promise { - const result = await this._storage.tryLoad(); + const result = await this._storage.tryLoad(); const storageExists = result !== void 0; - this.options = result || DEFAULT_DASHBOARD_OPTIONS; + this.options = result || { ...DEFAULT_DASHBOARD_OPTIONS }; return storageExists; } diff --git a/src/dashboard/get-env-options.ts b/src/dashboard/get-env-options.ts new file mode 100644 index 0000000000..3b6224977e --- /dev/null +++ b/src/dashboard/get-env-options.ts @@ -0,0 +1,27 @@ +import { DasboardOptions } from './interfaces'; + +function parseBooleanVariable (value?: string): boolean { + return value === 'false' || value === '0' ? false : !!value; +} + +function parseNumber (value?: string): number | undefined { + const parsed = value === void 0 ? Number.NaN : Number.parseInt(value, 10); + + if (Number.isNaN(parsed)) + return void 0; + + return parsed; +} + +export default function getEnvOptions (): DasboardOptions { + return { + url: process.env.TESTCAFE_DASHBOARD_URL, + token: process.env.TESTCAFE_DASHBOARD_TOKEN, + buildId: process.env.TESTCAFE_DASHBOARD_BUILD_ID, + isLogEnabled: parseBooleanVariable(process.env.TESTCAFE_DASHBOARD_ENABLE_LOG), + noScreenshotUpload: parseBooleanVariable(process.env.TESTCAFE_DASHBOARD_NO_SCREENSHOT_UPLOAD), + noVideoUpload: parseBooleanVariable(process.env.TESTCAFE_DASHBOARD_NO_VIDEO_UPLOAD), + responseTimeout: parseNumber(process.env.TESTCAFE_DASHBOARD_RESPONSE_TIMEOUT), + requestRetryCount: parseNumber(process.env.TESTCAFE_DASHBOARD_REQUEST_RETRY_COUNT), + }; +} diff --git a/src/dashboard/interfaces.ts b/src/dashboard/interfaces.ts index 779b0d1dec..490140b60a 100644 --- a/src/dashboard/interfaces.ts +++ b/src/dashboard/interfaces.ts @@ -5,3 +5,15 @@ export interface DashboardAuthenticationToken { export type SendReportState = undefined | 'on' | 'off'; +// TODO: make token and sendReport properties required +export interface DasboardOptions { + url?: string; + token?: string; + buildId?: string; + noScreenshotUpload?: boolean; + noVideoUpload?: boolean; + isLogEnabled?: boolean; + requestRetryCount?: number; + responseTimeout?: number; + sendReport?: boolean; +} diff --git a/src/runner/index.js b/src/runner/index.js index 2378b7f337..2e64719045 100644 --- a/src/runner/index.js +++ b/src/runner/index.js @@ -49,6 +49,7 @@ import detectDisplay from '../utils/detect-display'; import { validateQuarantineOptions } from '../utils/get-options/quarantine'; import logEntry from '../utils/log-entry'; import MessageBus from '../utils/message-bus'; +import getEnvOptions from '../dashboard/get-env-options'; const DEBUG_LOGGER = debug('testcafe:runner'); const DASHBOARD_REPORTER_NAME = 'dashboard'; @@ -547,6 +548,8 @@ export default class Runner extends EventEmitter { if (!options) options = await this._loadDashboardOptionsFromStorage(); + this._mergeEnvDashboardOptions(options); + return options; } @@ -558,6 +561,15 @@ export default class Runner extends EventEmitter { return storage.options; } + _mergeEnvDashboardOptions (options) { + const envDashboardOptions = getEnvOptions(); + + for (const key in envDashboardOptions) { + if (envDashboardOptions[key]) + options[key] = envDashboardOptions[key]; + } + } + async _prepareClientScripts (tests, clientScripts) { return Promise.all(tests.map(async test => { if (test.isLegacy) diff --git a/test/server/runner-test.js b/test/server/runner-test.js index a63a8e0e6c..9159b13e15 100644 --- a/test/server/runner-test.js +++ b/test/server/runner-test.js @@ -222,6 +222,62 @@ describe('Runner', () => { sendReport: true, }; + describe('Environment options', () => { + before(() => { + process.env.TESTCAFE_DASHBOARD_URL = 'test-url'; + process.env.TESTCAFE_DASHBOARD_TOKEN = 'test-token'; + process.env.TESTCAFE_DASHBOARD_BUILD_ID = 'test-id'; + process.env.TESTCAFE_DASHBOARD_ENABLE_LOG = 'false'; + process.env.TESTCAFE_DASHBOARD_NO_SCREENSHOT_UPLOAD = '0'; + process.env.TESTCAFE_DASHBOARD_NO_VIDEO_UPLOAD = '1'; + process.env.TESTCAFE_DASHBOARD_RESPONSE_TIMEOUT = '1000'; + process.env.TESTCAFE_DASHBOARD_REQUEST_RETRY_COUNT = '5'; + }); + + after(() => { + delete process.env.TESTCAFE_DASHBOARD_URL; + delete process.env.TESTCAFE_DASHBOARD_TOKEN; + delete process.env.TESTCAFE_DASHBOARD_BUILD_ID; + delete process.env.TESTCAFE_DASHBOARD_ENABLE_LOG; + delete process.env.TESTCAFE_DASHBOARD_NO_SCREENSHOT_UPLOAD; + delete process.env.TESTCAFE_DASHBOARD_NO_VIDEO_UPLOAD; + delete process.env.TESTCAFE_DASHBOARD_RESPONSE_TIMEOUT; + delete process.env.TESTCAFE_DASHBOARD_REQUEST_RETRY_COUNT; + }); + + it('Should add dashboard reporter options from environment', async () => { + await runner._addDashboardReporterIfNeeded(); + + const reporter = runner.configuration.getOption('reporter')[0]; + + expect(reporter.name).to.equal('dashboard'); + expect(reporter.options).to.contains({ + token: 'test-token', + sendReport: true, + url: 'test-url', + buildId: 'test-id', + noVideoUpload: true, + responseTimeout: 1000, + requestRetryCount: 5, + }); + }); + + it('Should recover config options', async () => { + process.env.TESTCAFE_DASHBOARD_TOKEN = 'test-token'; + + runner.configuration.mergeOptions({ dashboard: { token: 'config-token ' } }); + + await runner._addDashboardReporterIfNeeded(); + + const reporter = runner.configuration.getOption('reporter')[0]; + + expect(reporter.name).to.equal('dashboard'); + expect(reporter.options).to.contains({ + token: 'test-token', + }); + }); + }); + it('Should turn on screenshots flags autoTakeOnFails and takeOnFails', async () => { runner._loadDashboardOptionsFromStorage = () => { return TEST_DASHBOARD_SETTINGS;