From b91a59925c464429d29f3c351d5b0896ddee980b Mon Sep 17 00:00:00 2001 From: Popov Aleksey Date: Fri, 13 Jan 2023 11:59:11 +0400 Subject: [PATCH] feat: added the possibility to run tests in ESM mode (#7411) ## Purpose Add the possibility to run tests in ESM mode ## Approach - [X] Research ways to implement running ESM files consistently - [X] Research ways to compile test files on the run - [X] Add loader hooks for ESM - [X] Run tests with dynamic import - [x] Fix tests - [X] Add handling error `ERR_REQUIRE_ESM` - [X] Add workflow for running tests in ESM - [x] Add additional tests ## References Closes #6853 PR in `bin-v8-flags-filter` https://github.com/DevExpress/bin-v8-flags-filter/pull/1 PR in `callsite-record` https://github.com/inikulin/callsite-record/pull/28 Temp `bin-v8-flags-filter` assembling [bin-v8-flags-filter-1.2.0.zip](https://github.com/DevExpress/testcafe/files/10175237/bin-v8-flags-filter-1.2.0.zip) Temp `callsite-record` assembling [callsite-record-4.1.3.zip](https://github.com/DevExpress/testcafe/files/10175241/callsite-record-4.1.3.zip) ## Pre-Merge TODO - [x] Write tests for your proposed changes - [x] Make sure that existing tests do not fail --- .github/workflows/deploy-to-artifacts.yml | 1 + .../workflows/test-functional-local-esm.yml | 103 ++++++++++++++++++ .gitignore | 1 + Gulpfile.js | 19 +++- bin/testcafe-with-v8-flag-filter.js | 19 +++- gulp/esm-package/package.json | 1 + .../helpers/create-package-files-for-tests.js | 27 +++++ package.json | 6 +- src/api/exportable-lib/index.mjs | 27 +++++ src/api/structure/fixture.ts | 13 +++ src/api/structure/test.ts | 14 +++ src/cli/argument-parser/index.ts | 5 +- src/cli/cli.js | 2 + src/client/browser/index.js | 7 +- src/compiler/babel/load-libs.js | 17 +-- src/compiler/compilers.js | 12 +- src/compiler/esm-loader.ts | 50 +++++++++ src/compiler/index.js | 13 ++- src/compiler/interfaces.ts | 1 + src/compiler/prevent-module-caching-suffix.ts | 3 + src/compiler/test-file/add-export-api.ts | 10 +- src/compiler/test-file/api-based.js | 50 ++++++--- src/compiler/test-file/base.js | 4 + src/compiler/test-file/exportble-lib-path.ts | 5 - .../formats/coffeescript/compiler.js | 7 +- .../test-file/formats/es-next/compiler.js | 37 +++++-- src/compiler/test-file/formats/extensions.ts | 11 ++ .../test-file/formats/typescript/compiler.ts | 46 +++++--- .../test-file/get-exportable-lib-path.ts | 11 ++ src/configuration/option-names.ts | 1 + src/errors/is-internal-stack-frame.ts | 3 +- src/errors/process-test-fn-error.js | 3 +- src/errors/runtime/index.js | 21 +++- src/errors/runtime/templates.js | 1 + src/errors/test-run/utils.js | 7 +- src/errors/types.js | 1 + src/runner/bootstrapper.ts | 5 +- src/services/compiler/service.ts | 4 +- src/shared/utils/is-file-protocol.ts | 7 ++ src/testcafe.js | 2 +- src/tsconfig.json | 5 +- src/utils/setup-sourcemap-support.ts | 12 +- test/functional/config.js | 4 + test/functional/esm-utils/package.json | 3 + .../{ => esm-utils}/window-helpers.js | 15 ++- .../testcafe-fixtures/browser-info-test.js | 7 +- .../api/es-next/client-function/test.js | 20 ++-- .../testcafe-fixtures/client-fn-test.js | 4 +- .../console/testcafe-fixtures/console-test.js | 21 ++-- .../{helpers => common}/index.js | 0 .../testcafe-fixtures/folder/index.js | 2 +- .../testcafe-fixtures/mixed.js | 2 +- .../testcafe-fixtures/runner.js | 2 +- .../testcafe-fixtures/specified-page.js | 2 +- .../testcafe-fixtures/test-api.js | 2 +- .../testcafe-fixtures/default-test.js | 2 +- .../fixture-disabled-reloads-test.js | 2 +- .../fixture-enabled-reloads-test.js | 2 +- ...ture-enabled-test-disabled-reloads-test.js | 2 +- .../globally-disabled-reloads-test.js | 2 +- .../test-disabled-reloads-test.js | 2 +- .../test-enabled-reloads-test.js | 2 +- .../drag/testcafe-fixtures/drag-test.js | 4 +- .../error-in-test-code-test.js | 2 +- .../external-assertion-lib-errors-test.js | 2 +- .../hooks/testcafe-fixtures/fixture-ctx.js | 2 +- .../testcafe-fixtures/fixture-hooks-seq.js | 2 +- .../hooks/testcafe-fixtures/fixture-hooks.js | 2 +- .../testcafe-fixtures/maximize-window-test.js | 2 +- .../navigate-to-and-test-page/dirname.js | 1 + .../es-next/navigate-to-and-test-page/test.js | 1 - .../testcafe-fixtures/navigate-to-test.js | 50 ++++----- .../request-hooks/common/mock-routes.js | 4 +- .../api/add-remove-request-hook.js | 3 +- .../api/conditional-adding.js | 2 +- .../testcafe-fixtures/api/execution-order.js | 8 +- .../testcafe-fixtures/api/i4122.js | 5 +- .../api/request-hook-events.js | 5 +- .../request-logger/mocked-requests.js | 2 +- .../request-logger/multi-browser.js | 2 +- .../request-mock/async-response-function.js | 2 +- .../testcafe-fixtures/request-mock/basic.js | 2 +- .../request-mock/respond-error.js | 2 +- .../testcafe-fixtures/resize-window-test.js | 2 +- .../hash-based-navigation-test.js | 2 +- .../testcafe-fixtures/init-error-test.js | 4 +- .../roles/testcafe-fixtures/same-url-test.js | 2 +- .../take-element-screenshot.js | 4 +- .../take-full-page-screenshot.js | 2 +- .../testcafe-fixtures/take-screenshot.js | 8 +- .../api/es-next/test-controller/test.js | 7 +- .../testcafe-fixtures/test-controller-test.js | 2 +- .../api/es-next/test-structure/test.js | 6 +- .../attached-test.js | 0 .../imported-fixture-test.js | 0 .../imported-test-test.js | 0 .../test-structure-test.js | 2 +- .../synchronous-selectors.js | 4 +- .../testcafe-fixtures/concurrent-test.js | 2 +- .../multibrowser-concurrent-test.js | 2 +- .../testcafe-fixtures/role-test.js | 2 +- .../testcafe-fixtures/sequential-test.js | 2 +- test/functional/fixtures/esm/test.js | 23 ++++ .../esm/testcafe-fixtures/esm-package.mjs | 1 + .../esm/testcafe-fixtures/import-esm.js | 7 ++ test/functional/fixtures/live/test.js | 2 +- .../live/testcafe-fixtures/client-scripts.js | 2 +- .../live/testcafe-fixtures/quarantine.js | 2 +- .../fixtures/live/testcafe-fixtures/smoke.js | 2 +- .../fixtures/live/testcafe-fixtures/test-2.js | 2 +- .../testcafe-fixtures/api/api-test.js | 2 +- .../testcafe-fixtures/features/emulation.js | 2 +- .../testcafe-fixtures/fixture-and-test.js | 2 +- .../testcafe-fixtures/test-controller.js | 9 +- .../testcafe-fixtures/test-quarantine-mode.js | 2 +- .../gh-1054/testcafe-fixtures/index.test.js | 2 +- .../fixture-with-absolute-url.js | 2 +- .../fixture-with-relative-url.js | 2 +- .../testcafe-fixtures/fixture-without-url.js | 2 +- .../gh-1940/testcafe-fixtures/index.js | 4 +- .../gh-2015/testcafe-fixtures/index.js | 2 +- .../gh-2074/testcafe-fixtures/index.js | 2 +- .../gh-2546/testcafe-fixtures/index.js | 2 +- .../gh-3127/testcafe-fixtures/index-test.js | 2 +- .../gh-3298/testcafe-fixtures/index.js | 2 +- .../gh-4516/testcafe-fixtures/index.js | 4 +- .../gh-4613/testcafe-fixtures/first.js | 2 +- .../gh-4613/testcafe-fixtures/last.js | 2 +- .../gh-4613/testcafe-fixtures/middle.js | 2 +- .../gh-5961/testcafe-fixtures/index.js | 2 +- .../fixtures/regression/gh-6205/test.js | 4 +- .../fixtures/regression/gh-637/test.js | 2 +- .../{data => testcafe-fixtures}/testfile.js | 0 .../gh-6722/testcafe-fixtures/index.js | 2 +- .../gh-913/testcafe-fixtures/index-test.js | 4 +- .../testcafe-fixtures/fixture.js | 2 +- .../testcafe-fixtures/single-test.js | 2 +- .../testcafe-fixtures/test-run.js | 2 +- .../testcafe-fixtures/screenshots-on-fails.js | 2 +- .../ui/testcafe-fixtures/status-bar-test.js | 2 +- test/functional/setup.js | 1 + test/server/cli-argument-parser-test.js | 3 +- ts-defs-src/runner-api/configuration.d.ts | 4 +- 143 files changed, 700 insertions(+), 272 deletions(-) create mode 100644 .github/workflows/test-functional-local-esm.yml create mode 100644 gulp/esm-package/package.json create mode 100644 gulp/helpers/create-package-files-for-tests.js create mode 100644 src/api/exportable-lib/index.mjs create mode 100644 src/compiler/esm-loader.ts create mode 100644 src/compiler/prevent-module-caching-suffix.ts delete mode 100644 src/compiler/test-file/exportble-lib-path.ts create mode 100644 src/compiler/test-file/formats/extensions.ts create mode 100644 src/compiler/test-file/get-exportable-lib-path.ts create mode 100644 src/shared/utils/is-file-protocol.ts create mode 100644 test/functional/esm-utils/package.json rename test/functional/{ => esm-utils}/window-helpers.js (68%) rename test/functional/fixtures/api/es-next/custom-client-scripts/{helpers => common}/index.js (100%) create mode 100644 test/functional/fixtures/api/es-next/navigate-to-and-test-page/dirname.js rename test/functional/fixtures/api/es-next/test-structure/{testcafe-test-structure => testcafe-fixtures}/attached-test.js (100%) rename test/functional/fixtures/api/es-next/test-structure/{testcafe-test-structure => testcafe-fixtures}/imported-fixture-test.js (100%) rename test/functional/fixtures/api/es-next/test-structure/{testcafe-test-structure => testcafe-fixtures}/imported-test-test.js (100%) rename test/functional/fixtures/api/es-next/test-structure/{testcafe-test-structure => testcafe-fixtures}/test-structure-test.js (69%) create mode 100644 test/functional/fixtures/esm/test.js create mode 100644 test/functional/fixtures/esm/testcafe-fixtures/esm-package.mjs create mode 100644 test/functional/fixtures/esm/testcafe-fixtures/import-esm.js rename test/functional/fixtures/regression/gh-637/{data => testcafe-fixtures}/testfile.js (100%) diff --git a/.github/workflows/deploy-to-artifacts.yml b/.github/workflows/deploy-to-artifacts.yml index b4ed586df5b..bbcb191e4e0 100644 --- a/.github/workflows/deploy-to-artifacts.yml +++ b/.github/workflows/deploy-to-artifacts.yml @@ -165,6 +165,7 @@ jobs: tasks.push('test-functional-docker.yml'); tasks.push('test-functional-local-chrome.yml'); + tasks.push('test-functional-local-esm.yml'); tasks.push('test-functional-local-debug-1.yml'); tasks.push('test-functional-local-debug-2.yml'); tasks.push('test-functional-local-firefox.yml'); diff --git a/.github/workflows/test-functional-local-esm.yml b/.github/workflows/test-functional-local-esm.yml new file mode 100644 index 00000000000..8145332984a --- /dev/null +++ b/.github/workflows/test-functional-local-esm.yml @@ -0,0 +1,103 @@ +name: Test Functional (ESM) + +on: + workflow_dispatch: + inputs: + sha: + desciption: 'The test commit SHA or ref' + required: true + default: 'master' + merged_sha: + description: 'The merge commit SHA' + deploy_run_id: + description: 'The ID of a deployment workspace run with artifacts' +jobs: + test: + runs-on: ubuntu-latest + environment: test-functional + env: + RETRY_FAILED_TESTS: true + steps: + - uses: actions/github-script@v3 + with: + script: | + await github.repos.createCommitStatus({ + owner: context.repo.owner, + repo: context.repo.repo, + sha: context.payload.inputs.sha, + context: context.workflow, + state: 'pending', + target_url: `https://github.com/${context.repo.owner}/${context.repo.repo}/actions/runs/${context.runId}` + }); + - uses: actions/checkout@v2 + with: + ref: ${{github.event.inputs.merged_sha || github.event.inputs.sha}} + + - uses: actions/setup-node@v2 + with: + node-version: 16 + + - uses: actions/github-script@v3 + with: + script: | + const delay = ms => new Promise(resolve => setTimeout(resolve, ms)); + + let artifacts = {}; + + for(let i = 0;i<36&&!artifacts.total_count;i++,await delay(5000)) { + try { + ({ data: artifacts } = await github.actions.listWorkflowRunArtifacts({ + repo: context.repo.repo, + owner: context.repo.owner, + run_id: context.payload.inputs.deploy_run_id + })); + } + catch (e) { + console.log(e); + } + } + + const { data: artifact } = await github.request(artifacts.artifacts.find(artifact=> artifact.name === 'npm').archive_download_url); + require('fs').writeFileSync(require('path').join(process.env.GITHUB_WORKSPACE, 'package.zip'), Buffer.from(artifact)) + + - run: | + unzip package.zip + tar --strip-components=1 -xzf testcafe-*.tgz + + - name: Get npm cache directory + id: npm-cache-dir + run: | + echo "::set-output name=dir::$(npm config get cache)" + - uses: actions/cache@v2 + with: + path: ${{ steps.npm-cache-dir.outputs.dir }} + key: ${{ runner.os }}-node-${{ hashFiles('**/package-lock.json') }} + restore-keys: | + ${{ runner.os }}-node- + - run: npm ci + - run: npx gulp prepare-functional-tests --steps-as-tasks + - run: npm run test-functional-local-headless-chrome-run-esm + timeout-minutes: 60 + - uses: actions/github-script@v3 + with: + script: | + await github.repos.createCommitStatus({ + owner: context.repo.owner, + repo: context.repo.repo, + sha: context.payload.inputs.sha, + context: context.workflow, + state: 'success', + target_url: `https://github.com/${context.repo.owner}/${context.repo.repo}/actions/runs/${context.runId}` + }); + - uses: actions/github-script@v3 + if: failure() || cancelled() + with: + script: | + await github.repos.createCommitStatus({ + owner: context.repo.owner, + repo: context.repo.repo, + sha: context.payload.inputs.sha, + context: context.workflow, + state: 'failure', + target_url: `https://github.com/${context.repo.owner}/${context.repo.repo}/actions/runs/${context.runId}` + }); diff --git a/.gitignore b/.gitignore index 66c0c70672b..c305079ebb8 100644 --- a/.gitignore +++ b/.gitignore @@ -14,3 +14,4 @@ Gemfile.lock .npmrc .DS_Store !gulp +/test/functional/fixtures/**/package.json \ No newline at end of file diff --git a/Gulpfile.js b/Gulpfile.js index e2d943849c5..1068ef33c9e 100644 --- a/Gulpfile.js +++ b/Gulpfile.js @@ -29,6 +29,7 @@ const promisifyStream = require('./gulp/helpers/promisify-stream') const testFunctional = require('./gulp/helpers/test-functional'); const testClient = require('./gulp/helpers/test-client'); const moduleExportsTransform = require('./gulp/helpers/module-exports-transform'); +const createPackageFilesForTests = require('./gulp/helpers/create-package-files-for-tests'); const { TESTS_GLOB, @@ -251,16 +252,30 @@ gulp.step('images', () => { .pipe(gulp.dest('lib')); }); +gulp.step('esm-exportable-lib-script', () => { + return gulp + .src(['src/api/exportable-lib/index.mjs']) + .pipe(gulp.dest('lib/api/exportable-lib/')); +}); + //NOTE: Executing tasks in parallel can cause out-of-memory errors on Azure Pipelines const buildTasks = process.env.TF_BUILD ? gulp.series : gulp.parallel; -gulp.step('package-content', buildTasks('ts-defs', 'server-scripts', 'client-scripts', 'styles', 'images', 'templates')); +gulp.step('package-content', buildTasks('ts-defs', 'server-scripts', 'client-scripts', 'esm-exportable-lib-script', 'styles', 'images', 'templates')); gulp.task('fast-build', gulp.series('clean', 'package-content')); gulp.task('build', process.env.DEV_MODE === 'true' ? gulp.registry().get('fast-build') : buildTasks('lint', 'fast-build')); // Test +gulp.step('prepare-functional-tests', async () => { + return createPackageFilesForTests(); +}); + +gulp.step('clean-functional-tests', async () => { + return del('test/functional/fixtures/**/package.json'); +}); + gulp.step('prepare-tests', gulp.registry().get(SKIP_BUILD ? 'lint' : 'build')); gulp.step('test-server-run', () => { @@ -380,7 +395,7 @@ gulp.step('test-functional-local-headless-chrome-run', () => { return testFunctional(TESTS_GLOB, functionalTestConfig.testingEnvironmentNames.localHeadlessChrome); }); -gulp.task('test-functional-local-headless-chrome', gulp.series('prepare-tests', 'test-functional-local-headless-chrome-run')); +gulp.task('test-functional-local-headless-chrome', gulp.series('prepare-tests', 'prepare-functional-tests', 'test-functional-local-headless-chrome-run', 'clean-functional-tests')); gulp.step('test-functional-local-headless-firefox-run', () => { return testFunctional(TESTS_GLOB, functionalTestConfig.testingEnvironmentNames.localHeadlessFirefox); diff --git a/bin/testcafe-with-v8-flag-filter.js b/bin/testcafe-with-v8-flag-filter.js index 3e892e611f3..787f4891a1e 100755 --- a/bin/testcafe-with-v8-flag-filter.js +++ b/bin/testcafe-with-v8-flag-filter.js @@ -3,12 +3,25 @@ 'use strict'; const path = require('path'); -const v8FlagsFilter = require('bin-v8-flags-filter'); +const url = require('url'); +const v8FlagsFilter = require('@devexpress/bin-v8-flags-filter'); const EXPERIMENTAL_DEBUG_OPTION = '--experimental-debug'; +const EXPERIMENTAL_ESM_OPTION = '--experimental-esm'; if (process.argv.slice(2).includes(EXPERIMENTAL_DEBUG_OPTION)) require('../lib/cli'); -else - v8FlagsFilter(path.join(__dirname, '../lib/cli'), { useShutdownMessage: true }); +else { + const forcedArgs = []; + + if (process.argv.slice(2).includes(EXPERIMENTAL_ESM_OPTION)) { + forcedArgs.push('--no-warnings'); + forcedArgs.push(`--experimental-loader=${url.pathToFileURL(path.join(__dirname, '../lib/compiler/esm-loader.js')).href}`); + } + + v8FlagsFilter(path.join(__dirname, '../lib/cli'), { + useShutdownMessage: true, + forcedArgs, + }); +} diff --git a/gulp/esm-package/package.json b/gulp/esm-package/package.json new file mode 100644 index 00000000000..bb34440a365 --- /dev/null +++ b/gulp/esm-package/package.json @@ -0,0 +1 @@ +{ "type": "module" } \ No newline at end of file diff --git a/gulp/helpers/create-package-files-for-tests.js b/gulp/helpers/create-package-files-for-tests.js new file mode 100644 index 00000000000..2e99fe23969 --- /dev/null +++ b/gulp/helpers/create-package-files-for-tests.js @@ -0,0 +1,27 @@ +const gulp = require('gulp'); +const globby = require('globby'); +const path = require('path'); +const { Readable } = require('stream'); + + +module.exports = async function createPackageFilesForTests () { + const testFolders = await globby([ + 'test/functional/fixtures/**/testcafe-fixtures', + 'test/functional/fixtures/**/common', + ], { + ignore: ['test/functional/fixtures/**/raw', 'test/functional/fixtures/**/json'], + onlyDirectories: true, + }); + + let stream = new Readable(); + + stream.push('{ "type": "module" }'); + stream.push(null); + + testFolders.forEach(testFolder => { + stream = gulp.src(['gulp/esm-package/package.json']) + .pipe(gulp.dest( path.resolve(testFolder) )); + }); + + return stream; +}; diff --git a/package.json b/package.json index 256868c55bc..dadf1431024 100644 --- a/package.json +++ b/package.json @@ -53,6 +53,9 @@ ], "scripts": { "test": "gulp travis", + "build": "gulp build", + "test-functional-local-headless-chrome-run-esm": "cross-env NODE_OPTIONS='--experimental-loader=./lib/compiler/esm-loader.js' gulp test-functional-local-headless-chrome-run --steps-as-tasks", + "test-functional-local-headless-chrome-esm": "npm run build && npm run test-functional-local-headless-chrome-run-esm", "publish-please-only": "publish-please", "publish-please": "del-cli package-lock.json node_modules && npm i && publish-please", "prepublishOnly": "publish-please guard" @@ -79,7 +82,7 @@ "async-exit-hook": "^1.1.2", "babel-plugin-module-resolver": "^5.0.0", "babel-plugin-syntax-trailing-function-commas": "^6.22.0", - "bin-v8-flags-filter": "^1.1.2", + "@devexpress/bin-v8-flags-filter": "^1.3.0", "bowser": "^2.8.1", "callsite": "^1.0.0", "callsite-record": "^4.0.0", @@ -184,6 +187,7 @@ "chai-string": "^1.5.0", "chrome-launcher": "^0.15.0", "connect": "^3.4.0", + "cross-env": "^7.0.3", "devtools-protocol": "0.0.1078443", "dom-walk": "^0.1.1", "escape-string-regexp": "^4.0.0", diff --git a/src/api/exportable-lib/index.mjs b/src/api/exportable-lib/index.mjs new file mode 100644 index 00000000000..b3923332409 --- /dev/null +++ b/src/api/exportable-lib/index.mjs @@ -0,0 +1,27 @@ +import exportableLib from './index.js'; + +const { + Role, + ClientFunction, + Selector, + RequestLogger, + RequestMock, + RequestHook, + t, + userVariables, + fixture, + test, +} = exportableLib; + +export { + Role, + ClientFunction, + Selector, + RequestLogger, + RequestMock, + RequestHook, + t, + userVariables, + fixture, + test, +}; diff --git a/src/api/structure/fixture.ts b/src/api/structure/fixture.ts index c3a1e36791f..ad95b16bcd5 100644 --- a/src/api/structure/fixture.ts +++ b/src/api/structure/fixture.ts @@ -14,6 +14,11 @@ import RequestHook from '../request-hooks/hook'; import ClientScriptInit from '../../custom-client-scripts/client-script-init'; import { SPECIAL_BLANK_PAGE } from 'testcafe-hammerhead'; +interface FixtureInitOptions { + baseUrl?: string; + testFile: TestFile; +} + export default class Fixture extends TestingUnit { public path: string; public beforeEachFn: Function | null; @@ -39,6 +44,14 @@ export default class Fixture extends TestingUnit { return this.apiOrigin as unknown as Fixture; } + public static init (initOptions: FixtureInitOptions, name: string, ...rest: unknown[]): Fixture | null { + const { testFile, baseUrl } = initOptions; + + const fixture = new Fixture(testFile, baseUrl); + + return (fixture as unknown as Function)(name, ...rest); + } + protected _add (name: string, ...rest: unknown[]): Function { name = handleTagArgs(name, rest); diff --git a/src/api/structure/test.ts b/src/api/structure/test.ts index 38156ec493e..824697b43e6 100644 --- a/src/api/structure/test.ts +++ b/src/api/structure/test.ts @@ -17,6 +17,11 @@ import { TestTimeouts } from './interfaces'; import TestTimeout from './test-timeout'; import ESM_RUNTIME_HOLDER_NAME from '../../services/compiler/esm-runtime-holder-name'; +interface TestInitOptions { + testFile: TestFile; + baseUrl?: string; + isCompilerServiceMode?: boolean; +} export default class Test extends TestingUnit { public fixture: Fixture | null; @@ -55,12 +60,21 @@ export default class Test extends TestingUnit { return this.apiOrigin as unknown as Test; } + public static init (initOptions: TestInitOptions, name: string, fn: Function): Test { + const { testFile, baseUrl, isCompilerServiceMode } = initOptions; + + const test = new Test(testFile, isCompilerServiceMode, baseUrl); + + return (test as unknown as Function)(name, fn); + } + private _initFixture (testFile: TestFile): void { this.fixture = testFile.currentFixture; if (!this.fixture) return; + this.pageUrl = this.fixture.pageUrl || SPECIAL_BLANK_PAGE; this.requestHooks = this.fixture.requestHooks.slice(); this.clientScripts = this.fixture.clientScripts.slice(); this.skipJsErrorsOptions = this.fixture.skipJsErrorsOptions; diff --git a/src/cli/argument-parser/index.ts b/src/cli/argument-parser/index.ts index e6cc4a228f4..5c1f0e7266d 100644 --- a/src/cli/argument-parser/index.ts +++ b/src/cli/argument-parser/index.ts @@ -207,8 +207,9 @@ export default class CLIArgumentParser { .option('--color', 'force TestCafe to format CLI output with color') .option('--no-color', 'disable text color formatting in the CLI') - // NOTE: temporary hide experimental options from --help command - .addOption(new Option('--experimental-debug', 'enable experimental debug mode').hideHelp()) + // NOTE: Temporarily exclude experimental options from --help output + .addOption(new Option('--experimental-debug', 'enable experimental the debug mode').hideHelp()) + .addOption(new Option('--experimental-esm', 'enable experimental the esm mode').hideHelp()) .addOption(new Option('--disable-cross-domain', 'experimental').hideHelp()) .action((opts: CommandLineOptions) => { this.opts = opts; diff --git a/src/cli/cli.js b/src/cli/cli.js index 4ec57685290..376efe52cce 100644 --- a/src/cli/cli.js +++ b/src/cli/cli.js @@ -87,6 +87,7 @@ async function runTests (argParser) { v8Flags, experimentalProxyless, disableCrossDomain, + experimentalEsm, } = opts; const testCafe = await createTestCafe({ @@ -105,6 +106,7 @@ async function runTests (argParser) { v8Flags, experimentalProxyless, disableCrossDomain, + experimentalEsm, }); const correctedBrowsersAndSources = await correctBrowsersAndSources(argParser, testCafe.configuration); diff --git a/src/client/browser/index.js b/src/client/browser/index.js index bc9861faf05..bddb5f39233 100644 --- a/src/client/browser/index.js +++ b/src/client/browser/index.js @@ -4,6 +4,7 @@ import COMMAND from '../../browser/connection/command'; import HeartbeatStatus from '../../browser/connection/heartbeat-status'; import { HEARTBEAT_INTERVAL } from '../../utils/browser-connection-timeouts'; import SERVICE_ROUTES from '../../browser/connection/service-routes'; +import isFileProtocol from '../../shared/utils/is-file-protocol'; /*eslint-disable no-restricted-properties*/ const LOCATION_HREF = document.location.href; @@ -15,8 +16,6 @@ const MAX_STATUS_RETRY = 5; const SERVICE_WORKER_LOCATION = LOCATION_ORIGIN + SERVICE_ROUTES.serviceWorker; -const FILE_PROTOCOL_ORIGIN = 'file://'; - let allowInitScriptExecution = false; let heartbeatIntervalId = null; @@ -58,10 +57,6 @@ function isCurrentLocation (url) { return LOCATION_HREF.toLowerCase() === url.toLowerCase(); } -function isFileProtocol (url = '') { - return url.indexOf(FILE_PROTOCOL_ORIGIN) === 0; -} - //API export function startHeartbeat (heartbeatUrl, createXHR) { function heartbeat () { diff --git a/src/compiler/babel/load-libs.js b/src/compiler/babel/load-libs.js index 12f52ec12f6..65c3f287220 100644 --- a/src/compiler/babel/load-libs.js +++ b/src/compiler/babel/load-libs.js @@ -1,13 +1,13 @@ -import EXPORTABLE_LIB_PATH from '../test-file/exportble-lib-path'; +import getExportableLibPath from '../test-file/get-exportable-lib-path'; -function getPresetEnvForTestCodeOpts (isCompilerServiceMode) { +function getPresetEnvForTestCodeOpts (dontUseModules) { const opts = { targets: { node: 'current' }, loose: true, exclude: ['transform-regenerator'], }; - if (isCompilerServiceMode) + if (dontUseModules) opts.modules = false; return opts; @@ -20,11 +20,12 @@ function getPresetEnvForClientFunctionOpts () { }; } -function getModuleResolverOpts () { +function getModuleResolverOpts (experimentalEsm) { return { resolvePath (source) { + //NOTE: Prevent module caching to import 'fixture' and 'test' in ESM mode. if (source === 'testcafe') - return EXPORTABLE_LIB_PATH; + return getExportableLibPath(experimentalEsm); return source; }, @@ -55,7 +56,7 @@ function getPresetReact () { } // NOTE: lazy load heavy dependencies -export default function loadLibs (isCompilerServiceMode) { +export default function loadLibs ({ isCompilerServiceMode, experimentalEsm } = {}) { return { babel: require('@babel/core'), presetStage2: require('./preset-stage-2'), @@ -63,8 +64,8 @@ export default function loadLibs (isCompilerServiceMode) { transformRuntime: [require('@babel/plugin-transform-runtime'), getTransformRuntimeOpts()], transformForOfAsArray: [require('@babel/plugin-transform-for-of'), getTransformForOfOptions()], presetEnvForClientFunction: [require('@babel/preset-env'), getPresetEnvForClientFunctionOpts()], - presetEnvForTestCode: [require('@babel/preset-env'), getPresetEnvForTestCodeOpts(isCompilerServiceMode)], - moduleResolver: [require('babel-plugin-module-resolver'), getModuleResolverOpts()], + presetEnvForTestCode: [require('@babel/preset-env'), getPresetEnvForTestCodeOpts(isCompilerServiceMode || experimentalEsm)], + moduleResolver: [require('babel-plugin-module-resolver'), getModuleResolverOpts(experimentalEsm)], presetReact: getPresetReact(), proposalPrivateMethods: [require('@babel/plugin-proposal-private-methods'), { loose: true }], proposalClassProperties: [require('@babel/plugin-proposal-class-properties'), { loose: true }], diff --git a/src/compiler/compilers.js b/src/compiler/compilers.js index 41fcc6f0677..850078f62a7 100644 --- a/src/compiler/compilers.js +++ b/src/compiler/compilers.js @@ -7,12 +7,12 @@ import RawTestFileCompiler from './test-file/formats/raw/compiler'; import DevToolsTestFileCompiler from './test-file/formats/dev-tools/compiler'; import CustomizableCompilers from '../configuration/customizable-compilers'; -function createTestFileCompilers (compilerOptions = {}, { isCompilerServiceMode, baseUrl } = {}) { +function createTestFileCompilers (compilerOptions = {}, { isCompilerServiceMode, baseUrl, experimentalEsm } = {}) { return [ new LegacyTestFileCompiler(hammerhead.processScript), - new EsNextTestFileCompiler({ isCompilerServiceMode, baseUrl }), - new TypeScriptTestFileCompiler(compilerOptions[CustomizableCompilers.typescript], { isCompilerServiceMode, baseUrl }), - new CoffeeScriptTestFileCompiler({ baseUrl }), + new EsNextTestFileCompiler({ isCompilerServiceMode, baseUrl, experimentalEsm }), + new TypeScriptTestFileCompiler(compilerOptions[CustomizableCompilers.typescript], { isCompilerServiceMode, baseUrl, experimentalEsm }), + new CoffeeScriptTestFileCompiler({ baseUrl, experimentalEsm }), new RawTestFileCompiler({ baseUrl }), new DevToolsTestFileCompiler({ baseUrl }), ]; @@ -27,6 +27,6 @@ export function getTestFileCompilers () { return testFileCompilers; } -export function initTestFileCompilers (compilerOptions, { isCompilerServiceMode, baseUrl } = {}) { - testFileCompilers = createTestFileCompilers(compilerOptions, { isCompilerServiceMode, baseUrl }); +export function initTestFileCompilers (compilerOptions, { isCompilerServiceMode, baseUrl, experimentalEsm } = {}) { + testFileCompilers = createTestFileCompilers(compilerOptions, { isCompilerServiceMode, baseUrl, experimentalEsm }); } diff --git a/src/compiler/esm-loader.ts b/src/compiler/esm-loader.ts new file mode 100644 index 00000000000..b9ec9a05eae --- /dev/null +++ b/src/compiler/esm-loader.ts @@ -0,0 +1,50 @@ +import APIBasedTestFileCompilerBase from './test-file/api-based'; +import urlUtils from 'url'; +import Compiler from './index'; +import { fileContentsCache } from '../utils/setup-sourcemap-support'; + +interface Load { + format: string; + shortCircuit?: boolean; + source: string | ArrayBuffer; +} + +interface Context { + conditions: string[]; + format?: string | null; + importAssertions: Record; +} + +function getFilenameFromURL (url: string): string | null { + try { + return urlUtils.fileURLToPath(url); + } + catch (_) { + return null; + } +} + +export async function load (url: string, context: Context, defaultLoad: Function): Promise { + const isNodeModulesDep = APIBasedTestFileCompilerBase._isNodeModulesDep(url); + const isTestcafeLibDep = APIBasedTestFileCompilerBase._isTestCafeLibDep(url); + const filename = getFilenameFromURL(url); + + if (isNodeModulesDep || isTestcafeLibDep || !filename) + return defaultLoad(url, context, defaultLoad); + + const testFilesInfo = await Compiler.createTestFileInfo(filename); + + if (testFilesInfo?.compiler) { + const [compiledCode] = await testFilesInfo.compiler.precompile([testFilesInfo]); + + fileContentsCache[url] = compiledCode; + + return { + format: context.format || 'module', + source: compiledCode, + shortCircuit: true, + }; + } + + return defaultLoad(url, context, defaultLoad); +} diff --git a/src/compiler/index.js b/src/compiler/index.js index bd2722b5109..23d602c4dad 100644 --- a/src/compiler/index.js +++ b/src/compiler/index.js @@ -15,17 +15,18 @@ import { getTestFileCompilers, initTestFileCompilers } from './compilers'; const SOURCE_CHUNK_LENGTH = 1000; export default class Compiler { - constructor (sources, compilerOptions, { isCompilerServiceMode, baseUrl } = {} ) { + constructor (sources, compilerOptions, { isCompilerServiceMode, baseUrl, experimentalEsm } = {} ) { this.sources = sources; + this.experimentalEsm = experimentalEsm; - initTestFileCompilers(compilerOptions, { isCompilerServiceMode, baseUrl }); + initTestFileCompilers(compilerOptions, { isCompilerServiceMode, baseUrl, experimentalEsm }); } static getSupportedTestFileExtensions () { return uniq(flattenDeep(getTestFileCompilers().map(compiler => compiler.getSupportedExtension()))); } - async _createTestFileInfo (filename) { + static async createTestFileInfo (filename) { let code = null; try { @@ -52,13 +53,13 @@ export default class Compiler { } async _createTestFilesInfo (filenames) { - const testFilesInfo = await Promise.all(filenames.map(filename => this._createTestFileInfo(filename))); + const testFilesInfo = await Promise.all(filenames.map(filename => Compiler.createTestFileInfo(filename))); return testFilesInfo.filter(info => !!info); } async _precompileFiles (compiler, testFilesInfo) { - if (!compiler.canPrecompile) + if (!compiler.canPrecompile || this.experimentalEsm && compiler.canCompileInEsm) return; const precompiledCode = await compiler.precompile(testFilesInfo); @@ -86,7 +87,7 @@ export default class Compiler { } async _getTests ({ compiler, filename, code, compiledCode }) { - if (compiledCode) + if (compiledCode || this.experimentalEsm && compiler.canCompileInEsm) return await compiler.execute(compiledCode, filename); return await compiler.compile(code, filename); diff --git a/src/compiler/interfaces.ts b/src/compiler/interfaces.ts index 5a2999982eb..4b3ec663fc4 100644 --- a/src/compiler/interfaces.ts +++ b/src/compiler/interfaces.ts @@ -7,4 +7,5 @@ export interface CompilerArguments { export interface OptionalCompilerArguments { isCompilerServiceMode?: boolean; baseUrl?: string; + experimentalEsm?: boolean; } diff --git a/src/compiler/prevent-module-caching-suffix.ts b/src/compiler/prevent-module-caching-suffix.ts new file mode 100644 index 00000000000..b11b61c9388 --- /dev/null +++ b/src/compiler/prevent-module-caching-suffix.ts @@ -0,0 +1,3 @@ +const PREVENT_MODULE_CACHING_SUFFIX = 'preventModuleCachingSuffix'; + +export default PREVENT_MODULE_CACHING_SUFFIX; diff --git a/src/compiler/test-file/add-export-api.ts b/src/compiler/test-file/add-export-api.ts index cd03ae3b02b..86e63962c2b 100644 --- a/src/compiler/test-file/add-export-api.ts +++ b/src/compiler/test-file/add-export-api.ts @@ -8,18 +8,12 @@ export default function (testFile: TestFile, exportableLibExports: any, { baseUrl, }: OptionalCompilerArguments = {}): void { Object.defineProperty(exportableLibExports, 'fixture', { - get: () => new Fixture(testFile, baseUrl), + get: () => Fixture.init.bind(Fixture, { testFile, baseUrl }), configurable: true, }); Object.defineProperty(exportableLibExports, 'test', { - get: () => { - // NOTE: After wrapping the "import { test } from 'testcafe'" statement - // in service functions of the 'esm' module - // the 'test' directive executed a few times before the 'fixture' directive. - // We need to pass an additional flag to ensure correct 'Test' function loading. - return new Test(testFile, isCompilerServiceMode, baseUrl); - }, + get: () => Test.init.bind(Test, { testFile, isCompilerServiceMode, baseUrl }), configurable: true, }); } diff --git a/src/compiler/test-file/api-based.js b/src/compiler/test-file/api-based.js index d4d5e7c1d77..b051eccc37f 100644 --- a/src/compiler/test-file/api-based.js +++ b/src/compiler/test-file/api-based.js @@ -11,13 +11,20 @@ import TestFileCompilerBase from './base'; import TestFile from '../../api/structure/test-file'; import Fixture from '../../api/structure/fixture'; import Test from '../../api/structure/test'; -import { TestCompilationError, APIError } from '../../errors/runtime'; +import { + TestCompilationError, + APIError, + ImportESMInCommonJSError, +} from '../../errors/runtime'; import stackCleaningHook from '../../errors/stack-cleaning-hook'; import NODE_MODULES from '../../utils/node-modules-folder-name'; import cacheProxy from './cache-proxy'; import exportableLib from '../../api/exportable-lib'; import TEST_FILE_TEMP_VARIABLE_NAME from './test-file-temp-variable-name'; import addExportAPI from './add-export-api'; +import url from 'url'; +import PREVENT_MODULE_CACHING_SUFFIX from '../prevent-module-caching-suffix'; + const CWD = process.cwd(); @@ -28,14 +35,17 @@ const TESTCAFE_LIB_FOLDER_NAME = 'lib'; const Module = module.constructor; +const errRequireEsmErrorCode = 'ERR_REQUIRE_ESM'; + export default class APIBasedTestFileCompilerBase extends TestFileCompilerBase { - constructor ({ isCompilerServiceMode, baseUrl }) { + constructor ({ isCompilerServiceMode, baseUrl, experimentalEsm }) { super({ baseUrl }); this.isCompilerServiceMode = isCompilerServiceMode; this.cache = Object.create(null); this.origRequireExtensions = Object.create(null); this.cachePrefix = nanoid(7); + this.experimentalEsm = experimentalEsm; } static _getNodeModulesLookupPath (filename) { @@ -52,20 +62,30 @@ export default class APIBasedTestFileCompilerBase extends TestFileCompilerBase { static _isTestCafeLibDep (filename) { return relative(CWD, filename) - .split(pathSep)[0] === TESTCAFE_LIB_FOLDER_NAME; + .split(pathSep) + .includes(TESTCAFE_LIB_FOLDER_NAME); } - _execAsModule (code, filename) { - const mod = new Module(filename, module.parent); + async _execAsModule (code, filename) { + if (this.experimentalEsm) { + const fileUrl = url.pathToFileURL(filename); - mod.filename = filename; - mod.paths = APIBasedTestFileCompilerBase._getNodeModulesLookupPath(filename); + //NOTE: It is necessary to prevent module caching during live mode. + // eslint-disable-next-line no-eval + await eval(`import('${fileUrl}?${PREVENT_MODULE_CACHING_SUFFIX}=${Date.now()}')`); + } + else { + const mod = new Module(filename, module.parent); + + mod.filename = filename; + mod.paths = APIBasedTestFileCompilerBase._getNodeModulesLookupPath(filename); - cacheProxy.startExternalCaching(this.cachePrefix); + cacheProxy.startExternalCaching(this.cachePrefix); - mod._compile(code, filename); + mod._compile(code, filename); - cacheProxy.stopExternalCaching(); + cacheProxy.stopExternalCaching(); + } } _compileCode (code, filename) { @@ -209,7 +229,7 @@ export default class APIBasedTestFileCompilerBase extends TestFileCompilerBase { return global.fixture && global.test; } - _runCompiledCode (compiledCode, filename) { + async _runCompiledCode (compiledCode, filename) { const testFile = new TestFile(filename); this._addGlobalAPI(testFile); @@ -220,9 +240,12 @@ export default class APIBasedTestFileCompilerBase extends TestFileCompilerBase { this._setupRequireHook(testFile); try { - this._execAsModule(compiledCode, filename); + await this._execAsModule(compiledCode, filename); } catch (err) { + if (err.code === errRequireEsmErrorCode) + throw new ImportESMInCommonJSError(err, filename); + if (!(err instanceof APIError)) throw new TestCompilationError(stackCleaningHook.cleanError(err)); @@ -232,7 +255,8 @@ export default class APIBasedTestFileCompilerBase extends TestFileCompilerBase { this._removeRequireHook(); stackCleaningHook.enabled = false; - this._removeGlobalAPI(); + if (!this.experimentalEsm) + this._removeGlobalAPI(); } return testFile.getTests(); diff --git a/src/compiler/test-file/base.js b/src/compiler/test-file/base.js index 2ac88c93ed7..e1d9c3ffade 100644 --- a/src/compiler/test-file/base.js +++ b/src/compiler/test-file/base.js @@ -49,6 +49,10 @@ export default class TestFileCompilerBase { return this.supportedExtensionRe.test(filename); } + get canCompileInEsm () { + return false; + } + get canPrecompile () { return false; } diff --git a/src/compiler/test-file/exportble-lib-path.ts b/src/compiler/test-file/exportble-lib-path.ts deleted file mode 100644 index a4a3c7fa230..00000000000 --- a/src/compiler/test-file/exportble-lib-path.ts +++ /dev/null @@ -1,5 +0,0 @@ -import { join } from 'path'; - -const EXPORTABLE_LIB_PATH = join(__dirname, '../../api/exportable-lib'); - -export default EXPORTABLE_LIB_PATH; diff --git a/src/compiler/test-file/formats/coffeescript/compiler.js b/src/compiler/test-file/formats/coffeescript/compiler.js index 7497faec99b..00bd06647b4 100644 --- a/src/compiler/test-file/formats/coffeescript/compiler.js +++ b/src/compiler/test-file/formats/coffeescript/compiler.js @@ -1,6 +1,7 @@ import CoffeeScript from 'coffeescript'; import loadBabelLibs from '../../../babel/load-libs'; import ESNextTestFileCompiler from '../es-next/compiler.js'; +import Extensions from '../extensions'; const FIXTURE_RE = /(^|;|\s+)fixture\s*(\.|\(|'|")/; const TEST_RE = /(^|;|\s+)test\s*/; @@ -23,7 +24,7 @@ export default class CoffeeScriptTestFileCompiler extends ESNextTestFileCompiler }); const { babel } = loadBabelLibs(); - const babelOptions = ESNextTestFileCompiler.getBabelOptions(filename, code); + const babelOptions = ESNextTestFileCompiler.getBabelOptions(filename, code, this); const compiled = babel.transform(transpiled.js, babelOptions); this.cache[filename] = compiled.code; @@ -32,10 +33,10 @@ export default class CoffeeScriptTestFileCompiler extends ESNextTestFileCompiler } _getRequireCompilers () { - return { '.coffee': (code, filename) => this._compileCode(code, filename) }; + return { [Extensions.coffee]: (code, filename) => this._compileCode(code, filename) }; } getSupportedExtension () { - return '.coffee'; + return Extensions.coffee; } } diff --git a/src/compiler/test-file/formats/es-next/compiler.js b/src/compiler/test-file/formats/es-next/compiler.js index 5300b0291c1..77a202b6d01 100644 --- a/src/compiler/test-file/formats/es-next/compiler.js +++ b/src/compiler/test-file/formats/es-next/compiler.js @@ -3,15 +3,17 @@ import APIBasedTestFileCompilerBase from '../../api-based'; import isFlowCode from './is-flow-code'; import BASE_BABEL_OPTIONS from '../../../babel/get-base-babel-options'; import DISABLE_V8_OPTIMIZATION_NOTE from '../../disable-v8-optimization-note'; +import Extensions from '../extensions'; +//NOTE: The semicolon ; prevents the declaration from being bound with eval const DISABLE_V8_OPTIMIZATION_CODE = -`/*${DISABLE_V8_OPTIMIZATION_NOTE}*/ +`;/*${DISABLE_V8_OPTIMIZATION_NOTE}*/ eval(""); `; export default class ESNextTestFileCompiler extends APIBasedTestFileCompilerBase { - static getBabelOptions (filename, code, isCompilerServiceMode) { + static getBabelOptions (filename, code, { isCompilerServiceMode, experimentalEsm } = {}) { const { presetStage2, presetFlow, @@ -21,7 +23,7 @@ export default class ESNextTestFileCompiler extends APIBasedTestFileCompilerBase moduleResolver, proposalPrivateMethods, proposalClassProperties, - } = loadBabelLibs(isCompilerServiceMode); + } = loadBabelLibs({ isCompilerServiceMode, experimentalEsm }); const opts = Object.assign({}, BASE_BABEL_OPTIONS, { presets: [presetStage2, presetEnvForTestCode, presetReact], @@ -37,15 +39,15 @@ export default class ESNextTestFileCompiler extends APIBasedTestFileCompilerBase } _compileCode (code, filename) { - const { babel } = loadBabelLibs(); + const { babel } = loadBabelLibs(this); if (this.cache[filename]) return this.cache[filename]; - if (this.isCompilerServiceMode) + if (this.isCompilerServiceMode || this.experimentalEsm) code += DISABLE_V8_OPTIMIZATION_CODE; - const opts = ESNextTestFileCompiler.getBabelOptions(filename, code, this.isCompilerServiceMode); + const opts = ESNextTestFileCompiler.getBabelOptions(filename, code, this); const compiled = babel.transform(code, opts); this.cache[filename] = compiled.code; @@ -54,13 +56,28 @@ export default class ESNextTestFileCompiler extends APIBasedTestFileCompilerBase } _getRequireCompilers () { - return { - '.js': (code, filename) => this._compileCode(code, filename), - '.jsx': (code, filename) => this._compileCode(code, filename), + const requireCompilers = { + [Extensions.js]: (code, filename) => this._compileCode(code, filename), + [Extensions.jsx]: (code, filename) => this._compileCode(code, filename), + [Extensions.cjs]: (code, filename) => this._compileCode(code, filename), }; + + if (this.experimentalEsm) + requireCompilers[Extensions.mjs] = (code, filename) => this._compileCode(code, filename); + + return requireCompilers; + } + + get canCompileInEsm () { + return true; } getSupportedExtension () { - return ['.js', '.jsx']; + const supportedExtensions = [Extensions.js, Extensions.jsx, Extensions.cjs]; + + if (this.experimentalEsm) + supportedExtensions.push(Extensions.mjs); + + return supportedExtensions; } } diff --git a/src/compiler/test-file/formats/extensions.ts b/src/compiler/test-file/formats/extensions.ts new file mode 100644 index 00000000000..4cef5d334b8 --- /dev/null +++ b/src/compiler/test-file/formats/extensions.ts @@ -0,0 +1,11 @@ +enum Extensions { + js = '.js', + cjs = '.cjs', + mjs = '.mjs', + ts = '.ts', + jsx = '.jsx', + tsx = '.tsx', + coffee = '.coffee', +} + +export default Extensions; diff --git a/src/compiler/test-file/formats/typescript/compiler.ts b/src/compiler/test-file/formats/typescript/compiler.ts index 97bf2541679..9847f0a84e8 100644 --- a/src/compiler/test-file/formats/typescript/compiler.ts +++ b/src/compiler/test-file/formats/typescript/compiler.ts @@ -8,7 +8,7 @@ import { GeneralError } from '../../../../errors/runtime'; import { RUNTIME_ERRORS } from '../../../../errors/types'; import debug from 'debug'; import { isRelative } from '../../../../api/test-page-url'; -import EXPORTABLE_LIB_PATH from '../../exportble-lib-path'; +import getExportableLibPath from '../../get-exportable-lib-path'; import DISABLE_V8_OPTIMIZATION_NOTE from '../../disable-v8-optimization-note'; // NOTE: For type definitions only @@ -27,6 +27,7 @@ import TypeScript, { import { Dictionary, TypeScriptCompilerOptions } from '../../../../configuration/interfaces'; import { OptionalCompilerArguments } from '../../../interfaces'; +import Extensions from '../extensions'; declare type TypeScriptInstance = typeof TypeScript; @@ -44,12 +45,15 @@ interface RequireCompilers { [extension: string]: RequireCompilerFunction; } -function testcafeImportPathReplacer (): TransformerFactory { +function testcafeImportPathReplacer (experimentalEsm?: boolean): TransformerFactory { return context => { const visit: Visitor = (node): VisitResult => { // @ts-ignore - if (node.parent?.kind === SyntaxKind.ImportDeclaration && node.kind === SyntaxKind.StringLiteral && node.text === 'testcafe') - return tsFactory.createStringLiteral(EXPORTABLE_LIB_PATH); + if (node.parent?.kind === SyntaxKind.ImportDeclaration && node.kind === SyntaxKind.StringLiteral && node.text === 'testcafe') { + const libPath = getExportableLibPath(experimentalEsm); + + return tsFactory.createStringLiteral(libPath); + } return visitEachChild(node, child => visit(child), context); }; @@ -80,7 +84,7 @@ function disableV8OptimizationCodeAppender (): TransformerFactor const DEBUG_LOGGER = debug('testcafe:compiler:typescript'); -const RENAMED_DEPENDENCIES_MAP = new Map([['testcafe', EXPORTABLE_LIB_PATH]]); +const RENAMED_DEPENDENCIES_MAP = new Map([['testcafe', getExportableLibPath()]]); const DEFAULT_TYPESCRIPT_COMPILER_PATH = 'typescript'; @@ -91,8 +95,8 @@ export default class TypeScriptTestFileCompiler extends APIBasedTestFileCompiler private readonly _compilerPath: string; private readonly _customCompilerOptions?: object; - public constructor (compilerOptions?: TypeScriptCompilerOptions, { isCompilerServiceMode, baseUrl }: OptionalCompilerArguments = {}) { - super({ isCompilerServiceMode, baseUrl }); + public constructor (compilerOptions?: TypeScriptCompilerOptions, { isCompilerServiceMode, baseUrl, experimentalEsm }: OptionalCompilerArguments = {}) { + super({ isCompilerServiceMode, baseUrl, experimentalEsm }); // NOTE: At present, it's necessary create an instance TypeScriptTestFileCompiler // to collect a list of supported test file extensions. @@ -103,7 +107,7 @@ export default class TypeScriptTestFileCompiler extends APIBasedTestFileCompiler const configPath = compilerOptions && compilerOptions.configPath || null; this._customCompilerOptions = compilerOptions && compilerOptions.options; - this._tsConfig = new TypescriptConfiguration(configPath, isCompilerServiceMode); + this._tsConfig = new TypescriptConfiguration(configPath, isCompilerServiceMode || experimentalEsm); this._compilerPath = TypeScriptTestFileCompiler._getCompilerPath(compilerOptions); } @@ -207,9 +211,9 @@ export default class TypeScriptTestFileCompiler extends APIBasedTestFileCompiler } private _getTypescriptTransformers (): TransformerFactory[] { - const transformers: TransformerFactory[] = [testcafeImportPathReplacer()]; + const transformers: TransformerFactory[] = [testcafeImportPathReplacer(this.experimentalEsm)]; - if (this.isCompilerServiceMode) + if (this.isCompilerServiceMode || this.experimentalEsm) transformers.push(disableV8OptimizationCodeAppender()); return transformers; @@ -235,19 +239,29 @@ export default class TypeScriptTestFileCompiler extends APIBasedTestFileCompiler } public _getRequireCompilers (): RequireCompilers { - return { - '.ts': (code, filename) => this._compileCode(code, filename), - '.tsx': (code, filename) => this._compileCode(code, filename), - '.js': (code, filename) => ESNextTestFileCompiler.prototype._compileCode.call(this, code, filename), - '.jsx': (code, filename) => ESNextTestFileCompiler.prototype._compileCode.call(this, code, filename), + const requireCompilers: RequireCompilers = { + [Extensions.ts]: (code, filename) => this._compileCode(code, filename), + [Extensions.tsx]: (code, filename) => this._compileCode(code, filename), + [Extensions.js]: (code, filename) => ESNextTestFileCompiler.prototype._compileCode.call(this, code, filename), + [Extensions.cjs]: (code, filename) => ESNextTestFileCompiler.prototype._compileCode.call(this, code, filename), + [Extensions.jsx]: (code, filename) => ESNextTestFileCompiler.prototype._compileCode.call(this, code, filename), }; + + if (this.experimentalEsm) + requireCompilers[Extensions.mjs] = (code, filename) => ESNextTestFileCompiler.prototype._compileCode.call(this, code, filename); + + return requireCompilers; } public get canPrecompile (): boolean { return true; } + public get canCompileInEsm (): boolean { + return true; + } + public getSupportedExtension (): string[] { - return ['.ts', '.tsx']; + return [Extensions.ts, Extensions.tsx]; } } diff --git a/src/compiler/test-file/get-exportable-lib-path.ts b/src/compiler/test-file/get-exportable-lib-path.ts new file mode 100644 index 00000000000..d65b1b5ef87 --- /dev/null +++ b/src/compiler/test-file/get-exportable-lib-path.ts @@ -0,0 +1,11 @@ +import { join } from 'path'; +import { pathToFileURL } from 'url'; +import PREVENT_MODULE_CACHING_SUFFIX from '../prevent-module-caching-suffix'; + +const EXPORTABLE_LIB_PATH = join(__dirname, '../../api/exportable-lib/index.js'); +const EXPORTABLE_LIB_ESM_PATH = pathToFileURL(join(__dirname, '../../api/exportable-lib/index.mjs')).href; + +export default function (experimentalEsm?: boolean): string { + //NOTE: Prevent module caching to import 'fixture' and 'test' in ESM mode. + return experimentalEsm ? `${EXPORTABLE_LIB_ESM_PATH}?${PREVENT_MODULE_CACHING_SUFFIX}=${Date.now()}` : EXPORTABLE_LIB_PATH; +} diff --git a/src/configuration/option-names.ts b/src/configuration/option-names.ts index e5e6f7ca9d2..b8c6852b83b 100644 --- a/src/configuration/option-names.ts +++ b/src/configuration/option-names.ts @@ -56,6 +56,7 @@ enum OptionNames { dashboard = 'dashboard', baseUrl = 'baseUrl', disableCrossDomain = 'disableCrossDomain', + experimentalEsm = 'experimentalEsm', customActions = 'customActions', } diff --git a/src/errors/is-internal-stack-frame.ts b/src/errors/is-internal-stack-frame.ts index 3961e08ba84..ae182caf3e2 100644 --- a/src/errors/is-internal-stack-frame.ts +++ b/src/errors/is-internal-stack-frame.ts @@ -2,6 +2,7 @@ import { sep, join } from 'path'; import { escapeRegExp as escapeRe } from 'lodash'; import INTERNAL_MODULES_PREFIX from './internal-modules-prefix'; import { StackFrame } from 'error-stack-parser'; +import isFileProtocol from '../shared/utils/is-file-protocol'; const BABEL = require.resolve('@babel/core'); const BABEL_MODULES_DIR = BABEL.replace(new RegExp(`^(.*${escapeRe(sep)}node_modules${escapeRe(sep)})(.*)`), '$1'); @@ -33,7 +34,7 @@ const INTERNAL_INCLUDES_PATH_SEGMENTS = [ function isInternalFile (filename = ''): boolean { return !filename || - !filename.includes(sep) || + !(filename.includes(sep) || isFileProtocol(filename)) || INTERNAL_INCLUDES_PATH_SEGMENTS.some(pathSegment => filename.includes(pathSegment)) || INTERNAL_STARTS_WITH_PATH_SEGMENTS.some(pathSegment => filename.startsWith(pathSegment)); } diff --git a/src/errors/process-test-fn-error.js b/src/errors/process-test-fn-error.js index 0d02d74ee1f..f3c7e9f89f0 100644 --- a/src/errors/process-test-fn-error.js +++ b/src/errors/process-test-fn-error.js @@ -12,6 +12,7 @@ import { UncaughtExceptionError, } from './test-run'; import debug from 'debug'; +import isFileProtocol from '../shared/utils/is-file-protocol'; const debugLog = debug('testcafe:errors'); @@ -21,7 +22,7 @@ function isAssertionErrorCallsiteFrame (frame) { // NOTE: filter out the internals of node.js and assertion libraries return filename && - filename.includes(sep) && + (filename.includes(sep) || isFileProtocol(filename)) && !filename.startsWith(INTERNAL_MODULES_PREFIX) && !filename.includes(`${sep}${NODE_MODULES}${sep}`); } diff --git a/src/errors/runtime/index.js b/src/errors/runtime/index.js index bd1937aff3f..a1e1eefd28a 100644 --- a/src/errors/runtime/index.js +++ b/src/errors/runtime/index.js @@ -6,6 +6,8 @@ import renderCallsiteSync from '../../utils/render-callsite-sync'; import { RUNTIME_ERRORS } from '../types'; import getRenderers from '../../utils/get-renderes'; import util from 'util'; +import semver from 'semver'; +import { removePreventModuleCachingSuffix } from '../test-run/utils'; const ERROR_SEPARATOR = '\n\n'; @@ -35,7 +37,7 @@ export class GeneralError extends Error { export class TestCompilationError extends Error { constructor (originalError) { const template = TEMPLATES[RUNTIME_ERRORS.cannotPrepareTestsDueToError]; - const errorMessage = originalError.toString(); + const errorMessage = removePreventModuleCachingSuffix(originalError.toString()); super(renderTemplate(template, errorMessage)); @@ -45,7 +47,7 @@ export class TestCompilationError extends Error { }); // NOTE: stack includes message as well. - this.stack = renderTemplate(template, originalError.stack); + this.stack = renderTemplate(template, removePreventModuleCachingSuffix(originalError.stack)); } } @@ -170,3 +172,18 @@ export class SkipJsErrorsArgumentApiError extends APIError { super('skipJsErrors', code, ...args); } } + +export class ImportESMInCommonJSError extends GeneralError { + constructor (originalError, targetFile) { + const esModule = ImportESMInCommonJSError._getESModule(originalError); + + super(RUNTIME_ERRORS.cannotImportESMInCommonsJS, esModule, targetFile); + } + + static _getESModule (err) { + const regExp = semver.gte(process.version, '16.0.0') ? new RegExp(/ES Module (\S*)/) : /ES Module: (\S*)/; + const [, esModule] = err.toString().match(regExp); + + return esModule; + } +} diff --git a/src/errors/runtime/templates.js b/src/errors/runtime/templates.js index d7d3cbe3671..a092711ff09 100644 --- a/src/errors/runtime/templates.js +++ b/src/errors/runtime/templates.js @@ -142,4 +142,5 @@ export default { [RUNTIME_ERRORS.invalidCommandInJsonCompiler]: `TestCafe terminated the test run. The "{path}" file contains an unknown Chrome User Flow action "{action}". Remove the action to continue. Refer to the following article for the definitive list of supported Chrome User Flow actions: https://testcafe.io/documentation/403998/guides/experimental-capabilities/chrome-replay-support#supported-replay-actions`, [RUNTIME_ERRORS.invalidCustomActionsOptionType]: `The value of the customActions option does not belong to type Object. Refer to the following article for custom action setup instructions: ${ DOCUMENTATION_LINKS.CUSTOM_ACTIONS }`, [RUNTIME_ERRORS.invalidCustomActionType]: `TestCafe cannot parse the "{actionName}" action, because the action definition is invalid. Format the definition in accordance with the custom actions guide: ${ DOCUMENTATION_LINKS.CUSTOM_ACTIONS }`, + [RUNTIME_ERRORS.cannotImportESMInCommonsJS]: 'Cannot import the {esModule} ECMAScript module from {targetFile}. Use a dynamic import() statement or enable the --experimental-esm CLI flag.', }; diff --git a/src/errors/test-run/utils.js b/src/errors/test-run/utils.js index 487e2654eee..77d6b44ef68 100644 --- a/src/errors/test-run/utils.js +++ b/src/errors/test-run/utils.js @@ -1,5 +1,6 @@ import dedent from 'dedent'; import { escape as escapeHtml, repeat } from 'lodash'; +import PREVENT_MODULE_CACHING_SUFFIX from '../../compiler/prevent-module-caching-suffix'; import TEST_RUN_PHASE from '../../test-run/phase'; import { TEST_RUN_ERRORS } from '../types'; @@ -71,6 +72,10 @@ export function shouldSkipCallsite (err) { err.code === TEST_RUN_ERRORS.uncaughtException; } +export function removePreventModuleCachingSuffix (err) { + return err.replace(new RegExp(`\\?${PREVENT_MODULE_CACHING_SUFFIX}=\\d*`, 'g'), ''); +} + export function markup (err, msgMarkup, errCallsite = '') { msgMarkup = dedent(`${SUBTITLES[err.testRunPhase]}
${dedent(msgMarkup)}
`); @@ -91,7 +96,7 @@ export function markup (err, msgMarkup, errCallsite = '') { msgMarkup += `\n\n${callsiteMarkup}`; } - return msgMarkup.replace('\t', ' '.repeat(4)); + return removePreventModuleCachingSuffix(msgMarkup.replace('\t', ' '.repeat(4))); } export function renderDiff (diff) { diff --git a/src/errors/types.js b/src/errors/types.js index 915ad37e0c6..6a95c7a2f32 100644 --- a/src/errors/types.js +++ b/src/errors/types.js @@ -184,4 +184,5 @@ export const RUNTIME_ERRORS = { invalidCommandInJsonCompiler: 'E1078', invalidCustomActionsOptionType: 'E1079', invalidCustomActionType: 'E1080', + cannotImportESMInCommonsJS: 'E1081', }; diff --git a/src/runner/bootstrapper.ts b/src/runner/bootstrapper.ts index daa4828a368..62304359558 100644 --- a/src/runner/bootstrapper.ts +++ b/src/runner/bootstrapper.ts @@ -198,7 +198,8 @@ export default class Bootstrapper { } private async _compileTests ({ sourceList, compilerOptions, runnableConfigurationId }: CompilerArguments): Promise { - const baseUrl = this.configuration.getOption(OPTION_NAMES.baseUrl) as string; + const baseUrl = this.configuration.getOption(OPTION_NAMES.baseUrl) as string; + const experimentalEsm = this.configuration.getOption(OPTION_NAMES.experimentalEsm) as boolean; if (this.compilerService) { await this.compilerService.init(); @@ -207,7 +208,7 @@ export default class Bootstrapper { return this.compilerService.getTests({ sourceList, compilerOptions, runnableConfigurationId }, baseUrl); } - const compiler = new Compiler(sourceList, compilerOptions, { baseUrl, isCompilerServiceMode: false }); + const compiler = new Compiler(sourceList, compilerOptions, { baseUrl, isCompilerServiceMode: false, experimentalEsm }); return compiler.getTests(); } diff --git a/src/services/compiler/service.ts b/src/services/compiler/service.ts index 06421b24e38..8b7b383042e 100644 --- a/src/services/compiler/service.ts +++ b/src/services/compiler/service.ts @@ -86,7 +86,7 @@ import { } from '../../errors/test-run'; import { renderHtmlWithoutStack, shouldRenderHtmlWithoutStack } from '../../errors/test-run/render-error-template/utils'; -import setupSourceMapSupport from '../../utils/setup-sourcemap-support'; +import { setupSourceMapSupport } from '../../utils/setup-sourcemap-support'; import { formatError } from '../../utils/handle-errors'; import { SwitchToWindowPredicateError } from '../../shared/errors'; import MessageBus from '../../utils/message-bus'; @@ -322,7 +322,7 @@ class CompilerService implements CompilerProtocol { } public async getTests ({ sourceList, compilerOptions, runnableConfigurationId }: CompilerArguments, baseUrl?: string): Promise { - const compiler = new Compiler(sourceList, compilerOptions, { isCompilerServiceMode: true, baseUrl }); + const compiler = new Compiler(sourceList, compilerOptions, { isCompilerServiceMode: true, baseUrl, experimentalEsm: false }); const tests = await compiler.getTests(); const units = flattenTestStructure(tests); diff --git a/src/shared/utils/is-file-protocol.ts b/src/shared/utils/is-file-protocol.ts new file mode 100644 index 00000000000..3f86cfa0348 --- /dev/null +++ b/src/shared/utils/is-file-protocol.ts @@ -0,0 +1,7 @@ +const FILE_PROTOCOL = 'file:///'; + +function isFileProtocol (url = ''): boolean { + return url.indexOf(FILE_PROTOCOL) === 0; +} + +export default isFileProtocol; diff --git a/src/testcafe.js b/src/testcafe.js index eb8e277842e..67c20a37c6f 100644 --- a/src/testcafe.js +++ b/src/testcafe.js @@ -3,7 +3,7 @@ import { RUNTIME_ERRORS } from './errors/types'; import CONTENT_TYPES from './assets/content-types'; import OPTION_NAMES from './configuration/option-names'; import * as INJECTABLES from './assets/injectables'; -import setupSourceMapSupport from './utils/setup-sourcemap-support'; +import { setupSourceMapSupport } from './utils/setup-sourcemap-support'; const lazyRequire = require('import-lazy')(require); const hammerhead = lazyRequire('testcafe-hammerhead'); diff --git a/src/tsconfig.json b/src/tsconfig.json index 0ac4ae35e06..c9b26abd32d 100644 --- a/src/tsconfig.json +++ b/src/tsconfig.json @@ -4,7 +4,10 @@ "../ts-defs-src", "./" ], - "exclude": ["./client"], + "exclude": [ + "./client", + "./api/exportable-lib/index.mjs" +], "compilerOptions": { "outDir": "../lib", "target": "es2017", diff --git a/src/utils/setup-sourcemap-support.ts b/src/utils/setup-sourcemap-support.ts index fc469554bdf..1000b1a3178 100644 --- a/src/utils/setup-sourcemap-support.ts +++ b/src/utils/setup-sourcemap-support.ts @@ -1,9 +1,19 @@ +import { Dictionary } from 'lodash'; import sourceMapSupport from 'source-map-support'; -export default function (): void { +const fileContentsCache: Dictionary = {}; + +function retrieveFile (fileName: string): string { + return fileContentsCache[fileName]; +} + +function setupSourceMapSupport (): void { sourceMapSupport.install({ hookRequire: true, handleUncaughtExceptions: false, environment: 'node', + retrieveFile, }); } + +export { setupSourceMapSupport, fileContentsCache }; diff --git a/test/functional/config.js b/test/functional/config.js index 8c7a468208c..e2dd188c0c6 100644 --- a/test/functional/config.js +++ b/test/functional/config.js @@ -270,4 +270,8 @@ module.exports = { hasBrowser (alias) { return this.currentEnvironment.browsers.some(browser => browser.alias.includes(alias)); }, + + get experimentalESM () { + return !!process.env.NODE_OPTIONS && process.env.NODE_OPTIONS.includes('--experimental-loader'); + }, }; diff --git a/test/functional/esm-utils/package.json b/test/functional/esm-utils/package.json new file mode 100644 index 00000000000..96ae6e57eb3 --- /dev/null +++ b/test/functional/esm-utils/package.json @@ -0,0 +1,3 @@ +{ + "type": "module" +} \ No newline at end of file diff --git a/test/functional/window-helpers.js b/test/functional/esm-utils/window-helpers.js similarity index 68% rename from test/functional/window-helpers.js rename to test/functional/esm-utils/window-helpers.js index 99e076e5599..cbc2da0a57c 100644 --- a/test/functional/window-helpers.js +++ b/test/functional/esm-utils/window-helpers.js @@ -7,13 +7,13 @@ const getWindowState = ClientFunction(() => ({ height: window.innerHeight, })); -export async function saveWindowState (t) { +async function saveWindowState (t) { const boundGetWindowState = getWindowState.with({ boundTestRun: t }); t.ctx._savedWindowState = await boundGetWindowState(); } -export async function restoreWindowState (t) { +async function restoreWindowState (t) { if (!t.ctx._savedWindowState) return; @@ -23,5 +23,12 @@ export async function restoreWindowState (t) { await t.resizeWindow(t.ctx._savedWindowState.width, t.ctx._savedWindowState.height); } -export const getWindowWidth = ClientFunction(() => window.innerWidth); -export const getWindowHeight = ClientFunction(() => window.innerHeight); +const getWindowWidth = ClientFunction(() => window.innerWidth); +const getWindowHeight = ClientFunction(() => window.innerHeight); + +export { + saveWindowState, + restoreWindowState, + getWindowWidth, + getWindowHeight, +}; diff --git a/test/functional/fixtures/api/es-next/browser-info/testcafe-fixtures/browser-info-test.js b/test/functional/fixtures/api/es-next/browser-info/testcafe-fixtures/browser-info-test.js index 5b1d51ce5bd..0cf51220d34 100644 --- a/test/functional/fixtures/api/es-next/browser-info/testcafe-fixtures/browser-info-test.js +++ b/test/functional/fixtures/api/es-next/browser-info/testcafe-fixtures/browser-info-test.js @@ -1,11 +1,10 @@ import { ClientFunction } from 'testcafe'; -import { parseUserAgent } from '../../../../../../../lib/utils/parse-user-agent'; -import config from '../../../../../config'; +import { parseUserAgent } from '../../../../../../../lib/utils/parse-user-agent.js'; +import config from '../../../../../config.js'; fixture `Browser information in headless Chrome`; -test - .page `http://localhost:3000/fixtures/api/es-next/browser-info/pages/index.html` +test.page `http://localhost:3000/fixtures/api/es-next/browser-info/pages/index.html` ('t.browser', async t => { const userAgent = ClientFunction(() => window.navigator.userAgent); const parsedUserAgent = parseUserAgent(await userAgent()); diff --git a/test/functional/fixtures/api/es-next/client-function/test.js b/test/functional/fixtures/api/es-next/client-function/test.js index 3870ea52a78..c4c31290712 100644 --- a/test/functional/fixtures/api/es-next/client-function/test.js +++ b/test/functional/fixtures/api/es-next/client-function/test.js @@ -68,7 +68,7 @@ describe('[API] ClientFunction', function () { .catch(function (errs) { expect(errs[0]).contains('An error occurred in ClientFunction code:'); expect(errs[0]).contains('Error: Hey ya!'); - expect(errs[0]).contains('> 126 | await fn();'); + expect(errs[0]).contains('> 124 | await fn();'); }); }); @@ -77,7 +77,7 @@ describe('[API] ClientFunction', function () { .catch(function (errs) { expect(errs[0]).contains('An error occurred in ClientFunction code:'); expect(errs[0]).contains('Error: 42'); - expect(errs[0]).contains('> 136 | await fn();'); + expect(errs[0]).contains('> 134 | await fn();'); }); }); @@ -90,7 +90,7 @@ describe('[API] ClientFunction', function () { 'Cannot initialize a ClientFunction because ClientFunction is number, and not a function.' )).eql(0); - expect(errs[0]).contains('> 32 | await ClientFunction(123)();'); + expect(errs[0]).contains('> 33 | await ClientFunction(123)();'); }); }); @@ -103,7 +103,7 @@ describe('[API] ClientFunction', function () { 'ClientFunction cannot implicitly resolve the test run in context of which it should be executed.' )).eql(0); - expect(errs[0]).contains(' > 43 | await fn();'); + expect(errs[0]).contains(' > 42 | await fn();'); }); }); @@ -116,7 +116,7 @@ describe('[API] ClientFunction', function () { 'ClientFunction code, arguments or dependencies cannot contain generators or "async/await" syntax (use Promises instead).' )).eql(0); - expect(errs[0]).contains('> 54 | ClientFunction(async () => Promise.resolve());'); + expect(errs[0]).contains('> 53 | ClientFunction(async () => Promise.resolve());'); }); }); @@ -129,7 +129,7 @@ describe('[API] ClientFunction', function () { 'ClientFunction code, arguments or dependencies cannot contain generators or "async/await" syntax (use Promises instead).' )).eql(0); - expect(errs[0]).contains('> 58 | ClientFunction(function*() { '); + expect(errs[0]).contains('> 57 | ClientFunction(function*() { '); }); }); @@ -142,7 +142,7 @@ describe('[API] ClientFunction', function () { 'Cannot resolve the "boundTestRun" option because its value is not a test controller.' )).eql(0); - expect(errs[0]).contains('> 94 | ClientFunction(() => 123).with({ boundTestRun: {} });'); + expect(errs[0]).contains('> 92 | ClientFunction(() => 123).with({ boundTestRun: {} });'); }); }); @@ -150,7 +150,7 @@ describe('[API] ClientFunction', function () { return runTests('./testcafe-fixtures/client-fn-test.js', 'Redirect during execution', { shouldFail: true }) .catch(function (errs) { expect(errs[0]).contains('ClientFunction execution was interrupted by page unload.'); - expect(errs[0]).contains('> 160 | await fn();'); + expect(errs[0]).contains('> 158 | await fn();'); }); }); @@ -163,7 +163,7 @@ describe('[API] ClientFunction', function () { expect(errs[0]).contains( 'ClientFunction code, arguments or dependencies cannot contain generators or "async/await" syntax (use Promises instead).' ); - expect(errs[0]).contains(' > 209 | await hfn(async () => Promise.resolve());'); + expect(errs[0]).contains(' > 207 | await hfn(async () => Promise.resolve());'); }); }); @@ -171,7 +171,7 @@ describe('[API] ClientFunction', function () { return runTests('./testcafe-fixtures/client-fn-test.js', 'DOM node return value', { shouldFail: true }) .catch(function (errs) { expect(errs[0]).contains('ClientFunction cannot return DOM elements. Use Selector functions for this purpose.'); - expect(errs[0]).contains(' > 230 | await getSomeNodes();'); + expect(errs[0]).contains(' > 228 | await getSomeNodes();'); }); }); }); diff --git a/test/functional/fixtures/api/es-next/client-function/testcafe-fixtures/client-fn-test.js b/test/functional/fixtures/api/es-next/client-function/testcafe-fixtures/client-fn-test.js index ef73e52e6cc..1616dd19a98 100644 --- a/test/functional/fixtures/api/es-next/client-function/testcafe-fixtures/client-fn-test.js +++ b/test/functional/fixtures/api/es-next/client-function/testcafe-fixtures/client-fn-test.js @@ -1,6 +1,7 @@ // NOTE: to preserve callsites, add new tests AFTER the existing ones import { ClientFunction } from 'testcafe'; import { expect } from 'chai'; +import fs from 'fs'; fixture `ClientFunction` .page `http://localhost:3000/fixtures/api/es-next/client-function/pages/index.html`; @@ -33,8 +34,6 @@ test('ClientFunction fn is not a function', async () => { }); test('ClientFunction fn test run is unresolvable', async () => { - const fs = require('fs'); - const fn = ClientFunction(() => 123); return new Promise((resolve, reject) => { @@ -61,7 +60,6 @@ test('Generator in ClientFunction', async () => { }); test('Bind ClientFunction', async t => { - const fs = require('fs'); const boundGetLocation = getLocation.with({ boundTestRun: t }); // NOTE: binding does not modify the original function, diff --git a/test/functional/fixtures/api/es-next/console/testcafe-fixtures/console-test.js b/test/functional/fixtures/api/es-next/console/testcafe-fixtures/console-test.js index 912379ffc04..89aa89b699b 100644 --- a/test/functional/fixtures/api/es-next/console/testcafe-fixtures/console-test.js +++ b/test/functional/fixtures/api/es-next/console/testcafe-fixtures/console-test.js @@ -1,8 +1,6 @@ fixture `getBrowserConsoleMessages`; - -test - .page `http://localhost:3000/fixtures/api/es-next/console/pages/index.html` +test.page `http://localhost:3000/fixtures/api/es-next/console/pages/index.html` ('t.getBrowserConsoleMessages', async t => { let messages = await t.getBrowserConsoleMessages(); @@ -30,8 +28,7 @@ test .expect(messages.info).eql(['info1', 'info2']); }); -test - .page `http://localhost:3000/fixtures/api/es-next/console/pages/empty.html` +test.page `http://localhost:3000/fixtures/api/es-next/console/pages/empty.html` ('messages formatting', async t => { /* eslint-disable no-console */ await t.eval(() => console.log('a', 1, null, void 0, ['b', 2], { c: 3 })); @@ -42,8 +39,7 @@ test await t.expect(log[0]).eql('a 1 null undefined b,2 [object Object]'); }); -test - .page `http://localhost:3000/fixtures/api/es-next/console/pages/empty.html` +test.page `http://localhost:3000/fixtures/api/es-next/console/pages/empty.html` ('empty collections (GH-4662)', async t => { const { log, warn, info } = await t.getBrowserConsoleMessages(); @@ -53,10 +49,9 @@ test .expect(info).eql([]); }); -test - .page('http://localhost:3000/fixtures/api/es-next/console/pages/i5600.html') - ("page with overridden 'Object.keys' method (GH-5600)", async t => { - const { log } = await t.getBrowserConsoleMessages(); +test.page('http://localhost:3000/fixtures/api/es-next/console/pages/i5600.html') +("page with overridden 'Object.keys' method (GH-5600)", async t => { + const { log } = await t.getBrowserConsoleMessages(); - await t.expect(log.toString()).eql('console message 1'); - }); + await t.expect(log.toString()).eql('console message 1'); +}); diff --git a/test/functional/fixtures/api/es-next/custom-client-scripts/helpers/index.js b/test/functional/fixtures/api/es-next/custom-client-scripts/common/index.js similarity index 100% rename from test/functional/fixtures/api/es-next/custom-client-scripts/helpers/index.js rename to test/functional/fixtures/api/es-next/custom-client-scripts/common/index.js diff --git a/test/functional/fixtures/api/es-next/custom-client-scripts/testcafe-fixtures/folder/index.js b/test/functional/fixtures/api/es-next/custom-client-scripts/testcafe-fixtures/folder/index.js index eccc0a8b02f..f0045a7c955 100644 --- a/test/functional/fixtures/api/es-next/custom-client-scripts/testcafe-fixtures/folder/index.js +++ b/test/functional/fixtures/api/es-next/custom-client-scripts/testcafe-fixtures/folder/index.js @@ -1,4 +1,4 @@ -import { getFlag1 } from '../../helpers'; +import { getFlag1 } from '../../common/index.js'; fixture `Fixture` .clientScripts('script.js'); diff --git a/test/functional/fixtures/api/es-next/custom-client-scripts/testcafe-fixtures/mixed.js b/test/functional/fixtures/api/es-next/custom-client-scripts/testcafe-fixtures/mixed.js index dc632014559..43e772f733c 100644 --- a/test/functional/fixtures/api/es-next/custom-client-scripts/testcafe-fixtures/mixed.js +++ b/test/functional/fixtures/api/es-next/custom-client-scripts/testcafe-fixtures/mixed.js @@ -1,4 +1,4 @@ -import { getFlag1, getFlag2 } from '../helpers'; +import { getFlag1, getFlag2 } from '../common/index.js'; fixture `Fixture`; diff --git a/test/functional/fixtures/api/es-next/custom-client-scripts/testcafe-fixtures/runner.js b/test/functional/fixtures/api/es-next/custom-client-scripts/testcafe-fixtures/runner.js index 41012be16fd..7ba593f375f 100644 --- a/test/functional/fixtures/api/es-next/custom-client-scripts/testcafe-fixtures/runner.js +++ b/test/functional/fixtures/api/es-next/custom-client-scripts/testcafe-fixtures/runner.js @@ -1,4 +1,4 @@ -import { getFlag1 } from '../helpers'; +import { getFlag1 } from '../common/index.js'; fixture `Fixture`; diff --git a/test/functional/fixtures/api/es-next/custom-client-scripts/testcafe-fixtures/specified-page.js b/test/functional/fixtures/api/es-next/custom-client-scripts/testcafe-fixtures/specified-page.js index d5c9d3fb63e..5dbd11aaf21 100644 --- a/test/functional/fixtures/api/es-next/custom-client-scripts/testcafe-fixtures/specified-page.js +++ b/test/functional/fixtures/api/es-next/custom-client-scripts/testcafe-fixtures/specified-page.js @@ -1,4 +1,4 @@ -import { getFlag1 } from '../helpers'; +import { getFlag1 } from '../common/index.js'; fixture `Fixture`; diff --git a/test/functional/fixtures/api/es-next/custom-client-scripts/testcafe-fixtures/test-api.js b/test/functional/fixtures/api/es-next/custom-client-scripts/testcafe-fixtures/test-api.js index eeb36887628..cc416def12d 100644 --- a/test/functional/fixtures/api/es-next/custom-client-scripts/testcafe-fixtures/test-api.js +++ b/test/functional/fixtures/api/es-next/custom-client-scripts/testcafe-fixtures/test-api.js @@ -1,4 +1,4 @@ -import { getFlag1, getFlag2 } from '../helpers'; +import { getFlag1, getFlag2 } from '../common/index.js'; fixture `Fixture` .clientScripts('../data/set-flag1.js'); diff --git a/test/functional/fixtures/api/es-next/disable-reloads/testcafe-fixtures/default-test.js b/test/functional/fixtures/api/es-next/disable-reloads/testcafe-fixtures/default-test.js index 9194f36ec8c..9949759062b 100644 --- a/test/functional/fixtures/api/es-next/disable-reloads/testcafe-fixtures/default-test.js +++ b/test/functional/fixtures/api/es-next/disable-reloads/testcafe-fixtures/default-test.js @@ -1,4 +1,4 @@ -import { checkPageTestData, setPageTestData } from './helpers'; +import { checkPageTestData, setPageTestData } from './helpers.js'; fixture `Default`; diff --git a/test/functional/fixtures/api/es-next/disable-reloads/testcafe-fixtures/fixture-disabled-reloads-test.js b/test/functional/fixtures/api/es-next/disable-reloads/testcafe-fixtures/fixture-disabled-reloads-test.js index f575dbcce44..9dab2996d20 100644 --- a/test/functional/fixtures/api/es-next/disable-reloads/testcafe-fixtures/fixture-disabled-reloads-test.js +++ b/test/functional/fixtures/api/es-next/disable-reloads/testcafe-fixtures/fixture-disabled-reloads-test.js @@ -1,4 +1,4 @@ -import { checkPageTestData, setPageTestData } from './helpers'; +import { checkPageTestData, setPageTestData } from './helpers.js'; fixture.disablePageReloads `Default`; diff --git a/test/functional/fixtures/api/es-next/disable-reloads/testcafe-fixtures/fixture-enabled-reloads-test.js b/test/functional/fixtures/api/es-next/disable-reloads/testcafe-fixtures/fixture-enabled-reloads-test.js index 3d344d7644b..be88af96014 100644 --- a/test/functional/fixtures/api/es-next/disable-reloads/testcafe-fixtures/fixture-enabled-reloads-test.js +++ b/test/functional/fixtures/api/es-next/disable-reloads/testcafe-fixtures/fixture-enabled-reloads-test.js @@ -1,4 +1,4 @@ -import { checkPageTestData, setPageTestData } from './helpers'; +import { checkPageTestData, setPageTestData } from './helpers.js'; fixture.enablePageReloads `Default`; diff --git a/test/functional/fixtures/api/es-next/disable-reloads/testcafe-fixtures/fixture-enabled-test-disabled-reloads-test.js b/test/functional/fixtures/api/es-next/disable-reloads/testcafe-fixtures/fixture-enabled-test-disabled-reloads-test.js index 04d5e587365..6f549119d21 100644 --- a/test/functional/fixtures/api/es-next/disable-reloads/testcafe-fixtures/fixture-enabled-test-disabled-reloads-test.js +++ b/test/functional/fixtures/api/es-next/disable-reloads/testcafe-fixtures/fixture-enabled-test-disabled-reloads-test.js @@ -1,4 +1,4 @@ -import { checkPageTestData, setPageTestData } from './helpers'; +import { checkPageTestData, setPageTestData } from './helpers.js'; fixture.enablePageReloads `Default`; diff --git a/test/functional/fixtures/api/es-next/disable-reloads/testcafe-fixtures/globally-disabled-reloads-test.js b/test/functional/fixtures/api/es-next/disable-reloads/testcafe-fixtures/globally-disabled-reloads-test.js index 71d3b2b2d17..2458a426e52 100644 --- a/test/functional/fixtures/api/es-next/disable-reloads/testcafe-fixtures/globally-disabled-reloads-test.js +++ b/test/functional/fixtures/api/es-next/disable-reloads/testcafe-fixtures/globally-disabled-reloads-test.js @@ -1,4 +1,4 @@ -import { checkPageTestData, setPageTestData } from './helpers'; +import { checkPageTestData, setPageTestData } from './helpers.js'; fixture `Default`; diff --git a/test/functional/fixtures/api/es-next/disable-reloads/testcafe-fixtures/test-disabled-reloads-test.js b/test/functional/fixtures/api/es-next/disable-reloads/testcafe-fixtures/test-disabled-reloads-test.js index 407fff4749f..705d1e77f55 100644 --- a/test/functional/fixtures/api/es-next/disable-reloads/testcafe-fixtures/test-disabled-reloads-test.js +++ b/test/functional/fixtures/api/es-next/disable-reloads/testcafe-fixtures/test-disabled-reloads-test.js @@ -1,4 +1,4 @@ -import { checkPageTestData, setPageTestData } from './helpers'; +import { checkPageTestData, setPageTestData } from './helpers.js'; fixture `Default`; diff --git a/test/functional/fixtures/api/es-next/disable-reloads/testcafe-fixtures/test-enabled-reloads-test.js b/test/functional/fixtures/api/es-next/disable-reloads/testcafe-fixtures/test-enabled-reloads-test.js index d40f00b0888..11275435445 100644 --- a/test/functional/fixtures/api/es-next/disable-reloads/testcafe-fixtures/test-enabled-reloads-test.js +++ b/test/functional/fixtures/api/es-next/disable-reloads/testcafe-fixtures/test-enabled-reloads-test.js @@ -1,4 +1,4 @@ -import { checkPageTestData, setPageTestData } from './helpers'; +import { checkPageTestData, setPageTestData } from './helpers.js'; fixture.disablePageReloads `Default`; diff --git a/test/functional/fixtures/api/es-next/drag/testcafe-fixtures/drag-test.js b/test/functional/fixtures/api/es-next/drag/testcafe-fixtures/drag-test.js index c473365e08f..670b1beedfe 100644 --- a/test/functional/fixtures/api/es-next/drag/testcafe-fixtures/drag-test.js +++ b/test/functional/fixtures/api/es-next/drag/testcafe-fixtures/drag-test.js @@ -1,7 +1,7 @@ // NOTE: to preserve callsites, add new tests AFTER the existing ones import { Selector } from 'testcafe'; -import { saveWindowState, restoreWindowState } from '../../../../../window-helpers'; -import config from '../../../../../config'; +import { saveWindowState, restoreWindowState } from '../../../../../esm-utils/window-helpers.js'; +import config from '../../../../../config.js'; fixture `Drag` .page `http://localhost:3000/fixtures/api/es-next/drag/pages/index.html`; diff --git a/test/functional/fixtures/api/es-next/generic-errors/testcafe-fixtures/error-in-test-code-test.js b/test/functional/fixtures/api/es-next/generic-errors/testcafe-fixtures/error-in-test-code-test.js index 850da8fe85d..22d24f2fb75 100644 --- a/test/functional/fixtures/api/es-next/generic-errors/testcafe-fixtures/error-in-test-code-test.js +++ b/test/functional/fixtures/api/es-next/generic-errors/testcafe-fixtures/error-in-test-code-test.js @@ -1,5 +1,5 @@ // NOTE: to preserve callsites, add new tests AFTER the existing ones -import throwError from './helpers'; +import throwError from './helpers.js'; fixture `Errors in test code` .page `http://localhost:3000/fixtures/api/es-next/generic-errors/pages/index.html`; diff --git a/test/functional/fixtures/api/es-next/generic-errors/testcafe-fixtures/external-assertion-lib-errors-test.js b/test/functional/fixtures/api/es-next/generic-errors/testcafe-fixtures/external-assertion-lib-errors-test.js index 33a629b4fb7..74939a487d1 100644 --- a/test/functional/fixtures/api/es-next/generic-errors/testcafe-fixtures/external-assertion-lib-errors-test.js +++ b/test/functional/fixtures/api/es-next/generic-errors/testcafe-fixtures/external-assertion-lib-errors-test.js @@ -1,6 +1,6 @@ import assert from 'assert'; import { expect, config as chaiConfig } from 'chai'; -import { assertionError } from './helpers'; +import { assertionError } from './helpers.js'; // NOTE: set this flag to check that TestCafe adds a correct callsite to // the report when there are chai module files on top of the error stack. diff --git a/test/functional/fixtures/api/es-next/hooks/testcafe-fixtures/fixture-ctx.js b/test/functional/fixtures/api/es-next/hooks/testcafe-fixtures/fixture-ctx.js index bca825e74ef..c5cd8409ba4 100644 --- a/test/functional/fixtures/api/es-next/hooks/testcafe-fixtures/fixture-ctx.js +++ b/test/functional/fixtures/api/es-next/hooks/testcafe-fixtures/fixture-ctx.js @@ -1,5 +1,5 @@ import { expect } from 'chai'; -import config from '../../../../../config'; +import config from '../../../../../config.js'; fixture `Fixture1` diff --git a/test/functional/fixtures/api/es-next/hooks/testcafe-fixtures/fixture-hooks-seq.js b/test/functional/fixtures/api/es-next/hooks/testcafe-fixtures/fixture-hooks-seq.js index 64896d1d1c9..211982f27c4 100644 --- a/test/functional/fixtures/api/es-next/hooks/testcafe-fixtures/fixture-hooks-seq.js +++ b/test/functional/fixtures/api/es-next/hooks/testcafe-fixtures/fixture-hooks-seq.js @@ -1,4 +1,4 @@ -import delay from '../../../../../../../lib/utils/delay'; +import delay from '../../../../../../../lib/utils/delay.js'; fixture `Fixture 1` .after(async () => { diff --git a/test/functional/fixtures/api/es-next/hooks/testcafe-fixtures/fixture-hooks.js b/test/functional/fixtures/api/es-next/hooks/testcafe-fixtures/fixture-hooks.js index 2e0e7dec000..518e65688eb 100644 --- a/test/functional/fixtures/api/es-next/hooks/testcafe-fixtures/fixture-hooks.js +++ b/test/functional/fixtures/api/es-next/hooks/testcafe-fixtures/fixture-hooks.js @@ -1,4 +1,4 @@ -import delay from '../../../../../../../lib/utils/delay'; +import delay from '../../../../../../../lib/utils/delay.js'; import getTimeLimitedPromise from 'time-limit-promise'; const hooksExecuted = { diff --git a/test/functional/fixtures/api/es-next/maximize-window/testcafe-fixtures/maximize-window-test.js b/test/functional/fixtures/api/es-next/maximize-window/testcafe-fixtures/maximize-window-test.js index 0631183eb29..ce24b4e467c 100644 --- a/test/functional/fixtures/api/es-next/maximize-window/testcafe-fixtures/maximize-window-test.js +++ b/test/functional/fixtures/api/es-next/maximize-window/testcafe-fixtures/maximize-window-test.js @@ -1,6 +1,6 @@ import { expect } from 'chai'; import { ClientFunction } from 'testcafe'; -import { saveWindowState, restoreWindowState } from '../../../../../window-helpers'; +import { saveWindowState, restoreWindowState } from '../../../../../esm-utils/window-helpers.js'; const getWindowDimensionsInfo = ClientFunction(() => { diff --git a/test/functional/fixtures/api/es-next/navigate-to-and-test-page/dirname.js b/test/functional/fixtures/api/es-next/navigate-to-and-test-page/dirname.js new file mode 100644 index 00000000000..5092b4dd8c8 --- /dev/null +++ b/test/functional/fixtures/api/es-next/navigate-to-and-test-page/dirname.js @@ -0,0 +1 @@ +module.exports = __dirname; diff --git a/test/functional/fixtures/api/es-next/navigate-to-and-test-page/test.js b/test/functional/fixtures/api/es-next/navigate-to-and-test-page/test.js index 992139cd5ec..170a1d4d1ae 100644 --- a/test/functional/fixtures/api/es-next/navigate-to-and-test-page/test.js +++ b/test/functional/fixtures/api/es-next/navigate-to-and-test-page/test.js @@ -1,6 +1,5 @@ const expect = require('chai').expect; - describe('[API] t.navigateTo', function () { it('Should validate the url argument', function () { return runTests('./testcafe-fixtures/navigate-to-test.js', 'Incorrect protocol', { shouldFail: 'true' }) diff --git a/test/functional/fixtures/api/es-next/navigate-to-and-test-page/testcafe-fixtures/navigate-to-test.js b/test/functional/fixtures/api/es-next/navigate-to-and-test-page/testcafe-fixtures/navigate-to-test.js index c4fa4485eb8..70694bee6ab 100644 --- a/test/functional/fixtures/api/es-next/navigate-to-and-test-page/testcafe-fixtures/navigate-to-test.js +++ b/test/functional/fixtures/api/es-next/navigate-to-and-test-page/testcafe-fixtures/navigate-to-test.js @@ -1,9 +1,12 @@ import path from 'path'; import { ClientFunction } from 'testcafe'; +import dirname from '../dirname.js'; + fixture `NavigateTo` .page `http://localhost:3000/fixtures/api/es-next/navigate-to-and-test-page/pages/index.html`; +const __dirname = dirname; const getLocation = ClientFunction(() => window.location.toString().toLowerCase().replace(/\/\/\/(\w):[\\/]/g, '//$1:/')); const resolveFileUrl = relativeUrl => `file://${path.join(__dirname, relativeUrl)}`.replace(/\\/g, '/').toLowerCase(); @@ -28,38 +31,35 @@ test('Navigate to scheme-less http page', async t => { .expect(getLocation()).eql('http://localhost:3000/fixtures/api/es-next/navigate-to-and-test-page/pages/navigation.html'); }); -test - .page `file://${path.join(__dirname, '../pages/index.html')}` +test.page `file://${path.join(__dirname, './pages/index.html')}` ('Navigate to relative file page', async t => { await t .navigateTo('navigation.html') .click('#button') - .expect(getLocation()).eql(resolveFileUrl('../pages/navigation.html')); + .expect(getLocation()).eql(resolveFileUrl('./pages/navigation.html')); }); -test - .page `file://${path.join(__dirname, '../pages/index.html')}` +test.page `file://${path.join(__dirname, './pages/index.html')}` ('Navigate to absolute file page', async t => { await t - .navigateTo(path.join(__dirname, '../pages/navigation.html')) + .navigateTo(path.join(__dirname, './pages/navigation.html')) .click('#button') - .expect(getLocation()).eql(resolveFileUrl('../pages/navigation.html')); + .expect(getLocation()).eql(resolveFileUrl('./pages/navigation.html')); }); -test - .page `file://${path.join(__dirname, '../pages/index.html')}` +test.page `file://${path.join(__dirname, './pages/index.html')}` ('Navigate to scheme-less file page', async t => { await t - .navigateTo(`//${path.join(__dirname, '../pages/navigation.html')}`) + .navigateTo(`//${path.join(__dirname, './pages/navigation.html')}`) .click('#button') - .expect(getLocation()).eql(resolveFileUrl('../pages/navigation.html')); + .expect(getLocation()).eql(resolveFileUrl('./pages/navigation.html')); }); test('Navigate to absolute file page with scheme', async t => { await t - .navigateTo(`file://${path.join(__dirname, '../pages/navigation.html')}`) + .navigateTo(`file://${path.join(__dirname, './pages/navigation.html')}`) .click('#button') - .expect(getLocation()).eql(resolveFileUrl('../pages/navigation.html')); + .expect(getLocation()).eql(resolveFileUrl('./pages/navigation.html')); }); test('Navigate to about:blank', async t => { @@ -77,48 +77,42 @@ test.page `http://localhost:3000/fixtures/api/es-next/navigate-to-and-test-page/ await t.click('#button'); }); -test - .page `../pages/navigation.html` +test.page `../pages/navigation.html` ('Relative file page', async t => { await t .click('#button') - .expect(getLocation()).eql(resolveFileUrl('../pages/navigation.html')); + .expect(getLocation()).eql(resolveFileUrl('./pages/navigation.html')); }); -test - .page `${path.join(__dirname, '../pages/navigation.html')}` +test.page `${path.join(__dirname, './pages/navigation.html')}` ('Absolute file page', async t => { await t .click('#button') - .expect(getLocation()).eql(resolveFileUrl('../pages/navigation.html')); + .expect(getLocation()).eql(resolveFileUrl('./pages/navigation.html')); }); -test - .page `localhost:3000/fixtures/api/es-next/navigate-to-and-test-page/pages/navigation.html` +test.page `localhost:3000/fixtures/api/es-next/navigate-to-and-test-page/pages/navigation.html` ('Scheme-less http page 1', async t => { await t .click('#button') .expect(getLocation()).eql('http://localhost:3000/fixtures/api/es-next/navigate-to-and-test-page/pages/navigation.html'); }); -test - .page `//localhost:3000/fixtures/api/es-next/navigate-to-and-test-page/pages/navigation.html` +test.page `//localhost:3000/fixtures/api/es-next/navigate-to-and-test-page/pages/navigation.html` ('Scheme-less http page 2', async t => { await t .click('#button') .expect(getLocation()).eql('http://localhost:3000/fixtures/api/es-next/navigate-to-and-test-page/pages/navigation.html'); }); -test - .page `file://${path.join(__dirname, '../pages/navigation.html')}` +test.page `file://${path.join(__dirname, './pages/navigation.html')}` ('Absolute file page with scheme', async t => { await t .click('#button') - .expect(getLocation()).eql(resolveFileUrl('../pages/navigation.html')); + .expect(getLocation()).eql(resolveFileUrl('./pages/navigation.html')); }); -test - .page `about:blank` +test.page `about:blank` ('about:blank', async t => { await t.expect(getLocation()).eql('about:blank'); }); diff --git a/test/functional/fixtures/api/es-next/request-hooks/common/mock-routes.js b/test/functional/fixtures/api/es-next/request-hooks/common/mock-routes.js index d754afbc6e3..b72efd03b74 100644 --- a/test/functional/fixtures/api/es-next/request-hooks/common/mock-routes.js +++ b/test/functional/fixtures/api/es-next/request-hooks/common/mock-routes.js @@ -1,6 +1,8 @@ -module.exports = { +const routes = { main: 'http://one-dummy-url.com', get: 'http://one-dummy-url.com/get', post: 'http://one-dummy-url.com/post', another: 'https://another-dummy-url.com', }; + +export default routes; diff --git a/test/functional/fixtures/api/es-next/request-hooks/testcafe-fixtures/api/add-remove-request-hook.js b/test/functional/fixtures/api/es-next/request-hooks/testcafe-fixtures/api/add-remove-request-hook.js index 79890b44817..733055cee8e 100644 --- a/test/functional/fixtures/api/es-next/request-hooks/testcafe-fixtures/api/add-remove-request-hook.js +++ b/test/functional/fixtures/api/es-next/request-hooks/testcafe-fixtures/api/add-remove-request-hook.js @@ -1,8 +1,7 @@ import { RequestHook } from 'testcafe'; -import path from 'path'; +import ReExecutablePromise from '../../../../../../../../lib/utils/re-executable-promise.js'; -const ReExecutablePromise = require(path.resolve('./lib/utils/re-executable-promise')); const pageUrl = 'http://localhost:3000/fixtures/api/es-next/request-hooks/pages/request-logger/index.html'; class TestRequestHook extends RequestHook { diff --git a/test/functional/fixtures/api/es-next/request-hooks/testcafe-fixtures/api/conditional-adding.js b/test/functional/fixtures/api/es-next/request-hooks/testcafe-fixtures/api/conditional-adding.js index 82df6f6f22a..10133ae10d4 100644 --- a/test/functional/fixtures/api/es-next/request-hooks/testcafe-fixtures/api/conditional-adding.js +++ b/test/functional/fixtures/api/es-next/request-hooks/testcafe-fixtures/api/conditional-adding.js @@ -1,5 +1,5 @@ import { ClientFunction, RequestLogger } from 'testcafe'; -import { parseUserAgent } from '../../../../../../../../lib/utils/parse-user-agent'; +import { parseUserAgent } from '../../../../../../../../lib/utils/parse-user-agent.js'; const pageUrl = 'http://localhost:3000/fixtures/api/es-next/request-hooks/pages/request-logger/index.html'; const logger1 = new RequestLogger(pageUrl); diff --git a/test/functional/fixtures/api/es-next/request-hooks/testcafe-fixtures/api/execution-order.js b/test/functional/fixtures/api/es-next/request-hooks/testcafe-fixtures/api/execution-order.js index 6aed57f42b6..414d1079205 100644 --- a/test/functional/fixtures/api/es-next/request-hooks/testcafe-fixtures/api/execution-order.js +++ b/test/functional/fixtures/api/es-next/request-hooks/testcafe-fixtures/api/execution-order.js @@ -1,9 +1,9 @@ import { RequestHook } from 'testcafe'; -import path from 'path'; +import ReExecutablePromise from '../../../../../../../../lib/utils/re-executable-promise.js'; -const ReExecutablePromise = require(path.resolve('./lib/utils/re-executable-promise')); -const result = []; -const pageUrl = 'http://localhost:3000/fixtures/api/es-next/request-hooks/pages/request-logger/index.html'; + +const result = []; +const pageUrl = 'http://localhost:3000/fixtures/api/es-next/request-hooks/pages/request-logger/index.html'; class TestRequestHook extends RequestHook { constructor (name) { diff --git a/test/functional/fixtures/api/es-next/request-hooks/testcafe-fixtures/api/i4122.js b/test/functional/fixtures/api/es-next/request-hooks/testcafe-fixtures/api/i4122.js index a166cc87907..e7f54723a49 100644 --- a/test/functional/fixtures/api/es-next/request-hooks/testcafe-fixtures/api/i4122.js +++ b/test/functional/fixtures/api/es-next/request-hooks/testcafe-fixtures/api/i4122.js @@ -1,9 +1,8 @@ import { RequestHook } from 'testcafe'; -import path from 'path'; +import ReExecutablePromise from '../../../../../../../../lib/utils/re-executable-promise.js'; -const ReExecutablePromise = require(path.resolve('./lib/utils/re-executable-promise')); -const pageUrl = 'http://localhost:3000/fixtures/api/es-next/request-hooks/pages/request-logger/index.html'; +const pageUrl = 'http://localhost:3000/fixtures/api/es-next/request-hooks/pages/request-logger/index.html'; const log = []; diff --git a/test/functional/fixtures/api/es-next/request-hooks/testcafe-fixtures/api/request-hook-events.js b/test/functional/fixtures/api/es-next/request-hooks/testcafe-fixtures/api/request-hook-events.js index 395a296028c..cc8136752ef 100644 --- a/test/functional/fixtures/api/es-next/request-hooks/testcafe-fixtures/api/request-hook-events.js +++ b/test/functional/fixtures/api/es-next/request-hooks/testcafe-fixtures/api/request-hook-events.js @@ -1,8 +1,7 @@ import { RequestHook } from 'testcafe'; -import path from 'path'; +import ReExecutablePromise from '../../../../../../../../lib/utils/re-executable-promise.js'; -const pageUrl = 'http://localhost:3000/fixtures/api/es-next/request-hooks/pages/request-logger/index.html'; -const ReExecutablePromise = require(path.resolve('./lib/utils/re-executable-promise')); +const pageUrl = 'http://localhost:3000/fixtures/api/es-next/request-hooks/pages/request-logger/index.html'; class RequestHookEventClasses extends RequestHook { constructor () { diff --git a/test/functional/fixtures/api/es-next/request-hooks/testcafe-fixtures/request-logger/mocked-requests.js b/test/functional/fixtures/api/es-next/request-hooks/testcafe-fixtures/request-logger/mocked-requests.js index 88ead64ff9b..ebe2f234888 100644 --- a/test/functional/fixtures/api/es-next/request-hooks/testcafe-fixtures/request-logger/mocked-requests.js +++ b/test/functional/fixtures/api/es-next/request-hooks/testcafe-fixtures/request-logger/mocked-requests.js @@ -1,5 +1,5 @@ import { RequestMock, RequestLogger } from 'testcafe'; -import MOCK_ROUTES from '../../common/mock-routes'; +import MOCK_ROUTES from '../../common/mock-routes.js'; const logger = RequestLogger(MOCK_ROUTES.post, { logRequestHeaders: true, diff --git a/test/functional/fixtures/api/es-next/request-hooks/testcafe-fixtures/request-logger/multi-browser.js b/test/functional/fixtures/api/es-next/request-hooks/testcafe-fixtures/request-logger/multi-browser.js index 7089caf01e5..8f8c19f55f6 100644 --- a/test/functional/fixtures/api/es-next/request-hooks/testcafe-fixtures/request-logger/multi-browser.js +++ b/test/functional/fixtures/api/es-next/request-hooks/testcafe-fixtures/request-logger/multi-browser.js @@ -1,5 +1,5 @@ import { RequestLogger, Selector, ClientFunction } from 'testcafe'; -import { parseUserAgent } from '../../../../../../../../lib/utils/parse-user-agent'; +import { parseUserAgent } from '../../../../../../../../lib/utils/parse-user-agent.js'; const pageUrl = 'http://localhost:3000/fixtures/api/es-next/request-hooks/pages/request-logger/multi-browser.html'; diff --git a/test/functional/fixtures/api/es-next/request-hooks/testcafe-fixtures/request-mock/async-response-function.js b/test/functional/fixtures/api/es-next/request-hooks/testcafe-fixtures/request-mock/async-response-function.js index 5c110755c25..49735108ec1 100644 --- a/test/functional/fixtures/api/es-next/request-hooks/testcafe-fixtures/request-mock/async-response-function.js +++ b/test/functional/fixtures/api/es-next/request-hooks/testcafe-fixtures/request-mock/async-response-function.js @@ -1,5 +1,5 @@ import { RequestMock, Selector } from 'testcafe'; -import DUMMY_URLS from '../../common/mock-routes'; +import DUMMY_URLS from '../../common/mock-routes.js'; const responsePromise = new Promise(resolve => { setTimeout(() => { diff --git a/test/functional/fixtures/api/es-next/request-hooks/testcafe-fixtures/request-mock/basic.js b/test/functional/fixtures/api/es-next/request-hooks/testcafe-fixtures/request-mock/basic.js index c45759f8b3c..ecd28359d46 100644 --- a/test/functional/fixtures/api/es-next/request-hooks/testcafe-fixtures/request-mock/basic.js +++ b/test/functional/fixtures/api/es-next/request-hooks/testcafe-fixtures/request-mock/basic.js @@ -1,5 +1,5 @@ import { Selector, RequestMock } from 'testcafe'; -import DUMMY_URLS from '../../common/mock-routes'; +import DUMMY_URLS from '../../common/mock-routes.js'; const testPageMarkup = ` diff --git a/test/functional/fixtures/api/es-next/request-hooks/testcafe-fixtures/request-mock/respond-error.js b/test/functional/fixtures/api/es-next/request-hooks/testcafe-fixtures/request-mock/respond-error.js index f0d8abbf4b5..160f30ac387 100644 --- a/test/functional/fixtures/api/es-next/request-hooks/testcafe-fixtures/request-mock/respond-error.js +++ b/test/functional/fixtures/api/es-next/request-hooks/testcafe-fixtures/request-mock/respond-error.js @@ -1,5 +1,5 @@ import { RequestMock } from 'testcafe'; -import DUMMY_URLS from '../../common/mock-routes'; +import DUMMY_URLS from '../../common/mock-routes.js'; const mock = RequestMock() .onRequestTo(DUMMY_URLS.get) diff --git a/test/functional/fixtures/api/es-next/resize-window/testcafe-fixtures/resize-window-test.js b/test/functional/fixtures/api/es-next/resize-window/testcafe-fixtures/resize-window-test.js index 436341538c2..f8e997614bc 100644 --- a/test/functional/fixtures/api/es-next/resize-window/testcafe-fixtures/resize-window-test.js +++ b/test/functional/fixtures/api/es-next/resize-window/testcafe-fixtures/resize-window-test.js @@ -7,7 +7,7 @@ import { restoreWindowState, getWindowHeight, getWindowWidth, -} from '../../../../../window-helpers'; +} from '../../../../../esm-utils/window-helpers.js'; const setWindowOnresizeHandler = ClientFunction(() => { diff --git a/test/functional/fixtures/api/es-next/roles/testcafe-fixtures/hash-based-navigation-test.js b/test/functional/fixtures/api/es-next/roles/testcafe-fixtures/hash-based-navigation-test.js index ef13ae7227e..d852171ecaa 100644 --- a/test/functional/fixtures/api/es-next/roles/testcafe-fixtures/hash-based-navigation-test.js +++ b/test/functional/fixtures/api/es-next/roles/testcafe-fixtures/hash-based-navigation-test.js @@ -1,5 +1,5 @@ import { Role, Selector } from 'testcafe'; -import { setFlag, hasFlag } from './utils'; +import { setFlag, hasFlag } from './utils.js'; fixture `Should always reload the target url` .page `http://localhost:3000/fixtures/api/es-next/roles/pages/page-with-hash-navigation.html` diff --git a/test/functional/fixtures/api/es-next/roles/testcafe-fixtures/init-error-test.js b/test/functional/fixtures/api/es-next/roles/testcafe-fixtures/init-error-test.js index 55afde9432f..42838fd7e92 100644 --- a/test/functional/fixtures/api/es-next/roles/testcafe-fixtures/init-error-test.js +++ b/test/functional/fixtures/api/es-next/roles/testcafe-fixtures/init-error-test.js @@ -1,4 +1,4 @@ -import { noop } from 'lodash'; +import lodash from 'lodash'; import { Role, t } from 'testcafe'; const roleWithInitErr = Role('http://localhost:3000/fixtures/api/es-next/roles/pages/login-page.html', () => { @@ -12,7 +12,7 @@ test('Test1', async () => { await t.useRole(roleWithInitErr); }); -test('Test2', noop); +test('Test2', lodash.noop); test('Test3', async () => { await t.useRole(roleWithInitErr); diff --git a/test/functional/fixtures/api/es-next/roles/testcafe-fixtures/same-url-test.js b/test/functional/fixtures/api/es-next/roles/testcafe-fixtures/same-url-test.js index 2d8b7d90e1a..bba9e6a6cec 100644 --- a/test/functional/fixtures/api/es-next/roles/testcafe-fixtures/same-url-test.js +++ b/test/functional/fixtures/api/es-next/roles/testcafe-fixtures/same-url-test.js @@ -1,5 +1,5 @@ import { Role, ClientFunction } from 'testcafe'; -import { hasFlag, setFlag } from './utils'; +import { hasFlag, setFlag } from './utils.js'; const loginPageUrl = 'http://localhost:3000/fixtures/api/es-next/roles/pages/login-page.html'; diff --git a/test/functional/fixtures/api/es-next/take-screenshot/testcafe-fixtures/take-element-screenshot.js b/test/functional/fixtures/api/es-next/take-screenshot/testcafe-fixtures/take-element-screenshot.js index f1f3c41c946..afbe8cc8af4 100644 --- a/test/functional/fixtures/api/es-next/take-screenshot/testcafe-fixtures/take-element-screenshot.js +++ b/test/functional/fixtures/api/es-next/take-screenshot/testcafe-fixtures/take-element-screenshot.js @@ -1,6 +1,6 @@ import { ClientFunction } from 'testcafe'; -import { parseUserAgent } from '../../../../../../../lib/utils/parse-user-agent'; -import { saveWindowState, restoreWindowState } from '../../../../../window-helpers'; +import { parseUserAgent } from '../../../../../../../lib/utils/parse-user-agent.js'; +import { saveWindowState, restoreWindowState } from '../../../../../esm-utils/window-helpers.js'; const getUserAgent = ClientFunction(() => navigator.userAgent.toString()); diff --git a/test/functional/fixtures/api/es-next/take-screenshot/testcafe-fixtures/take-full-page-screenshot.js b/test/functional/fixtures/api/es-next/take-screenshot/testcafe-fixtures/take-full-page-screenshot.js index 9fd70734ba3..e06c1527dff 100644 --- a/test/functional/fixtures/api/es-next/take-screenshot/testcafe-fixtures/take-full-page-screenshot.js +++ b/test/functional/fixtures/api/es-next/take-screenshot/testcafe-fixtures/take-full-page-screenshot.js @@ -1,5 +1,5 @@ import { ClientFunction } from 'testcafe'; -import { parseUserAgent } from '../../../../../../../lib/utils/parse-user-agent'; +import { parseUserAgent } from '../../../../../../../lib/utils/parse-user-agent.js'; const getUserAgent = ClientFunction(() => navigator.userAgent.toString()); diff --git a/test/functional/fixtures/api/es-next/take-screenshot/testcafe-fixtures/take-screenshot.js b/test/functional/fixtures/api/es-next/take-screenshot/testcafe-fixtures/take-screenshot.js index 70bfb8a654d..e679966a4a8 100644 --- a/test/functional/fixtures/api/es-next/take-screenshot/testcafe-fixtures/take-screenshot.js +++ b/test/functional/fixtures/api/es-next/take-screenshot/testcafe-fixtures/take-screenshot.js @@ -1,9 +1,9 @@ import { ClientFunction } from 'testcafe'; -import { parseUserAgent } from '../../../../../../../lib/utils/parse-user-agent'; -import { saveWindowState, restoreWindowState } from '../../../../../window-helpers'; -import quarantineScope from './quarantineScope'; +import { parseUserAgent } from '../../../../../../../lib/utils/parse-user-agent.js'; +import { saveWindowState, restoreWindowState } from '../../../../../esm-utils/window-helpers.js'; +import quarantineScope from './quarantineScope.js'; import sanitizeFilename from 'sanitize-filename'; -import { readPngFile } from '../../../../../../../lib/utils/promisified-functions'; +import { readPngFile } from '../../../../../../../lib/utils/promisified-functions.js'; import config from '../../../../../config.js'; import { join } from 'path'; diff --git a/test/functional/fixtures/api/es-next/test-controller/test.js b/test/functional/fixtures/api/es-next/test-controller/test.js index edf37225827..2d5f5041fef 100644 --- a/test/functional/fixtures/api/es-next/test-controller/test.js +++ b/test/functional/fixtures/api/es-next/test-controller/test.js @@ -1,4 +1,5 @@ const { expect } = require('chai'); +const semver = require('semver'); // NOTE: we run tests in chrome only, because we mainly test server API functionality. // Actions functionality is tested in lower-level raw API. @@ -101,8 +102,12 @@ describe('[API] TestController', () => { return runTests('./testcafe-fixtures/test-controller-test.js', 'GH-2557', { shouldFail: true, only: 'chrome' }) .catch(errs => { + const expectedMessage = semver.gte(process.version, '16.0.0') + ? "TypeError: Cannot read properties of undefined (reading 'someProperty') [[user-agent]]" + : "TypeError: Cannot read property 'someProperty' of undefined [[user-agent]]"; + expect(errs.length).eql(1); - expect(errs[0]).contains("TypeError: Cannot read property 'someProperty' of undefined [[user-agent]]"); + expect(errs[0]).contains(expectedMessage); }); }); diff --git a/test/functional/fixtures/api/es-next/test-controller/testcafe-fixtures/test-controller-test.js b/test/functional/fixtures/api/es-next/test-controller/testcafe-fixtures/test-controller-test.js index 7757e4eefff..40319a22dbe 100644 --- a/test/functional/fixtures/api/es-next/test-controller/testcafe-fixtures/test-controller-test.js +++ b/test/functional/fixtures/api/es-next/test-controller/testcafe-fixtures/test-controller-test.js @@ -1,4 +1,4 @@ -import { missingAwaitFn } from './helpers'; +import { missingAwaitFn } from './helpers.js'; // NOTE: to preserve callsites, add new tests AFTER the existing ones fixture `TestController` .page `http://localhost:3000/fixtures/api/es-next/test-controller/pages/index.html`; diff --git a/test/functional/fixtures/api/es-next/test-structure/test.js b/test/functional/fixtures/api/es-next/test-structure/test.js index a1fb5085e64..e9a39a792bc 100644 --- a/test/functional/fixtures/api/es-next/test-structure/test.js +++ b/test/functional/fixtures/api/es-next/test-structure/test.js @@ -5,15 +5,15 @@ const DEFAULT_RUN_OPTIONS = { describe('Test structure', function () { it('Should work import "test"', function () { - return runTests('./testcafe-test-structure/imported-fixture-test.js', '', DEFAULT_RUN_OPTIONS); + return runTests('./testcafe-fixtures/imported-fixture-test.js', '', DEFAULT_RUN_OPTIONS); }); it('Should work import "fixture"', function () { - return runTests('./testcafe-test-structure/imported-test-test.js', '', DEFAULT_RUN_OPTIONS); + return runTests('./testcafe-fixtures/imported-test-test.js', '', DEFAULT_RUN_OPTIONS); }); it('Should work attached tests', function () { - return runTests('./testcafe-test-structure/test-structure-test.js', 'Attached tests should work', DEFAULT_RUN_OPTIONS); + return runTests('./testcafe-fixtures/test-structure-test.js', 'Attached tests should work', DEFAULT_RUN_OPTIONS); }); }); diff --git a/test/functional/fixtures/api/es-next/test-structure/testcafe-test-structure/attached-test.js b/test/functional/fixtures/api/es-next/test-structure/testcafe-fixtures/attached-test.js similarity index 100% rename from test/functional/fixtures/api/es-next/test-structure/testcafe-test-structure/attached-test.js rename to test/functional/fixtures/api/es-next/test-structure/testcafe-fixtures/attached-test.js diff --git a/test/functional/fixtures/api/es-next/test-structure/testcafe-test-structure/imported-fixture-test.js b/test/functional/fixtures/api/es-next/test-structure/testcafe-fixtures/imported-fixture-test.js similarity index 100% rename from test/functional/fixtures/api/es-next/test-structure/testcafe-test-structure/imported-fixture-test.js rename to test/functional/fixtures/api/es-next/test-structure/testcafe-fixtures/imported-fixture-test.js diff --git a/test/functional/fixtures/api/es-next/test-structure/testcafe-test-structure/imported-test-test.js b/test/functional/fixtures/api/es-next/test-structure/testcafe-fixtures/imported-test-test.js similarity index 100% rename from test/functional/fixtures/api/es-next/test-structure/testcafe-test-structure/imported-test-test.js rename to test/functional/fixtures/api/es-next/test-structure/testcafe-fixtures/imported-test-test.js diff --git a/test/functional/fixtures/api/es-next/test-structure/testcafe-test-structure/test-structure-test.js b/test/functional/fixtures/api/es-next/test-structure/testcafe-fixtures/test-structure-test.js similarity index 69% rename from test/functional/fixtures/api/es-next/test-structure/testcafe-test-structure/test-structure-test.js rename to test/functional/fixtures/api/es-next/test-structure/testcafe-fixtures/test-structure-test.js index e69af1176ec..f8c46b7fbe8 100644 --- a/test/functional/fixtures/api/es-next/test-structure/testcafe-test-structure/test-structure-test.js +++ b/test/functional/fixtures/api/es-next/test-structure/testcafe-fixtures/test-structure-test.js @@ -1,3 +1,3 @@ // NOTE: We can just import file with tests to run them. // eslint-disable-next-line @typescript-eslint/no-unused-vars -import * as attachedTest from './attached-test'; +import * as attachedTest from './attached-test.js'; diff --git a/test/functional/fixtures/compiler-service/testcafe-fixtures/synchronous-selectors.js b/test/functional/fixtures/compiler-service/testcafe-fixtures/synchronous-selectors.js index 71213b70f08..c16ce2943bf 100644 --- a/test/functional/fixtures/compiler-service/testcafe-fixtures/synchronous-selectors.js +++ b/test/functional/fixtures/compiler-service/testcafe-fixtures/synchronous-selectors.js @@ -1,7 +1,7 @@ import { Selector, ClientFunction } from 'testcafe'; import { expect } from 'chai'; -import selectorApiExecutionMode from '../../../../../lib/client-functions/selector-api-execution-mode'; -import CHECK_ELEMENT_DELAY from '../../../../../lib/client/driver/command-executors/client-functions/selector-executor/check-element-delay'; +import selectorApiExecutionMode from '../../../../../lib/client-functions/selector-api-execution-mode.js'; +import CHECK_ELEMENT_DELAY from '../../../../../lib/client/driver/command-executors/client-functions/selector-executor/check-element-delay.js'; fixture `Selector` .page`../../api/es-next/selector/pages/index.html` diff --git a/test/functional/fixtures/concurrency/testcafe-fixtures/concurrent-test.js b/test/functional/fixtures/concurrency/testcafe-fixtures/concurrent-test.js index 6b0e38210e8..49044aec141 100644 --- a/test/functional/fixtures/concurrency/testcafe-fixtures/concurrent-test.js +++ b/test/functional/fixtures/concurrency/testcafe-fixtures/concurrent-test.js @@ -1,4 +1,4 @@ -import testInfo from '../test-info'; +import testInfo from '../test-info.js'; fixture `Concurrent` .page`../pages/index.html` diff --git a/test/functional/fixtures/concurrency/testcafe-fixtures/multibrowser-concurrent-test.js b/test/functional/fixtures/concurrency/testcafe-fixtures/multibrowser-concurrent-test.js index 9e6ae28d0f3..3bf68ca4a6c 100644 --- a/test/functional/fixtures/concurrency/testcafe-fixtures/multibrowser-concurrent-test.js +++ b/test/functional/fixtures/concurrency/testcafe-fixtures/multibrowser-concurrent-test.js @@ -1,4 +1,4 @@ -import testInfo from '../test-info'; +import testInfo from '../test-info.js'; import { ClientFunction } from 'testcafe'; const getUserAgent = ClientFunction(() => navigator.userAgent); diff --git a/test/functional/fixtures/concurrency/testcafe-fixtures/role-test.js b/test/functional/fixtures/concurrency/testcafe-fixtures/role-test.js index 215ac5ffb16..f4817d2af55 100644 --- a/test/functional/fixtures/concurrency/testcafe-fixtures/role-test.js +++ b/test/functional/fixtures/concurrency/testcafe-fixtures/role-test.js @@ -1,5 +1,5 @@ import { Role } from 'testcafe'; -import testInfo from '../test-info'; +import testInfo from '../test-info.js'; const DEMO_ROLE = Role('http://localhost:3000/fixtures/concurrency/pages/index.html', async () => { }); diff --git a/test/functional/fixtures/concurrency/testcafe-fixtures/sequential-test.js b/test/functional/fixtures/concurrency/testcafe-fixtures/sequential-test.js index c2d499c3fc0..a3d8a9530b9 100644 --- a/test/functional/fixtures/concurrency/testcafe-fixtures/sequential-test.js +++ b/test/functional/fixtures/concurrency/testcafe-fixtures/sequential-test.js @@ -1,4 +1,4 @@ -import testInfo from '../test-info'; +import testInfo from '../test-info.js'; fixture `Sequential` .page`../pages/index.html` diff --git a/test/functional/fixtures/esm/test.js b/test/functional/fixtures/esm/test.js new file mode 100644 index 00000000000..ff6af5bd1e8 --- /dev/null +++ b/test/functional/fixtures/esm/test.js @@ -0,0 +1,23 @@ +const expect = require('chai').expect; +const config = require('../../config'); +const path = require('path'); + +describe('ESM support', function () { + if (config.experimentalESM || config.experimentalDebug) { + it('Should import ESM without errors in ESM mode', function () { + return runTests('./testcafe-fixtures/import-esm.js', null, { only: 'chrome' }); + }); + } + else { + it('Should throw an error if ESM imported in CommonJS mode', function () { + return runTests('./testcafe-fixtures/import-esm.js', null, { shouldFail: true, only: 'chrome' }) + .catch(function (errs) { + const targetFileName = path.resolve('test/functional/fixtures/esm/testcafe-fixtures/import-esm.js'); + const ESModule = path.resolve('test/functional/fixtures/esm/testcafe-fixtures/esm-package.mjs'); + + expect(errs.message).to.contains(`Cannot import the ${ESModule} ECMAScript module from ${targetFileName}. Use a dynamic import() statement or enable the --experimental-esm CLI flag.`); + }); + }); + } + +}); diff --git a/test/functional/fixtures/esm/testcafe-fixtures/esm-package.mjs b/test/functional/fixtures/esm/testcafe-fixtures/esm-package.mjs new file mode 100644 index 00000000000..3c72b2d38a2 --- /dev/null +++ b/test/functional/fixtures/esm/testcafe-fixtures/esm-package.mjs @@ -0,0 +1 @@ +export default () => true; diff --git a/test/functional/fixtures/esm/testcafe-fixtures/import-esm.js b/test/functional/fixtures/esm/testcafe-fixtures/import-esm.js new file mode 100644 index 00000000000..2f470885709 --- /dev/null +++ b/test/functional/fixtures/esm/testcafe-fixtures/import-esm.js @@ -0,0 +1,7 @@ +import func from './esm-package.mjs'; + +fixture `Runner`; + +test(`Basic test`, async t => { + await t.expect(func()).ok(); +}); diff --git a/test/functional/fixtures/live/test.js b/test/functional/fixtures/live/test.js index c006bdbee8f..e6497d2d98d 100644 --- a/test/functional/fixtures/live/test.js +++ b/test/functional/fixtures/live/test.js @@ -244,7 +244,7 @@ if (config.useLocalBrowsers && !config.useHeadlessBrowsers) { }); }); } -else if (testingEnvironmentName === 'local-headless-chrome') { +else if (testingEnvironmentName === 'local-headless-chrome' && !config.experimentalESM) { describe('Live Mode', () => { it('Experimental debug', () => { const markerFile = path.join(__dirname, 'testcafe-fixtures', '.test-completed.marker'); diff --git a/test/functional/fixtures/live/testcafe-fixtures/client-scripts.js b/test/functional/fixtures/live/testcafe-fixtures/client-scripts.js index 8b97c937a96..322f633f1e0 100644 --- a/test/functional/fixtures/live/testcafe-fixtures/client-scripts.js +++ b/test/functional/fixtures/live/testcafe-fixtures/client-scripts.js @@ -1,5 +1,5 @@ import { ClientFunction } from 'testcafe'; -import helper from '../test-helper'; +import helper from '../test-helper.js'; const getTestVariableValue = ClientFunction(() => window.test); diff --git a/test/functional/fixtures/live/testcafe-fixtures/quarantine.js b/test/functional/fixtures/live/testcafe-fixtures/quarantine.js index e7842386318..6c5f688293c 100644 --- a/test/functional/fixtures/live/testcafe-fixtures/quarantine.js +++ b/test/functional/fixtures/live/testcafe-fixtures/quarantine.js @@ -1,4 +1,4 @@ -import helper from '../test-helper'; +import helper from '../test-helper.js'; fixture `Live` .page `../pages/index.html` diff --git a/test/functional/fixtures/live/testcafe-fixtures/smoke.js b/test/functional/fixtures/live/testcafe-fixtures/smoke.js index 4957e730b07..45aa268e490 100644 --- a/test/functional/fixtures/live/testcafe-fixtures/smoke.js +++ b/test/functional/fixtures/live/testcafe-fixtures/smoke.js @@ -1,4 +1,4 @@ -import helper from '../test-helper'; +import helper from '../test-helper.js'; fixture `Live` .page `../pages/index.html` diff --git a/test/functional/fixtures/live/testcafe-fixtures/test-2.js b/test/functional/fixtures/live/testcafe-fixtures/test-2.js index 6922e82819e..42f0deec332 100644 --- a/test/functional/fixtures/live/testcafe-fixtures/test-2.js +++ b/test/functional/fixtures/live/testcafe-fixtures/test-2.js @@ -1,4 +1,4 @@ -import helper from '../test-helper'; +import helper from '../test-helper.js'; fixture `Stops and starts 2` .page `../pages/index.html` diff --git a/test/functional/fixtures/multiple-windows/testcafe-fixtures/api/api-test.js b/test/functional/fixtures/multiple-windows/testcafe-fixtures/api/api-test.js index 12ac49e7521..791eb301338 100644 --- a/test/functional/fixtures/multiple-windows/testcafe-fixtures/api/api-test.js +++ b/test/functional/fixtures/multiple-windows/testcafe-fixtures/api/api-test.js @@ -5,7 +5,7 @@ import { restoreWindowState, getWindowHeight, getWindowWidth, -} from '../../../../window-helpers'; +} from '../../../../esm-utils/window-helpers.js'; const reload = ClientFunction(() => window.location.reload()); diff --git a/test/functional/fixtures/multiple-windows/testcafe-fixtures/features/emulation.js b/test/functional/fixtures/multiple-windows/testcafe-fixtures/features/emulation.js index 3721431bea7..5a759fa54ff 100644 --- a/test/functional/fixtures/multiple-windows/testcafe-fixtures/features/emulation.js +++ b/test/functional/fixtures/multiple-windows/testcafe-fixtures/features/emulation.js @@ -1,5 +1,5 @@ import { getViewportSize } from 'device-specs'; -import { getWindowHeight, getWindowWidth } from '../../../../window-helpers'; +import { getWindowHeight, getWindowWidth } from '../../../../esm-utils/window-helpers.js'; fixture `Resize in emulation` .page `http://localhost:3000/fixtures/multiple-windows/pages/features/emulation/index.html`; diff --git a/test/functional/fixtures/page-js-errors/testcafe-fixtures/fixture-and-test.js b/test/functional/fixtures/page-js-errors/testcafe-fixtures/fixture-and-test.js index 26bf405e0cd..77080b7fcf0 100644 --- a/test/functional/fixtures/page-js-errors/testcafe-fixtures/fixture-and-test.js +++ b/test/functional/fixtures/page-js-errors/testcafe-fixtures/fixture-and-test.js @@ -1,4 +1,4 @@ -const { SKIP_JS_ERRORS_CALLBACK_OPTIONS } = require('../constants'); +import { SKIP_JS_ERRORS_CALLBACK_OPTIONS } from '../constants.js'; fixture`Fixture and Test methods` .page('http://localhost:3000/fixtures/page-js-errors/pages/skip-js-errors.html') diff --git a/test/functional/fixtures/page-js-errors/testcafe-fixtures/test-controller.js b/test/functional/fixtures/page-js-errors/testcafe-fixtures/test-controller.js index 16081264180..e9b175f65e8 100644 --- a/test/functional/fixtures/page-js-errors/testcafe-fixtures/test-controller.js +++ b/test/functional/fixtures/page-js-errors/testcafe-fixtures/test-controller.js @@ -1,10 +1,10 @@ -const { +import { CLIENT_ERROR_MESSAGE, CLIENT_PAGE_URL, CLIENT_PAGE_URL_REGEXP, SKIP_JS_ERRORS_CALLBACK_OPTIONS, CLIENT_ERROR_REGEXP, -} = require('../constants'); +} from '../constants.js'; fixture`TestController method` .page('http://localhost:3000/fixtures/page-js-errors/pages/skip-js-errors.html'); @@ -54,9 +54,10 @@ test('Should correctly skip JS errors with multiple method calls', async t => { }); test('Should fail with SkipJsErrorsCallbackOptions', async t => { + const errMessage = CLIENT_ERROR_MESSAGE; const callbackOptions = { - fn: ({ message, pageUrl }) => message === CLIENT_ERROR_MESSAGE && pageUrl === 'incorrect url', - dependencies: { CLIENT_ERROR_MESSAGE }, + fn: ({ message, pageUrl }) => message === errMessage && pageUrl === 'incorrect url', + dependencies: { errMessage }, }; await t.skipJsErrors(callbackOptions) diff --git a/test/functional/fixtures/quarantine/testcafe-fixtures/test-quarantine-mode.js b/test/functional/fixtures/quarantine/testcafe-fixtures/test-quarantine-mode.js index 8343f7aa670..28515d015a4 100644 --- a/test/functional/fixtures/quarantine/testcafe-fixtures/test-quarantine-mode.js +++ b/test/functional/fixtures/quarantine/testcafe-fixtures/test-quarantine-mode.js @@ -1,4 +1,4 @@ -import quarantineScope from './quarantineScope'; +import quarantineScope from './quarantineScope.js'; fixture `Test Quarantine Mode` diff --git a/test/functional/fixtures/regression/gh-1054/testcafe-fixtures/index.test.js b/test/functional/fixtures/regression/gh-1054/testcafe-fixtures/index.test.js index a94b624cd7f..1da72f8f173 100644 --- a/test/functional/fixtures/regression/gh-1054/testcafe-fixtures/index.test.js +++ b/test/functional/fixtures/regression/gh-1054/testcafe-fixtures/index.test.js @@ -1,5 +1,5 @@ import { ClientFunction, Selector } from 'testcafe'; -import { parseUserAgent } from '../../../../../../lib/utils/parse-user-agent'; +import { parseUserAgent } from '../../../../../../lib/utils/parse-user-agent.js'; fixture `Check the target element value when the first input event raised` .page('http://localhost:3000/fixtures/regression/gh-1054/pages/index.html'); diff --git a/test/functional/fixtures/regression/gh-1932/testcafe-fixtures/fixture-with-absolute-url.js b/test/functional/fixtures/regression/gh-1932/testcafe-fixtures/fixture-with-absolute-url.js index 4d817dba07e..09f4789fb09 100644 --- a/test/functional/fixtures/regression/gh-1932/testcafe-fixtures/fixture-with-absolute-url.js +++ b/test/functional/fixtures/regression/gh-1932/testcafe-fixtures/fixture-with-absolute-url.js @@ -4,7 +4,7 @@ import { INDEX1_URL, INDEX2_RELATIVE_URL, INDEX2_URL, -} from '../constants'; +} from '../constants.js'; const getLocation = ClientFunction(() => window.location.href); diff --git a/test/functional/fixtures/regression/gh-1932/testcafe-fixtures/fixture-with-relative-url.js b/test/functional/fixtures/regression/gh-1932/testcafe-fixtures/fixture-with-relative-url.js index 2f29085328f..387d15c0167 100644 --- a/test/functional/fixtures/regression/gh-1932/testcafe-fixtures/fixture-with-relative-url.js +++ b/test/functional/fixtures/regression/gh-1932/testcafe-fixtures/fixture-with-relative-url.js @@ -6,7 +6,7 @@ import { INDEX1_RELATIVE_URL, INDEX2_RELATIVE_URL, INDEX1_WITH_UPDIR_RELATIVE_URL, -} from '../constants'; +} from '../constants.js'; const getLocation = ClientFunction(() => window.location.href); const ABSOLUTE_BASE_PATH = pathToFileURL(join(FILE_PROTOCOL_URL, '/')); diff --git a/test/functional/fixtures/regression/gh-1932/testcafe-fixtures/fixture-without-url.js b/test/functional/fixtures/regression/gh-1932/testcafe-fixtures/fixture-without-url.js index d87e34d6293..db38f2316ee 100644 --- a/test/functional/fixtures/regression/gh-1932/testcafe-fixtures/fixture-without-url.js +++ b/test/functional/fixtures/regression/gh-1932/testcafe-fixtures/fixture-without-url.js @@ -1,5 +1,5 @@ import { ClientFunction } from 'testcafe'; -import { INDEX1_URL, INDEX2_URL } from '../constants'; +import { INDEX1_URL, INDEX2_URL } from '../constants.js'; const getLocation = ClientFunction(() => window.location.href); diff --git a/test/functional/fixtures/regression/gh-1940/testcafe-fixtures/index.js b/test/functional/fixtures/regression/gh-1940/testcafe-fixtures/index.js index 50d2d69b8b1..39a2019419b 100644 --- a/test/functional/fixtures/regression/gh-1940/testcafe-fixtures/index.js +++ b/test/functional/fixtures/regression/gh-1940/testcafe-fixtures/index.js @@ -1,6 +1,6 @@ import { ClientFunction } from 'testcafe'; -import { saveWindowState, restoreWindowState } from '../../../../window-helpers'; -import config from '../../../../config'; +import { saveWindowState, restoreWindowState } from '../../../../esm-utils/window-helpers.js'; +import config from '../../../../config.js'; const removeBodyMargin = ClientFunction(() => { diff --git a/test/functional/fixtures/regression/gh-2015/testcafe-fixtures/index.js b/test/functional/fixtures/regression/gh-2015/testcafe-fixtures/index.js index 7d04c681389..2cd8f835033 100644 --- a/test/functional/fixtures/regression/gh-2015/testcafe-fixtures/index.js +++ b/test/functional/fixtures/regression/gh-2015/testcafe-fixtures/index.js @@ -2,7 +2,7 @@ import { Selector } from 'testcafe'; fixture `Should restore local storage correctly on UseRole with PreserveUrl`; -import userRole from './role'; +import userRole from './role.js'; test('Should log in with role navigation', async t => { await t.useRole(userRole); diff --git a/test/functional/fixtures/regression/gh-2074/testcafe-fixtures/index.js b/test/functional/fixtures/regression/gh-2074/testcafe-fixtures/index.js index 0be3f5b0e03..dda06b587a3 100644 --- a/test/functional/fixtures/regression/gh-2074/testcafe-fixtures/index.js +++ b/test/functional/fixtures/regression/gh-2074/testcafe-fixtures/index.js @@ -1,3 +1,3 @@ -import libraryTest from './lib'; +import libraryTest from './lib.js'; libraryTest(); diff --git a/test/functional/fixtures/regression/gh-2546/testcafe-fixtures/index.js b/test/functional/fixtures/regression/gh-2546/testcafe-fixtures/index.js index f4ea6e00e12..6371aedfd2e 100644 --- a/test/functional/fixtures/regression/gh-2546/testcafe-fixtures/index.js +++ b/test/functional/fixtures/regression/gh-2546/testcafe-fixtures/index.js @@ -1,4 +1,4 @@ -import unhandledRejection from '../unhandled-rejection'; +import unhandledRejection from '../unhandled-rejection.js'; fixture `Should fail on unhandled promise rejection` .after(() => { diff --git a/test/functional/fixtures/regression/gh-3127/testcafe-fixtures/index-test.js b/test/functional/fixtures/regression/gh-3127/testcafe-fixtures/index-test.js index a8345a7fe62..1dde0967d76 100644 --- a/test/functional/fixtures/regression/gh-3127/testcafe-fixtures/index-test.js +++ b/test/functional/fixtures/regression/gh-3127/testcafe-fixtures/index-test.js @@ -4,7 +4,7 @@ import { homedir } from 'os'; import { promisify } from 'util'; import timeLimit from 'time-limit-promise'; import del from 'del'; -import delay from '../../../../../../lib/utils/delay'; +import delay from '../../../../../../lib/utils/delay.js'; const DOWNLOADED_JSON_FILE_PATH = join(homedir(), 'Downloads', 'package.json'); const DOWNLOADED_ZIP_FILE_PATH = join(homedir(), 'Downloads', 'dummy.zip'); diff --git a/test/functional/fixtures/regression/gh-3298/testcafe-fixtures/index.js b/test/functional/fixtures/regression/gh-3298/testcafe-fixtures/index.js index d0707257eda..7503a483c48 100644 --- a/test/functional/fixtures/regression/gh-3298/testcafe-fixtures/index.js +++ b/test/functional/fixtures/regression/gh-3298/testcafe-fixtures/index.js @@ -1,4 +1,4 @@ -import actual from '../actual'; +import actual from '../actual.js'; fixture `Define hooks of the first fixture` .page `http://localhost:3000/fixtures/regression/gh-3298/pages/index.html` diff --git a/test/functional/fixtures/regression/gh-4516/testcafe-fixtures/index.js b/test/functional/fixtures/regression/gh-4516/testcafe-fixtures/index.js index 618996be24d..dc15cdc97d8 100644 --- a/test/functional/fixtures/regression/gh-4516/testcafe-fixtures/index.js +++ b/test/functional/fixtures/regression/gh-4516/testcafe-fixtures/index.js @@ -1,7 +1,5 @@ import { RequestHook, Selector } from 'testcafe'; -import { resolve } from 'path'; - -const ReExecutablePromise = require(resolve('./lib/utils/re-executable-promise')); +import ReExecutablePromise from '../../../../../../lib/utils/re-executable-promise.js'; export default class CustomHook extends RequestHook { constructor (config) { diff --git a/test/functional/fixtures/regression/gh-4613/testcafe-fixtures/first.js b/test/functional/fixtures/regression/gh-4613/testcafe-fixtures/first.js index 78450bd0b5d..f0ee29a998a 100644 --- a/test/functional/fixtures/regression/gh-4613/testcafe-fixtures/first.js +++ b/test/functional/fixtures/regression/gh-4613/testcafe-fixtures/first.js @@ -1,4 +1,4 @@ -import methodWithFailedAssertion from '../common/method-with-failed-assertion'; +import methodWithFailedAssertion from '../common/method-with-failed-assertion.js'; fixture `First`; diff --git a/test/functional/fixtures/regression/gh-4613/testcafe-fixtures/last.js b/test/functional/fixtures/regression/gh-4613/testcafe-fixtures/last.js index d48720cfa3a..cad1d6c2004 100644 --- a/test/functional/fixtures/regression/gh-4613/testcafe-fixtures/last.js +++ b/test/functional/fixtures/regression/gh-4613/testcafe-fixtures/last.js @@ -1,4 +1,4 @@ -import methodWithFailedAssertion from '../common/method-with-failed-assertion'; +import methodWithFailedAssertion from '../common/method-with-failed-assertion.js'; fixture `Last`; diff --git a/test/functional/fixtures/regression/gh-4613/testcafe-fixtures/middle.js b/test/functional/fixtures/regression/gh-4613/testcafe-fixtures/middle.js index 8afef936c69..d1fdaab7594 100644 --- a/test/functional/fixtures/regression/gh-4613/testcafe-fixtures/middle.js +++ b/test/functional/fixtures/regression/gh-4613/testcafe-fixtures/middle.js @@ -1,4 +1,4 @@ -import methodWithFailedAssertion from '../common/method-with-failed-assertion'; +import methodWithFailedAssertion from '../common/method-with-failed-assertion.js'; fixture `Middle`; diff --git a/test/functional/fixtures/regression/gh-5961/testcafe-fixtures/index.js b/test/functional/fixtures/regression/gh-5961/testcafe-fixtures/index.js index 8d3e139e383..8fcaeb24fe2 100644 --- a/test/functional/fixtures/regression/gh-5961/testcafe-fixtures/index.js +++ b/test/functional/fixtures/regression/gh-5961/testcafe-fixtures/index.js @@ -1,5 +1,5 @@ import { ClientFunction } from 'testcafe'; -import { parseUserAgent } from '../../../../../../lib/utils/parse-user-agent'; +import { parseUserAgent } from '../../../../../../lib/utils/parse-user-agent.js'; fixture`Getting Started` .page`http://localhost:3000/fixtures/regression/gh-5961/pages/index.html`; diff --git a/test/functional/fixtures/regression/gh-6205/test.js b/test/functional/fixtures/regression/gh-6205/test.js index 311c9875641..95f92a66f3e 100644 --- a/test/functional/fixtures/regression/gh-6205/test.js +++ b/test/functional/fixtures/regression/gh-6205/test.js @@ -7,7 +7,7 @@ describe('[Regression](GH-6205)', function () { 'Should throw an error', { shouldFail: true, only: 'chrome' } ).catch((errs) => { - expect(errs[0]).match(/at .*index.js:4:11/); + expect(errs[0]).match(/at .*index.js.*:4/); }); }); @@ -17,7 +17,7 @@ describe('[Regression](GH-6205)', function () { null, { shouldFail: true, only: 'chrome' } ).catch((err) => { - expect(err.stack).match(/at .*index.js:1/); + expect(err.stack).match(/at .*index.js.*:1/); }); }); }); diff --git a/test/functional/fixtures/regression/gh-637/test.js b/test/functional/fixtures/regression/gh-637/test.js index d30155ab103..716e48dfb0b 100644 --- a/test/functional/fixtures/regression/gh-637/test.js +++ b/test/functional/fixtures/regression/gh-637/test.js @@ -7,7 +7,7 @@ describe('[Regression](GH-637)', function () { tmp.setGracefulCleanup(); const tmpDir = tmp.dirSync().name; - const srcDir = path.join(__dirname, './data'); + const srcDir = path.join(__dirname, './testcafe-fixtures'); return copy(srcDir, tmpDir).then(function () { const testFile = path.join(tmpDir, './testfile.js'); diff --git a/test/functional/fixtures/regression/gh-637/data/testfile.js b/test/functional/fixtures/regression/gh-637/testcafe-fixtures/testfile.js similarity index 100% rename from test/functional/fixtures/regression/gh-637/data/testfile.js rename to test/functional/fixtures/regression/gh-637/testcafe-fixtures/testfile.js diff --git a/test/functional/fixtures/regression/gh-6722/testcafe-fixtures/index.js b/test/functional/fixtures/regression/gh-6722/testcafe-fixtures/index.js index 8b9bc148c94..a8dd62c2b27 100644 --- a/test/functional/fixtures/regression/gh-6722/testcafe-fixtures/index.js +++ b/test/functional/fixtures/regression/gh-6722/testcafe-fixtures/index.js @@ -3,7 +3,7 @@ import { SUCCESS_RESULT_ATTEMPTS, ERRORS, Counter, -} from '../constants'; +} from '../constants.js'; const counter = new Counter(); diff --git a/test/functional/fixtures/regression/gh-913/testcafe-fixtures/index-test.js b/test/functional/fixtures/regression/gh-913/testcafe-fixtures/index-test.js index 37472fad1e0..04c2c3995c9 100644 --- a/test/functional/fixtures/regression/gh-913/testcafe-fixtures/index-test.js +++ b/test/functional/fixtures/regression/gh-913/testcafe-fixtures/index-test.js @@ -1,7 +1,7 @@ import { expect } from 'chai'; import { ClientFunction } from 'testcafe'; -import { saveWindowState, restoreWindowState } from '../../../../window-helpers'; -import config from '../../../../config'; +import { saveWindowState, restoreWindowState } from '../../../../esm-utils/window-helpers.js'; +import config from '../../../../config.js'; fixture `gh-913` diff --git a/test/functional/fixtures/run-options/disable-page-caching/testcafe-fixtures/fixture.js b/test/functional/fixtures/run-options/disable-page-caching/testcafe-fixtures/fixture.js index 2c80cbfc41f..1506c9583f5 100644 --- a/test/functional/fixtures/run-options/disable-page-caching/testcafe-fixtures/fixture.js +++ b/test/functional/fixtures/run-options/disable-page-caching/testcafe-fixtures/fixture.js @@ -1,4 +1,4 @@ -import { role, url, expectedRoleLastPageLocation } from '../common/index'; +import { role, url, expectedRoleLastPageLocation } from '../common/index.js'; fixture .disablePageCaching `Fixture` diff --git a/test/functional/fixtures/run-options/disable-page-caching/testcafe-fixtures/single-test.js b/test/functional/fixtures/run-options/disable-page-caching/testcafe-fixtures/single-test.js index 51ff2806c94..aa5693c3f9d 100644 --- a/test/functional/fixtures/run-options/disable-page-caching/testcafe-fixtures/single-test.js +++ b/test/functional/fixtures/run-options/disable-page-caching/testcafe-fixtures/single-test.js @@ -1,4 +1,4 @@ -import { role, url, expectedRoleLastPageLocation } from '../common/index'; +import { role, url, expectedRoleLastPageLocation } from '../common/index.js'; fixture `Fixture`; diff --git a/test/functional/fixtures/run-options/disable-page-caching/testcafe-fixtures/test-run.js b/test/functional/fixtures/run-options/disable-page-caching/testcafe-fixtures/test-run.js index f001f4a3729..d72217d4c75 100644 --- a/test/functional/fixtures/run-options/disable-page-caching/testcafe-fixtures/test-run.js +++ b/test/functional/fixtures/run-options/disable-page-caching/testcafe-fixtures/test-run.js @@ -1,4 +1,4 @@ -import { role, url, expectedRoleLastPageLocation } from '../common/index'; +import { role, url, expectedRoleLastPageLocation } from '../common/index.js'; fixture `Fixture` .page(url) diff --git a/test/functional/fixtures/screenshots-on-fails/testcafe-fixtures/screenshots-on-fails.js b/test/functional/fixtures/screenshots-on-fails/testcafe-fixtures/screenshots-on-fails.js index 6fd2c0cf7cc..3fcdeb93570 100644 --- a/test/functional/fixtures/screenshots-on-fails/testcafe-fixtures/screenshots-on-fails.js +++ b/test/functional/fixtures/screenshots-on-fails/testcafe-fixtures/screenshots-on-fails.js @@ -1,5 +1,5 @@ import { expect } from 'chai'; -import { saveWindowState, restoreWindowState } from '../../../window-helpers'; +import { saveWindowState, restoreWindowState } from '../../../esm-utils/window-helpers.js'; // NOTE: to preserve callsites, add new tests AFTER the existing ones diff --git a/test/functional/fixtures/ui/testcafe-fixtures/status-bar-test.js b/test/functional/fixtures/ui/testcafe-fixtures/status-bar-test.js index bcd01e9abb6..a45f4f08d11 100644 --- a/test/functional/fixtures/ui/testcafe-fixtures/status-bar-test.js +++ b/test/functional/fixtures/ui/testcafe-fixtures/status-bar-test.js @@ -1,7 +1,7 @@ import { Selector, ClientFunction } from 'testcafe'; import assert from 'assert'; import OS from 'os-family'; -import { saveWindowState, restoreWindowState } from '../../../window-helpers'; +import { saveWindowState, restoreWindowState } from '../../../esm-utils/window-helpers.js'; fixture `Status Bar` .page `http://localhost:3000/fixtures/ui/pages/empty-page.html` diff --git a/test/functional/setup.js b/test/functional/setup.js index 2bc4ba393c7..7a29ea1eeb0 100644 --- a/test/functional/setup.js +++ b/test/functional/setup.js @@ -161,6 +161,7 @@ before(function () { port: 1337, isUserVariables: true, }, + experimentalEsm: config.experimentalESM, }; return createTestCafe(testCafeOptions) diff --git a/test/server/cli-argument-parser-test.js b/test/server/cli-argument-parser-test.js index 282c0b40037..0c87697b75f 100644 --- a/test/server/cli-argument-parser-test.js +++ b/test/server/cli-argument-parser-test.js @@ -859,6 +859,7 @@ describe('CLI argument parser', function () { { long: '--experimental-proxyless' }, { long: '--base-url' }, { long: '--disable-cross-domain' }, + { long: '--experimental-esm' }, ]; const parser = new CliArgumentParser(''); @@ -875,7 +876,7 @@ describe('CLI argument parser', function () { } const expectedRunOptionsCount = 22; - const expectedOtherOptionsCount = 37; + const expectedOtherOptionsCount = 38; const otherOptionsCount = options.length - expectedRunOptionsCount; expect(runOptionNames.length).eql(expectedRunOptionsCount, ADD_TO_RUN_OPTIONS_WARNING); diff --git a/ts-defs-src/runner-api/configuration.d.ts b/ts-defs-src/runner-api/configuration.d.ts index 8e4035a3cf2..6a8376fc65f 100644 --- a/ts-defs-src/runner-api/configuration.d.ts +++ b/ts-defs-src/runner-api/configuration.d.ts @@ -25,9 +25,9 @@ interface TestCafeConfigurationOptions extends RunOptions, StartOptions { videoOptions: VideoConfigOptions['options']; videoEncodingOptions: VideoConfigOptions['encodingOptions']; - filter: FilterDescriptor; + filter: FilterDescriptor; clientScripts: ClientScriptOptions; compilerOptions: CompilerOptions; -} \ No newline at end of file +}