Skip to content

Commit

Permalink
FI-1345 refactor: Playwright config (use object)
Browse files Browse the repository at this point in the history
refactor: reexport `devices` from `@playwright/test`
fix: add Playwright's browser version check
fix: add using Playwright version from `package.json`
feat: support UI mode flag
feat: add internal `isUiMode` flag
fix: remove text style from run errors in HTML report
feat: add `enableCsp` flag to pack config and to test options
fix: ingore timeouts in UI mode
fix: mix up of JS errors and browser console logs from other test runs
chore: update `@types/node` to 22.5.1
  • Loading branch information
uid11 committed Aug 31, 2024
1 parent 25b6186 commit 0370c99
Show file tree
Hide file tree
Showing 38 changed files with 234 additions and 85 deletions.
4 changes: 3 additions & 1 deletion Dockerfile
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
FROM mcr.microsoft.com/playwright:v1.46.1-noble
ARG PLAYWRIGHT_VERSION

FROM mcr.microsoft.com/playwright:v${PLAYWRIGHT_VERSION}-noble

COPY ./build/node_modules/e2ed /node_modules/e2ed

Expand Down
6 changes: 4 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -256,9 +256,9 @@ The functions accept a start info object, and can return new full pack config,
which in this case will be included in the start info object, and will be used for running pack.
Each function can thus access the results of the previous function.

`enableHeadlessMode: boolean`: enables headless mode (if browser supports such mode).
`enableCsp: boolean`: enables Content-Security-Policy checks in browser.

`enableLiveMode: boolean`: enables live mode for test development (only for locally running).
`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).

Expand All @@ -271,6 +271,8 @@ only those tests for which the function returned `true` get into the pack.

`fullMocks: FullMocks | null`: functions that specify the "full mocks" functionality.

`getTestNamePrefixInUiMode: (testOptions: TestOptions<TestMeta>) => string`: get prefix for test name in UI mode by test options.

`liteReportFileName: string | null`: the name of the file under which, after running the tests,
the lite JSON report will be saved in the `autotests/reports` directory, for example, `lite-report.json`.
If `null`, the lite report will not be saved.
Expand Down
3 changes: 2 additions & 1 deletion autotests/packs/allTests.ts
Original file line number Diff line number Diff line change
Expand Up @@ -49,12 +49,13 @@ export const pack: Pack = {
deviceScaleFactor: 1,
doAfterPack,
doBeforePack,
enableCsp: true,
enableHeadlessMode: true,
enableLiveMode: false,
enableMobileDeviceMode: false,
enableTouchEventEmulation: false,
filterTestsIntoPack,
fullMocks,
getTestNamePrefixInUiMode: (testOptions) => testOptions.meta.testId,
liteReportFileName: 'lite-report.json',
logFileName: 'pack-logs.log',
mapBackendResponseErrorToLog,
Expand Down
18 changes: 11 additions & 7 deletions autotests/tests/e2edReportExample/browserData.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,6 @@ import {
test('correctly read data from browser', {meta: {testId: '14'}}, async () => {
await navigateToPage(E2edReportExample);

await waitForInterfaceStabilization(100);

await createClientFunction(() => {
console.error('error');
console.info('info');
Expand All @@ -23,10 +21,7 @@ test('correctly read data from browser', {meta: {testId: '14'}}, async () => {

setTimeout(() => {
throw new Error('foo');
}, 8);
setTimeout(() => {
throw new Error('bar');
}, 32);
}, 100);
})();

const consoleMessages = getBrowserConsoleMessages();
Expand All @@ -47,5 +42,14 @@ test('correctly read data from browser', {meta: {testId: '14'}}, async () => {

const jsErrors = getBrowserJsErrors();

await expect(jsErrors.length === 0, 'getBrowserJsErrors read JS errors').eql(true);
await expect(
jsErrors.length,
'getBrowserJsErrors read zero JS errors when there are no errors',
).eql(0);

await waitForInterfaceStabilization(100);

await expect(jsErrors.length, 'getBrowserJsErrors read all JS errors').eql(1);

await expect(String(jsErrors[0]), 'getBrowserJsErrors read all JS errors').contains('foo');
});
2 changes: 1 addition & 1 deletion autotests/tests/e2edReportExample/fullMocks.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import {mockApiRoute, navigateToPage, unmockApiRoute} from 'e2ed/actions';
import type {DeviceId, Product, ProductId} from 'autotests/types';
import type {Url} from 'e2ed/types';

test('full mocks works correctly', {meta: {testId: '18'}}, async () => {
test('full mocks works correctly', {enableCsp: false, meta: {testId: '18'}}, async () => {
await navigateToPage(E2edReportExample);

await mockApiRoute(CreateProductRoute, (routeParams, {method, query, requestBody, url}) => {
Expand Down
4 changes: 2 additions & 2 deletions bin/addPackageJsonToBuildDocker.sh
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
#!/usr/bin/env sh
set -eu

VERSION=`./bin/getVersion.sh`
E2ED_VERSION=`./bin/getE2edVersion.sh`

echo "{\n \"dependencies\": {\n \"e2ed\": \"$VERSION\"\n }\n}" > ./build/docker/package.json
echo "{\n \"dependencies\": {\n \"e2ed\": \"$E2ED_VERSION\"\n }\n}" > ./build/docker/package.json
8 changes: 6 additions & 2 deletions bin/buildDocker.sh
Original file line number Diff line number Diff line change
@@ -1,6 +1,10 @@
#!/usr/bin/env sh
set -eu

VERSION=`./bin/getVersion.sh`
E2ED_VERSION=`./bin/getE2edVersion.sh`
PLAYWRIGHT_VERSION=`./bin/getPlaywrightVersion.sh`

docker build --tag e2edhub/e2ed:$VERSION --tag e2edhub/e2ed:latest .
docker build \
--build-arg PLAYWRIGHT_VERSION=$PLAYWRIGHT_VERSION \
--tag e2edhub/e2ed:$E2ED_VERSION \
--tag e2edhub/e2ed:latest .
6 changes: 6 additions & 0 deletions bin/checkPlaywrightBrowserChromiumVersion.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
#!/usr/bin/env sh
set -eu

PLAYWRIGHT_VERSION=`./bin/getPlaywrightVersion.sh`

grep "\"@playwright/browser-chromium\": \"$PLAYWRIGHT_VERSION\"" ./package.json
File renamed without changes.
4 changes: 4 additions & 0 deletions bin/getPlaywrightVersion.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
#!/usr/bin/env sh
set -eu

grep -m1 @playwright/test ./package.json | cut -d '"' -f 4
8 changes: 4 additions & 4 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

5 changes: 3 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@
},
"devDependencies": {
"@playwright/browser-chromium": "1.46.1",
"@types/node": "22.5.0",
"@types/node": "22.5.1",
"@typescript-eslint/eslint-plugin": "7.18.0",
"@typescript-eslint/parser": "7.18.0",
"assert-modules-support-case-insensitive-fs": "1.0.1",
Expand Down Expand Up @@ -67,8 +67,9 @@
"scripts": {
"asserts": "assert-modules-support-case-insensitive-fs ./autotests ./src && assert-package-lock-is-consistent",
"precheck:all": "npm run asserts && npm run clear:lint:cache && npm run build",
"check:all": "npm audit && npm run parallel check:branch lint test",
"check:all": "npm audit && npm run parallel check:branch check:playwright-version lint test",
"check:branch": "node ./build/checkBranch.js",
"check:playwright-version": "./bin/checkPlaywrightBrowserChromiumVersion.sh",
"clear:lint:cache": "rm -f ./build/tsconfig.tsbuildinfo ./node_modules/.cache/lint-*",
"lint": "npm run parallel lint:es lint:prettier lint:types",
"lint:es": "eslint --cache --cache-location=./node_modules/.cache/lint-es --cache-strategy=content --ext=.ts --max-warnings=0 --report-unused-disable-directives .",
Expand Down
9 changes: 9 additions & 0 deletions src/bin/runE2edInLocalEnvironment.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,18 @@ import {setPathToPack} from '../utils/environment';
import {registerEndE2edRunEvent, registerStartE2edRunEvent} from '../utils/events';
import {logStartE2edError} from '../utils/generalLog';
import {runPackWithArgs} from '../utils/pack';
import {setUiMode} from '../utils/uiMode';

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

const uiFlagIndex = process.argv.indexOf('--ui');

if (uiFlagIndex !== -1) {
setUiMode();

process.argv.splice(uiFlagIndex, 1);
}

const [pathToPack] = process.argv.splice(2, 1);

assertValueIsDefined(pathToPack, 'pathToPack is defined', {argv: process.argv});
Expand Down
63 changes: 22 additions & 41 deletions src/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,11 +21,12 @@ import {assertUserlandPack} from './utils/config/assertUserlandPack';
import {getPathToPack} from './utils/environment';
import {setCustomInspectOnFunction} from './utils/fn';
import {setReadonlyProperty} from './utils/setReadonlyProperty';
import {isUiMode} from './utils/uiMode';
import {isLocalRun} from './configurator';

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

import {defineConfig} from '@playwright/test';
import {defineConfig, type PlaywrightTestConfig} from '@playwright/test';

const maxTimeoutInMs = 3600_000;

Expand Down Expand Up @@ -85,12 +86,29 @@ setCustomInspectOnFunction(mapLogPayloadInConsole);
setCustomInspectOnFunction(mapLogPayloadInLogFile);
setCustomInspectOnFunction(mapLogPayloadInReport);

if (isDebug) {
if (isDebug || isUiMode) {
setReadonlyProperty(userlandPack, 'packTimeout', maxTimeoutInMs);
setReadonlyProperty(userlandPack, 'testIdleTimeout', maxTimeoutInMs);
setReadonlyProperty(userlandPack, 'testTimeout', maxTimeoutInMs);
}

const useOptions: PlaywrightTestConfig['use'] = {
actionTimeout: userlandPack.testIdleTimeout,
browserName: userlandPack.browserName,
// eslint-disable-next-line @typescript-eslint/naming-convention
bypassCSP: !userlandPack.enableCsp,
deviceScaleFactor: userlandPack.deviceScaleFactor,
hasTouch: userlandPack.enableTouchEventEmulation,
headless: isLocalRun ? userlandPack.enableHeadlessMode : true,
isMobile: userlandPack.enableMobileDeviceMode,
launchOptions: {args: [...userlandPack.browserFlags]},
navigationTimeout: userlandPack.pageRequestTimeout,
trace: 'retain-on-failure',
userAgent: userlandPack.userAgent,
viewport: {height: userlandPack.viewportHeight, width: userlandPack.viewportWidth},
...userlandPack.overriddenConfigFields?.use,
};

const playwrightConfig = defineConfig({
expect: {timeout: userlandPack.assertionTimeout},

Expand All @@ -100,19 +118,7 @@ const playwrightConfig = defineConfig({

outputDir: join(relativePathFromInstalledE2edToRoot, INTERNAL_REPORTS_DIRECTORY_PATH),

projects: [
{
name: userlandPack.browserName,
use: {
browserName: userlandPack.browserName,
deviceScaleFactor: userlandPack.deviceScaleFactor,
hasTouch: userlandPack.enableTouchEventEmulation,
isMobile: userlandPack.enableMobileDeviceMode,
userAgent: userlandPack.userAgent,
viewport: {height: userlandPack.viewportHeight, width: userlandPack.viewportWidth},
},
},
],
projects: [{name: userlandPack.browserName, use: useOptions}],

retries: isLocalRun ? 0 : userlandPack.maxRetriesCountInDocker - 1,

Expand All @@ -126,32 +132,7 @@ const playwrightConfig = defineConfig({

...userlandPack.overriddenConfigFields,

use: {
actionTimeout: userlandPack.testIdleTimeout,

browserName: userlandPack.browserName,

// eslint-disable-next-line @typescript-eslint/naming-convention
bypassCSP: true,

deviceScaleFactor: userlandPack.deviceScaleFactor,

hasTouch: userlandPack.enableTouchEventEmulation,

headless: isLocalRun ? userlandPack.enableHeadlessMode : true,

isMobile: userlandPack.enableMobileDeviceMode,

navigationTimeout: userlandPack.pageRequestTimeout,

trace: 'retain-on-failure',

userAgent: userlandPack.userAgent,

viewport: {height: userlandPack.viewportHeight, width: userlandPack.viewportWidth},

...userlandPack.overriddenConfigFields?.use,
},
use: useOptions,
});

const config: FullPackConfig = Object.assign(playwrightConfig, userlandPack);
Expand Down
8 changes: 7 additions & 1 deletion src/constants/environment.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import type {E2edEnvironment} from '../types/internal';
export const e2edEnvironment = process.env as E2edEnvironment;

/**
* `true` if e2ed run in debug mode, `false` otherwise.
* `true` if e2ed run in debug mode, and `false` otherwise.
*/
export const isDebug = Boolean(e2edEnvironment.E2ED_DEBUG);

Expand Down Expand Up @@ -48,3 +48,9 @@ export const RUN_LABEL_VARIABLE_NAME = '__INTERNAL_E2ED_RUN_LABEL';
* @internal
*/
export const START_TIME_IN_MS_VARIABLE_NAME = '__INTERNAL_E2ED_START_TIME_IN_MS';

/**
* Name of e2ed environment variable for UI-mode flag.
* @internal
*/
export const UI_MODE_VARIABLE_NAME = '__INTERNAL_E2ED_UI_MODE';
1 change: 1 addition & 0 deletions src/constants/internal.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ export {
RUN_ENVIRONMENT_VARIABLE_NAME,
RUN_LABEL_VARIABLE_NAME,
START_TIME_IN_MS_VARIABLE_NAME,
UI_MODE_VARIABLE_NAME,
} from './environment';
export {READ_FILE_OPTIONS} from './fs';
/** @internal */
Expand Down
21 changes: 20 additions & 1 deletion src/context/consoleMessages.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,27 @@ import {useContext} from '../useContext';

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

/**
* Raw get and set browser console messages array.
* @internal
*/
const [getRawConsoleMessagesFromContext, setRawConsoleMessagesFromContext] =
useContext<readonly ConsoleMessage[]>();

/**
* Get browser console messages array.
* @internal
*/
export const [getConsoleMessagesFromContext] = useContext<readonly ConsoleMessage[]>([]);
export const getConsoleMessagesFromContext = (): readonly ConsoleMessage[] => {
const maybeConsoleMessages = getRawConsoleMessagesFromContext();

if (maybeConsoleMessages !== undefined) {
return maybeConsoleMessages;
}

const consoleMessages: readonly ConsoleMessage[] = [];

setRawConsoleMessagesFromContext(consoleMessages);

return consoleMessages;
};
20 changes: 19 additions & 1 deletion src/context/jsError.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,25 @@
import {useContext} from '../useContext';

/**
* Raw get and set browser JS errors array.
* @internal
*/
const [getRawJsErrorsFromContext, setRawJsErrorsFromContext] = useContext<readonly Error[]>();

/**
* Get browser JS errors array.
* @internal
*/
export const [getJsErrorsFromContext] = useContext<readonly Error[]>([]);
export const getJsErrorsFromContext = (): readonly Error[] => {
const maybeJsErrors = getRawJsErrorsFromContext();

if (maybeJsErrors !== undefined) {
return maybeJsErrors;
}

const jsErrors: readonly Error[] = [];

setRawJsErrorsFromContext(jsErrors);

return jsErrors;
};
20 changes: 19 additions & 1 deletion src/context/onResponseCallbacks.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,26 @@ import type {ResponseWithRequest} from '../types/internal';

type Callback = (this: void, response: ResponseWithRequest) => void;

/**
* Raw get and set `onResponseCallbacks` list.
* @internal
*/
const [getRawOnResponseCallbacks, setRawOnResponseCallbacks] = useContext<Callback[]>();

/**
* Get `onResponseCallbacks` list.
* @internal
*/
export const [getOnResponseCallbacks] = useContext<Callback[]>([]);
export const getOnResponseCallbacks = (): Callback[] => {
const maybeCallbacks = getRawOnResponseCallbacks();

if (maybeCallbacks !== undefined) {
return maybeCallbacks;
}

const callbacks: Callback[] = [];

setRawOnResponseCallbacks(callbacks);

return callbacks;
};
Loading

0 comments on commit 0370c99

Please sign in to comment.