Skip to content

Conversation

Copy link
Contributor

Copilot AI commented Nov 20, 2025

Motivation/Description of the PR

Documentation incorrectly recommended ts-node/esm with experimentalSpecifierResolution: "node" for TypeScript ESM projects. This causes "Cannot find module" errors when importing page objects:

// User's test following docs
import loginPage from "./pages/Login"  // ❌ Error: Cannot find module

Feature("Login")
Scenario("test", () => {
  loginPage.login("user")
})

Root cause: experimentalSpecifierResolution is deprecated and doesn't affect Node.js runtime. ts-node/esm doesn't resolve extensionless imports and is incompatible with "type": "module".

Fix: Recommend tsx/cjs which handles ESM correctly:

// codecept.conf.ts
export const config = {
  require: ['tsx/cjs'],  // ✅ Works with extensionless imports
  tests: './**/*_test.ts',
  helpers: { /* ... */ }
}
// package.json - "type": "module" works with tsx
{
  "type": "module",
  "devDependencies": { "tsx": "^4.20.6" }
}

Changes

Documentation:

  • lib/utils/loaderCheck.js: Removed experimentalSpecifierResolution, added ts-node/esm limitations warning
  • docs/typescript.md: Reordered to emphasize tsx, marked ts-node/esm as not recommended

Test case (test/data/typescript-tsx-esm/):

  • Demonstrates tsx with "type": "module" and extensionless imports
  • Page object import without extension works correctly
  • All scenarios pass

Code cleanup:

  • lib/mocha/factory.js: Removed broken ESM fallback logic

Applicable helpers:

  • Playwright
  • Puppeteer
  • WebDriver
  • REST
  • FileHelper
  • Appium

Applicable plugins:

  • allure
  • autoDelay
  • autoLogin
  • customLocator
  • pauseOnFail
  • coverage
  • retryFailedStep
  • screenshotOnFail
  • selenoid
  • stepByStepReport
  • stepTimeout
  • wdio
  • subtitles

Type of change

  • 🔥 Breaking changes
  • 🚀 New functionality
  • 🐛 Bug fix
  • 🧹 Chore
  • 📋 Documentation changes/updates
  • ♨️ Hot fix
  • 🔨 Markdown files fix - not related to source code
  • 💅 Polish code

Checklist:

  • Tests have been added
  • Documentation has been added (Run npm run docs)
  • Lint checking (Run npm run lint)
  • Local tests are passed (Run npm test)

Warning

Firewall rules blocked me from connecting to one or more addresses (expand for details)

I tried to connect to the following addresses, but was blocked by firewall rules:

  • googlechromelabs.github.io
    • Triggering command: node install.mjs (dns block)
  • https://storage.googleapis.com/chrome-for-testing-public/138.0.7204.168/linux64/chrome-headless-shell-linux64.zip
    • Triggering command: node install.mjs (http block)
  • jsonplaceholder.typicode.com
    • Triggering command: node /home/REDACTED/work/CodeceptJS/CodeceptJS/node_modules/.bin/mocha test/unit --recursive --timeout 10000 --reporter @testomatio/reporter/mocha (dns block)

If you need me to access, download, or install something from one of these locations, you can either:

Original prompt

This section details on the original issue you should resolve

<issue_title>CodeceptJS 4.x: The console's docs suggests "module": "ESNext", but can't import page object with such setting (Cannot find module)</issue_title>
<issue_description>config codecept.conf.ts

export const config: CodeceptJS.MainConfig = {
  tests: "./*_test.ts",
  output: "./output",
  helpers: {
    Playwright: {
      browser: "chromium",
      url: "http://localhost",
      show: true,
    },
  },
  name: "esm",
  require: ["ts-node/esm"], // ! You can also test it with `require: ["tsx/esm"]`, but you get the same issue !
};

tsconfig.json (as suggested by CodeceptJS if you run a test without any "require" in your config) in 4.x

Image
{
  "ts-node": {
    "files": true,
    "esm": true,
    "experimentalSpecifierResolution": "node"
  },
  "compilerOptions": {
    "target": "es2022",
    "lib": ["es2022", "DOM"],
    "esModuleInterop": true,
    "module": "esnext",
    "moduleResolution": "node",
    "strictNullChecks": false,
    "types": ["codeceptjs", "node"],
    "declaration": true,
    "skipLibCheck": true,
  },
  "exclude": ["node_modules"],
}

PO pages/Login.ts:

const { I } = inject();

export default {
  login(username: string) {
    I.say("Logging in with user: " + username);
  },
};

test My_test.ts

import loginPage from "./pages/Login";

Feature("My");

Scenario("test something", () => {
  loginPage.login("myusername");
});

Test fails ❌

mirao@rog:~/workspace/esm$ npx codeceptjs run --verbose
***************************************
nodeInfo:  20.19.5
osInfo:  Linux 6.14 Ubuntu 24.04.3 LTS 24.04.3 LTS (Noble Numbat)
cpuInfo:  (16) x64 AMD Ryzen 7 9700X 8-Core Processor
chromeInfo:  142.0.7444.175
edgeInfo:  "N/A"
firefoxInfo:  145.0
safariInfo:  N/A
playwrightBrowsers:  "chromium: 141.0.7390.37, firefox: 142.0.1, webkit: 26.0"
If you need more detailed info, just run this: npx codeceptjs info
***************************************
Error: Cannot find module '/home/mirao/workspace/esm/pages/Login' imported from /home/mirao/workspace/esm/My_test.ts
Did you mean to import "./pages/Login.ts"?
    at finalizeResolution (node:internal/modules/esm/resolve:283:11)
    at moduleResolve (node:internal/modules/esm/resolve:952:10)
    at defaultResolve (node:internal/modules/esm/resolve:1188:11)
    at ModuleLoader.defaultResolve (node:internal/modules/esm/loader:708:12)
    at ModuleLoader.#cachedDefaultResolve (node:internal/modules/esm/loader:657:25)
    at ModuleLoader.getModuleJobForRequire (node:internal/modules/esm/loader:413:53)
    at new ModuleJobSync (node:internal/modules/esm/module_job:375:34)
    at ModuleLoader.importSyncForRequire (node:internal/modules/esm/loader:386:11)
    at loadESMFromCJS (node:internal/modules/cjs/loader:1363:24)
    at Module._compile (node:internal/modules/cjs/loader:1503:5)

Cannot find module '/home/mirao/workspace/esm/pages/Login' imported from /home/mirao/workspace/esm/My_test.ts
Did you mean to import "./pages/Login.ts"?

Error: 
    at finalizeResolution (node:internal/modules/esm/resolve:283:11)
    at moduleResolve (node:internal/modules/esm/resolve:952:10)
    at defaultResolve (node:internal/modules/esm/resolve:1188:11)
    at ModuleLoader.defaultResolve (node:internal/modules/esm/loader:708:12)
    at ModuleLoader.#cachedDefaultResolve (node:internal/modules/esm/loader:657:25)
    at ModuleLoader.getModuleJobForRequire (node:internal/modules/esm/loader:413:53)
    at new ModuleJobSync (node:internal/modules/esm/module_job:375:34)
    at ModuleLoader.importSyncForRequire (node:internal/modules/esm/loader:386:11)
    at loadESMFromCJS (node:internal/modules/cjs/loader:1363:24)
    at Module._compile (node:internal/modules/cjs/loader:1503:5)

If you use the default option "module": "commonjs", it works well ✔️

CodeceptJS v4.0.1-beta.15 #StandWithUkraine
Using test root "/home/mirao/workspace/esm"
Helpers: Playwright
Plugins: screenshotOnFail

My --
/home/mirao/workspace/esm/My_test.ts
    [1]  Starting recording promises
    Timeouts: 
 › [Session] Starting singleton browser session
  test something
 › [New Session] {"ignoreHTTPSErrors":false,"acceptDownloads":true}
  Scenario()
    I say "Logging in with user: myusername"
   Logging in with user: myusername
  ✔ OK in 4ms


  OK  | 1 passed   // 5s

So a question is: How to setup PO for the suggested ESM module setting?

Used SW:

  • CodeceptJS 4.0.1-beta.15</issue_description>

<agent_instructions>- investigate and fix issue

  • add tests</agent_instructions>

Comments on the Issue (you are @copilot in this section)


💬 We'd love your input! Share your thoughts on Copilot coding agent in our 2 minute survey.

Copilot AI and others added 3 commits November 20, 2025 14:55
Co-authored-by: kobenguyent <7845001+kobenguyent@users.noreply.github.com>
…node/esm

Co-authored-by: kobenguyent <7845001+kobenguyent@users.noreply.github.com>
Co-authored-by: kobenguyent <7845001+kobenguyent@users.noreply.github.com>
Copilot AI changed the title [WIP] Fix module import issue with ESNext setting in CodeceptJS Fix TypeScript ESM module resolution docs - recommend tsx over ts-node/esm Nov 20, 2025
Copilot AI requested a review from kobenguyent November 20, 2025 15:04
Copilot finished work on behalf of kobenguyent November 20, 2025 15:04
@kobenguyent
Copy link
Collaborator

tested locally, worked fine.

CodeceptJS v4.0.1-beta.16 #StandWithUkraine
Using test root "/Users/t/Desktop/projects/codeceptjs-4-tryout"
Helpers: Playwright, MyHelper
Plugins: screenshotOnFail

My --
/Users/t/Desktop/projects/codeceptjs-4-tryout/test_test.ts
    [1]  Starting recording promises
    Timeouts: 
 › [Session] Starting singleton browser session
  test something
 › [New Session] {"ignoreHTTPSErrors":false,"acceptDownloads":true}
  Scenario()
    I say "Logging in with user: myusername"
   Logging in with user: myusername
  ✔ OK in 5ms


  OK  | 1 passed   // 2s

@kobenguyent kobenguyent marked this pull request as ready for review November 20, 2025 15:24
@kobenguyent kobenguyent merged commit 23fad16 into 4.x Nov 20, 2025
15 of 20 checks passed
@kobenguyent kobenguyent deleted the copilot/fix-module-import-issue branch November 20, 2025 15:25
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants