Skip to content

Commit 550009e

Browse files
authored
fix several 4.x issues (#5368)
* fix several 4.x issues * fix several 4.x issues * fix several 4.x issues * fix several 4.x issues * fix missing worker index in cli mode * fix missing worker index in cli mode * fix missing worker index in cli mode
1 parent 6c916ce commit 550009e

File tree

8 files changed

+140
-46
lines changed

8 files changed

+140
-46
lines changed

lib/command/workers/runTests.js

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -104,7 +104,9 @@ initPromise = (async function () {
104104
// important deep merge so dynamic things e.g. functions on config are not overridden
105105
config = deepMerge(baseConfig, overrideConfigs)
106106

107-
codecept = new Codecept(config, options)
107+
// Pass workerIndex as child option for output.process() to display worker prefix
108+
const optsWithChild = { ...options, child: workerIndex }
109+
codecept = new Codecept(config, optsWithChild)
108110
await codecept.init(testRoot)
109111
codecept.loadTests()
110112
mocha = container.mocha()

lib/config.js

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ import fs from 'fs'
22
import path from 'path'
33
import { createRequire } from 'module'
44
import { fileExists, isFile, deepMerge, deepClone } from './utils.js'
5-
import { transpileTypeScript, cleanupTempFiles } from './utils/typescript.js'
5+
import { transpileTypeScript, cleanupTempFiles, fixErrorStack } from './utils/typescript.js'
66

77
const defaultConfig = {
88
output: './_output',
@@ -159,12 +159,13 @@ async function loadConfigFile(configFile) {
159159
try {
160160
// Use the TypeScript transpilation utility
161161
const typescript = require('typescript')
162-
const { tempFile, allTempFiles } = await transpileTypeScript(configFile, typescript)
162+
const { tempFile, allTempFiles, fileMapping } = await transpileTypeScript(configFile, typescript)
163163

164164
try {
165165
configModule = await import(tempFile)
166166
cleanupTempFiles(allTempFiles)
167167
} catch (err) {
168+
fixErrorStack(err, fileMapping)
168169
cleanupTempFiles(allTempFiles)
169170
throw err
170171
}

lib/container.js

Lines changed: 45 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ import debugModule from 'debug'
55
const debug = debugModule('codeceptjs:container')
66
import { MetaStep } from './step.js'
77
import { methodsOfObject, fileExists, isFunction, isAsyncFunction, installedLocally, deepMerge } from './utils.js'
8-
import { transpileTypeScript, cleanupTempFiles } from './utils/typescript.js'
8+
import { transpileTypeScript, cleanupTempFiles, fixErrorStack } from './utils/typescript.js'
99
import Translation from './translation.js'
1010
import MochaFactory from './mocha/factory.js'
1111
import recorder from './recorder.js'
@@ -34,6 +34,7 @@ let container = {
3434
/** @type {Result | null} */
3535
result: null,
3636
sharedKeys: new Set(), // Track keys shared via share() function
37+
tsFileMapping: null, // TypeScript file mapping for error stack fixing
3738
}
3839

3940
/**
@@ -88,7 +89,7 @@ class Container {
8889
container.support.I = mod
8990
}
9091
} catch (e) {
91-
throw new Error(`Could not include object I: ${e.message}`)
92+
throw e
9293
}
9394
} else {
9495
// Create default actor - this sets up the callback in asyncHelperPromise
@@ -176,6 +177,15 @@ class Container {
176177
return container.translation
177178
}
178179

180+
/**
181+
* Get TypeScript file mapping for error stack fixing
182+
*
183+
* @api
184+
*/
185+
static tsFileMapping() {
186+
return container.tsFileMapping
187+
}
188+
179189
/**
180190
* Get Mocha instance
181191
*
@@ -401,18 +411,27 @@ async function requireHelperFromModule(helperName, config, HelperClass) {
401411
// Handle TypeScript files
402412
let importPath = moduleName
403413
let tempJsFile = null
414+
let fileMapping = null
404415
const ext = path.extname(moduleName)
405416

406417
if (ext === '.ts') {
407418
try {
408419
// Use the TypeScript transpilation utility
409420
const typescript = await import('typescript')
410-
const { tempFile, allTempFiles } = await transpileTypeScript(importPath, typescript)
421+
const { tempFile, allTempFiles, fileMapping: mapping } = await transpileTypeScript(importPath, typescript)
411422

412423
debug(`Transpiled TypeScript helper: ${importPath} -> ${tempFile}`)
413424

414425
importPath = tempFile
415426
tempJsFile = allTempFiles
427+
fileMapping = mapping
428+
// Store file mapping in container for runtime error fixing (merge with existing)
429+
if (!container.tsFileMapping) {
430+
container.tsFileMapping = new Map()
431+
}
432+
for (const [key, value] of mapping.entries()) {
433+
container.tsFileMapping.set(key, value)
434+
}
416435
} catch (tsError) {
417436
throw new Error(`Failed to load TypeScript helper ${importPath}: ${tsError.message}. Make sure 'typescript' package is installed.`)
418437
}
@@ -433,6 +452,11 @@ async function requireHelperFromModule(helperName, config, HelperClass) {
433452
cleanupTempFiles(filesToClean)
434453
}
435454
} catch (err) {
455+
// Fix error stack to point to original .ts files
456+
if (fileMapping) {
457+
fixErrorStack(err, fileMapping)
458+
}
459+
436460
// Clean up temp files before rethrowing
437461
if (tempJsFile) {
438462
const filesToClean = Array.isArray(tempJsFile) ? tempJsFile : [tempJsFile]
@@ -731,6 +755,7 @@ async function loadSupportObject(modulePath, supportObjectName) {
731755
// Use dynamic import for both ESM and CJS modules
732756
let importPath = modulePath
733757
let tempJsFile = null
758+
let fileMapping = null
734759

735760
if (typeof importPath === 'string') {
736761
const ext = path.extname(importPath)
@@ -740,14 +765,22 @@ async function loadSupportObject(modulePath, supportObjectName) {
740765
try {
741766
// Use the TypeScript transpilation utility
742767
const typescript = await import('typescript')
743-
const { tempFile, allTempFiles } = await transpileTypeScript(importPath, typescript)
768+
const { tempFile, allTempFiles, fileMapping: mapping } = await transpileTypeScript(importPath, typescript)
744769

745770
debug(`Transpiled TypeScript file: ${importPath} -> ${tempFile}`)
746771

747772
// Attach cleanup handler
748773
importPath = tempFile
749774
// Store temp files list in a way that cleanup can access them
750775
tempJsFile = allTempFiles
776+
fileMapping = mapping
777+
// Store file mapping in container for runtime error fixing (merge with existing)
778+
if (!container.tsFileMapping) {
779+
container.tsFileMapping = new Map()
780+
}
781+
for (const [key, value] of mapping.entries()) {
782+
container.tsFileMapping.set(key, value)
783+
}
751784
} catch (tsError) {
752785
throw new Error(`Failed to load TypeScript file ${importPath}: ${tsError.message}. Make sure 'typescript' package is installed.`)
753786
}
@@ -761,6 +794,11 @@ async function loadSupportObject(modulePath, supportObjectName) {
761794
try {
762795
obj = await import(importPath)
763796
} catch (importError) {
797+
// Fix error stack to point to original .ts files
798+
if (fileMapping) {
799+
fixErrorStack(importError, fileMapping)
800+
}
801+
764802
// Clean up temp files if created before rethrowing
765803
if (tempJsFile) {
766804
const filesToClean = Array.isArray(tempJsFile) ? tempJsFile : [tempJsFile]
@@ -809,7 +847,9 @@ async function loadSupportObject(modulePath, supportObjectName) {
809847

810848
throw new Error(`Support object "${supportObjectName}" should be an object, class, or function, but got ${typeof actualObj}`)
811849
} catch (err) {
812-
throw new Error(`Could not include object ${supportObjectName} from module '${modulePath}'\n${err.message}\n${err.stack}`)
850+
const newErr = new Error(`Could not include object ${supportObjectName} from module '${modulePath}': ${err.message}`)
851+
newErr.stack = err.stack
852+
throw newErr
813853
}
814854
}
815855

lib/step/base.js

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -147,9 +147,22 @@ class Step {
147147
line() {
148148
const lines = this.stack.split('\n')
149149
if (lines[STACK_LINE]) {
150-
return lines[STACK_LINE].trim()
150+
let line = lines[STACK_LINE].trim()
151151
.replace(global.codecept_dir || '', '.')
152152
.trim()
153+
154+
// Map .temp.mjs back to original .ts files using container's tsFileMapping
155+
const fileMapping = global.container?.tsFileMapping?.()
156+
if (line.includes('.temp.mjs') && fileMapping) {
157+
for (const [tsFile, mjsFile] of fileMapping.entries()) {
158+
if (line.includes(mjsFile)) {
159+
line = line.replace(mjsFile, tsFile)
160+
break
161+
}
162+
}
163+
}
164+
165+
return line
153166
}
154167
return ''
155168
}

lib/step/record.js

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import output from '../output.js'
55
import store from '../store.js'
66
import { TIMEOUT_ORDER } from '../timeout.js'
77
import retryStep from './retry.js'
8+
import { fixErrorStack } from '../utils/typescript.js'
89
function recordStep(step, args) {
910
step.status = 'queued'
1011

@@ -60,6 +61,13 @@ function recordStep(step, args) {
6061
recorder.catch(err => {
6162
step.status = 'failed'
6263
step.endTime = +Date.now()
64+
65+
// Fix error stack to point to original .ts files (lazy import to avoid circular dependency)
66+
const fileMapping = global.container?.tsFileMapping?.()
67+
if (fileMapping) {
68+
fixErrorStack(err, fileMapping)
69+
}
70+
6371
event.emit(event.step.failed, step, err)
6472
event.emit(event.step.finished, step)
6573
throw err

0 commit comments

Comments
 (0)