From ab3e25c43e1039d4b1e530f1902189e0c7ca7ee9 Mon Sep 17 00:00:00 2001 From: uid11 Date: Thu, 28 Dec 2023 22:36:47 +0300 Subject: [PATCH] FI-1031 feat: add typed browser parameters to pack config --- README.md | 22 +++ autotests/packs/allTests.ts | 19 ++- src/Page.ts | 2 +- src/README.md | 41 +++--- src/actions/navigateToUrl.ts | 2 +- .../waitFor/waitForAllRequestsComplete.ts | 2 +- .../waitFor/waitForInterfaceStabilization.ts | 2 +- src/actions/waitFor/waitForRequest.ts | 2 +- src/actions/waitFor/waitForRequestToRoute.ts | 2 +- src/actions/waitFor/waitForResponse.ts | 2 +- src/actions/waitFor/waitForResponseToRoute.ts | 2 +- src/bin/runTestsSubprocess.ts | 2 +- src/testcaferc.ts | 20 ++- src/types/config/config.ts | 4 +- src/types/config/ownE2edConfig.ts | 48 ++++++ src/utils/asserts/index.ts | 1 + src/utils/asserts/number.ts | 16 ++ .../browser/getPreparedUserAgentString.ts | 15 ++ .../browser/getTestCafeBrowsersString.ts | 39 +++++ src/utils/{ => browser}/hasBrowsersArg.ts | 4 +- src/utils/browser/index.ts | 4 + src/utils/collectTestFilePaths.ts | 2 +- src/utils/config/assertUserlandPack.ts | 137 ++++++++++++++++++ .../getFullPackConfig.ts | 0 .../{getFullPackConfig => config}/index.ts | 0 .../updateConfig.ts | 0 src/utils/events/registerStartE2edRunEvent.ts | 2 +- src/utils/events/runAfterPackFunctions.ts | 2 +- src/utils/expect/createExpectMethod.ts | 2 +- src/utils/generalLog/generalLog.ts | 2 +- src/utils/generalLog/logFile.ts | 2 +- src/utils/index.ts | 3 +- src/utils/log/log.ts | 2 +- src/utils/log/logBackendResponse.ts | 2 +- src/utils/log/mapBackendResponseForLogs.ts | 2 +- src/utils/pack/packTimeout.ts | 2 +- src/utils/pack/runPackWithArgs.ts | 6 +- src/utils/report/collectReportData.ts | 2 +- src/utils/report/getReportErrors.ts | 2 +- .../report/render/renderScriptConstants.ts | 2 +- .../killTestCafeProcessesOccupyingPorts.ts | 2 +- src/utils/retry/processRetries.ts | 2 +- src/utils/retry/runRetry.ts | 2 +- src/utils/startInfo/getStartInfo.ts | 2 +- src/utils/test/beforeTest.ts | 2 +- src/utils/test/getIsTestIncludedInPack.ts | 2 +- .../test/takeScreenshotsOnErrorIfNeeded.ts | 2 +- src/utils/tests/runTests.ts | 6 +- 48 files changed, 371 insertions(+), 72 deletions(-) create mode 100644 src/utils/asserts/number.ts create mode 100644 src/utils/browser/getPreparedUserAgentString.ts create mode 100644 src/utils/browser/getTestCafeBrowsersString.ts rename src/utils/{ => browser}/hasBrowsersArg.ts (61%) create mode 100644 src/utils/browser/index.ts create mode 100644 src/utils/config/assertUserlandPack.ts rename src/utils/{getFullPackConfig => config}/getFullPackConfig.ts (100%) rename src/utils/{getFullPackConfig => config}/index.ts (100%) rename src/utils/{getFullPackConfig => config}/updateConfig.ts (100%) diff --git a/README.md b/README.md index 37d290d2..0257683b 100644 --- a/README.md +++ b/README.md @@ -245,6 +245,12 @@ that exports the pack's config under the name `pack`. Here are the basic fields of the pack config. +`browser: string`: browser name as a command to launch it +(like `chrome`, `chromium`, `firefox`, `webkit`, etc). + +`browserFlags: string[]`: array of browser flags, like `--disable-dev-shm-usage`, +with which the browser is launched to run tests. + `concurrency: number`: the number of browser windows in which tests will run in parallel. `customPackProperties: CustomPackProperties`: a custom set of fields defined within the project. @@ -265,6 +271,16 @@ Each function can thus access the results of the previous function. `dockerImage: string`: the name of the docker image where the tests will run. The image must be based on the e2ed base image. +`enableChromeDevToolsProtocol: boolean`: enables [Chrome DevTools Protocol](https://chromedevtools.github.io/devtools-protocol/) +for browser control in tests (instead of `testcafe-hammerhead`). + +`enableHeadlessMode: boolean`: enables headless mode (if browser supports such mode). + +`enableMobileDeviceMode: boolean`: enables Chromium [mobile device mode](https://developer.chrome.com/docs/devtools/device-mode). + +`enableTouchEventEmulation: boolean`: enables touch event emulation. +If `true`, page fires `touch` events when test interact with the page (instead of `click` events). + `filterTestsIntoPack: (testStaticOptions: TestStaticOptions) => boolean`: this function filters tests (tasks) by their static options — only those tests for which the function returned `true` get into the pack. @@ -314,6 +330,8 @@ If the mapping returns `undefined`, the log entry is not skipped, but is printed `your-project/autotests/bin/runDocker.sh` (until the test passes). For example, if it is equal to three, the test will be run no more than three times. +`overriddenUserAgent: string | null`: if not `null`, then this value will override the browser's user agent in tests. + `packTimeout: number`: timeout (in millisecond) for the entire pack of tests (tasks). If the test pack takes longer than this timeout, the pack will fail with the appropriate error. @@ -356,6 +374,10 @@ This parameter can be overridden in the test-specific options. If the test run takes longer than this timeout, the test fails and rerun on the next retry. This parameter can be overridden in the test-specific options. +`viewportHeight: number`: height of viewport of page in pixels. + +`viewportWidth: number`: width of viewport of page in pixels. + `waitForAllRequestsComplete.maxIntervalBetweenRequestsInMs: number`: default maximum interval (in milliseconds) between requests for `waitForAllRequestsComplete` function. If there are no new requests for more than this interval, then the promise diff --git a/autotests/packs/allTests.ts b/autotests/packs/allTests.ts index 1bf54aa4..5941c863 100644 --- a/autotests/packs/allTests.ts +++ b/autotests/packs/allTests.ts @@ -22,29 +22,37 @@ import type {FilterTestsIntoPack, Pack} from 'autotests/types/packSpecific'; const isLocalRun = runEnvironment === RunEnvironment.Local; -const browser = isLocalRun ? 'chrome:headless' : 'chromium:headless'; const browserFlags = [ '--disable-dev-shm-usage', '--disable-web-security', '--ignore-certificate-errors', ]; +const browser = isLocalRun ? 'chrome' : 'chromium'; + const filterTestsIntoPack: FilterTestsIntoPack = ({options}) => options.meta.testId !== '13'; +const overriddenUserAgent = + 'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.35 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.35'; + /** * Pack of tests or tasks (pack configuration object). */ export const pack: Pack = { ajaxRequestTimeout: 40_000, assertionTimeout: 5_000, - browser: [browser, ...browserFlags].join(' '), + browser, + browserFlags, browserInitTimeout: 60_000, concurrency: isLocalRun ? 1 : 2, customPackProperties: {internalPackRunId: 0, name: 'allTests'}, - disableNativeAutomation: true, doAfterPack, doBeforePack, dockerImage: 'e2edhub/e2ed', + enableChromeDevToolsProtocol: true, + enableHeadlessMode: true, + enableMobileDeviceMode: false, + enableTouchEventEmulation: false, filterTestsIntoPack, liteReportFileName: 'lite-report.json', logFileName: 'pack-logs.log', @@ -54,7 +62,8 @@ export const pack: Pack = { mapLogPayloadInLogFile, mapLogPayloadInReport, maxRetriesCountInDocker: 3, - packTimeout: 90 * 60_000, + overriddenUserAgent, + packTimeout: 5 * 60_000, pageRequestTimeout: 30_000, pageStabilizationInterval: 500, pathToScreenshotsDirectoryForReport: './screenshots', @@ -68,6 +77,8 @@ export const pack: Pack = { testFileGlobs: ['./autotests/tests/**/*.ts', '!**/*.skip.ts'], testIdleTimeout: 20_000, testTimeout: 60_000, + viewportHeight: 800, + viewportWidth: 1280, waitForAllRequestsComplete: { maxIntervalBetweenRequestsInMs: 500, timeout: 30_000, diff --git a/src/Page.ts b/src/Page.ts index b4a5cd45..f7142011 100644 --- a/src/Page.ts +++ b/src/Page.ts @@ -2,7 +2,7 @@ import {waitForAllRequestsComplete, waitForInterfaceStabilization} from './actions/waitFor'; import {CREATE_PAGE_TOKEN} from './constants/internal'; import {assertValueIsTrue} from './utils/asserts'; -import {getFullPackConfig} from './utils/getFullPackConfig'; +import {getFullPackConfig} from './utils/config'; import type {PageRoute} from './PageRoute'; import type {AsyncVoid, PageClassTypeArgs, ParamsKeyType} from './types/internal'; diff --git a/src/README.md b/src/README.md index 1117717c..6c0c17b4 100644 --- a/src/README.md +++ b/src/README.md @@ -8,10 +8,10 @@ Modules in the dependency graph should only import the modules above them: 1. `types` 2. `configurator` 3. `constants` -4. `testcaferc` -5. `testcafe` -6. `esm` -7. `generators` +4. `testcafe` +5. `esm` +6. `generators` +7. `utils/browser` 8. `utils/getDurationWithUnits` 9. `utils/setReadonlyProperty` 10. `utils/selectors` @@ -22,19 +22,20 @@ Modules in the dependency graph should only import the modules above them: 15. `utils/userland` 16. `utils/fn` 17. `utils/environment` -18. `utils/getFullPackConfig` -19. `utils/runLabel` -20. `utils/generalLog` -21. `utils/fs` -22. `selectors` -23. `Route` -24. `ApiRoute` -25. `PageRoute` -26. `testController` -27. `useContext` -28. `context` -29. `utils/log` -30. `utils/waitForEvents` -31. `utils/expect` -32. `expect` -33. ... +18. `testcaferc` +19. `utils/config` +20. `utils/runLabel` +21. `utils/generalLog` +22. `utils/fs` +23. `selectors` +24. `Route` +25. `ApiRoute` +26. `PageRoute` +27. `testController` +28. `useContext` +29. `context` +30. `utils/log` +31. `utils/waitForEvents` +32. `utils/expect` +33. `expect` +34. ... diff --git a/src/actions/navigateToUrl.ts b/src/actions/navigateToUrl.ts index c5ee5309..77c052cd 100644 --- a/src/actions/navigateToUrl.ts +++ b/src/actions/navigateToUrl.ts @@ -1,7 +1,7 @@ import {LogEventType} from '../constants/internal'; import {createClientFunction} from '../createClientFunction'; import {testController} from '../testController'; -import {getFullPackConfig} from '../utils/getFullPackConfig'; +import {getFullPackConfig} from '../utils/config'; import {log} from '../utils/log'; import type {Url, Void} from '../types/internal'; diff --git a/src/actions/waitFor/waitForAllRequestsComplete.ts b/src/actions/waitFor/waitForAllRequestsComplete.ts index f495ce41..2d18df6c 100644 --- a/src/actions/waitFor/waitForAllRequestsComplete.ts +++ b/src/actions/waitFor/waitForAllRequestsComplete.ts @@ -1,10 +1,10 @@ import {LogEventType} from '../../constants/internal'; import {getTestRunPromise} from '../../context/testRunPromise'; import {getWaitForEventsState} from '../../context/waitForEventsState'; +import {getFullPackConfig} from '../../utils/config'; import {E2edError} from '../../utils/error'; import {setCustomInspectOnFunction} from '../../utils/fn'; import {getDurationWithUnits} from '../../utils/getDurationWithUnits'; -import {getFullPackConfig} from '../../utils/getFullPackConfig'; import {log} from '../../utils/log'; import {getPromiseWithResolveAndReject} from '../../utils/promise'; import {RequestHookToWaitForEvents} from '../../utils/requestHooks'; diff --git a/src/actions/waitFor/waitForInterfaceStabilization.ts b/src/actions/waitFor/waitForInterfaceStabilization.ts index db37d345..c069781a 100644 --- a/src/actions/waitFor/waitForInterfaceStabilization.ts +++ b/src/actions/waitFor/waitForInterfaceStabilization.ts @@ -2,8 +2,8 @@ import {LogEventType} from '../../constants/internal'; import {createClientFunction} from '../../createClientFunction'; +import {getFullPackConfig} from '../../utils/config'; import {getDurationWithUnits} from '../../utils/getDurationWithUnits'; -import {getFullPackConfig} from '../../utils/getFullPackConfig'; import {log} from '../../utils/log'; import type {UtcTimeInMs} from '../../types/internal'; diff --git a/src/actions/waitFor/waitForRequest.ts b/src/actions/waitFor/waitForRequest.ts index c33b4395..fa2a60b6 100644 --- a/src/actions/waitFor/waitForRequest.ts +++ b/src/actions/waitFor/waitForRequest.ts @@ -1,10 +1,10 @@ import {LogEventType} from '../../constants/internal'; import {getTestRunPromise} from '../../context/testRunPromise'; import {getWaitForEventsState} from '../../context/waitForEventsState'; +import {getFullPackConfig} from '../../utils/config'; import {E2edError} from '../../utils/error'; import {setCustomInspectOnFunction} from '../../utils/fn'; import {getDurationWithUnits} from '../../utils/getDurationWithUnits'; -import {getFullPackConfig} from '../../utils/getFullPackConfig'; import {log} from '../../utils/log'; import {getPromiseWithResolveAndReject} from '../../utils/promise'; import {RequestHookToWaitForEvents} from '../../utils/requestHooks'; diff --git a/src/actions/waitFor/waitForRequestToRoute.ts b/src/actions/waitFor/waitForRequestToRoute.ts index 33b2bdd5..b47f5bbd 100644 --- a/src/actions/waitFor/waitForRequestToRoute.ts +++ b/src/actions/waitFor/waitForRequestToRoute.ts @@ -1,8 +1,8 @@ import {LogEventType} from '../../constants/internal'; import {assertValueIsDefined, assertValueIsUndefined} from '../../utils/asserts'; +import {getFullPackConfig} from '../../utils/config'; import {setCustomInspectOnFunction} from '../../utils/fn'; import {getDurationWithUnits} from '../../utils/getDurationWithUnits'; -import {getFullPackConfig} from '../../utils/getFullPackConfig'; import {getRouteInstanceFromUrl} from '../../utils/getRouteInstanceFromUrl'; import {log} from '../../utils/log'; diff --git a/src/actions/waitFor/waitForResponse.ts b/src/actions/waitFor/waitForResponse.ts index c2812c9a..5700034f 100644 --- a/src/actions/waitFor/waitForResponse.ts +++ b/src/actions/waitFor/waitForResponse.ts @@ -1,10 +1,10 @@ import {LogEventType} from '../../constants/internal'; import {getTestRunPromise} from '../../context/testRunPromise'; import {getWaitForEventsState} from '../../context/waitForEventsState'; +import {getFullPackConfig} from '../../utils/config'; import {E2edError} from '../../utils/error'; import {setCustomInspectOnFunction} from '../../utils/fn'; import {getDurationWithUnits} from '../../utils/getDurationWithUnits'; -import {getFullPackConfig} from '../../utils/getFullPackConfig'; import {log} from '../../utils/log'; import {getPromiseWithResolveAndReject} from '../../utils/promise'; import {RequestHookToWaitForEvents} from '../../utils/requestHooks'; diff --git a/src/actions/waitFor/waitForResponseToRoute.ts b/src/actions/waitFor/waitForResponseToRoute.ts index 4014600e..15d9a6aa 100644 --- a/src/actions/waitFor/waitForResponseToRoute.ts +++ b/src/actions/waitFor/waitForResponseToRoute.ts @@ -1,8 +1,8 @@ import {LogEventType} from '../../constants/internal'; import {assertValueIsDefined, assertValueIsUndefined} from '../../utils/asserts'; +import {getFullPackConfig} from '../../utils/config'; import {setCustomInspectOnFunction} from '../../utils/fn'; import {getDurationWithUnits} from '../../utils/getDurationWithUnits'; -import {getFullPackConfig} from '../../utils/getFullPackConfig'; import {getRouteInstanceFromUrl} from '../../utils/getRouteInstanceFromUrl'; import {log} from '../../utils/log'; diff --git a/src/bin/runTestsSubprocess.ts b/src/bin/runTestsSubprocess.ts index 64fbbe23..6499b53d 100644 --- a/src/bin/runTestsSubprocess.ts +++ b/src/bin/runTestsSubprocess.ts @@ -1,4 +1,4 @@ -import {getFullPackConfig} from '../utils/getFullPackConfig'; +import {getFullPackConfig} from '../utils/config'; import {runTests} from '../utils/tests'; import type {RunRetryOptions} from '../types/internal'; diff --git a/src/testcaferc.ts b/src/testcaferc.ts index a4eb9b6b..a6121e81 100644 --- a/src/testcaferc.ts +++ b/src/testcaferc.ts @@ -1,6 +1,6 @@ /** * @file Full pack configuration (extended TestCafe configuration) for running tests. - * Don't import this module. Instead, use `utils/getFullPackConfig`. + * Don't import this module. Instead, use `getFullPackConfig` from `utils/config`. */ import {join} from 'node:path'; @@ -11,6 +11,9 @@ import { SCREENSHOTS_DIRECTORY_PATH, } from './constants/internal'; import {assertValueIsTrue} from './utils/asserts'; +import {getTestCafeBrowsersString} from './utils/browser'; +// eslint-disable-next-line import/no-internal-modules +import {assertUserlandPack} from './utils/config/assertUserlandPack'; import {getPathToPack} from './utils/environment'; import {setCustomInspectOnFunction} from './utils/fn'; @@ -31,13 +34,11 @@ const absoluteCompiledUserlandPackPath = join( // eslint-disable-next-line @typescript-eslint/no-var-requires, import/no-dynamic-require const userlandPack = require<{pack: UserlandPack}>(absoluteCompiledUserlandPackPath).pack; +assertUserlandPack(userlandPack); + const frozenPartOfTestCafeConfig: FrozenPartOfTestCafeConfig = { color: true, - compilerOptions: { - typescript: { - options: {esModuleInterop: true, resolveJsonModule: true}, - }, - }, + compilerOptions: {typescript: {options: {esModuleInterop: true, resolveJsonModule: true}}}, disableMultipleWindows: true, hostname: 'localhost', pageLoadTimeout: 0, @@ -55,7 +56,8 @@ const frozenPartOfTestCafeConfig: FrozenPartOfTestCafeConfig = { const fullPackConfig: FullPackConfig = { ...userlandPack, - browsers: userlandPack.browser, + browsers: getTestCafeBrowsersString(userlandPack), + disableNativeAutomation: !userlandPack.enableChromeDevToolsProtocol, src: userlandPack.testFileGlobs, ...frozenPartOfTestCafeConfig, }; @@ -64,6 +66,8 @@ const { doAfterPack, doBeforePack, filterTestsIntoPack, + mapBackendResponseErrorToLog, + mapBackendResponseToLog, mapLogPayloadInConsole, mapLogPayloadInLogFile, mapLogPayloadInReport, @@ -78,6 +82,8 @@ for (const fn of doBeforePack) { } setCustomInspectOnFunction(filterTestsIntoPack); +setCustomInspectOnFunction(mapBackendResponseErrorToLog); +setCustomInspectOnFunction(mapBackendResponseToLog); setCustomInspectOnFunction(mapLogPayloadInConsole); setCustomInspectOnFunction(mapLogPayloadInLogFile); setCustomInspectOnFunction(mapLogPayloadInReport); diff --git a/src/types/config/config.ts b/src/types/config/config.ts index 275b3cf3..6da8e901 100644 --- a/src/types/config/config.ts +++ b/src/types/config/config.ts @@ -15,10 +15,8 @@ import type {OwnE2edConfig} from './ownE2edConfig'; type UserlandTestCafeConfig = Readonly<{ ajaxRequestTimeout: number; assertionTimeout: number; - browser: string; browserInitTimeout: number; concurrency: number; - disableNativeAutomation: boolean; pageRequestTimeout: number; port1: number; port2: number; @@ -67,7 +65,7 @@ export type FullPackConfigWithoutDoBeforePack< TestMeta >) & FrozenPartOfTestCafeConfig & - Readonly<{browsers: string; src: readonly string[]}>; + Readonly<{browsers: string; disableNativeAutomation: boolean; src: readonly string[]}>; /** * The complete userland pack config. diff --git a/src/types/config/ownE2edConfig.ts b/src/types/config/ownE2edConfig.ts index 5d20c93c..772d8205 100644 --- a/src/types/config/ownE2edConfig.ts +++ b/src/types/config/ownE2edConfig.ts @@ -19,6 +19,16 @@ export type OwnE2edConfig< SkipTests = SkipTestsPlaceholder, TestMeta = TestMetaPlaceholder, > = Readonly<{ + /** + * Browser name as a command to launch it (like `chrome`, `chromium`, `firefox`, `webkit`, etc). + */ + browser: string; + + /** + * Array of browser flags, like `--disable-dev-shm-usage`, with which the browser is launched to run tests. + */ + browserFlags: readonly string[]; + /** * Custom pack properties for using in hooks, etc. */ @@ -41,6 +51,29 @@ export type OwnE2edConfig< */ dockerImage: string; + /** + * Enables Chrome DevTools Protocol for browser control in tests (instead of `testcafe-hammerhead`). + * {@link https://chromedevtools.github.io/devtools-protocol/} + */ + enableChromeDevToolsProtocol: boolean; + + /** + * Enables headless mode (if browser supports such mode). + */ + enableHeadlessMode: boolean; + + /** + * Enables Chromium mobile device mode. + * {@link https://developer.chrome.com/docs/devtools/device-mode} + */ + enableMobileDeviceMode: boolean; + + /** + * Enables touch event emulation. + * If `true`, page fires `touch` events when test interact with the page (instead of `click` events). + */ + enableTouchEventEmulation: boolean; + /** * This function filters tests (tasks) by their static options — * only those tests for which the function returned `true` get into the pack. @@ -110,6 +143,11 @@ export type OwnE2edConfig< */ maxRetriesCountInDocker: number; + /** + * If not `null`, then this value will override the browser's user agent in tests. + */ + overriddenUserAgent: string | null; + /** * Timeout (in millisecond) for the entire pack of tests (tasks). * If the test pack takes longer than this timeout, the pack will fail with the appropriate error. @@ -183,6 +221,16 @@ export type OwnE2edConfig< */ testTimeout: number; + /** + * Height of viewport of page in pixels. + */ + viewportHeight: number; + + /** + * Width of viewport of page in pixels. + */ + viewportWidth: number; + /** * Group of settings for the `waitForAllRequestsComplete` function. */ diff --git a/src/utils/asserts/index.ts b/src/utils/asserts/index.ts index 067c0de1..a403e07f 100644 --- a/src/utils/asserts/index.ts +++ b/src/utils/asserts/index.ts @@ -1,6 +1,7 @@ export {assertValueIsFalse, assertValueIsTrue} from './boolean'; export {assertValueIsNever} from './never'; export {assertValueIsNotNull, assertValueIsNull} from './null'; +export {assertNumberIsPositiveInteger} from './number'; export {assertValueHasProperty} from './properties'; export {assertFunctionThrows} from './throws'; export {assertValueIsDefined, assertValueIsUndefined} from './undefined'; diff --git a/src/utils/asserts/number.ts b/src/utils/asserts/number.ts new file mode 100644 index 00000000..58e3b434 --- /dev/null +++ b/src/utils/asserts/number.ts @@ -0,0 +1,16 @@ +import {E2edError} from '../error'; + +import type {LogParams} from '../../types/internal'; + +/** + * Asserts that number is positive integer. + */ +export function assertNumberIsPositiveInteger( + value: number, + check: string, + payload?: LogParams, +): asserts value is number { + if (typeof value !== 'number' || !Number.isInteger(value) || value <= 0) { + throw new E2edError('Asserted number is not a positive integer', {check, payload, value}); + } +} diff --git a/src/utils/browser/getPreparedUserAgentString.ts b/src/utils/browser/getPreparedUserAgentString.ts new file mode 100644 index 00000000..94d664d3 --- /dev/null +++ b/src/utils/browser/getPreparedUserAgentString.ts @@ -0,0 +1,15 @@ +import {RunEnvironment, runEnvironment} from '../../configurator'; + +/** + * Get prepared user agent string for using in TestCafe `browsers` string + * for overriding browser user agent. + * @internal + */ +export const getPreparedUserAgentString = (userAgent: string): string => { + const userAgentWithEscaping = userAgent.replace(/(? { + const parts: string[] = [userlandPack.browser]; + + if (userlandPack.enableHeadlessMode) { + parts.push(':headless'); + } + + parts.push(':emulation:'); + + parts.push(`width=${userlandPack.viewportWidth};`); + parts.push(`height=${userlandPack.viewportHeight};`); + + parts.push(`mobile=${userlandPack.enableMobileDeviceMode};`); + + const orientation = + userlandPack.viewportWidth > userlandPack.viewportHeight ? 'horizontal' : 'vertical'; + + parts.push(`orientation=${orientation};`); + + parts.push(`touch=${userlandPack.enableTouchEventEmulation};`); + + if (userlandPack.overriddenUserAgent !== null) { + const preparedUserAgentString = getPreparedUserAgentString(userlandPack.overriddenUserAgent); + + parts.push(`userAgent=${preparedUserAgentString}`); + } + + const browserWithoutFlags = parts.join(''); + + return [browserWithoutFlags, ...userlandPack.browserFlags].join(' '); +}; diff --git a/src/utils/hasBrowsersArg.ts b/src/utils/browser/hasBrowsersArg.ts similarity index 61% rename from src/utils/hasBrowsersArg.ts rename to src/utils/browser/hasBrowsersArg.ts index 89809a15..dea010a5 100644 --- a/src/utils/hasBrowsersArg.ts +++ b/src/utils/browser/hasBrowsersArg.ts @@ -1,7 +1,7 @@ -import {AUTOTESTS_DIRECTORY_PATH} from '../constants/internal'; +import {AUTOTESTS_DIRECTORY_PATH} from '../../constants/internal'; /** - * Returns `true`, if current node args for TestCafe has browsers arg, and `false` otherwise. + * Returns `true`, if current node arguments for TestCafe has browsers arg, and `false` otherwise. * @internal */ export const hasBrowsersArg = (): boolean => { diff --git a/src/utils/browser/index.ts b/src/utils/browser/index.ts new file mode 100644 index 00000000..eb9809ff --- /dev/null +++ b/src/utils/browser/index.ts @@ -0,0 +1,4 @@ +/** @internal */ +export {getTestCafeBrowsersString} from './getTestCafeBrowsersString'; +/** @internal */ +export {hasBrowsersArg} from './hasBrowsersArg'; diff --git a/src/utils/collectTestFilePaths.ts b/src/utils/collectTestFilePaths.ts index c4fea1ae..7e7791ea 100644 --- a/src/utils/collectTestFilePaths.ts +++ b/src/utils/collectTestFilePaths.ts @@ -2,7 +2,7 @@ import {normalize} from 'node:path'; import globby from 'globby'; -import {getFullPackConfig} from './getFullPackConfig'; +import {getFullPackConfig} from './config'; import type {TestFilePath} from '../types/internal'; diff --git a/src/utils/config/assertUserlandPack.ts b/src/utils/config/assertUserlandPack.ts new file mode 100644 index 00000000..08f0d5ea --- /dev/null +++ b/src/utils/config/assertUserlandPack.ts @@ -0,0 +1,137 @@ +import {assertNumberIsPositiveInteger} from '../asserts'; + +import type {UserlandPack} from '../../types/internal'; + +/** + * Asserts that userland pack is correct. + * @internal + */ +// eslint-disable-next-line max-lines-per-function, max-statements +export const assertUserlandPack = (userlandPack: UserlandPack): void => { + const logParams = {userlandPack}; + + assertNumberIsPositiveInteger( + userlandPack.ajaxRequestTimeout, + 'ajaxRequestTimeout is positive integer', + logParams, + ); + + assertNumberIsPositiveInteger( + userlandPack.assertionTimeout, + 'assertionTimeout is positive integer', + logParams, + ); + + assertNumberIsPositiveInteger( + userlandPack.concurrency, + 'concurrency is positive integer', + logParams, + ); + + assertNumberIsPositiveInteger( + userlandPack.browserInitTimeout, + 'browserInitTimeout is positive integer', + logParams, + ); + + assertNumberIsPositiveInteger( + userlandPack.maxRetriesCountInDocker, + 'maxRetriesCountInDocker is positive integer', + logParams, + ); + + assertNumberIsPositiveInteger( + userlandPack.packTimeout, + 'packTimeout is positive integer', + logParams, + ); + + if (userlandPack.pageRequestTimeout !== 0) { + assertNumberIsPositiveInteger( + userlandPack.pageRequestTimeout, + 'pageRequestTimeout is positive integer', + logParams, + ); + } + + if (userlandPack.pageStabilizationInterval !== 0) { + assertNumberIsPositiveInteger( + userlandPack.pageStabilizationInterval, + 'pageStabilizationInterval is positive integer', + logParams, + ); + } + + assertNumberIsPositiveInteger(userlandPack.port1, 'port1 is positive integer', logParams); + assertNumberIsPositiveInteger(userlandPack.port2, 'port2 is positive integer', logParams); + + assertNumberIsPositiveInteger( + userlandPack.selectorTimeout, + 'selectorTimeout is positive integer', + logParams, + ); + + assertNumberIsPositiveInteger( + userlandPack.testIdleTimeout, + 'testIdleTimeout is positive integer', + logParams, + ); + + assertNumberIsPositiveInteger( + userlandPack.testTimeout, + 'testTimeout is positive integer', + logParams, + ); + + assertNumberIsPositiveInteger( + userlandPack.viewportHeight, + 'viewportHeight is positive integer', + logParams, + ); + + assertNumberIsPositiveInteger( + userlandPack.viewportWidth, + 'viewportWidth is positive integer', + logParams, + ); + + if (userlandPack.waitForAllRequestsComplete.maxIntervalBetweenRequestsInMs !== 0) { + assertNumberIsPositiveInteger( + userlandPack.waitForAllRequestsComplete.maxIntervalBetweenRequestsInMs, + 'waitForAllRequestsComplete.maxIntervalBetweenRequestsInMs is positive integer', + logParams, + ); + } + + assertNumberIsPositiveInteger( + userlandPack.waitForAllRequestsComplete.timeout, + 'waitForAllRequestsComplete.timeout is positive integer', + logParams, + ); + + if (userlandPack.waitForInterfaceStabilization.stabilizationInterval !== 0) { + assertNumberIsPositiveInteger( + userlandPack.waitForInterfaceStabilization.stabilizationInterval, + 'waitForInterfaceStabilization.stabilizationInterval is positive integer', + logParams, + ); + } + + assertNumberIsPositiveInteger( + userlandPack.waitForInterfaceStabilization.timeout, + 'waitForInterfaceStabilization.timeout is positive integer', + logParams, + ); + + assertNumberIsPositiveInteger( + userlandPack.waitForRequestTimeout, + 'waitForRequestTimeout is positive integer', + logParams, + ); + + assertNumberIsPositiveInteger( + userlandPack.waitForResponseTimeout, + 'waitForResponseTimeout is positive integer', + logParams, + ); +}; diff --git a/src/utils/getFullPackConfig/getFullPackConfig.ts b/src/utils/config/getFullPackConfig.ts similarity index 100% rename from src/utils/getFullPackConfig/getFullPackConfig.ts rename to src/utils/config/getFullPackConfig.ts diff --git a/src/utils/getFullPackConfig/index.ts b/src/utils/config/index.ts similarity index 100% rename from src/utils/getFullPackConfig/index.ts rename to src/utils/config/index.ts diff --git a/src/utils/getFullPackConfig/updateConfig.ts b/src/utils/config/updateConfig.ts similarity index 100% rename from src/utils/getFullPackConfig/updateConfig.ts rename to src/utils/config/updateConfig.ts diff --git a/src/utils/events/registerStartE2edRunEvent.ts b/src/utils/events/registerStartE2edRunEvent.ts index 506d0a0f..bddd9821 100644 --- a/src/utils/events/registerStartE2edRunEvent.ts +++ b/src/utils/events/registerStartE2edRunEvent.ts @@ -1,11 +1,11 @@ import {RunEnvironment} from '../../configurator'; import {EVENTS_DIRECTORY_PATH, ExitCode, TMP_DIRECTORY_PATH} from '../../constants/internal'; +import {getFullPackConfig, updateConfig} from '../config'; import {E2edError} from '../error'; import {setGlobalExitCode} from '../exit'; import {createDirectory, removeDirectory, writeStartInfo} from '../fs'; import {generalLog, writeLogsToFile} from '../generalLog'; -import {getFullPackConfig, updateConfig} from '../getFullPackConfig'; import {compilePack, setPackTimeout} from '../pack'; import {getStartInfo} from '../startInfo'; diff --git a/src/utils/events/runAfterPackFunctions.ts b/src/utils/events/runAfterPackFunctions.ts index 1177865e..e6508761 100644 --- a/src/utils/events/runAfterPackFunctions.ts +++ b/src/utils/events/runAfterPackFunctions.ts @@ -1,5 +1,5 @@ +import {getFullPackConfig} from '../config'; import {generalLog} from '../generalLog'; -import {getFullPackConfig} from '../getFullPackConfig'; import {runArrayOfUserlandFunctions} from '../userland'; import type {CustomReportPropertiesPlaceholder, LiteReport, Void} from '../../types/internal'; diff --git a/src/utils/expect/createExpectMethod.ts b/src/utils/expect/createExpectMethod.ts index 2d014629..8dc42c80 100644 --- a/src/utils/expect/createExpectMethod.ts +++ b/src/utils/expect/createExpectMethod.ts @@ -1,9 +1,9 @@ import {LogEventStatus, LogEventType, RESOLVED_PROMISE} from '../../constants/internal'; import {testController} from '../../testController'; +import {getFullPackConfig} from '../config'; import {E2edError} from '../error'; import {getDurationWithUnits} from '../getDurationWithUnits'; -import {getFullPackConfig} from '../getFullPackConfig'; import {log} from '../log'; import {addTimeoutToPromise} from '../promise'; import {getDescriptionFromSelector} from '../selectors'; diff --git a/src/utils/generalLog/generalLog.ts b/src/utils/generalLog/generalLog.ts index 19f643bd..0e2882cc 100644 --- a/src/utils/generalLog/generalLog.ts +++ b/src/utils/generalLog/generalLog.ts @@ -1,4 +1,4 @@ -import {getFullPackConfig} from '../getFullPackConfig'; +import {getFullPackConfig} from '../config'; import {getLogMessageBody} from './getLogMessageBody'; import {getLogPrefix} from './getLogPrefix'; diff --git a/src/utils/generalLog/logFile.ts b/src/utils/generalLog/logFile.ts index da563054..eb60050f 100644 --- a/src/utils/generalLog/logFile.ts +++ b/src/utils/generalLog/logFile.ts @@ -3,7 +3,7 @@ import {join} from 'node:path'; import {REPORTS_DIRECTORY_PATH, RESOLVED_PROMISE} from '../../constants/internal'; -import {getFullPackConfig} from '../getFullPackConfig'; +import {getFullPackConfig} from '../config'; /** * Array of pack logs. Logs are stored in this array for further saving in the pack logs file. diff --git a/src/utils/index.ts b/src/utils/index.ts index 72487223..3784af58 100644 --- a/src/utils/index.ts +++ b/src/utils/index.ts @@ -1,5 +1,6 @@ export { assertFunctionThrows, + assertNumberIsPositiveInteger, assertValueHasProperty, assertValueIsDefined, assertValueIsFalse, @@ -10,6 +11,7 @@ export { assertValueIsUndefined, } from './asserts'; export {cloneWithoutUndefinedProperties} from './clone'; +export {getFullPackConfig as untypedGetFullPackConfig} from './config'; export { assertStringIsSameSite, getCookieHeaderString, @@ -29,7 +31,6 @@ export {E2edError, getStackTrace} from './error'; export {getFunctionPresentationForLogs, setCustomInspectOnFunction} from './fn'; export {writeFile} from './fs'; export {removeStyleFromString} from './generalLog'; -export {getFullPackConfig as untypedGetFullPackConfig} from './getFullPackConfig'; export {getKeysCounter} from './getKeysCounter'; export {log} from './log'; export {parseMaybeEmptyValueAsJson} from './parseMaybeEmptyValueAsJson'; diff --git a/src/utils/log/log.ts b/src/utils/log/log.ts index aacc5815..3f44ce78 100644 --- a/src/utils/log/log.ts +++ b/src/utils/log/log.ts @@ -1,9 +1,9 @@ import {LogEventType} from '../../constants/internal'; import {getRunId} from '../../context/runId'; +import {getFullPackConfig} from '../config'; // eslint-disable-next-line import/no-internal-modules import {registerLogEvent} from '../events/registerLogEvent'; -import {getFullPackConfig} from '../getFullPackConfig'; import {logWithPreparedOptions} from './logWithPreparedOptions'; diff --git a/src/utils/log/logBackendResponse.ts b/src/utils/log/logBackendResponse.ts index 5f2be099..8450a7b3 100644 --- a/src/utils/log/logBackendResponse.ts +++ b/src/utils/log/logBackendResponse.ts @@ -1,8 +1,8 @@ import {LogEventType} from '../../constants/internal'; import {getRunId} from '../../context/runId'; +import {getFullPackConfig} from '../config'; import {getTestRunEvent} from '../events'; -import {getFullPackConfig} from '../getFullPackConfig'; import {log} from './log'; import {logWithPreparedOptions} from './logWithPreparedOptions'; diff --git a/src/utils/log/mapBackendResponseForLogs.ts b/src/utils/log/mapBackendResponseForLogs.ts index 45dc8338..c93c5176 100644 --- a/src/utils/log/mapBackendResponseForLogs.ts +++ b/src/utils/log/mapBackendResponseForLogs.ts @@ -1,4 +1,4 @@ -import {getFullPackConfig} from '../getFullPackConfig'; +import {getFullPackConfig} from '../config'; import {logBackendResponse} from './logBackendResponse'; import {logBackendResponseError} from './logBackendResponseError'; diff --git a/src/utils/pack/packTimeout.ts b/src/utils/pack/packTimeout.ts index e4bc8cc4..f08c4e36 100644 --- a/src/utils/pack/packTimeout.ts +++ b/src/utils/pack/packTimeout.ts @@ -1,8 +1,8 @@ import {startTimeInMs} from '../../configurator'; import {EndE2edReason} from '../../constants/internal'; +import {getFullPackConfig} from '../config'; import {endE2ed, endE2edReason} from '../end'; -import {getFullPackConfig} from '../getFullPackConfig'; let rejectPackTimeoutPromise: (() => void) | undefined; diff --git a/src/utils/pack/runPackWithArgs.ts b/src/utils/pack/runPackWithArgs.ts index 71f6bdf1..19c41821 100644 --- a/src/utils/pack/runPackWithArgs.ts +++ b/src/utils/pack/runPackWithArgs.ts @@ -1,10 +1,10 @@ import {EndE2edReason, TESTCAFERC_PATH} from '../../constants/internal'; import {assertValueIsDefined} from '../asserts'; +import {hasBrowsersArg} from '../browser'; +import {getFullPackConfig} from '../config'; import {endE2ed} from '../end'; import {setRunLabel} from '../environment'; -import {getFullPackConfig} from '../getFullPackConfig'; -import {hasBrowsersArg} from '../hasBrowsersArg'; import {createRunLabel} from '../runLabel'; import {getPackTimeoutPromise} from './packTimeout'; @@ -19,7 +19,7 @@ export const runPackWithArgs = async (): Promise => { setRunLabel(runLabel); - if (browsers.length > 0 && hasBrowsersArg() === false) { + if (browsers.length > 0 && !hasBrowsersArg()) { process.argv.splice(2, 0, String(browsers)); } diff --git a/src/utils/report/collectReportData.ts b/src/utils/report/collectReportData.ts index f337f2bd..b826bb88 100644 --- a/src/utils/report/collectReportData.ts +++ b/src/utils/report/collectReportData.ts @@ -1,5 +1,5 @@ +import {getFullPackConfig} from '../config'; import {getExitCode} from '../exit'; -import {getFullPackConfig} from '../getFullPackConfig'; import {assertThatTestNamesAndFilePathsAreUnique} from './assertThatTestNamesAndFilePathsAreUnique'; import {getFailedTestsMainParams} from './getFailedTestsMainParams'; diff --git a/src/utils/report/getReportErrors.ts b/src/utils/report/getReportErrors.ts index 6ab3156f..179e880d 100644 --- a/src/utils/report/getReportErrors.ts +++ b/src/utils/report/getReportErrors.ts @@ -1,6 +1,6 @@ import {RunEnvironment} from '../../configurator'; -import {getFullPackConfig} from '../getFullPackConfig'; +import {getFullPackConfig} from '../config'; import {getUnvisitedTestFilePaths} from '../getUnvisitedTestFilePaths'; import type {FullTestRun, TestFilePath} from '../../types/internal'; diff --git a/src/utils/report/render/renderScriptConstants.ts b/src/utils/report/render/renderScriptConstants.ts index eaf49f7e..07b46bfc 100644 --- a/src/utils/report/render/renderScriptConstants.ts +++ b/src/utils/report/render/renderScriptConstants.ts @@ -1,4 +1,4 @@ -import {getFullPackConfig} from '../../getFullPackConfig'; +import {getFullPackConfig} from '../../config'; import {createSafeHtmlWithoutSanitize} from '../client'; diff --git a/src/utils/retry/killTestCafeProcessesOccupyingPorts.ts b/src/utils/retry/killTestCafeProcessesOccupyingPorts.ts index 6b5630a1..588c65ab 100644 --- a/src/utils/retry/killTestCafeProcessesOccupyingPorts.ts +++ b/src/utils/retry/killTestCafeProcessesOccupyingPorts.ts @@ -2,8 +2,8 @@ import {execFileSync} from 'node:child_process'; import {EXEC_FILE_OPTIONS} from '../../constants/internal'; +import {getFullPackConfig} from '../config'; import {generalLog} from '../generalLog'; -import {getFullPackConfig} from '../getFullPackConfig'; const nodeProcessPidRegexp = /\b(?\d+)\/node\b/; diff --git a/src/utils/retry/processRetries.ts b/src/utils/retry/processRetries.ts index 394bccba..5434ef9f 100644 --- a/src/utils/retry/processRetries.ts +++ b/src/utils/retry/processRetries.ts @@ -1,7 +1,7 @@ import {EndE2edReason} from '../../constants/internal'; +import {getFullPackConfig} from '../config'; import {endE2ed, endE2edReason} from '../end'; -import {getFullPackConfig} from '../getFullPackConfig'; import {processRetry} from './processRetry'; diff --git a/src/utils/retry/runRetry.ts b/src/utils/retry/runRetry.ts index c6c28749..3d462f21 100644 --- a/src/utils/retry/runRetry.ts +++ b/src/utils/retry/runRetry.ts @@ -3,10 +3,10 @@ import {join} from 'node:path'; import {INSTALLED_E2ED_DIRECTORY_PATH} from '../../constants/internal'; +import {getFullPackConfig} from '../config'; import {E2edError} from '../error'; import {getLastLogEventTimeInMs, writeLogEventTime} from '../fs'; import {getDurationWithUnits} from '../getDurationWithUnits'; -import {getFullPackConfig} from '../getFullPackConfig'; import {setTestsSubprocess, testsSubprocess} from '../tests'; import {killTestCafeProcessesOccupyingPorts} from './killTestCafeProcessesOccupyingPorts'; diff --git a/src/utils/startInfo/getStartInfo.ts b/src/utils/startInfo/getStartInfo.ts index 3ceb39eb..81949787 100644 --- a/src/utils/startInfo/getStartInfo.ts +++ b/src/utils/startInfo/getStartInfo.ts @@ -6,8 +6,8 @@ import { INSTALLED_E2ED_DIRECTORY_PATH, } from '../../constants/internal'; +import {getFullPackConfig} from '../config'; import {getPathToPack} from '../environment'; -import {getFullPackConfig} from '../getFullPackConfig'; import {testCafeHammerheadUpPackagePath} from '../paths'; import {getPackageInfo} from './getPackageInfo'; diff --git a/src/utils/test/beforeTest.ts b/src/utils/test/beforeTest.ts index 6b411f7e..a02a1473 100644 --- a/src/utils/test/beforeTest.ts +++ b/src/utils/test/beforeTest.ts @@ -4,9 +4,9 @@ import {setRunId} from '../../context/runId'; import {setTestIdleTimeout} from '../../context/testIdleTimeout'; import {setTestTimeout} from '../../context/testTimeout'; +import {getFullPackConfig} from '../config'; import {getRunLabel} from '../environment'; import {registerStartTestRunEvent} from '../events'; -import {getFullPackConfig} from '../getFullPackConfig'; import {getUserlandHooks} from '../userland'; import {getTestFnAndReject} from './getTestFnAndReject'; diff --git a/src/utils/test/getIsTestIncludedInPack.ts b/src/utils/test/getIsTestIncludedInPack.ts index a01aa945..d68e8e3a 100644 --- a/src/utils/test/getIsTestIncludedInPack.ts +++ b/src/utils/test/getIsTestIncludedInPack.ts @@ -1,4 +1,4 @@ -import {getFullPackConfig} from '../getFullPackConfig'; +import {getFullPackConfig} from '../config'; import type {TestStaticOptions} from '../../types/internal'; diff --git a/src/utils/test/takeScreenshotsOnErrorIfNeeded.ts b/src/utils/test/takeScreenshotsOnErrorIfNeeded.ts index cc9c746a..a764570a 100644 --- a/src/utils/test/takeScreenshotsOnErrorIfNeeded.ts +++ b/src/utils/test/takeScreenshotsOnErrorIfNeeded.ts @@ -3,8 +3,8 @@ import {join} from 'node:path'; // eslint-disable-next-line import/no-internal-modules import {takeScreenshot} from '../../actions/takeScreenshot'; +import {getFullPackConfig} from '../config'; import {getRunLabel} from '../environment'; -import {getFullPackConfig} from '../getFullPackConfig'; import {getRunLabelObject} from '../runLabel'; import {getScreenshotFileNames} from './getScreenshotFileNames'; diff --git a/src/utils/tests/runTests.ts b/src/utils/tests/runTests.ts index 90621b75..e1335558 100644 --- a/src/utils/tests/runTests.ts +++ b/src/utils/tests/runTests.ts @@ -3,6 +3,7 @@ import {join} from 'node:path'; import {ABSOLUTE_PATH_TO_PROJECT_ROOT_DIRECTORY, TESTCAFERC_PATH} from '../../constants/internal'; import {createTestCafe} from '../../testcafe'; +import {getFullPackConfig} from '../config'; import {setRunLabel} from '../environment'; import {E2edError} from '../error'; import { @@ -11,7 +12,6 @@ import { setSuccessfulTotalInPreviousRetries, writeLogsToFile, } from '../generalLog'; -import {getFullPackConfig} from '../getFullPackConfig'; import {getNotIncludedInPackTests} from '../notIncludedInPackTests'; import type {Inner} from 'testcafe-without-typecheck'; @@ -37,8 +37,8 @@ export const runTests = async ({ setSuccessfulTotalInPreviousRetries(successfulTotalInPreviousRetries); - const {browser} = getFullPackConfig(); - const browsers = [browser]; + const {browsers: browsersString} = getFullPackConfig(); + const browsers = [browsersString]; const notIncludedInPackTests = await getNotIncludedInPackTests(); const notIncludedInPackTestsInAbsolutePaths = notIncludedInPackTests.map((testFilePath) =>