Repository navigation
Description
What are you trying to achieve?
Run a suite with the Playwright helper and restart: true (one browser per test), with every browser closed after its test.
What do you get instead?
Each test file leaves one Chromium instance running until the process ends. In our CI, all 15 parallel jobs (about 125 tests each) were OOMKilled after 25 to 30 minutes (memory limit 2.5 GiB). A local run ended with 36 Chromium processes and 4 GB of memory, against 5 processes and 1 GB with the workaround below.
The helper starts two browsers for the first test of each file:
_beforeSuite()starts a browser becauseisRunningis false (the_after()of the previous test stopped its browser):
CodeceptJS/lib/helper/Playwright.js
Lines 550 to 562 in 744dba8
_beforeSuite() { // Skip browser start in dry-run mode (used by check command) if (store.dryRun) { this.debugSection('Dry Run', 'Skipping browser start') return } // Start browser if not manually started and not already running // Browser should start in singleton mode (restart: false) or when restart strategy is enabled if (!this.options.manualStart && !this.isRunning) { this.debugSection('Session', 'Starting singleton browser session') return this._startBrowser() } _before()of the first test seesisRunning === true, takes therestartsBrowser()branch and calls_startBrowser()again. That overwritesthis.browserwithout closing the browser from step 1:
CodeceptJS/lib/helper/Playwright.js
Lines 604 to 609 in 744dba8
// Start browser if needed (initial start or browser restart strategy) if (!this.isRunning && !this.options.manualStart) await this._startBrowser() else if (restartsBrowser() && !this.options.manualStart) { // Browser restart strategy: start browser for each test await this._startBrowser() } _after()closes onlythis.browser, so the browser from step 1 is never closed.
The later tests of the same file are fine, because _after() sets isRunning to false and _before() starts exactly one browser.
Workaround
We skip _beforeSuite() when every test starts its own browser anyway:
const { _beforeSuite } = Playwright.prototype
Playwright.prototype._beforeSuite = function () {
return (this.options.restart === true) ? undefined : _beforeSuite.call(this)
}In the helper, the same check fits into _beforeSuite(): if (!this.options.manualStart && !this.isRunning && !restartsBrowser()). That also covers restart: 'browser'.
Steps to reproduce
- Configure the Playwright helper with
restart: true. - Create a few test files with one scenario each, e.g.
I.amOnPage('about:blank'). - Run them in one process and count the Chromium processes during and after the run (or run with
--debug: each file logs "Starting singleton browser session" from_beforeSuite(), followed by a second browser start in_before()).
Details
- CodeceptJS version: 4.2.2 (same code on
4.xat 744dba8) - NodeJS Version: 24.21.0 locally, CI image
wolfi/playwright:1.64.1 - Operating System: Windows 11 locally, Linux in CI
- playwright version: 1.64.1 (Chromium 156.0.8078.4)
- Configuration file (relevant part):
helpers: {
Playwright: {
browser: 'chromium',
restart: true
}
}
Activity
fix(playwright): start no browser in _beforeSuite with restart: brows…