diff --git a/Dockerfile b/Dockerfile index 15608d38..9eee12e9 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,4 +1,4 @@ -FROM mcr.microsoft.com/playwright:v1.45.1-noble +FROM mcr.microsoft.com/playwright:v1.46.0-noble COPY ./build/node_modules/e2ed /node_modules/e2ed diff --git a/README.md b/README.md index bc9b756a..986b7bf0 100644 --- a/README.md +++ b/README.md @@ -255,10 +255,6 @@ 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. -`enableChromeDevToolsProtocol: boolean`: enables [Chrome DevTools Protocol](https://chromedevtools.github.io/devtools-protocol/) -for browser control in tests (instead of `testcafe-hammerhead`). -In CDP mode, tests disable Service Workers (if they are used on the page). - `enableHeadlessMode: boolean`: enables headless mode (if browser supports such mode). `enableLiveMode: boolean`: enables live mode for test development (only for locally running). @@ -319,8 +315,10 @@ 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. +`overriddenConfigFields: PlaywrightTestConfig | null`: if not `null`, then this value will override +fields of internal Playwright config. + `overriddenUserAgent: string | null`: if not `null`, then this value will override the browser's user agent in tests. -This override only works when `enableChromeDevToolsProtocol` is `true`. `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. diff --git a/autotests/actions/setPageCookiesAndNavigateToUrl.ts b/autotests/actions/setPageCookiesAndNavigateToUrl.ts index b7fe7ba8..cb6df489 100644 --- a/autotests/actions/setPageCookiesAndNavigateToUrl.ts +++ b/autotests/actions/setPageCookiesAndNavigateToUrl.ts @@ -12,12 +12,12 @@ export const setPageCookiesAndNavigateToUrl = async ( pageCookies: readonly Cookie[], ): Promise => { const mapResponseHeaders = (headers: StringHeaders): StringHeaders => { - let cookiesArray: SetCookieHeaderString[] = []; const setCookies = getHeaderValue(headers, 'set-cookie'); - if (setCookies !== undefined) { - cookiesArray.push(setCookies as SetCookieHeaderString); - } + let cookiesArray = (setCookies ?? '') + .split('\n') + .map((line) => line.trim()) + .filter(Boolean) as SetCookieHeaderString[]; for (const cookie of pageCookies) { cookiesArray = replaceSetCookie(cookiesArray, cookie); diff --git a/autotests/packs/allTests.ts b/autotests/packs/allTests.ts index 2fcd3f42..a26c7d76 100644 --- a/autotests/packs/allTests.ts +++ b/autotests/packs/allTests.ts @@ -51,7 +51,6 @@ export const pack: Pack = { customPackProperties: {internalPackRunId: 0, name: 'allTests'}, doAfterPack, doBeforePack, - enableChromeDevToolsProtocol: true, enableHeadlessMode: true, enableLiveMode: false, enableMobileDeviceMode: false, @@ -66,6 +65,7 @@ export const pack: Pack = { mapLogPayloadInLogFile, mapLogPayloadInReport, maxRetriesCountInDocker: 3, + overriddenConfigFields: null, overriddenUserAgent, packTimeout: packTimeoutInMinutes * msInMinute, pageRequestTimeout: 30_000, diff --git a/package-lock.json b/package-lock.json index 8b682a63..78f258a2 100644 --- a/package-lock.json +++ b/package-lock.json @@ -9,7 +9,7 @@ "version": "0.18.5", "license": "MIT", "dependencies": { - "@playwright/test": "1.45.3", + "@playwright/test": "1.46.0", "bin-v8-flags-filter": "1.2.0", "create-locator": "0.0.23", "get-modules-graph": "0.0.9", @@ -20,7 +20,7 @@ "e2ed-init": "bin/init.js" }, "devDependencies": { - "@playwright/browser-chromium": "1.45.3", + "@playwright/browser-chromium": "1.46.0", "@types/node": "22.1.0", "@typescript-eslint/eslint-plugin": "7.18.0", "@typescript-eslint/parser": "7.18.0", @@ -175,24 +175,24 @@ } }, "node_modules/@playwright/browser-chromium": { - "version": "1.45.3", - "resolved": "https://registry.npmjs.org/@playwright/browser-chromium/-/browser-chromium-1.45.3.tgz", - "integrity": "sha512-UVPW8HveE8SghaahoMy8CfG0QdJ2mO0BZLOcPT8nlQh7Z97Gkv4e3Ad69D1oCqM3m3zYkDPAiGB+hOASNS0d/g==", + "version": "1.46.0", + "resolved": "https://registry.npmjs.org/@playwright/browser-chromium/-/browser-chromium-1.46.0.tgz", + "integrity": "sha512-80MZZEcX8jodG2u6h/kYi2r3K2awkSXXstoSCFgD4KKZzjF7kUa+0akySMfTsyBIKOTZqy2deF2ZaMHM/rh5Lw==", "dev": true, "hasInstallScript": true, "dependencies": { - "playwright-core": "1.45.3" + "playwright-core": "1.46.0" }, "engines": { "node": ">=18" } }, "node_modules/@playwright/test": { - "version": "1.45.3", - "resolved": "https://registry.npmjs.org/@playwright/test/-/test-1.45.3.tgz", - "integrity": "sha512-UKF4XsBfy+u3MFWEH44hva1Q8Da28G6RFtR2+5saw+jgAFQV5yYnB1fu68Mz7fO+5GJF3wgwAIs0UelU8TxFrA==", + "version": "1.46.0", + "resolved": "https://registry.npmjs.org/@playwright/test/-/test-1.46.0.tgz", + "integrity": "sha512-/QYft5VArOrGRP5pgkrfKksqsKA6CEFyGQ/gjNe6q0y4tZ1aaPfq4gIjudr1s3D+pXyrPRdsy4opKDrjBabE5w==", "dependencies": { - "playwright": "1.45.3" + "playwright": "1.46.0" }, "bin": { "playwright": "cli.js" @@ -2517,11 +2517,11 @@ } }, "node_modules/playwright": { - "version": "1.45.3", - "resolved": "https://registry.npmjs.org/playwright/-/playwright-1.45.3.tgz", - "integrity": "sha512-QhVaS+lpluxCaioejDZ95l4Y4jSFCsBvl2UZkpeXlzxmqS+aABr5c82YmfMHrL6x27nvrvykJAFpkzT2eWdJww==", + "version": "1.46.0", + "resolved": "https://registry.npmjs.org/playwright/-/playwright-1.46.0.tgz", + "integrity": "sha512-XYJ5WvfefWONh1uPAUAi0H2xXV5S3vrtcnXe6uAOgdGi3aSpqOSXX08IAjXW34xitfuOJsvXU5anXZxPSEQiJw==", "dependencies": { - "playwright-core": "1.45.3" + "playwright-core": "1.46.0" }, "bin": { "playwright": "cli.js" @@ -2534,9 +2534,9 @@ } }, "node_modules/playwright-core": { - "version": "1.45.3", - "resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.45.3.tgz", - "integrity": "sha512-+ym0jNbcjikaOwwSZycFbwkWgfruWvYlJfThKYAlImbxUgdWFO2oW70ojPm4OpE4t6TAo2FY/smM+hpVTtkhDA==", + "version": "1.46.0", + "resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.46.0.tgz", + "integrity": "sha512-9Y/d5UIwuJk8t3+lhmMSAJyNP1BUC/DqP3cQJDQQL/oWqAiuPTLgy7Q5dzglmTLwcBRdetzgNM/gni7ckfTr6A==", "bin": { "playwright-core": "cli.js" }, diff --git a/package.json b/package.json index 3af02610..e3b059e6 100644 --- a/package.json +++ b/package.json @@ -24,14 +24,14 @@ "url": "git+https://github.com/joomcode/e2ed.git" }, "dependencies": { - "@playwright/test": "1.45.3", + "@playwright/test": "1.46.0", "bin-v8-flags-filter": "1.2.0", "create-locator": "0.0.23", "get-modules-graph": "0.0.9", "globby": "11.1.0" }, "devDependencies": { - "@playwright/browser-chromium": "1.45.3", + "@playwright/browser-chromium": "1.46.0", "@types/node": "22.1.0", "@typescript-eslint/eslint-plugin": "7.18.0", "@typescript-eslint/parser": "7.18.0", diff --git a/src/README.md b/src/README.md index 77cfac89..d6bb6c31 100644 --- a/src/README.md +++ b/src/README.md @@ -8,45 +8,42 @@ Modules in the dependency graph should only import the modules above them: 1. `types` 2. `constants` 3. `configurator` -4. `testcafe` -5. `esm` -6. `generators` -7. `utils/browser` -8. `utils/getDurationWithUnits` -9. `utils/setReadonlyProperty` -10. `utils/selectors` -11. `utils/paths` -12. `utils/valueToString` -13. `utils/error` -14. `utils/asserts` -15. `utils/testCafe` -16. `utils/runLabel` -17. `utils/clone` -18. `utils/notIncludedInPackTests` -19. `utils/userland` -20. `utils/fn` -21. `utils/environment` -22. `utils/packCompiler` -23. `testcaferc` -24. `utils/config` -25. `utils/generalLog` -26. `utils/exit` -27. `utils/promise` -28. `utils/resourceUsage` -29. `utils/fs` -30. `utils/tests` -31. `utils/disconnectedBrowsers` -32. `utils/end` -33. `utils/pack` -34. `selectors` -35. `Route` -36. `ApiRoute` -37. `PageRoute` -38. `testController` -39. `useContext` -40. `context` -41. `utils/log` -42. `utils/waitForEvents` -43. `utils/expect` -44. `expect` -45. ... +4. `generators` +5. `utils/browser` +6. `utils/getDurationWithUnits` +7. `utils/setReadonlyProperty` +8. `utils/selectors` +9. `utils/paths` +10. `utils/valueToString` +11. `utils/error` +12. `utils/asserts` +13. `utils/runLabel` +14. `utils/clone` +15. `utils/notIncludedInPackTests` +16. `utils/userland` +17. `utils/fn` +18. `utils/environment` +19. `utils/packCompiler` +20. `config` +21. `utils/config` +22. `utils/generalLog` +23. `utils/testFilePaths` +24. `utils/exit` +25. `utils/promise` +26. `utils/resourceUsage` +27. `utils/fs` +28. `utils/tests` +29. `utils/end` +30. `utils/pack` +31. `selectors` +32. `Route` +33. `ApiRoute` +34. `PageRoute` +35. `testController` +36. `useContext` +37. `context` +38. `utils/log` +39. `utils/waitForEvents` +40. `utils/expect` +41. `expect` +42. ... diff --git a/src/config.ts b/src/config.ts index b3477a8b..936cbcf5 100644 --- a/src/config.ts +++ b/src/config.ts @@ -9,8 +9,10 @@ import { ABSOLUTE_PATH_TO_INSTALLED_E2ED_DIRECTORY, ABSOLUTE_PATH_TO_PROJECT_ROOT_DIRECTORY, COMPILED_USERLAND_CONFIG_DIRECTORY, + e2edEnvironment, INTERNAL_REPORTS_DIRECTORY_PATH, isDebug, + PATH_TO_TEST_FILE_VARIABLE_NAME, TESTS_DIRECTORY_PATH, } from './constants/internal'; import {assertValueIsTrue} from './utils/asserts'; @@ -21,7 +23,7 @@ import {setCustomInspectOnFunction} from './utils/fn'; import {setReadonlyProperty} from './utils/setReadonlyProperty'; import {isLocalRun} from './configurator'; -import type {FullPackConfig, UserlandPack} from './types/internal'; +import type {FullPackConfig, Mutable, UserlandPack} from './types/internal'; import {defineConfig, devices} from '@playwright/test'; @@ -83,6 +85,13 @@ if (isDebug) { setReadonlyProperty(userlandPack, 'testTimeout', maxTimeoutInMs); } +const pathToTestFile = e2edEnvironment[PATH_TO_TEST_FILE_VARIABLE_NAME]; + +const testMatch = + pathToTestFile === undefined + ? (userlandPack.testFileGlobs as Mutable) + : join('**', pathToTestFile); + const playwrightConfig = defineConfig({ fullyParallel: true, @@ -99,7 +108,7 @@ const playwrightConfig = defineConfig({ testDir: join(relativePathFromInstalledE2edToRoot, TESTS_DIRECTORY_PATH), testIgnore: '**/*.skip.ts', - testMatch: userlandPack.testFileGlobs as (typeof userlandPack.testFileGlobs)[number][], + testMatch, timeout: userlandPack.testTimeout, @@ -117,6 +126,8 @@ const playwrightConfig = defineConfig({ }, workers: userlandPack.concurrency, + + ...userlandPack.overriddenConfigFields, }); const config: FullPackConfig = Object.assign(playwrightConfig, userlandPack); diff --git a/src/constants/environment.ts b/src/constants/environment.ts index d031aacd..69bff90d 100644 --- a/src/constants/environment.ts +++ b/src/constants/environment.ts @@ -17,6 +17,12 @@ export const isDebug = Boolean(e2edEnvironment.E2ED_DEBUG); */ export const PATH_TO_PACK_VARIABLE_NAME = '__INTERNAL_E2ED_PATH_TO_PACK'; +/** + * Name of e2ed environment variable with path to one test file. + * @internal + */ +export const PATH_TO_TEST_FILE_VARIABLE_NAME = '__INTERNAL_E2ED_PATH_TO_TEST_FILE'; + /** * Name of e2ed environment variable with run environment. * @internal diff --git a/src/constants/internal.ts b/src/constants/internal.ts index 8db0562f..fc5fca3f 100644 --- a/src/constants/internal.ts +++ b/src/constants/internal.ts @@ -8,6 +8,7 @@ export {isDebug, RunEnvironment} from './environment'; export { e2edEnvironment, PATH_TO_PACK_VARIABLE_NAME, + PATH_TO_TEST_FILE_VARIABLE_NAME, RUN_ENVIRONMENT_VARIABLE_NAME, RUN_LABEL_VARIABLE_NAME, START_TIME_IN_MS_VARIABLE_NAME, diff --git a/src/types/config/ownE2edConfig.ts b/src/types/config/ownE2edConfig.ts index 9ea3c527..fb2d0acb 100644 --- a/src/types/config/ownE2edConfig.ts +++ b/src/types/config/ownE2edConfig.ts @@ -1,3 +1,5 @@ +import type {PlaywrightTestConfig} from '@playwright/test'; + import type {FullMocksConfig} from '../fullMocks'; import type {MapBackendResponseToLog, MapLogPayload, MapLogPayloadInReport} from '../log'; import type {MaybePromise} from '../promise'; @@ -46,12 +48,6 @@ export type OwnE2edConfig< liteReport: LiteReport, ) => MaybePromise)[]; - /** - * 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). */ @@ -149,9 +145,13 @@ export type OwnE2edConfig< */ maxRetriesCountInDocker: number; + /** + * If not `null`, then this value will override fields of internal Playwright config. + */ + overriddenConfigFields: PlaywrightTestConfig | null; + /** * If not `null`, then this value will override the browser's user agent in tests. - * This override only works when `enableChromeDevToolsProtocol` is `true`. */ overriddenUserAgent: string | null; diff --git a/src/types/environment.ts b/src/types/environment.ts index 34142a96..e7e30b3b 100644 --- a/src/types/environment.ts +++ b/src/types/environment.ts @@ -1,6 +1,7 @@ import type {RunEnvironment} from '../configurator'; import type { PATH_TO_PACK_VARIABLE_NAME, + PATH_TO_TEST_FILE_VARIABLE_NAME, RUN_ENVIRONMENT_VARIABLE_NAME, RUN_LABEL_VARIABLE_NAME, START_TIME_IN_MS_VARIABLE_NAME, @@ -19,6 +20,7 @@ export type E2edEnvironment = { ['E2ED_PATH_TO_TS_CONFIG_OF_PROJECT_FROM_ROOT']?: string; ['E2ED_TERMINATION_SIGNAL']?: NodeJS.Signals; [PATH_TO_PACK_VARIABLE_NAME]?: string; + [PATH_TO_TEST_FILE_VARIABLE_NAME]?: string; ['PWD']?: string; ['PWDEBUG']?: 'console'; [RUN_ENVIRONMENT_VARIABLE_NAME]?: RunEnvironment; diff --git a/src/utils/collectTestFilePaths.ts b/src/utils/collectTestFilePaths.ts deleted file mode 100644 index 7e7791ea..00000000 --- a/src/utils/collectTestFilePaths.ts +++ /dev/null @@ -1,20 +0,0 @@ -import {normalize} from 'node:path'; - -import globby from 'globby'; - -import {getFullPackConfig} from './config'; - -import type {TestFilePath} from '../types/internal'; - -/** - * Collects test file paths for current pack. - * @internal - */ -export const collectTestFilePaths = async (): Promise => { - const {testFileGlobs} = getFullPackConfig(); - - const rawTestFilesPaths = await globby(testFileGlobs); - const testFilesPaths = rawTestFilesPaths.map(normalize as (path: string) => TestFilePath); - - return testFilesPaths; -}; diff --git a/src/utils/generalLog/index.ts b/src/utils/generalLog/index.ts index 5e169c23..4569c2eb 100644 --- a/src/utils/generalLog/index.ts +++ b/src/utils/generalLog/index.ts @@ -10,6 +10,6 @@ export {writeLogsToFile} from './logFile'; export {logStartE2edError} from './logStartE2edError'; export {removeStyleFromString} from './removeStyleFromString'; /** @internal */ -export {getSuccessfulTestRuns} from './successfulTestRunCount'; +export {getSuccessfulTestFilePaths} from './successfulTestRuns'; /** @internal */ export {truncateArrayForLogs} from './truncateArrayForLogs'; diff --git a/src/utils/generalLog/logEndTestRunEvent.ts b/src/utils/generalLog/logEndTestRunEvent.ts index 07dece5c..a39a390a 100644 --- a/src/utils/generalLog/logEndTestRunEvent.ts +++ b/src/utils/generalLog/logEndTestRunEvent.ts @@ -7,7 +7,7 @@ import { import {generalLog} from './generalLog'; import {getMessageWithBackgroundColor} from './getMessageWithBackgroundColor'; -import {addSuccessfulInCurrentRetry, getSuccessfulTestRuns} from './successfulTestRunCount'; +import {addSuccessfulTestRun, getSuccessfulTestFilePaths} from './successfulTestRuns'; import type {FullTestRun} from '../../types/internal'; @@ -19,7 +19,7 @@ export const logEndTestRunEvent = async (fullTestRun: FullTestRun): Promise => { +export const getSuccessfulTestFilePaths = async (): Promise => { let successfulTestsFile = ''; try { @@ -30,16 +30,16 @@ export const getSuccessfulTestRuns = async (): Promise }; /** - * Adds one successful test run (in current retry). + * Adds one successful test run. * @internal */ -export const addSuccessfulInCurrentRetry = async (testFilePath: TestFilePath): Promise => { - const successfulTestRuns = await getSuccessfulTestRuns(); +export const addSuccessfulTestRun = async (testFilePath: TestFilePath): Promise => { + const successfulTestFilePaths = await getSuccessfulTestFilePaths(); assertValueIsFalse( - successfulTestRuns.includes(testFilePath), + successfulTestFilePaths.includes(testFilePath), 'There is no duplicate test file path in successful test runs', - {successfulTestRuns, testFilePath}, + {successfulTestFilePaths, testFilePath}, ); await appendFile(SUCCESSFUL_TESTS_PATH, `${testFilePath}\n`); diff --git a/src/utils/report/getReportErrors.ts b/src/utils/report/getReportErrors.ts index 1cd62163..a58eb0be 100644 --- a/src/utils/report/getReportErrors.ts +++ b/src/utils/report/getReportErrors.ts @@ -1,7 +1,11 @@ import {RunEnvironment} from '../../configurator'; import {getFullPackConfig} from '../config'; -import {getUnvisitedTestFilePaths} from '../getUnvisitedTestFilePaths'; +import { + collectTestFilePaths, + getUnsuccessfulTestFilePaths, + getUnvisitedTestFilePaths, +} from '../testFilePaths'; import type {FullTestRun, TestFilePath} from '../../types/internal'; @@ -18,15 +22,32 @@ export const getReportErrors = async ( const errors: string[] = []; if (runEnvironment === RunEnvironment.Docker) { - const unvisitedTestFilePaths = await getUnvisitedTestFilePaths( + const allTestFilePaths = await collectTestFilePaths(); + const unsuccessfulTestFilePaths = await getUnsuccessfulTestFilePaths( + allTestFilePaths, + notIncludedInPackTests, + ); + const unvisitedTestFilePaths = getUnvisitedTestFilePaths( fullTestRuns, + allTestFilePaths, notIncludedInPackTests, ); + const unsuccessfulCount = unsuccessfulTestFilePaths.length; const unvisitedCount = unvisitedTestFilePaths.length; + const thereAreManyUnsuccessfulFiles = unsuccessfulCount > 1; const thereAreManyUnvisitedFiles = unvisitedCount > 1; + const wordTest = thereAreManyUnsuccessfulFiles ? 'tests' : 'test'; const wordFile = thereAreManyUnvisitedFiles ? 'files' : 'file'; const wordGlob = testFileGlobs.length > 1 ? 'globs' : 'glob'; + if (unsuccessfulCount !== 0) { + errors.push( + `Error: There ${ + thereAreManyUnsuccessfulFiles ? 'are' : 'is' + } ${unsuccessfulCount} unsuccessful ${wordTest}: ${unsuccessfulTestFilePaths.join(', ')}`, + ); + } + if (unvisitedCount !== 0) { errors.push( `Error: There ${ diff --git a/src/utils/test/getRunTest.ts b/src/utils/test/getRunTest.ts index 09a831cd..459c3fc8 100644 --- a/src/utils/test/getRunTest.ts +++ b/src/utils/test/getRunTest.ts @@ -25,8 +25,6 @@ export const getRunTest = (test: Test): RunTest => ({context, page, request}: PlaywrightTestArgs, testInfo: TestInfo): Promise => { const runTest = async (): Promise => { - await preparePage(page); - const retry = testInfo.retry + 1; const runId = createRunId(); @@ -44,6 +42,8 @@ export const getRunTest = return; } + await preparePage(page); + beforeTest({retry, runId, testFn: test.testFn, testStaticOptions}); const testController = {context, page, request}; diff --git a/src/utils/test/getShouldRunTest.ts b/src/utils/test/getShouldRunTest.ts index 48a75309..8565f8f6 100644 --- a/src/utils/test/getShouldRunTest.ts +++ b/src/utils/test/getShouldRunTest.ts @@ -1,4 +1,4 @@ -import {getSuccessfulTestRuns} from '../generalLog'; +import {getSuccessfulTestFilePaths} from '../generalLog'; import {addTestToNotIncludedInPackTests} from '../notIncludedInPackTests'; import {getIsTestIncludedInPack} from './getIsTestIncludedInPack'; @@ -18,7 +18,7 @@ export const getShouldRunTest = async (testStaticOptions: TestStaticOptions): Pr return false; } - const successfulTestRuns = await getSuccessfulTestRuns(); + const successfulTestFilePaths = await getSuccessfulTestFilePaths(); - return !successfulTestRuns.includes(testStaticOptions.filePath); + return !successfulTestFilePaths.includes(testStaticOptions.filePath); }; diff --git a/src/utils/testFilePaths/collectTestFilePaths.ts b/src/utils/testFilePaths/collectTestFilePaths.ts new file mode 100644 index 00000000..068f3a1f --- /dev/null +++ b/src/utils/testFilePaths/collectTestFilePaths.ts @@ -0,0 +1,23 @@ +import {normalize} from 'node:path'; + +import globby from 'globby'; + +import {getFullPackConfig} from '../config'; + +import type {TestFilePath} from '../../types/internal'; + +/** + * Collects all test file paths for current pack by test file globs + * (includes tests that will be filtered by the `filterTestsIntoPack` function). + * @internal + */ +export const collectTestFilePaths = async (): Promise => { + const {testFileGlobs} = getFullPackConfig(); + + const rawTestFilesPaths = await globby(testFileGlobs); + const testFilesPaths = rawTestFilesPaths + .map(normalize as (path: string) => TestFilePath) + .filter((testFilePath) => !testFilePath.endsWith('.skip.ts')); + + return testFilesPaths; +}; diff --git a/src/utils/testFilePaths/getUnsuccessfulTestFilePaths.ts b/src/utils/testFilePaths/getUnsuccessfulTestFilePaths.ts new file mode 100644 index 00000000..c550f97e --- /dev/null +++ b/src/utils/testFilePaths/getUnsuccessfulTestFilePaths.ts @@ -0,0 +1,59 @@ +import {assertValueIsFalse, assertValueIsTrue} from '../asserts'; +import {getSuccessfulTestFilePaths} from '../generalLog'; + +import type {TestFilePath} from '../../types/internal'; + +/** + * Get unsuccessful test file paths, that is, file paths that are not successful and included in pack. + * @internal + */ +export const getUnsuccessfulTestFilePaths = async ( + allTestFilePaths: readonly TestFilePath[], + notIncludedInPackTests: readonly TestFilePath[], +): Promise => { + const successfulTestFilePaths = await getSuccessfulTestFilePaths(); + + const successfulAndNotIncludedTestFilePathsHash = Object.create(null) as Record< + TestFilePath, + true + >; + + for (const filePath of successfulTestFilePaths) { + successfulAndNotIncludedTestFilePathsHash[filePath] = true; + } + + for (const filePath of notIncludedInPackTests) { + assertValueIsFalse( + filePath in successfulAndNotIncludedTestFilePathsHash, + 'There is no intersection between successful tests and not included in pack tests', + {filePath, notIncludedInPackTests, successfulAndNotIncludedTestFilePathsHash}, + ); + + successfulAndNotIncludedTestFilePathsHash[filePath] = true; + } + + const allTestFilePathsHash = Object.create(null) as Record; + + for (const filePath of allTestFilePaths) { + allTestFilePathsHash[filePath] = true; + } + + // eslint-disable-next-line guard-for-in + for (const filePath in successfulAndNotIncludedTestFilePathsHash) { + assertValueIsTrue( + filePath in allTestFilePathsHash, + 'filePath included in allTestFilePathsHash', + {allTestFilePathsHash, filePath}, + ); + } + + const unsuccessfulTestFilePaths: TestFilePath[] = []; + + for (const filePath of allTestFilePaths) { + if (!(filePath in successfulAndNotIncludedTestFilePathsHash)) { + unsuccessfulTestFilePaths.push(filePath); + } + } + + return unsuccessfulTestFilePaths; +}; diff --git a/src/utils/getUnvisitedTestFilePaths.ts b/src/utils/testFilePaths/getUnvisitedTestFilePaths.ts similarity index 61% rename from src/utils/getUnvisitedTestFilePaths.ts rename to src/utils/testFilePaths/getUnvisitedTestFilePaths.ts index 02a1596c..52a15d26 100644 --- a/src/utils/getUnvisitedTestFilePaths.ts +++ b/src/utils/testFilePaths/getUnvisitedTestFilePaths.ts @@ -1,18 +1,16 @@ -import {assertValueIsFalse} from './asserts'; -import {collectTestFilePaths} from './collectTestFilePaths'; +import {assertValueIsFalse} from '../asserts'; -import type {FullTestRun, TestFilePath} from '../types/internal'; +import type {FullTestRun, TestFilePath} from '../../types/internal'; /** * Get unvisited test file paths, that is, file paths to tests for which there was no test runs. * @internal */ -export const getUnvisitedTestFilePaths = async ( +export const getUnvisitedTestFilePaths = ( fullTestRuns: readonly FullTestRun[], + allTestFilePaths: readonly TestFilePath[], notIncludedInPackTests: readonly TestFilePath[], -): Promise => { - const allTestFilePaths = await collectTestFilePaths(); - +): readonly TestFilePath[] => { const visitedTestFilePathsHash = Object.create(null) as Record; for (const {filePath} of fullTestRuns) { @@ -31,9 +29,9 @@ export const getUnvisitedTestFilePaths = async ( const unvisitedTestFilePaths: TestFilePath[] = []; - for (const testFilePath of allTestFilePaths) { - if (!(testFilePath in visitedTestFilePathsHash) && !testFilePath.endsWith('.skip.ts')) { - unvisitedTestFilePaths.push(testFilePath); + for (const filePath of allTestFilePaths) { + if (!(filePath in visitedTestFilePathsHash)) { + unvisitedTestFilePaths.push(filePath); } } diff --git a/src/utils/testFilePaths/index.ts b/src/utils/testFilePaths/index.ts new file mode 100644 index 00000000..2823f74d --- /dev/null +++ b/src/utils/testFilePaths/index.ts @@ -0,0 +1,6 @@ +/** @internal */ +export {collectTestFilePaths} from './collectTestFilePaths'; +/** @internal */ +export {getUnsuccessfulTestFilePaths} from './getUnsuccessfulTestFilePaths'; +/** @internal */ +export {getUnvisitedTestFilePaths} from './getUnvisitedTestFilePaths'; diff --git a/src/utils/tests/runTests.ts b/src/utils/tests/runTests.ts index 556ae8fd..5986c883 100644 --- a/src/utils/tests/runTests.ts +++ b/src/utils/tests/runTests.ts @@ -1,10 +1,13 @@ import {fork} from 'node:child_process'; -import {CONFIG_PATH, e2edEnvironment} from '../../constants/internal'; +import { + CONFIG_PATH, + e2edEnvironment, + PATH_TO_TEST_FILE_VARIABLE_NAME, +} from '../../constants/internal'; import {getFullPackConfig} from '../config'; import {getRunLabel, setRunLabel} from '../environment'; -import {E2edError} from '../error'; import {generalLog} from '../generalLog'; import {startResourceUsageReading} from '../resourceUsage'; @@ -40,13 +43,8 @@ export const runTests = async ({runLabel}: RunRetryOptions): Promise => { beforeRunFirstTestTimeoutId.unref(); - // const notIncludedInPackTests = await getNotIncludedInPackTests(); - // const notIncludedInPackTestsInAbsolutePaths = notIncludedInPackTests.map((testFilePath) => - // join(ABSOLUTE_PATH_TO_PROJECT_ROOT_DIRECTORY, testFilePath), - // ); - await new Promise((resolve, reject) => { - const playwrightArgs = ['test', '--config', CONFIG_PATH]; + const playwrightArgs = ['test', `--config=${CONFIG_PATH}`]; // eslint-disable-next-line @typescript-eslint/strict-boolean-expressions if (e2edEnvironment.E2ED_DEBUG) { @@ -59,6 +57,12 @@ export const runTests = async ({runLabel}: RunRetryOptions): Promise => { playwrightArgs.push('--ui'); } + const pathToTestFile = process.argv[2]; + + if (pathToTestFile !== undefined) { + e2edEnvironment[PATH_TO_TEST_FILE_VARIABLE_NAME] = pathToTestFile; + } + if (!beforeRunFirstTestWasCalled) { beforeRunFirstTestWasCalled = true; @@ -83,14 +87,14 @@ export const runTests = async ({runLabel}: RunRetryOptions): Promise => { playwrightProcess.on('error', reject); - playwrightProcess.on('exit', (exitCode): void => { - const error = new E2edError( - `Playwright process with label "${runLabel}" exit with non-zero exit code ${String( - exitCode, - )}`, + playwrightProcess.on('exit', (exitCode, exitSignal): void => { + const signalMessage = exitSignal === null ? '' : ` and signal ${exitSignal}`; + + generalLog( + `Playwright process with label "${runLabel}" exit with code ${String(exitCode)}${signalMessage}`, ); - return exitCode === 0 ? resolve() : reject(error); + resolve(); }); }); } catch (error) {