| 17 | |
| 18 | class MochaFactory { |
| 19 | static create(config, opts) { |
| 20 | const merged = Object.assign({}, config, opts) |
| 21 | mocha = new Mocha(merged) |
| 22 | if (merged.cleanReferencesAfterRun !== true) { |
| 23 | mocha.cleanReferencesAfterRun(false) |
| 24 | } |
| 25 | output.process(opts.child) |
| 26 | mocha.ui(scenarioUiFunction) |
| 27 | |
| 28 | // Manually trigger UI setup for globals to be available in ESM context |
| 29 | // This ensures Feature, Scenario, Before, etc. are available immediately |
| 30 | if (mocha.suite && mocha.suite.emit) { |
| 31 | const context = {} |
| 32 | mocha.suite.emit('pre-require', context, '', mocha) |
| 33 | // Also set globals immediately so they're available when ESM modules load |
| 34 | initMochaGlobals(context) |
| 35 | } |
| 36 | |
| 37 | Mocha.Runner.prototype.uncaught = function (err) { |
| 38 | if (err) { |
| 39 | if (err.toString().indexOf('ECONNREFUSED') >= 0) { |
| 40 | // Handle ECONNREFUSED without dynamic import for now |
| 41 | err = new Error('Connection refused: ' + err.toString()) |
| 42 | } |
| 43 | const fileMapping = container?.tsFileMapping?.() |
| 44 | if (fileMapping) { |
| 45 | fixErrorStack(err, fileMapping) |
| 46 | } |
| 47 | output.error(err) |
| 48 | output.print(err.stack) |
| 49 | process.exit(1) |
| 50 | } |
| 51 | output.error('Uncaught undefined exception') |
| 52 | process.exit(1) |
| 53 | } |
| 54 | |
| 55 | // Override loadFiles to handle feature files |
| 56 | const originalLoadFiles = Mocha.prototype.loadFiles |
| 57 | mocha.loadFiles = function (fn) { |
| 58 | // load features |
| 59 | const featureFiles = this.files.filter(file => file.match(/\.feature$/)) |
| 60 | if (featureFiles.length > 0) { |
| 61 | // Load translations for Gherkin features |
| 62 | loadTranslations().catch(() => { |
| 63 | // Ignore if translations can't be loaded |
| 64 | }) |
| 65 | |
| 66 | for (const file of featureFiles) { |
| 67 | const suite = gherkinParser(fs.readFileSync(file, 'utf8'), file) |
| 68 | this.suite.addSuite(suite) |
| 69 | } |
| 70 | |
| 71 | // remove feature files |
| 72 | const jsFiles = this.files.filter(file => !file.match(/\.feature$/)) |
| 73 | this.files = this.files.filter(file => !file.match(/\.feature$/)) |
| 74 | |
| 75 | // Load JavaScript test files using original loadFiles |
| 76 | if (jsFiles.length > 0) { |