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 d529ab3
Show file tree
Hide file tree
Showing 31 changed files with 373 additions and 5,250 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;
9 changes: 4 additions & 5 deletions src/test.ts
Original file line number Diff line number Diff line change
@@ -1,16 +1,15 @@
import {getRunTest, safeJsError} from './utils/test';
import {fixture, test as testcafeTest} from './testcafe';
import {getRunTest} from './utils/test';

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

import {test as playwrightTest} from '@playwright/test';

/**
* Creates test with name, metatags, options and test function.
* @internal
*/
export const test: TestFunction = (name, options, testFn) => {
fixture(' - e2ed - ').skipJsErrors(safeJsError);

const runTest = getRunTest({name, options, testFn});

testcafeTest(name, runTest);
playwrightTest(name, runTest);
};
33 changes: 1 addition & 32 deletions src/testController.ts
Original file line number Diff line number Diff line change
@@ -1,32 +1 @@
import {t as originalTestController} from 'testcafe-without-typecheck';

import {E2edError} from './utils/error';

import type {TestController, Values} from './types/internal';

/**
* Proxy handler for wrapping all tries to get TestController properties.
*/
const get: ProxyHandler<TestController>['get'] = (
target,
property,
receiver,
): Values<TestController> => {
try {
const result = Reflect.get(target, property, receiver) as Values<TestController>;

return result;
} catch (cause) {
throw new E2edError(
`Caught an error on getting property "${String(property)}" of testController`,
{cause},
);
}
};

/**
* TestController from TestCafe with wrapping of all thrown errors.
*/
export const testController: typeof originalTestController = new Proxy(originalTestController, {
get,
});
export const testController = {};
22 changes: 11 additions & 11 deletions src/testcafe.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
import type {fixture as testCafeFixture, test as testCafeTest} from 'testcafe-without-typecheck';

declare const global: Readonly<{fixture?: unknown; test?: unknown}>;

Object.defineProperty(exports, 'fixture', {
Expand All @@ -14,13 +12,15 @@ Object.defineProperty(exports, 'test', {
},
});

export {
default as createTestCafe,
RequestHook,
RequestLogger,
RequestMock,
Selector,
} from 'testcafe-without-typecheck';
export const createTestCafe = () => {};

export const RequestHook = function () {};

Check warning on line 17 in src/testcafe.ts

View workflow job for this annotation

GitHub Actions / lint

Unexpected unnamed function

export const RequestLogger = function () {};

Check warning on line 19 in src/testcafe.ts

View workflow job for this annotation

GitHub Actions / lint

Unexpected unnamed function

export const RequestMock = function () {};

Check warning on line 21 in src/testcafe.ts

View workflow job for this annotation

GitHub Actions / lint

Unexpected unnamed function

export const Selector = () => {};

export declare const fixture: typeof testCafeFixture;
export declare const test: typeof testCafeTest;
export declare const fixture: object;
export declare const test: object;
27 changes: 27 additions & 0 deletions src/types/global.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,33 @@ declare module 'testcafe-hammerhead-up/lib/request-pipeline/request-hooks/events
export default RequestHookEventFactory;
}

/**
* Temporary TestCafe inner types.
*/
declare module 'testcafe-without-typecheck' {
export namespace Inner {
export type Assertion = object;
export type CookieOptions = object;
export type OffsetOptions = object;
export type RequestHook = object;
export type RequestLogger = object;
export type RequestMock = object;
export type RequestOptions = object;
export type ResponseMock = object;
export type ScrollPosition = object;
export type Selector = object;
export type SelectorAPI = object;
export type SelectorFactory = object;
export type TestCafe = object;
}

export const RequestHook: object;
export const RequestLogger: object;
export const RequestMock: object;

export const Selector: object;
}

/**
* Internal TestCafe module with request-hooks frame navigated events factory class for native automation.
* @internal
Expand Down
2 changes: 0 additions & 2 deletions src/types/startInfo.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,5 @@ export type StartInfo<FullPackConfigArg = FullPackConfig> = Readonly<{
pwd: string | undefined;
runEnvironment: RunEnvironment;
startTimeInMs: UtcTimeInMs;
testCafeHammerheadUp: PackageInfo;
testCafeWithoutTypeCheck: PackageInfo;
totalSystemMemoryInMb: number;
}>;
4 changes: 3 additions & 1 deletion src/types/testRun.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@ import type {TestFilePath} from './paths';
import type {StringForLogs} from './string';
import type {TestMetaPlaceholder} from './userland';

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

/**
* Reject test run.
*/
Expand All @@ -33,7 +35,7 @@ export type RunId = Brand<string, 'RunId'>;
/**
* Test function itself.
*/
export type TestFn = () => Promise<void>;
export type TestFn = (testController: PlaywrightTestArgs) => Promise<void>;

/**
* Test options with userland metadata.
Expand Down
21 changes: 3 additions & 18 deletions src/utils/clientFunction/getClientFunctionWithTimeout.ts
Original file line number Diff line number Diff line change
@@ -1,21 +1,14 @@
import {ClientFunction as TestCafeClientFunction} from 'testcafe-without-typecheck';

import {getTestIdleTimeout} from '../../context/testIdleTimeout';

import {E2edError} from '../error';
import {getDurationWithUnits} from '../getDurationWithUnits';
import {getPromiseWithResolveAndReject} from '../promise';
import {wrapInTestRunTracker} from '../testRun';

import {clientFunctionWrapper} from './clientFunctionWrapper';
import {getPrintedClientFunctionName} from './getPrintedClientFunctionName';
import {getRunClientFunction} from './getRunClientFunction';

import type {
ClientFunction,
ClientFunctionState,
ClientFunctionWrapperResult,
} from '../../types/internal';
import type {ClientFunction, ClientFunctionState} from '../../types/internal';

/**
* Get client function with timeout (wrapped into timeout) and error logging.
Expand All @@ -29,17 +22,9 @@ export const getClientFunctionWithTimeout = <Args extends readonly unknown[], Re

const clientFunctionWithTimeout = (...args: Args): Promise<Result> => {
if (clientFunctionState.clientFunction === undefined) {
// TODO: client Function
// eslint-disable-next-line no-param-reassign
clientFunctionState.clientFunction = TestCafeClientFunction<
ClientFunctionWrapperResult<Awaited<Result>>,
// @ts-expect-error; readonly unknown[] cannot be assigned to any[]
Args
>(
clientFunctionWrapper as unknown as (
...args: Args
) => ClientFunctionWrapperResult<Awaited<Result>>,
{dependencies: {originalFn, printedClientFunctionName}},
);
clientFunctionState.clientFunction = (args) => originalFn(...args);
}

const clientFunctionTimeout = timeout ?? getTestIdleTimeout();
Expand Down
2 changes: 1 addition & 1 deletion src/utils/config/getFullPackConfig.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ export const getFullPackConfig = <
>(): FullPackConfig<CustomPackProperties, CustomReportProperties, SkipTests, TestMeta> => {
if (updatedConfig === undefined) {
// eslint-disable-next-line global-require, @typescript-eslint/no-var-requires
const {fullPackConfig} = require<typeof import('../../testcaferc')>('../../testcaferc');
const fullPackConfig = require<typeof import('../../config')>('../../config').default;

updatedConfig = fullPackConfig;

Expand Down
Loading

0 comments on commit d529ab3

Please sign in to comment.