Skip to content

feat(workers): run serial tests after parallel workers - #5728

Open
DavertMik wants to merge 1 commit into
fix/workers-grep-distributionfrom
feat/workers-serial
Open

DavertMik wants to merge 1 commit into
fix/workers-grep-distributionfrom
feat/workers-serial

Conversation

@DavertMik

@DavertMik DavertMik commented Oct 1, 2026 •

Copy link
Copy Markdown
Contributor

Stacked on #5727 — review that one first. This PR targets its branch and should be retargeted to 4.x once it merges.

Problem

Some tests mutate global state and can't run next to anything else. Today the only way to handle them with workers is two commands:

npx codeceptjs run-workers 3 --grep "^(?!.*@serial)"
npx codeceptjs run --grep "@serial"

That means two processes, so CI needs shell to combine exit codes, bootstrapAll / teardownAll don't wrap the second command, and any reporter listening on event.workers.* sees two separate runs that have to be merged afterwards.

Change

New serial config key:

export const config = {
  serial: '@serial',
}

run-workers keeps matching tests out of the parallel workers. When those exit, one more worker starts and runs the serial tests one by one.

$ codeceptjs run-workers 2
Test distribution:
  Worker 1: 1 test
  Worker 2: 2 tests
  Worker 3: 3 tests (serial)
  • One run: bootstrapAll, teardownAll, event.workers.before/after/result fire once; the exit code covers both parts.
  • The pattern is matched against the full title, same as --grep, so a tag on a Feature or added with .tag() works.
  • Works with --by test, suite and pool. Pool workers receive whole files, so they get the serial test ids and drop them after loading a file.
  • Combines with --grep: only serial tests that also match are run; if none match, no serial worker starts; if only serial tests match, no parallel worker starts.

Design choices

  • Config key, not a CLI flag. Which tests are serial is a property of the suite, not of one invocation. It has to be in the config file: -o overrides are applied in workers only, so they don't reach the main process where tests are split.
  • Extra worker instead of draining the pool. The serial worker is a regular worker with a fixed test list, so the same code path serves all --by strategies and it gets its own bootstrap / teardown.
  • Test files are loaded once more in the main process (with reload) to collect serial tests, only when serial is set.

Limits

  • With multiple browser profiles each profile gets its own serial worker, started one after another. I did not run this path — the sandbox has no fixture for it.
  • With --by suite, a Feature holding both serial and parallel scenarios is split, so its BeforeSuite runs in both workers. Tagging the Feature keeps it whole.
  • The fixture is all-passing; a failing serial test goes through the same result merge as any other worker but has no dedicated test.
  • Custom scripts that call spawn() / addTests() themselves are unaffected; the lane only applies to the built-in split.

Tests

New fixture test/data/sandbox/workers-serial records start/end of each test to a timeline file; run_workers_test.js asserts serial tests come last and don't overlap, for each --by strategy and with --grep. test/runner/run_workers_test.js + test/unit/worker_test.js: 51 passing, 2 pending locally.

🤖 Generated with Claude Code

Tests matching the new `serial` config pattern are kept out of the
parallel workers. Once those exit, one more worker starts and runs the
serial tests one by one, inside the same run: bootstrapAll, teardownAll
and workers.result fire once and the exit code covers both parts.

Works with every --by strategy and combines with --grep.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

This branch has not been deployed

No deployments
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.

1 participant