Skip to content

Commit 6008734

Browse files
DavertMikclaude
andcommitted
refactor: merge Runner class back into Codecept
The Runner extraction created a runtime object cycle: Codecept owned a Runner, and Runner held a back-reference to Codecept for config, opts, testFiles, requiringModules, loadTests, and the event.all.before/after payload. Since Runner had one creation site, no independent state, and its only collaborator was the object it pointed back to, the class wasn't carrying its weight. Moving the methods back onto Codecept puts behavior next to its data and eliminates the cycle by construction. - lib/codecept.js: absorbs getSuites, run, runSuite, runTest, _execute - lib/runner.js: deleted - lib/index.js: Runner removed from public exports (unreleased API) Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
1 parent a15acc3 commit 6008734

3 files changed

Lines changed: 92 additions & 127 deletions

File tree

lib/codecept.js

Lines changed: 91 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -10,15 +10,20 @@ import { createRequire } from 'module'
1010
const __filename = fileURLToPath(import.meta.url)
1111
const __dirname = dirname(__filename)
1212

13+
import escapeRe from 'escape-string-regexp'
1314
import Helper from '@codeceptjs/helper'
14-
import Runner from './runner.js'
15+
import MochaFactory from './mocha/factory.js'
1516
import container from './container.js'
1617
import Config from './config.js'
18+
import event from './event.js'
19+
import recorder from './recorder.js'
20+
import output from './output.js'
1721
import runHook from './hooks.js'
1822
import ActorFactory from './actor.js'
1923
import { emptyFolder } from './utils.js'
2024
import { initCodeceptGlobals } from './globals.js'
2125
import store from './store.js'
26+
import { validateTypeScriptSetup, getTSNodeESMWarning } from './utils/loaderCheck.js'
2227

2328
import storeListener from './listener/store.js'
2429
import stepsListener from './listener/steps.js'
@@ -100,7 +105,6 @@ class Codecept {
100105
await this.requireModules(this.requiringModules)
101106
// initializing listeners
102107
await container.create(this.config, this.opts)
103-
this.runner = new Runner(this)
104108
await this.runHooks()
105109
}
106110

@@ -263,7 +267,26 @@ class Codecept {
263267
* @returns {Array<{title: string, file: string, tags: string[], tests: Array<{title: string, uid: string, tags: string[], fullTitle: string}>}>}
264268
*/
265269
getSuites(pattern) {
266-
return this.runner.getSuites(pattern)
270+
if (this.testFiles.length === 0) this.loadTests(pattern)
271+
272+
const tempMocha = MochaFactory.create(this.config.mocha || {}, this.opts || {})
273+
tempMocha.files = this.testFiles
274+
tempMocha.loadFiles()
275+
276+
const suites = []
277+
for (const suite of tempMocha.suite.suites) {
278+
suites.push({
279+
...suite.simplify(),
280+
file: suite.file || '',
281+
tests: suite.tests.map(test => ({
282+
...test.simplify(),
283+
fullTitle: test.fullTitle(),
284+
})),
285+
})
286+
}
287+
288+
tempMocha.unloadFiles()
289+
return suites
267290
}
268291

269292
/**
@@ -274,7 +297,7 @@ class Codecept {
274297
* @returns {Promise<void>}
275298
*/
276299
async runSuite(suite) {
277-
return this.runner.runSuite(suite)
300+
return this.run(suite.file)
278301
}
279302

280303
/**
@@ -285,7 +308,7 @@ class Codecept {
285308
* @returns {Promise<void>}
286309
*/
287310
async runTest(test) {
288-
return this.runner.runTest(test)
311+
return this._execute({ grep: `^${escapeRe(test.fullTitle)}$` })
289312
}
290313

291314
/**
@@ -295,7 +318,69 @@ class Codecept {
295318
* @returns {Promise<void>}
296319
*/
297320
async run(test) {
298-
return this.runner.run(test)
321+
let files = this.testFiles
322+
323+
if (test) {
324+
if (!fsPath.isAbsolute(test)) {
325+
test = fsPath.join(store.codeceptDir, test)
326+
}
327+
const testBasename = fsPath.basename(test, '.js')
328+
const testFeatureBasename = fsPath.basename(test, '.feature')
329+
files = files.filter(t => {
330+
return fsPath.basename(t, '.js') === testBasename || fsPath.basename(t, '.feature') === testFeatureBasename || t === test
331+
})
332+
}
333+
334+
return this._execute({ files })
335+
}
336+
337+
async _execute({ files, grep } = {}) {
338+
await container.started()
339+
340+
const tsValidation = validateTypeScriptSetup(this.testFiles, this.requiringModules || [])
341+
if (tsValidation.hasError) {
342+
output.error(tsValidation.message)
343+
process.exit(1)
344+
}
345+
346+
const tsWarning = getTSNodeESMWarning(this.requiringModules || [])
347+
if (tsWarning) {
348+
output.print(output.colors.yellow(tsWarning))
349+
}
350+
351+
try {
352+
const { loadTranslations } = await import('./mocha/gherkin.js')
353+
await loadTranslations()
354+
} catch (e) {
355+
// Ignore if gherkin module not available
356+
}
357+
358+
return new Promise((resolve, reject) => {
359+
const mocha = container.mocha()
360+
mocha.files = files || this.testFiles
361+
362+
if (grep) {
363+
mocha.grep(grep)
364+
}
365+
366+
const done = async (failures) => {
367+
event.emit(event.all.result, container.result())
368+
event.emit(event.all.after, this)
369+
await recorder.promise()
370+
if (failures) {
371+
process.exitCode = 1
372+
}
373+
resolve()
374+
}
375+
376+
try {
377+
event.emit(event.all.before, this)
378+
mocha.run(async (failures) => await done(failures))
379+
} catch (e) {
380+
output.error(e.stack)
381+
reject(e)
382+
}
383+
})
299384
}
300385

301386
/**

lib/index.js

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,6 @@ import ai from './ai.js'
2424
import Workers from './workers.js'
2525
import Secret, { secret } from './secret.js'
2626
import Result from './result.js'
27-
import Runner from './runner.js'
2827

2928
export default {
3029
/** @type {typeof CodeceptJS.Codecept} */
@@ -72,9 +71,7 @@ export default {
7271

7372
/** @type {typeof Result} */
7473
Result,
75-
76-
Runner,
7774
}
7875

7976
// Named exports for ESM compatibility
80-
export { codecept, codecept as Codecept, output, container, event, recorder, config, actor, helper, helper as Helper, pause, within, dataTable, dataTableArgument, store, locator, heal, ai, Workers, Secret, secret, Result, Runner }
77+
export { codecept, codecept as Codecept, output, container, event, recorder, config, actor, helper, helper as Helper, pause, within, dataTable, dataTableArgument, store, locator, heal, ai, Workers, Secret, secret, Result }

lib/runner.js

Lines changed: 0 additions & 117 deletions
This file was deleted.

0 commit comments

Comments
 (0)