diff --git a/README.md b/README.md index caa3138..0b3915a 100644 --- a/README.md +++ b/README.md @@ -31,11 +31,6 @@ This plugin maintains compatibility with the Cypress and Node.js versions listed [![11.2.0+ | 12.0.0+](https://img.shields.io/badge/Cypress-11.2.0%2B%20%7C%2012.0.0%2B-17202C?logo=cypress&labelColor=white&logoColor=17202C&style=flat)](https://cypress.io) [![16 | 18 | 20](https://img.shields.io/badge/Node.js-16%20%7C%2018%20%7C%2020-339933?logo=node.js&labelColor=white&logoColor=339933&style=flat)](https://nodejs.org) -For TypeScript projects, `typescript` version 4.7 or later is required, and `tsconfig.json` must set -[`moduleResolution`](https://www.typescriptlang.org/tsconfig#moduleResolution) to `node16` or -`nodenext`. This setting is required so that -[ES modules](https://nodejs.org/api/esm.html#modules-ecmascript-modules) resolve correctly. - ## Jest Plugin The Jest plugin enables users of the [Jest](https://jestjs.io) JavaScript test framework diff --git a/packages/cypress-plugin/README.md b/packages/cypress-plugin/README.md index 45a6715..961ceac 100644 --- a/packages/cypress-plugin/README.md +++ b/packages/cypress-plugin/README.md @@ -22,11 +22,6 @@ This plugin maintains compatibility with the Cypress and Node.js versions listed [![11.2.0+ | 12.0.0+](https://img.shields.io/badge/Cypress-11.2.0%2B%20%7C%2012.0.0%2B-17202C?logo=cypress&labelColor=white&logoColor=17202C&style=flat)](https://cypress.io) [![16 | 18 | 20](https://img.shields.io/badge/Node.js-16%20%7C%2018%20%7C%2020-339933?logo=node.js&labelColor=white&logoColor=339933&style=flat)](https://nodejs.org) -For TypeScript projects, `typescript` version 4.7 or later is required, and `tsconfig.json` must set -[`moduleResolution`](https://www.typescriptlang.org/tsconfig#moduleResolution) to `node16` or -`nodenext`. This setting is required so that -[ES modules](https://nodejs.org/api/esm.html#modules-ecmascript-modules) resolve correctly. - ## Contributing To report a bug or request a new feature, please diff --git a/packages/cypress-plugin/package.json b/packages/cypress-plugin/package.json index e6027b1..7f4d6f7 100644 --- a/packages/cypress-plugin/package.json +++ b/packages/cypress-plugin/package.json @@ -18,6 +18,10 @@ "types": "./dist/config-wrapper.d.ts", "default": "./dist/config-wrapper.mjs" }, + "./config-wrapper-sync": { + "types": "./dist/config-wrapper-sync.d.ts", + "default": "./dist/config-wrapper-sync.js" + }, "./reporter": { "types": "./dist/reporter.d.ts", "default": "./dist/reporter.js" @@ -33,6 +37,7 @@ "dist/**/*.mjs", "dist/index.d.ts", "dist/config-wrapper.d.ts", + "dist/config-wrapper-sync.d.ts", "dist/reporter.d.ts", "dist/skip-tests.d.ts" ], @@ -86,7 +91,7 @@ }, "scripts": { "build": "yarn clean && tsc --noEmit && tsc --noEmit -p src && yarn build:cjs && yarn build:esm", - "build:cjs": "rollup --config --dir dist", + "build:cjs": "rollup --config --dir dist && rollup --config --input src/config-wrapper-sync.ts --file dist/config-wrapper-sync.js --chunkFileNames \"[name]-[hash]-sync.js\"", "build:esm": "rollup --config --input src/config-wrapper.ts --file dist/config-wrapper.mjs --format es --chunkFileNames \"[name]-[hash].mjs\"", "clean": "rm -rf dist/", "test": "cross-env NODE_OPTIONS=--experimental-vm-modules jest --useStderr --verbose" diff --git a/packages/cypress-plugin/src/config-wrapper-sync.ts b/packages/cypress-plugin/src/config-wrapper-sync.ts new file mode 100644 index 0000000..3b4e983 --- /dev/null +++ b/packages/cypress-plugin/src/config-wrapper-sync.ts @@ -0,0 +1,12 @@ +// Copyright (c) 2023 Developer Innovations, LLC + +import { wrapCypressConfig } from "./index"; +import _debug from "debug"; +import { loadUserConfigSync } from "./load-user-config"; + +const debug = _debug("unflakable:config-wrapper-sync"); + +const userConfig = loadUserConfigSync(); +debug("Loaded user config %o", userConfig); + +export default wrapCypressConfig(userConfig); diff --git a/packages/cypress-plugin/src/config-wrapper.ts b/packages/cypress-plugin/src/config-wrapper.ts index ce904e0..507c638 100644 --- a/packages/cypress-plugin/src/config-wrapper.ts +++ b/packages/cypress-plugin/src/config-wrapper.ts @@ -2,57 +2,10 @@ import { wrapCypressConfig } from "./index"; import _debug from "debug"; -import { require } from "./utils"; -import { - ENV_VAR_USER_CONFIG_JSON, - ENV_VAR_USER_CONFIG_PATH, -} from "./config-env-vars"; -import path from "path"; +import { loadUserConfig } from "./load-user-config"; const debug = _debug("unflakable:config-wrapper"); -type LoadedConfig = - | { - default: Cypress.ConfigOptions; - } - | (Cypress.ConfigOptions & { default: undefined }); - -const loadUserConfig = async (): Promise> => { - if (ENV_VAR_USER_CONFIG_JSON.value !== undefined) { - debug(`Parsing inline user config ${ENV_VAR_USER_CONFIG_JSON.value}`); - - return JSON.parse( - ENV_VAR_USER_CONFIG_JSON.value - ) as Cypress.ConfigOptions; - } else if (ENV_VAR_USER_CONFIG_PATH.value === undefined) { - throw new Error("No user config to load"); - } - - debug(`Loading user config from ${ENV_VAR_USER_CONFIG_PATH.value}`); - - // Relative paths from the user's config need to resolve relative to the location of their - // cypress.config.js, not ours. This affects things like webpack for component testing. - const configPathDir = path.dirname(ENV_VAR_USER_CONFIG_PATH.value); - debug(`Changing working directory to ${configPathDir}`); - process.chdir(configPathDir); - - // For CommonJS projects, we need to use require(), at least for TypeScript config files. - // Dynamic import() doesn't support TypeScript imports in CommonJS projects, at least the way - // Cypress sets up the environment before loading the config. - try { - const config = require(ENV_VAR_USER_CONFIG_PATH.value) as LoadedConfig; - return config.default ?? config; - } catch (e) { - // require() can't import ES modules, so now we try a dynamic import(). This is what gets used - // for ESM projects. - debug(`require() failed; attempting dynamic import(): ${e as string}`); - const config = (await import( - ENV_VAR_USER_CONFIG_PATH.value - )) as LoadedConfig; - return config.default ?? config; - } -}; - const userConfig = await loadUserConfig(); debug("Loaded user config %o", userConfig); diff --git a/packages/cypress-plugin/src/load-user-config.ts b/packages/cypress-plugin/src/load-user-config.ts new file mode 100644 index 0000000..aee923e --- /dev/null +++ b/packages/cypress-plugin/src/load-user-config.ts @@ -0,0 +1,62 @@ +// Copyright (c) 2023 Developer Innovations, LLC + +import _debug from "debug"; +import { require } from "./utils"; +import { + ENV_VAR_USER_CONFIG_JSON, + ENV_VAR_USER_CONFIG_PATH, +} from "./config-env-vars"; +import path from "path"; + +const debug = _debug("unflakable:load-user-config"); + +export type LoadedConfig = + | { + default: Cypress.ConfigOptions; + } + | (Cypress.ConfigOptions & { default: undefined }); + +export const loadUserConfigSync = (): Cypress.ConfigOptions => { + if (ENV_VAR_USER_CONFIG_JSON.value !== undefined) { + debug(`Parsing inline user config ${ENV_VAR_USER_CONFIG_JSON.value}`); + + return JSON.parse( + ENV_VAR_USER_CONFIG_JSON.value + ) as Cypress.ConfigOptions; + } else if (ENV_VAR_USER_CONFIG_PATH.value === undefined) { + throw new Error("No user config to load"); + } + + debug(`Loading user config from ${ENV_VAR_USER_CONFIG_PATH.value}`); + + // Relative paths from the user's config need to resolve relative to the location of their + // cypress.config.js, not ours. This affects things like webpack for component testing. + const configPathDir = path.dirname(ENV_VAR_USER_CONFIG_PATH.value); + debug(`Changing working directory to ${configPathDir}`); + process.chdir(configPathDir); + + // For CommonJS projects, we need to use require(), at least for TypeScript config files. + // Dynamic import() doesn't support TypeScript imports in CommonJS projects, at least the way + // Cypress sets up the environment before loading the config. + const config = require(ENV_VAR_USER_CONFIG_PATH.value) as LoadedConfig; + return config.default ?? config; +}; + +export const loadUserConfig = async (): Promise< + Cypress.ConfigOptions +> => { + // For CommonJS projects, we need to use require(), at least for TypeScript config files. + // Dynamic import() doesn't support TypeScript imports in CommonJS projects, at least the way + // Cypress sets up the environment before loading the config. + try { + return loadUserConfigSync(); + } catch (e) { + // require() can't import ES modules, so now we try a dynamic import(). This is what gets used + // for ESM projects. + debug(`require() failed; attempting dynamic import(): ${e as string}`); + const config = (await import( + ENV_VAR_USER_CONFIG_PATH.value as string + )) as LoadedConfig; + return config.default ?? config; + } +}; diff --git a/packages/cypress-plugin/src/main.ts b/packages/cypress-plugin/src/main.ts index a8e62fd..9c7f65c 100755 --- a/packages/cypress-plugin/src/main.ts +++ b/packages/cypress-plugin/src/main.ts @@ -27,6 +27,8 @@ import { } from "./config-env-vars"; const CONFIG_WRAPPER_MODULE = "@unflakable/cypress-plugin/config-wrapper"; +const CONFIG_WRAPPER_SYNC_MODULE = + "@unflakable/cypress-plugin/config-wrapper-sync"; const debug = _debug("unflakable:main"); @@ -266,6 +268,8 @@ const main = async (): Promise => { const unflakableConfig = await loadConfig(projectRoot, args["test-suite-id"]); debug(`Unflakable plugin is ${unflakableConfig.enabled ? "en" : "dis"}abled`); + let configFile: string | undefined = undefined; + if (unflakableConfig.enabled) { if (args.branch !== undefined) { branchOverride.value = args.branch; @@ -293,43 +297,77 @@ const main = async (): Promise => { JSON.stringify(unflakableConfig); if (args["auto-config"]) { + let userConfigPath: string | undefined = undefined; if (runOptions.config !== undefined) { ENV_VAR_USER_CONFIG_JSON.value = JSON.stringify(runOptions.config); } else { - const userConfigPath = await resolveUserConfigPath( - projectRoot, - runOptions - ); + userConfigPath = await resolveUserConfigPath(projectRoot, runOptions); ENV_VAR_USER_CONFIG_PATH.value = userConfigPath; + } - // By default, Cypress invokes ts-node on CommonJS TypeScript projects by setting `dir` - // (deprecated alias for `cwd`) to the directory containing the Cypress config file: - // https://github.com/cypress-io/cypress/blob/62f58e00ec0e1f95bc0db3c644638e4882b91992/packages/server/lib/plugins/child/ts_node.js#L63 - - // For both ESM and CommonJS TypeScript projects, Cypress invokes ts-node with the CWD set - // to that directory: - // https://github.com/cypress-io/cypress/blob/62f58e00ec0e1f95bc0db3c644638e4882b91992/packages/data-context/src/data/ProjectConfigIpc.ts#L260 - - // Since we're passing our `config-wrapper.js` as the Cypress config, the CWD becomes our - // dist/ directory. However, we need ts-node to load the user's tsconfig.json, not our own, - // or the user's cypress.config.ts file may not load properly when we require()/import() - // it. - // To accomplish this, we try to discover the user's tsconfig.json by traversing the - // ancestor directories containing the user's Cypress config file. This is the same - // approach TypeScript uses: - // https://github.com/microsoft/TypeScript/blob/2beeb8b93143f75cdf788d05bb3678ce3ff0e2b3/src/compiler/program.ts#L340-L345 - - // If we find a tsconfig.json, we set the TS_NODE_PROJECT environment variable to the - // directory containing it, which ts-node then uses instead of searching the `dir` passed by - // Cypress. - const userTsConfig = await findUserTsConfig( - path.dirname(userConfigPath) - ); - if (userTsConfig !== null) { - const tsNodeProject = path.dirname(userTsConfig); - debug(`Setting TS_NODE_PROJECT to ${tsNodeProject}`); - process.env.TS_NODE_PROJECT = tsNodeProject; - } + // By default, Cypress invokes ts-node on CommonJS TypeScript projects by setting `dir` + // (deprecated alias for `cwd`) to the directory containing the Cypress config file: + // https://github.com/cypress-io/cypress/blob/62f58e00ec0e1f95bc0db3c644638e4882b91992/packages/server/lib/plugins/child/ts_node.js#L63 + + // For both ESM and CommonJS TypeScript projects, Cypress invokes ts-node with the CWD set + // to that directory: + // https://github.com/cypress-io/cypress/blob/62f58e00ec0e1f95bc0db3c644638e4882b91992/packages/data-context/src/data/ProjectConfigIpc.ts#L260 + + // Since we're passing our `config-wrapper.js` as the Cypress config, the CWD becomes our + // dist/ directory. However, we need ts-node to load the user's tsconfig.json, not our own, + // or the user's cypress.config.ts file may not load properly when we require()/import() + // it. + // To accomplish this, we try to discover the user's tsconfig.json by traversing the + // ancestor directories containing the user's Cypress config file. This is the same + // approach TypeScript uses: + // https://github.com/microsoft/TypeScript/blob/2beeb8b93143f75cdf788d05bb3678ce3ff0e2b3/src/compiler/program.ts#L340-L345 + + // If we find a tsconfig.json, we set the TS_NODE_PROJECT environment variable to the + // directory containing it, which ts-node then uses instead of searching the `dir` passed by + // Cypress. + const userTsConfig = await findUserTsConfig( + path.dirname(userConfigPath ?? projectRoot) + ); + if (userTsConfig !== null) { + const tsNodeProject = path.dirname(userTsConfig); + debug(`Setting TS_NODE_PROJECT to ${tsNodeProject}`); + process.env.TS_NODE_PROJECT = tsNodeProject; + } + + // ESM configuration files can only be imported via dynamic import(), which is async. + // However, + // CommonJS files can't have top-level await, which means we can't have a CommonJS wrapper + // (config-wrapper-sync) import an ESM configuration file. For that, we use the ESM config + // wrapper. However, the ESM wrapper doesn't work for CommonJS TypeScript projects unless + // they explicitly set `moduleResolution: node16` (or `nodenext`), which we don't want to + // require from users. This is why we detect whether the project and/or config file are ESM + // when deciding which config wrapper to use. We assume that if the Cypress config file is + // .mjs/.mts, the TypeScript project (if any) is already using `node16`/`nodenext` (since + // otherwise, the project's own Cypress config file wouldn't work). + const userConfigIsEsm = + userConfigPath !== undefined + ? [".mjs", ".mts"].includes(path.extname(userConfigPath)) + : false; + + // Try to determine whether the project is using ESM, as Cypress does. See + // https://github.com/cypress-io/cypress/blob/62f58e00ec0e1f95bc0db3c644638e4882b91992/packages/data-context/src/data/ProjectConfigIpc.ts#L276-L285 + try { + const pkgJson = JSON.parse( + (await fs.readFile(path.join(projectRoot, "package.json"))).toString( + "utf8" + ) + ) as { type?: string }; + + configFile = + pkgJson.type === "module" || userConfigIsEsm + ? require.resolve(CONFIG_WRAPPER_MODULE) + : require.resolve(CONFIG_WRAPPER_SYNC_MODULE); + } catch (e) { + // Project does not have `package.json` or it was not found. + // Reasonable to assume not using es modules unless config is explicitly ESM. + configFile = userConfigIsEsm + ? require.resolve(CONFIG_WRAPPER_MODULE) + : require.resolve(CONFIG_WRAPPER_SYNC_MODULE); } } @@ -344,7 +382,7 @@ const main = async (): Promise => { ...runOptions, ...(args["auto-config"] ? { - configFile: require.resolve(CONFIG_WRAPPER_MODULE), + configFile, } : {}), quiet: true, diff --git a/packages/cypress-plugin/test/integration-common/package.json b/packages/cypress-plugin/test/integration-common/package.json index 0749244..ea472b5 100644 --- a/packages/cypress-plugin/test/integration-common/package.json +++ b/packages/cypress-plugin/test/integration-common/package.json @@ -1,19 +1,6 @@ { "name": "cypress-integration-common", "private": true, - "exports": { - "./config": { - "types": "./dist/config.d.ts", - "default": "./dist/config.js" - }, - "./git": { - "types": "./dist/git.d.ts", - "default": "./dist/git.js" - }, - "./mock-cosmiconfig": { - "default": "./dist/mock-cosmiconfig.js" - } - }, "dependencies": { "debug": "^4.3.3", "expect": "^29.5.0", @@ -25,6 +12,7 @@ "@rollup/plugin-typescript": "^11.1.1", "@unflakable/plugins-common": "workspace:^", "rollup": "^3.21.1", + "rollup-plugin-dts": "^5.3.0", "typescript": "^4.9.5" }, "scripts": { diff --git a/packages/cypress-plugin/test/integration-common/rollup.config.mjs b/packages/cypress-plugin/test/integration-common/rollup.config.mjs index fcf3630..c7801cf 100644 --- a/packages/cypress-plugin/test/integration-common/rollup.config.mjs +++ b/packages/cypress-plugin/test/integration-common/rollup.config.mjs @@ -3,34 +3,61 @@ import pluginCommonJs from "@rollup/plugin-commonjs"; import pluginNodeResolve from "@rollup/plugin-node-resolve"; import pluginTypescript from "@rollup/plugin-typescript"; +import pluginDts from "rollup-plugin-dts"; // We emit a CommonJS bundle so that both CommonJS and ESM targets can use this package. The package // depends on plugins-common, which is ESM, so we have to use Rollup and can't rely solely on tsc. // Otherwise, the transitive dependency remains ESM, which fails at runtime during require(). /** - * @type {import("rollup").NormalizedInputOptions} + * @type {import("rollup").IsExternal} */ -export default { - input: ["src/config.ts", "src/git.ts", "src/mock-cosmiconfig.ts"], - output: { - dir: "dist", - format: "cjs", +const isExternal = (id) => + !id.startsWith(".") && + !id.startsWith("/") && + !id.startsWith("src/") && + !["@unflakable/plugins-common"].includes(id); + +/** + * @type {import("rollup").NormalizedInputOptions[]} + */ +export default [ + { + input: ["src/config.ts", "src/git.ts", "src/mock-cosmiconfig.ts"], + output: { + dir: "dist", + format: "cjs", + }, + external: isExternal, + // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment + plugins: [ + pluginCommonJs(), + pluginNodeResolve({ preferBuiltins: true }), + pluginTypescript({ tsconfig: "src/tsconfig.json" }), + ], + treeshake: { + // Assume internal modules do not have side effects when they're imported. This helps remove + // unnecessary require()'s from the transpiled code. + moduleSideEffects: (id, external) => external, + }, }, - external: (id) => - !id.startsWith(".") && - !id.startsWith("/") && - !id.startsWith("src/") && - !["@unflakable/plugins-common"].includes(id), - // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment - plugins: [ - pluginCommonJs(), - pluginNodeResolve({ preferBuiltins: true }), - pluginTypescript({ tsconfig: "src/tsconfig.json" }), - ], - treeshake: { - // Assume internal modules do not have side effects when they're imported. This helps remove - // unnecessary require()'s from the transpiled code. - moduleSideEffects: (id, external) => external, + // Rollup types so that UnflakableConfig from @unflakable/plugins-common is bundled. Otherwise, + // the integration-input* packages would need to depend on @unflakable/plugins-common, which we + // don't want since we need to test that @unflakable/cypress-plugin bundles everything it needs + // to. + { + input: "dist/config.d.ts", + output: { + file: "dist/config.d.ts", + format: "cjs", + }, + external: isExternal, + // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment + plugins: [ + pluginNodeResolve({ preferBuiltins: true }), + pluginDts({ + respectExternal: true, + }), + ], }, -}; +]; diff --git a/packages/cypress-plugin/test/integration-input-esm/cypress-config.cjs b/packages/cypress-plugin/test/integration-input-esm/cypress-config.cjs index 77e3bcf..f09d4d1 100644 --- a/packages/cypress-plugin/test/integration-input-esm/cypress-config.cjs +++ b/packages/cypress-plugin/test/integration-input-esm/cypress-config.cjs @@ -1,9 +1,11 @@ // Copyright (c) 2023 Developer Innovations, LLC -const { registerSimpleGitMock } = require("cypress-integration-common/git"); +const { + registerSimpleGitMock, +} = require("cypress-integration-common/dist/git"); const { registerCosmiconfigMock, -} = require("cypress-integration-common/config"); +} = require("cypress-integration-common/dist/config"); module.exports = { /** diff --git a/packages/cypress-plugin/test/integration-input-esm/cypress-config.js b/packages/cypress-plugin/test/integration-input-esm/cypress-config.js index 2b14fc0..d659108 100644 --- a/packages/cypress-plugin/test/integration-input-esm/cypress-config.js +++ b/packages/cypress-plugin/test/integration-input-esm/cypress-config.js @@ -3,8 +3,8 @@ import { openDevToolsOnLaunch } from "./config-js/devtools.js"; import { registerTasks } from "./config-js/tasks.js"; import webpackConfig from "./config-js/webpack.js"; -import { registerSimpleGitMock } from "cypress-integration-common/git"; -import { registerCosmiconfigMock } from "cypress-integration-common/config"; +import { registerSimpleGitMock } from "cypress-integration-common/dist/git.js"; +import { registerCosmiconfigMock } from "cypress-integration-common/dist/config.js"; /** * @type {Cypress.ConfigOptions} diff --git a/packages/cypress-plugin/test/integration-input-esm/cypress.config.ts b/packages/cypress-plugin/test/integration-input-esm/cypress.config.ts index 9bd64c9..1778af1 100644 --- a/packages/cypress-plugin/test/integration-input-esm/cypress.config.ts +++ b/packages/cypress-plugin/test/integration-input-esm/cypress.config.ts @@ -6,8 +6,8 @@ import { defineConfig } from "cypress"; import * as devtools from "./config/devtools.js"; import { registerTasks } from "./config/tasks.js"; import webpackConfig from "./config/webpack.js"; -import { registerSimpleGitMock } from "cypress-integration-common/git"; -import { registerCosmiconfigMock } from "cypress-integration-common/config"; +import { registerSimpleGitMock } from "cypress-integration-common/dist/git.js"; +import { registerCosmiconfigMock } from "cypress-integration-common/dist/config.js"; export default defineConfig({ component: { diff --git a/packages/cypress-plugin/test/integration-input-esm/tsconfig.json b/packages/cypress-plugin/test/integration-input-esm/tsconfig.json index aff7c99..30ac11e 100644 --- a/packages/cypress-plugin/test/integration-input-esm/tsconfig.json +++ b/packages/cypress-plugin/test/integration-input-esm/tsconfig.json @@ -2,7 +2,9 @@ "extends": "../../../../tsconfig.json", "compilerOptions": { "baseUrl": "./", - "module": "ES2022", + // node16 adds ESM support. + "module": "node16", + "moduleResolution": "node16", "preserveSymlinks": true, // The ts-loader that Cypress uses doesn't seem to support the `??` operator. "target": "ES2019", diff --git a/packages/cypress-plugin/test/integration-input-manual/cypress-config.mjs b/packages/cypress-plugin/test/integration-input-manual/cypress-config.mjs index 37d987c..1200a83 100644 --- a/packages/cypress-plugin/test/integration-input-manual/cypress-config.mjs +++ b/packages/cypress-plugin/test/integration-input-manual/cypress-config.mjs @@ -3,8 +3,8 @@ import devtools from "./config/devtools.js"; import tasks from "./config/tasks.js"; import webpackConfig from "./config/webpack.js"; -import { registerSimpleGitMock } from "cypress-integration-common/git"; -import { registerCosmiconfigMock } from "cypress-integration-common/config"; +import { registerSimpleGitMock } from "cypress-integration-common/dist/git.js"; +import { registerCosmiconfigMock } from "cypress-integration-common/dist/config.js"; import { registerUnflakable } from "@unflakable/cypress-plugin"; /** diff --git a/packages/cypress-plugin/test/integration-input-manual/cypress.config.js b/packages/cypress-plugin/test/integration-input-manual/cypress.config.js index f11bae3..0a7d411 100644 --- a/packages/cypress-plugin/test/integration-input-manual/cypress.config.js +++ b/packages/cypress-plugin/test/integration-input-manual/cypress.config.js @@ -3,10 +3,12 @@ const { openDevToolsOnLaunch } = require("./config/devtools"); const { registerTasks } = require("./config/tasks"); const webpackConfig = require("./config/webpack"); -const { registerSimpleGitMock } = require("cypress-integration-common/git"); +const { + registerSimpleGitMock, +} = require("cypress-integration-common/dist/git"); const { registerCosmiconfigMock, -} = require("cypress-integration-common/config"); +} = require("cypress-integration-common/dist/config"); const { registerUnflakable } = require("@unflakable/cypress-plugin"); diff --git a/packages/cypress-plugin/test/integration-input/cypress-config.js b/packages/cypress-plugin/test/integration-input/cypress-config.js index e83d1a0..421f448 100644 --- a/packages/cypress-plugin/test/integration-input/cypress-config.js +++ b/packages/cypress-plugin/test/integration-input/cypress-config.js @@ -3,10 +3,12 @@ const { openDevToolsOnLaunch } = require("config-js/devtools"); const webpackConfig = require("config-js/webpack"); const { registerTasks } = require("config-js/tasks"); -const { registerSimpleGitMock } = require("cypress-integration-common/git"); +const { + registerSimpleGitMock, +} = require("cypress-integration-common/dist/git"); const { registerCosmiconfigMock, -} = require("cypress-integration-common/config"); +} = require("cypress-integration-common/dist/config"); module.exports = { /** diff --git a/packages/cypress-plugin/test/integration-input/cypress-config.mjs b/packages/cypress-plugin/test/integration-input/cypress-config.mjs index 74e8a64..cb12ca2 100644 --- a/packages/cypress-plugin/test/integration-input/cypress-config.mjs +++ b/packages/cypress-plugin/test/integration-input/cypress-config.mjs @@ -3,8 +3,8 @@ import devtools from "./config-js/devtools.js"; import tasks from "./config-js/tasks.js"; import webpackConfig from "./config-js/webpack.js"; -import { registerSimpleGitMock } from "cypress-integration-common/git"; -import { registerCosmiconfigMock } from "cypress-integration-common/config"; +import { registerSimpleGitMock } from "cypress-integration-common/dist/git.js"; +import { registerCosmiconfigMock } from "cypress-integration-common/dist/config.js"; /** * @type {Cypress.ConfigOptions} diff --git a/packages/cypress-plugin/test/integration-input/cypress.config.ts b/packages/cypress-plugin/test/integration-input/cypress.config.ts index 4e1efe8..88a41f0 100644 --- a/packages/cypress-plugin/test/integration-input/cypress.config.ts +++ b/packages/cypress-plugin/test/integration-input/cypress.config.ts @@ -1,7 +1,7 @@ // Copyright (c) 2023 Developer Innovations, LLC -import { registerSimpleGitMock } from "cypress-integration-common/git"; -import { registerCosmiconfigMock } from "cypress-integration-common/config"; +import { registerSimpleGitMock } from "cypress-integration-common/dist/git"; +import { registerCosmiconfigMock } from "cypress-integration-common/dist/config"; import { defineConfig } from "cypress"; // This intentionally uses the CommonJS relative import syntax that doesn't start with `./` in // order to test that our inclusion of the user config file resolves relative path imports (via diff --git a/packages/cypress-plugin/test/integration-input/tsconfig.json b/packages/cypress-plugin/test/integration-input/tsconfig.json index 1243db8..e3ff93e 100644 --- a/packages/cypress-plugin/test/integration-input/tsconfig.json +++ b/packages/cypress-plugin/test/integration-input/tsconfig.json @@ -2,6 +2,8 @@ "extends": "../../../../tsconfig.json", "compilerOptions": { "baseUrl": "./", + // Make sure the plugin works without requiring `moduleResolution: nodenext`. + "moduleResolution": "node", "preserveSymlinks": true, // The ts-loader that Cypress uses doesn't seem to support the `??` operator. "target": "ES2019", diff --git a/packages/cypress-plugin/test/integration/src/basic-matrix.test.ts b/packages/cypress-plugin/test/integration/src/basic-matrix.test.ts index 3d5aec9..e8b29ca 100644 --- a/packages/cypress-plugin/test/integration/src/basic-matrix.test.ts +++ b/packages/cypress-plugin/test/integration/src/basic-matrix.test.ts @@ -60,6 +60,16 @@ integrationTestSuite(() => { } : null, configFile, + envVars: + // moduleResolution must be `node16` or `nodenext` in order to load a + // .mjs ESM config file. + projectName === "integration-input" && + configFile === "cypress-config.mjs" + ? { + TS_NODE_COMPILER_OPTIONS: + '{"moduleResolution":"node16"}', + } + : {}, project: projectName as TestProjectName, testMode, expectQuarantinedTestsToBeSkipped: diff --git a/packages/cypress-plugin/test/integration/src/run-test-case.ts b/packages/cypress-plugin/test/integration/src/run-test-case.ts index 8e13686..e5ab45a 100644 --- a/packages/cypress-plugin/test/integration/src/run-test-case.ts +++ b/packages/cypress-plugin/test/integration/src/run-test-case.ts @@ -26,11 +26,11 @@ import { promisify, TextDecoder } from "util"; import { CONFIG_MOCK_ENV_VAR, CosmiconfigMockParams, -} from "cypress-integration-common/config"; +} from "cypress-integration-common/dist/config"; import { GIT_MOCK_ENV_VAR, SimpleGitMockParams, -} from "cypress-integration-common/git"; +} from "cypress-integration-common/dist/git"; import path from "path"; import { SummaryTotals } from "./parse-output"; import { expect as expectExt } from "@jest/globals"; @@ -799,7 +799,7 @@ export const runTestCase = async ( const args = [ "--require", - require.resolve("cypress-integration-common/mock-cosmiconfig"), + require.resolve("cypress-integration-common/dist/mock-cosmiconfig"), cypressPluginBin, ...(params.project === "integration-input-manual" ? ["--no-auto-config", "--no-auto-support"] diff --git a/yarn.lock b/yarn.lock index c13a158..d57fbcf 100644 --- a/yarn.lock +++ b/yarn.lock @@ -24,6 +24,15 @@ __metadata: languageName: node linkType: hard +"@babel/code-frame@npm:^7.18.6": + version: 7.22.5 + resolution: "@babel/code-frame@npm:7.22.5" + dependencies: + "@babel/highlight": ^7.22.5 + checksum: cfe804f518f53faaf9a1d3e0f9f74127ab9a004912c3a16fda07fb6a633393ecb9918a053cb71804204c1b7ec3d49e1699604715e2cfb0c9f7bc4933d324ebb6 + languageName: node + linkType: hard + "@babel/compat-data@npm:^7.17.7, @babel/compat-data@npm:^7.20.5, @babel/compat-data@npm:^7.21.5, @babel/compat-data@npm:^7.22.0": version: 7.22.3 resolution: "@babel/compat-data@npm:7.22.3" @@ -292,6 +301,13 @@ __metadata: languageName: node linkType: hard +"@babel/helper-validator-identifier@npm:^7.22.5": + version: 7.22.5 + resolution: "@babel/helper-validator-identifier@npm:7.22.5" + checksum: 7f0f30113474a28298c12161763b49de5018732290ca4de13cdaefd4fd0d635a6fe3f6686c37a02905fb1e64f21a5ee2b55140cf7b070e729f1bd66866506aea + languageName: node + linkType: hard + "@babel/helper-validator-option@npm:^7.21.0": version: 7.21.0 resolution: "@babel/helper-validator-option@npm:7.21.0" @@ -333,6 +349,17 @@ __metadata: languageName: node linkType: hard +"@babel/highlight@npm:^7.22.5": + version: 7.22.5 + resolution: "@babel/highlight@npm:7.22.5" + dependencies: + "@babel/helper-validator-identifier": ^7.22.5 + chalk: ^2.0.0 + js-tokens: ^4.0.0 + checksum: f61ae6de6ee0ea8d9b5bcf2a532faec5ab0a1dc0f7c640e5047fc61630a0edb88b18d8c92eb06566d30da7a27db841aca11820ecd3ebe9ce514c9350fbed39c4 + languageName: node + linkType: hard + "@babel/parser@npm:^7.1.0, @babel/parser@npm:^7.14.7, @babel/parser@npm:^7.20.7, @babel/parser@npm:^7.21.9, @babel/parser@npm:^7.22.0, @babel/parser@npm:^7.22.4": version: 7.22.4 resolution: "@babel/parser@npm:7.22.4" @@ -4161,6 +4188,7 @@ __metadata: debug: ^4.3.3 expect: ^29.5.0 rollup: ^3.21.1 + rollup-plugin-dts: ^5.3.0 simple-git: ^3.16.0 typescript: ^4.9.5 languageName: unknown @@ -7636,6 +7664,15 @@ __metadata: languageName: node linkType: hard +"magic-string@npm:^0.30.0": + version: 0.30.0 + resolution: "magic-string@npm:0.30.0" + dependencies: + "@jridgewell/sourcemap-codec": ^1.4.13 + checksum: 7bdf22e27334d8a393858a16f5f840af63a7c05848c000fd714da5aa5eefa09a1bc01d8469362f25cc5c4a14ec01b46557b7fff8751365522acddf21e57c488d + languageName: node + linkType: hard + "make-dir@npm:^3.0.0": version: 3.1.0 resolution: "make-dir@npm:3.1.0" @@ -9112,6 +9149,22 @@ __metadata: languageName: node linkType: hard +"rollup-plugin-dts@npm:^5.3.0": + version: 5.3.0 + resolution: "rollup-plugin-dts@npm:5.3.0" + dependencies: + "@babel/code-frame": ^7.18.6 + magic-string: ^0.30.0 + peerDependencies: + rollup: ^3.0.0 + typescript: ^4.1 || ^5.0 + dependenciesMeta: + "@babel/code-frame": + optional: true + checksum: ba3a6065598586c52af60211877a234b8c829b8a7ecd6e6b426e52ec4dc2c8c8ac6e1fb9f47db6afd91858be0dcb09fd3d312c865e972fb72ae8c9ee00eb1d36 + languageName: node + linkType: hard + "rollup@npm:^3.21.1": version: 3.21.1 resolution: "rollup@npm:3.21.1"