codeceptjs 4.0.0-beta.17 → 4.0.0-beta.19

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/lib/codecept.js CHANGED
@@ -3,8 +3,9 @@ import { globSync } from 'glob'
3
3
  import shuffle from 'lodash.shuffle'
4
4
  import fsPath from 'path'
5
5
  import { resolve } from 'path'
6
- import { fileURLToPath } from 'url'
6
+ import { fileURLToPath, pathToFileURL } from 'url'
7
7
  import { dirname } from 'path'
8
+ import { createRequire } from 'module'
8
9
 
9
10
  const __filename = fileURLToPath(import.meta.url)
10
11
  const __dirname = dirname(__filename)
@@ -18,6 +19,7 @@ import ActorFactory from './actor.js'
18
19
  import output from './output.js'
19
20
  import { emptyFolder } from './utils.js'
20
21
  import { initCodeceptGlobals } from './globals.js'
22
+ import { validateTypeScriptSetup } from './utils/loaderCheck.js'
21
23
  import recorder from './recorder.js'
22
24
 
23
25
  import storeListener from './listener/store.js'
@@ -66,6 +68,21 @@ class Codecept {
66
68
  modulePath = `${modulePath}.js`
67
69
  }
68
70
  }
71
+ } else {
72
+ // For npm packages, resolve from the user's directory
73
+ // This ensures packages like tsx are found in user's node_modules
74
+ const userDir = global.codecept_dir || process.cwd()
75
+
76
+ try {
77
+ // Use createRequire to resolve from user's directory
78
+ const userRequire = createRequire(pathToFileURL(resolve(userDir, 'package.json')).href)
79
+ const resolvedPath = userRequire.resolve(requiredModule)
80
+ modulePath = pathToFileURL(resolvedPath).href
81
+ } catch (resolveError) {
82
+ // If resolution fails, try direct import (will check from CodeceptJS node_modules)
83
+ // This is the fallback for globally installed packages
84
+ modulePath = requiredModule
85
+ }
69
86
  }
70
87
  // Use dynamic import for ESM
71
88
  await import(modulePath)
@@ -246,6 +263,13 @@ class Codecept {
246
263
  async run(test) {
247
264
  await container.started()
248
265
 
266
+ // Check TypeScript loader configuration before running tests
267
+ const tsValidation = validateTypeScriptSetup(this.testFiles, this.requiringModules || [])
268
+ if (tsValidation.hasError) {
269
+ output.error(tsValidation.message)
270
+ process.exit(1)
271
+ }
272
+
249
273
  // Ensure translations are loaded for Gherkin features
250
274
  try {
251
275
  const { loadTranslations } = await import('./mocha/gherkin.js')
@@ -161,7 +161,7 @@ export default async function (initPath) {
161
161
  isTypeScript = true
162
162
  extension = isTypeScript === true ? 'ts' : 'js'
163
163
  packages.push('typescript')
164
- packages.push('ts-node')
164
+ packages.push('tsx') // Add tsx for TypeScript support
165
165
  packages.push('@types/node')
166
166
  }
167
167
 
@@ -172,6 +172,7 @@ export default async function (initPath) {
172
172
  config.tests = result.tests
173
173
  if (isTypeScript) {
174
174
  config.tests = `${config.tests.replace(/\.js$/, `.${extension}`)}`
175
+ config.require = ['tsx/cjs'] // Add tsx/cjs loader for TypeScript tests
175
176
  }
176
177
 
177
178
  // create a directory tests if it is included in tests path
@@ -0,0 +1,124 @@
1
+ /**
2
+ * Utilities for checking TypeScript loader availability
3
+ */
4
+
5
+ /**
6
+ * Check if a TypeScript loader is available for test files
7
+ * Note: This checks if loaders are in the require array, not if packages are installed
8
+ * Package installation is checked when actually requiring modules
9
+ * @param {string[]} requiredModules - Array of required modules from config
10
+ * @returns {boolean}
11
+ */
12
+ export function checkTypeScriptLoader(requiredModules = []) {
13
+ // Check if a loader is configured in the require array
14
+ return (
15
+ requiredModules.includes('tsx/esm') ||
16
+ requiredModules.includes('tsx/cjs') ||
17
+ requiredModules.includes('tsx') ||
18
+ requiredModules.includes('ts-node/esm') ||
19
+ requiredModules.includes('ts-node/register') ||
20
+ requiredModules.includes('ts-node')
21
+ )
22
+ }
23
+
24
+ /**
25
+ * Generate helpful error message if .ts tests found but no loader configured
26
+ * @param {string[]} testFiles - Array of test file paths
27
+ * @returns {string|null} Error message or null if no TypeScript files
28
+ */
29
+ export function getTypeScriptLoaderError(testFiles) {
30
+ const tsFiles = testFiles.filter(f => f.endsWith('.ts'))
31
+
32
+ if (tsFiles.length === 0) return null
33
+
34
+ return `
35
+ ╔═════════════════════════════════════════════════════════════════════════════╗
36
+ ║ ║
37
+ ║ ⚠️ TypeScript Test Files Detected but No Loader Configured ║
38
+ ║ ║
39
+ ╚═════════════════════════════════════════════════════════════════════════════╝
40
+
41
+ Found ${tsFiles.length} TypeScript test file(s) but no TypeScript loader is configured.
42
+
43
+ CodeceptJS 4.x uses ES Modules (ESM) and requires a loader to run TypeScript tests.
44
+
45
+ ┌─────────────────────────────────────────────────────────────────────────────┐
46
+ │ Option 1: tsx (Recommended - Fast, Zero Config) │
47
+ └─────────────────────────────────────────────────────────────────────────────┘
48
+
49
+ Installation:
50
+ npm install --save-dev tsx
51
+
52
+ Configuration:
53
+ Add to your codecept.conf.ts or codecept.conf.js:
54
+
55
+ export const config = {
56
+ tests: './**/*_test.ts',
57
+ require: ['tsx/cjs'], // ← Add this line
58
+ helpers: { /* ... */ }
59
+ }
60
+
61
+ Why tsx?
62
+ ⚡ Fast: Built on esbuild
63
+ 🎯 Zero config: No tsconfig.json required
64
+ ✅ Works with Mocha: Uses CommonJS hooks
65
+ ✅ Complete: Handles all TypeScript features
66
+
67
+ ┌─────────────────────────────────────────────────────────────────────────────┐
68
+ │ Option 2: ts-node/esm (Alternative - Established, Requires Config) │
69
+ └─────────────────────────────────────────────────────────────────────────────┘
70
+
71
+ Installation:
72
+ npm install --save-dev ts-node
73
+
74
+ Configuration:
75
+ 1. Add to your codecept.conf.ts:
76
+ require: ['ts-node/esm']
77
+
78
+ 2. Create tsconfig.json:
79
+ {
80
+ "compilerOptions": {
81
+ "module": "ESNext",
82
+ "target": "ES2022",
83
+ "moduleResolution": "node",
84
+ "esModuleInterop": true
85
+ },
86
+ "ts-node": {
87
+ "esm": true,
88
+ "experimentalSpecifierResolution": "node"
89
+ }
90
+ }
91
+
92
+ 📚 Documentation: https://codecept.io/typescript
93
+
94
+ Note: TypeScript config files (codecept.conf.ts) and helpers are automatically
95
+ transpiled. Only test files require a loader to be configured.
96
+ `
97
+ }
98
+
99
+ /**
100
+ * Check if user is trying to run TypeScript tests without proper loader
101
+ * @param {string[]} testFiles - Array of test file paths
102
+ * @param {string[]} requiredModules - Array of required modules from config
103
+ * @returns {{hasError: boolean, message: string|null}}
104
+ */
105
+ export function validateTypeScriptSetup(testFiles, requiredModules = []) {
106
+ const tsFiles = testFiles.filter(f => f.endsWith('.ts'))
107
+
108
+ if (tsFiles.length === 0) {
109
+ // No TypeScript test files, all good
110
+ return { hasError: false, message: null }
111
+ }
112
+
113
+ // Check if a loader is configured in the require array
114
+ const hasLoader = checkTypeScriptLoader(requiredModules)
115
+
116
+ if (hasLoader) {
117
+ // Loader configured, all good (package will be checked when requireModules runs)
118
+ return { hasError: false, message: null }
119
+ }
120
+
121
+ // No loader configured and TypeScript tests exist
122
+ const message = getTypeScriptLoaderError(testFiles)
123
+ return { hasError: true, message }
124
+ }
@@ -25,6 +25,9 @@ export async function transpileTypeScript(mainFilePath, typescript) {
25
25
  target: 99, // ScriptTarget.ESNext
26
26
  esModuleInterop: true,
27
27
  allowSyntheticDefaultImports: true,
28
+ lib: ['lib.esnext.d.ts'], // Enable latest features including top-level await
29
+ suppressOutputPathCheck: true,
30
+ skipLibCheck: true,
28
31
  })
29
32
 
30
33
  // Check if the code uses CommonJS globals
@@ -57,6 +60,7 @@ const require = (id) => {
57
60
  const hasKnownExt = ext && __knownExts.includes(ext.toLowerCase());
58
61
  if (!hasKnownExt) {
59
62
  // Try common extensions in order: .js, .cjs, .json, .node
63
+ // Note: .ts files cannot be required - they need transpilation first
60
64
  const extensions = ['.js', '.cjs', '.json', '.node'];
61
65
  for (const testExt of extensions) {
62
66
  try {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "codeceptjs",
3
- "version": "4.0.0-beta.17",
3
+ "version": "4.0.0-beta.19",
4
4
  "type": "module",
5
5
  "description": "Supercharged End 2 End Testing Framework for NodeJS",
6
6
  "keywords": [
@@ -183,6 +183,7 @@
183
183
  "ts-node": "10.9.2",
184
184
  "tsd": "^0.33.0",
185
185
  "tsd-jsdoc": "2.5.0",
186
+ "tsx": "^4.19.2",
186
187
  "typedoc": "0.28.13",
187
188
  "typedoc-plugin-markdown": "4.9.0",
188
189
  "typescript": "5.8.3",
@@ -191,6 +192,14 @@
191
192
  "xml2js": "0.6.2",
192
193
  "xpath": "0.0.34"
193
194
  },
195
+ "peerDependencies": {
196
+ "tsx": "^4.0.0"
197
+ },
198
+ "peerDependenciesMeta": {
199
+ "tsx": {
200
+ "optional": true
201
+ }
202
+ },
194
203
  "engines": {
195
204
  "node": ">=16.0",
196
205
  "npm": ">=5.6.0"