Skip to content

Commit

Permalink
FI-1237 feat: run tests via Playwright
Browse files Browse the repository at this point in the history
  • Loading branch information
uid11 committed Jun 19, 2024
1 parent 63199a9 commit 6070e0b
Show file tree
Hide file tree
Showing 43 changed files with 472 additions and 5,309 deletions.
13 changes: 13 additions & 0 deletions autotests/tests/example.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import {test} from 'autotests';

import {expect} from '@playwright/test';

test('get started link', {meta: {testId: '19'}}, async ({page}) => {
await page.goto('https://playwright.dev/');

// Click the get started link.
await page.getByRole('link', {name: 'Get started'}).click();

// Expects page to have a heading with the name of Installation.
await expect(page.getByRole('heading', {name: 'Installation'})).toBeVisible();
});
5,125 changes: 186 additions & 4,939 deletions package-lock.json

Large diffs are not rendered by default.

6 changes: 3 additions & 3 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -24,12 +24,13 @@
"url": "git+https://github.com/joomcode/e2ed.git"
},
"dependencies": {
"@playwright/browser-chromium": "1.44.1",
"@playwright/test": "1.44.1",
"bin-v8-flags-filter": "1.2.0",
"create-locator": "0.0.23",
"get-modules-graph": "0.0.9",
"globby": "11.1.0",
"pngjs": "7.0.0",
"testcafe-without-typecheck": "3.5.0-rc.2"
"pngjs": "7.0.0"
},
"devDependencies": {
"@types/node": "20.12.12",
Expand All @@ -46,7 +47,6 @@
"eslint-plugin-typescript-sort-keys": "3.2.0",
"husky": "9.0.11",
"prettier": "3.2.5",
"testcafe": "3.5.0",
"typescript": "5.4.5"
},
"peerDependencies": {
Expand Down
5 changes: 3 additions & 2 deletions src/actions/mock/mockApiRoute.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
import {RequestMock} from 'testcafe-without-typecheck';

import {LogEventType} from '../../constants/internal';
import {getApiMockState} from '../../context/apiMockState';
import {getFullMocksState} from '../../context/fullMocks';
Expand All @@ -18,6 +16,9 @@ import type {
Response,
} from '../../types/internal';

// TODO: mocks
class RequestMock {}

/**
* Mock API for some API route.
* Applicable only for routes with the `getParamsFromUrl` method.
Expand Down
63 changes: 28 additions & 35 deletions src/testcaferc.ts → src/config.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/**
* @file Full pack configuration (extended TestCafe configuration) for running tests.
* @file Full pack configuration for running tests.
* Don't import this module. Instead, use `getFullPackConfig` from `utils/config`.
*/

Expand All @@ -12,13 +12,13 @@ import {
} 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';

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

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

const pathToPack = getPathToPack();
const tsExtension = '.ts';

Expand All @@ -37,34 +37,6 @@ 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}}},
disableMultipleWindows: true,
hostname: 'localhost',
pageLoadTimeout: 0,
reporter: [{name: 'for-e2ed'}],
retryTestPages: true,
screenshots: {
path: SCREENSHOTS_DIRECTORY_PATH,
// eslint-disable-next-line no-template-curly-in-string
pathPattern: '${DATE}_${TIME}_${BROWSER}_${BROWSER_VERSION}/${TEST}/${FILE_INDEX}.png',
takeOnFails: false,
thumbnails: false,
},
skipJsErrors: true,
};

const fullPackConfig: FullPackConfig = {
...userlandPack,
browsers: getTestCafeBrowsersString(userlandPack),
disableNativeAutomation: !userlandPack.enableChromeDevToolsProtocol,
src: userlandPack.testFileGlobs,
...frozenPartOfTestCafeConfig,
};

const {
doAfterPack,
doBeforePack,
Expand All @@ -74,7 +46,7 @@ const {
mapLogPayloadInConsole,
mapLogPayloadInLogFile,
mapLogPayloadInReport,
} = fullPackConfig;
} = userlandPack;

for (const fn of doAfterPack) {
setCustomInspectOnFunction(fn);
Expand All @@ -91,7 +63,28 @@ setCustomInspectOnFunction(mapLogPayloadInConsole);
setCustomInspectOnFunction(mapLogPayloadInLogFile);
setCustomInspectOnFunction(mapLogPayloadInReport);

Object.assign(exports, fullPackConfig);
const config = defineConfig({
fullyParallel: true,

projects: [
{
name: 'chromium',
use: {...devices['Desktop Chrome']},
},
],

retries: 1,

testDir: '../../autotests/tests',
testMatch: 'example.spec.ts',

use: {
trace: 'on-first-retry',
},

workers: 2,
});

Object.assign(config, userlandPack);

// eslint-disable-next-line import/no-unused-modules
export {fullPackConfig};
export default config;
2 changes: 1 addition & 1 deletion src/constants/internal.ts
Original file line number Diff line number Diff line change
Expand Up @@ -40,13 +40,13 @@ export {
ABSOLUTE_PATH_TO_PROJECT_ROOT_DIRECTORY,
AUTOTESTS_DIRECTORY_PATH,
COMPILED_USERLAND_CONFIG_DIRECTORY,
CONFIG_PATH,
DOT_ENV_PATH,
EVENTS_DIRECTORY_PATH,
INSTALLED_E2ED_DIRECTORY_PATH,
REPORTS_DIRECTORY_PATH,
SCREENSHOTS_DIRECTORY_PATH,
START_INFO_PATH,
TESTCAFERC_PATH,
TMP_DIRECTORY_PATH,
} from './paths';
/** @internal */
Expand Down
17 changes: 7 additions & 10 deletions src/constants/paths.ts
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,13 @@ export const COMPILED_USERLAND_CONFIG_DIRECTORY = join(
'config',
) as DirectoryPathFromRoot;

/**
* Relative (from root) path to `config` file,
* that plays the role of the internal Playwright config.
* @internal
*/
export const CONFIG_PATH = join(INSTALLED_E2ED_DIRECTORY_PATH, 'config.js') as FilePathFromRoot;

/**
* Relative (from root) path to events directory.
* @internal
Expand All @@ -86,13 +93,3 @@ export const SCREENSHOTS_DIRECTORY_PATH = join(
* @internal
*/
export const START_INFO_PATH = join(TMP_DIRECTORY_PATH, 'startInfo.json') as FilePathFromRoot;

/**
* Relative (from root) path to `testcaferc` file,
* that plays the role of the internal TestCafe config.
* @internal
*/
export const TESTCAFERC_PATH = join(
INSTALLED_E2ED_DIRECTORY_PATH,
'testcaferc.js',
) as FilePathFromRoot;
8 changes: 5 additions & 3 deletions src/context/apiMockState.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@ import {useContext} from '../useContext';

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

import type {Page} from '@playwright/test';

/**
* Raw get and set internal (maybe `undefined`) API mock state.
* @internal
Expand All @@ -12,8 +14,8 @@ const [getRawApiMockState, setRawApiMockState] = useContext<ApiMockState>();
* Get internal always defined API mock state (for `mockApiRoute`).
* @internal
*/
export const getApiMockState = (): ApiMockState => {
const maybeApiMockState = getRawApiMockState();
export const getApiMockState = (page: Page): ApiMockState => {
const maybeApiMockState = getRawApiMockState(page);

if (maybeApiMockState !== undefined) {
return maybeApiMockState;
Expand All @@ -26,7 +28,7 @@ export const getApiMockState = (): ApiMockState => {
optionsWithRouteByUrl: Object.create(null) as {},
};

setRawApiMockState(apiMockState);
setRawApiMockState(page, apiMockState);

return apiMockState;
};
6 changes: 3 additions & 3 deletions src/context/cdpClient.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,10 +19,10 @@ export {getCdpClient};
* Set test `cdpClient` (can only be called once).
* @internal
*/
export const setCdpClient: typeof setRawCdpClient = (cdpClient) => {
const currentCdpClient = getCdpClient();
export const setCdpClient: typeof setRawCdpClient = (page, cdpClient) => {
const currentCdpClient = getCdpClient(page);

assertValueIsUndefined(currentCdpClient, 'currentCdpClient is not defined', {cdpClient});

return setRawCdpClient(cdpClient);
return setRawCdpClient(page, cdpClient);
};
6 changes: 3 additions & 3 deletions src/context/fullMocks.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,12 +19,12 @@ export {getFullMocksState};
* Set state of full mocks (can only be called once).
* @internal
*/
export const setFullMocksState: typeof setRawFullMocksState = (fullMocksState) => {
const currentFullMocksState = getFullMocksState();
export const setFullMocksState: typeof setRawFullMocksState = (page, fullMocksState) => {
const currentFullMocksState = getFullMocksState(page);

assertValueIsUndefined(currentFullMocksState, 'currentFullMocksState is not defined', {
fullMocksState,
});

return setRawFullMocksState(fullMocksState);
return setRawFullMocksState(page, fullMocksState);
};
12 changes: 7 additions & 5 deletions src/context/meta.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@ import {assertValueIsDefined, assertValueIsUndefined} from '../utils/asserts';

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

import type {Page} from '@playwright/test';

/**
* Raw get and set test metadata functions.
*/
Expand All @@ -11,8 +13,8 @@ const [getRawMeta, setRawMeta] = useContext<TestMetaPlaceholder>();
/**
* Get test metadata.
*/
export const getMeta = <TestMeta>(): TestMeta => {
const meta = getRawMeta() as TestMeta;
export const getMeta = <TestMeta>(page: Page): TestMeta => {
const meta = getRawMeta(page) as TestMeta;

assertValueIsDefined(meta, 'meta is defined');

Expand All @@ -23,10 +25,10 @@ export const getMeta = <TestMeta>(): TestMeta => {
* Set test metadata.
* @internal
*/
export const setMeta: typeof setRawMeta = (meta) => {
const currentMeta = getRawMeta();
export const setMeta: typeof setRawMeta = (page, meta) => {
const currentMeta = getRawMeta(page);

assertValueIsUndefined(currentMeta, 'currentMeta is not defined', {meta});

setRawMeta(meta);
setRawMeta(page, meta);
};
12 changes: 7 additions & 5 deletions src/context/runId.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@ import {assertValueIsDefined, assertValueIsUndefined} from '../utils/asserts';

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

import type {Page} from '@playwright/test';

/**
* Raw versions of `getRunId` and `setRunId`.
* @internal
Expand All @@ -13,8 +15,8 @@ const [getRawRunId, setRawRunId] = useContext<RunId>();
* Get test `runId`.
* @internal
*/
export const getRunId = (): RunId => {
const runId = getRawRunId();
export const getRunId = (page: Page): RunId => {
const runId = getRawRunId(page);

assertValueIsDefined(runId, 'runId is defined');

Expand All @@ -25,10 +27,10 @@ export const getRunId = (): RunId => {
* Set test `runId` (can only be called once).
* @internal
*/
export const setRunId: typeof setRawRunId = (runId) => {
const currentRunId = getRawRunId();
export const setRunId: typeof setRawRunId = (page, runId) => {
const currentRunId = getRawRunId(page);

assertValueIsUndefined(currentRunId, 'currentRunId is not defined', {runId});

return setRawRunId(runId);
return setRawRunId(page, runId);
};
12 changes: 7 additions & 5 deletions src/context/testIdleTimeout.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
import {useContext} from '../useContext';
import {assertValueIsDefined, assertValueIsUndefined} from '../utils/asserts';

import type {Page} from '@playwright/test';

/**
* Raw versions of `getTestIdleTimeout` and `setTestIdleTimeout`.
* @internal
Expand All @@ -11,8 +13,8 @@ const [getRawTestIdleTimeout, setRawTestIdleTimeout] = useContext<number>();
* Get test timeout in ms.
* @internal
*/
export const getTestIdleTimeout = (): number => {
const testIdleTimeout = getRawTestIdleTimeout();
export const getTestIdleTimeout = (page: Page): number => {
const testIdleTimeout = getRawTestIdleTimeout(page);

assertValueIsDefined(testIdleTimeout, 'testIdleTimeout is defined');

Expand All @@ -23,12 +25,12 @@ export const getTestIdleTimeout = (): number => {
* Set test timeout in ms (can only be called once).
* @internal
*/
export const setTestIdleTimeout: typeof setRawTestIdleTimeout = (testIdleTimeout) => {
const currentTestIdleTimeout = getRawTestIdleTimeout();
export const setTestIdleTimeout: typeof setRawTestIdleTimeout = (page, testIdleTimeout) => {
const currentTestIdleTimeout = getRawTestIdleTimeout(page);

assertValueIsUndefined(currentTestIdleTimeout, 'currentTestIdleTimeout is not defined', {
testIdleTimeout,
});

return setRawTestIdleTimeout(testIdleTimeout);
return setRawTestIdleTimeout(page, testIdleTimeout);
};
12 changes: 7 additions & 5 deletions src/context/testRunPromise.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
import {useContext} from '../useContext';
import {assertValueIsDefined, assertValueIsUndefined} from '../utils/asserts';

import type {Page} from '@playwright/test';

/**
* Raw versions of `getTestRunPromise` and `setTestRunPromise`.
* @internal
Expand All @@ -11,8 +13,8 @@ const [getRawTestRunPromise, setRawTestRunPromise] = useContext<Promise<undefine
* Get test run promise (it resolves when the test completes).
* @internal
*/
export const getTestRunPromise = (): Promise<undefined> => {
const testRunPromise = getRawTestRunPromise();
export const getTestRunPromise = (page: Page): Promise<undefined> => {
const testRunPromise = getRawTestRunPromise(page);

assertValueIsDefined(testRunPromise, 'testRunPromise is defined');

Expand All @@ -23,12 +25,12 @@ export const getTestRunPromise = (): Promise<undefined> => {
* Set test timeout in ms (can only be called once).
* @internal
*/
export const setTestRunPromise: typeof setRawTestRunPromise = (testRunPromise) => {
const currentTestRunPromise = getRawTestRunPromise();
export const setTestRunPromise: typeof setRawTestRunPromise = (page, testRunPromise) => {
const currentTestRunPromise = getRawTestRunPromise(page);

assertValueIsUndefined(currentTestRunPromise, 'currentTestRunPromise is not defined', {
testRunPromise,
});

return setRawTestRunPromise(testRunPromise);
return setRawTestRunPromise(page, testRunPromise);
};
Loading

0 comments on commit 6070e0b

Please sign in to comment.