Skip to content

Conversation

Copy link
Contributor

Copilot AI commented Nov 17, 2025

Motivation/Description of the PR

Node.js cannot import .ts files directly. When users specified TypeScript helpers via require in config, CodeceptJS failed with ERR_UNKNOWN_FILE_EXTENSION. Additionally, when TypeScript helpers imported other TypeScript files without extensions, they would fail with ERR_MODULE_NOT_FOUND. A further issue occurred when TypeScript files had dots in their names (e.g., abstract.helper.ts), where the transpiler failed to detect them as TypeScript files to transpile.

Example config that now works:

export const config: CodeceptJS.MainConfig = {
  helpers: {
    MaterialComponent: {
      require: "./helpers/material-component.helper.ts",
    },
  }
};

Example helper with extensionless imports that now works:

import { AbstractHelper } from "./abstract.helper";  // No .js or .ts needed!

class MaterialComponent extends AbstractHelper {
  // Helper implementation
}

export default MaterialComponent;

Changes:

  1. Modified requireHelperFromModule in lib/container.js to detect .ts extensions and transpile using existing transpileTypeScript utility before import. Cleanup of temporary files occurs on both success and error paths.

  2. Modified transpileTypeScript in lib/utils/typescript.js to automatically add .js extension to relative imports that don't have standard module extensions (.js, .mjs, .cjs, .json, .node). This ensures ESM compatibility and eliminates the need for users to manually add .js extensions to their TypeScript imports.

  3. Fixed TypeScript file detection in dependency discovery to properly identify files with dots in their names (e.g., abstract.helper.ts, material.component.helper.ts). The transpiler now checks for standard module extensions instead of treating any dot as a file extension, ensuring all TypeScript dependencies are correctly detected and transpiled.

These changes match the pattern already implemented in loadSupportObject for TypeScript support files and provide a seamless experience for TypeScript users with any naming convention.

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)
Original prompt

investigate an issue and fix it, which codeceptjs setup

  • mostly as typescripts
  • package.json sets "type": "module"
  • test files are typescripts
  • helpers are typescripts

Codeceptjs config

import {E2EEnvironment as Environment } from "../../environments"

export const config: CodeceptJS.MainConfig = {
tests: process.env.PROD === "true" ? './tests//*.spec.prod.ts' : './tests//*.spec.ts',
retry: 2,
output: '../../output',
require: ['tsx/cjs'],
emptyOutputFolder: true,
helpers: {
Playwright: {
url: Environment.url,
browser: 'chromium',
// @ts-ignore
show: process.env.HEADED || false,
timeout: 30000,
waitForTimeout: 30000,
waitForNavigation: 'load'
},
MaterialComponent: {
require: "./helpers/material-component.helper.ts",
},
Expect: {
require: '@codeceptjs/expect-helper'
},
},
include: {
I: '../../common/steps/steps_file.ts'
},
name: 'web-tests',
plugins: {
customLocator: {
enabled: true,
prefix: "=",
attribute: "data-e2e",
},
htmlReporter: {
enabled: true,
keepHistory: true,
}
}
};

when executing tests, got issue

node:internal/modules/esm/get_format:189
throw new ERR_UNKNOWN_FILE_EXTENSION(ext, filepath);
^

TypeError [ERR_UNKNOWN_FILE_EXTENSION]: Unknown file extension ".ts" for /Users/t/Desktop/projects/qa/tests/web/helpers/material-component.helper.ts
at Object.getFileProtocolModuleFormat [as file:] (node:internal/modules/esm/get_format:189:9)
at defaultGetFormat (node:internal/modules/esm/get_format:232:36)
at defaultLoad (node:internal/modules/esm/load:145:22)
at async ModuleLoader.loadAndTranslate (node:internal/modules/esm/loader:477:45)
at async ModuleJob._link (node:internal/modules/esm/module_job:110:19) {
code: 'ERR_UNKNOWN_FILE_EXTENSION'
}

Node.js v20.19.4


💡 You can make Copilot smarter by setting up custom instructions, customizing its development environment and configuring Model Context Protocol (MCP) servers. Learn more Copilot coding agent tips in the docs.

Copilot AI and others added 2 commits November 17, 2025 13:16
Copilot AI changed the title [WIP] Investigate and fix CodeceptJS setup issue with TypeScript Fix TypeScript helper loading via automatic transpilation Nov 17, 2025
Copilot AI requested a review from kobenguyent November 17, 2025 13:22
Copilot finished work on behalf of kobenguyent November 17, 2025 13:22
@kobenguyent kobenguyent marked this pull request as ready for review November 17, 2025 13:37
@kobenguyent
Copy link
Collaborator

@copilot when custom helper got another import, I got this error

node:internal/modules/esm/resolve:283
    throw new ERR_MODULE_NOT_FOUND(
          ^

Error [ERR_MODULE_NOT_FOUND]: Cannot find module '/Users/t/Desktop/projects/qa/tests/web/helpers/abstract.helper' imported from /Users/t/Desktop/projects/qa/tests/web/helpers/material-component.helper.temp.mjs

when I add .js to ./abstract.helper, tests are running successfully.


import { AbstractHelper } from "./abstract.helper.js";

// Functions to interact with material components.
class MaterialComponentHelper extends AbstractHelper {
  • investigate and fix issue
  • find a solution not to add any file extension, it should be added automatically. Cause right now, it's confusing, when we add .js just to make codeceptjs runs but the file actually is .ts file.

Automatically add .js extension to relative imports that don't have a standard module extension. This fixes the ERR_MODULE_NOT_FOUND error when TypeScript files import other files without specifying an extension (e.g., import from "./abstract.helper" instead of "./abstract.helper.js").

The transpiler now:
- Detects imports without standard extensions (.js, .mjs, .cjs, .json, .node)
- Adds .js extension automatically for ESM compatibility
- Handles both transpiled TypeScript files (rewrites to .temp.mjs) and regular JS files

Co-authored-by: kobenguyent <7845001+kobenguyent@users.noreply.github.com>
Copy link
Contributor Author

Copilot AI commented Nov 17, 2025

@copilot when custom helper got another import, I got this error

...

Fixed in commit 37878c1. The transpiler now automatically adds .js extension to imports without standard module extensions. You can write:

import { AbstractHelper } from "./abstract.helper";

Without needing to add .js manually. The transpiler detects:

  • TypeScript files being imported → rewrites to .temp.mjs
  • Other imports without extensions → adds .js automatically

All 31 container tests pass including new test for this scenario.

Copilot AI changed the title Fix TypeScript helper loading via automatic transpilation Fix TypeScript helper loading and import resolution Nov 17, 2025
Copilot finished work on behalf of kobenguyent November 17, 2025 14:41
@kobenguyent
Copy link
Collaborator

@copilot investigate, handle more scenarios and fix issues, here is one of the issues

  • when custom helper has import { AbstractHelper } from "./abstract.helper";, the custom helper is transpiled as .temp.mjs, also abstract.helper is transpiled as abstract.helper.temp.mjs but the transpiled code refers 'abstract.helper.js'

it leads to issue:

node:internal/modules/esm/resolve:283
    throw new ERR_MODULE_NOT_FOUND(
          ^

Error [ERR_MODULE_NOT_FOUND]: Cannot find module '/Users/t/Desktop/projects/qa/tests/web/helpers/abstract.helper.js' imported from /Users/t/Desktop/projects/qa/tests/web/helpers/material-component.helper.temp.mjs

Fixed issue where TypeScript files with dots in their names (e.g., abstract.helper.ts) were not being detected and transpiled when imported without extension. The problem was that path.extname() would consider the last dot as a file extension.

Now the transpiler:
- Checks if an import has a standard module extension (.js, .mjs, .cjs, .json, .node)
- If not, tries adding .ts extension to find and transpile TypeScript files
- This correctly handles files like abstract.helper.ts, material.component.helper.ts, etc.

Added test case to verify this scenario works correctly.

Co-authored-by: kobenguyent <7845001+kobenguyent@users.noreply.github.com>
Copy link
Contributor Author

Copilot AI commented Nov 17, 2025

@copilot investigate, handle more scenarios and fix issues, here is one of the issues

  • when custom helper has import { AbstractHelper } from "./abstract.helper";, the custom helper is transpiled a...

Fixed in commit 6adc892. The issue was that path.extname() treats dots in filenames (like abstract.helper) as file extensions, so the transpiler wasn't detecting these as TypeScript files to transpile.

The fix now checks for standard module extensions (.js, .mjs, .cjs, .json, .node) and if the import doesn't have one, it tries adding .ts to find and transpile the file.

Now both files are correctly transpiled:

  • abstract.helper.tsabstract.helper.temp.mjs
  • material.component.helper.tsmaterial.component.helper.temp.mjs

And the import is rewritten to: import { AbstractHelper } from './abstract.helper.temp.mjs';

All 32 container tests pass including new test for this scenario.

Copilot finished work on behalf of kobenguyent November 17, 2025 15:12
@kobenguyent kobenguyent merged commit 1a6aef2 into 4.x Nov 17, 2025
10 of 21 checks passed
@kobenguyent kobenguyent deleted the copilot/fix-codeceptjs-setup-issue branch November 17, 2025 16:18
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