Skip to content

Commit

Permalink
FI-1031 feat: add typed browser parameters to pack config
Browse files Browse the repository at this point in the history
  • Loading branch information
uid11 committed Dec 30, 2023
1 parent 5fd37e3 commit 30d23c5
Show file tree
Hide file tree
Showing 11 changed files with 177 additions and 33 deletions.
22 changes: 22 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand All @@ -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<TestMeta>) => boolean`: this function
filters tests (tasks) by their static options —
only those tests for which the function returned `true` get into the pack.
Expand Down Expand Up @@ -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.

Expand Down Expand Up @@ -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
Expand Down
17 changes: 14 additions & 3 deletions autotests/packs/allTests.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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',
Expand All @@ -54,6 +62,7 @@ export const pack: Pack = {
mapLogPayloadInLogFile,
mapLogPayloadInReport,
maxRetriesCountInDocker: 3,
overriddenUserAgent,
packTimeout: 90 * 60_000,
pageRequestTimeout: 30_000,
pageStabilizationInterval: 500,
Expand All @@ -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,
Expand Down
41 changes: 21 additions & 20 deletions src/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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`
Expand All @@ -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/getFullPackConfig`
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. ...
20 changes: 19 additions & 1 deletion src/testcaferc.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import {
SCREENSHOTS_DIRECTORY_PATH,
} from './constants/internal';
import {assertValueIsTrue} from './utils/asserts';
import {getTestCafeBrowsersString} from './utils/browser';
import {getPathToPack} from './utils/environment';
import {setCustomInspectOnFunction} from './utils/fn';

Expand Down Expand Up @@ -53,9 +54,22 @@ const frozenPartOfTestCafeConfig: FrozenPartOfTestCafeConfig = {
skipJsErrors: true,
};

assertValueIsTrue(
userlandPack.viewportHeight > 0 && Number.isInteger(userlandPack.viewportHeight),
'viewportHeight is positive integer',
{userlandPack},
);

assertValueIsTrue(
userlandPack.viewportWidth > 0 && Number.isInteger(userlandPack.viewportWidth),
'viewportWidth is positive integer',
{userlandPack},
);

const fullPackConfig: FullPackConfig = {
...userlandPack,
browsers: userlandPack.browser,
browsers: getTestCafeBrowsersString(userlandPack),
disableNativeAutomation: !userlandPack.enableChromeDevToolsProtocol,
src: userlandPack.testFileGlobs,
...frozenPartOfTestCafeConfig,
};
Expand All @@ -64,6 +78,8 @@ const {
doAfterPack,
doBeforePack,
filterTestsIntoPack,
mapBackendResponseErrorToLog,
mapBackendResponseToLog,
mapLogPayloadInConsole,
mapLogPayloadInLogFile,
mapLogPayloadInReport,
Expand All @@ -78,6 +94,8 @@ for (const fn of doBeforePack) {
}

setCustomInspectOnFunction(filterTestsIntoPack);
setCustomInspectOnFunction(mapBackendResponseErrorToLog);
setCustomInspectOnFunction(mapBackendResponseToLog);
setCustomInspectOnFunction(mapLogPayloadInConsole);
setCustomInspectOnFunction(mapLogPayloadInLogFile);
setCustomInspectOnFunction(mapLogPayloadInReport);
Expand Down
4 changes: 1 addition & 3 deletions src/types/config/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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.
Expand Down
48 changes: 48 additions & 0 deletions src/types/config/ownE2edConfig.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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.
*/
Expand All @@ -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.
Expand Down Expand Up @@ -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.
Expand Down Expand Up @@ -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.
*/
Expand Down
42 changes: 42 additions & 0 deletions src/utils/browser/getTestCafeBrowsersString.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
import {RunEnvironment, runEnvironment} from '../../configurator';

import type {UserlandPack} from '../../types/internal';

/**
* Get TestCafe `browsers` string, that is, value of `browsers` field in TestCafe config.
* @internal
*/
export const getTestCafeBrowsersString = (userlandPack: UserlandPack): string => {
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 isLocalRun = runEnvironment === RunEnvironment.Local;
const userAgent = isLocalRun
? `"${userlandPack.overriddenUserAgent}"`
: userlandPack.overriddenUserAgent;

parts.push(`userAgent=${userAgent}`);
}

const browserWithoutFlags = parts.join('');

return [browserWithoutFlags, ...userlandPack.browserFlags].join(' ');
};
Original file line number Diff line number Diff line change
@@ -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 => {
Expand Down
4 changes: 4 additions & 0 deletions src/utils/browser/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
/** @internal */
export {getTestCafeBrowsersString} from './getTestCafeBrowsersString';
/** @internal */
export {hasBrowsersArg} from './hasBrowsersArg';
4 changes: 2 additions & 2 deletions src/utils/pack/runPackWithArgs.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
import {EndE2edReason, TESTCAFERC_PATH} from '../../constants/internal';

import {assertValueIsDefined} from '../asserts';
import {hasBrowsersArg} from '../browser';
import {endE2ed} from '../end';
import {setRunLabel} from '../environment';
import {getFullPackConfig} from '../getFullPackConfig';
import {hasBrowsersArg} from '../hasBrowsersArg';
import {createRunLabel} from '../runLabel';

import {getPackTimeoutPromise} from './packTimeout';
Expand All @@ -19,7 +19,7 @@ export const runPackWithArgs = async (): Promise<void> => {

setRunLabel(runLabel);

if (browsers.length > 0 && hasBrowsersArg() === false) {
if (browsers.length > 0 && !hasBrowsersArg()) {
process.argv.splice(2, 0, String(browsers));
}

Expand Down
4 changes: 2 additions & 2 deletions src/utils/tests/runTests.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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) =>
Expand Down

0 comments on commit 30d23c5

Please sign in to comment.