[BUG] Processes continue running forever when test runner is closed early via CTRL+C
We've been setting up Playwright for the first time and while tests work and pass, we've been noticing Chromium processes sometimes continue running and accumulate and start driving our cpu up. We were confused why our laptops were getting so warm over a week or so of work and then we noticed this:
It appears that this happens when we CTRL+C quit the testing process before it's completed, usually because we're developing and realized we made a mistake or something needs to be changed. I asked about this in discord and was advised to report a bug.
I'm using a fixture based on the example in the playwright docs for shared authentication, because we modify server state. Not sure if that has anything to do with this.
System info
- Playwright Version: 1.37.1
- Operating System: Windows 11
- Browser: Chromium
- Other info: Node v18.17.0
Source code
playwright.config.js:
import { defineConfig, devices, type PlaywrightTestConfig } from '@playwright/test'
import dotenv from 'dotenv'
import dotenvExpand from 'dotenv-expand'
dotenvExpand.expand(dotenv.config({ override: true }))
// eslint-disable-next-line @typescript-eslint/naming-convention
const { CONTEXT_PATH, E2E_RUNSERVER, HOST, PLAYWRIGHT_WORKERS, PORT } = process.env
if (!PORT || !PLAYWRIGHT_WORKERS) {
throw new Error('Env variables missing')
}
const config: PlaywrightTestConfig = {
testDir: './tests/e2e',
timeout: 30_000,
expect: {
timeout: 30_000
},
fullyParallel: true,
reporter: 'html',
workers: Number.parseInt(PLAYWRIGHT_WORKERS, 10),
use: {
actionTimeout: 0,
baseURL: `${HOST}:5173${CONTEXT_PATH}/`,
screenshot: 'only-on-failure',
trace: 'on-first-retry'
},
projects: [
{
name: 'chromium',
use: {
...devices['Desktop Chrome']
}
}
],
outputDir: 'tests/e2e/artifacts'
}
if (E2E_RUNSERVER) {
config.webServer = {
command: 'yarn dev',
// Wait until tomcat server is up before running tests (vite should always be up by now)
port: Number.parseInt(PORT, 10),
stdout: 'pipe',
stderr: 'pipe',
timeout: 45_000
}
}
export default defineConfig(config)
fixtures.ts:
import { test as baseTest, expect } from '@playwright/test'
import config from '../../playwright.config'
import fs from 'node:fs'
import path from 'node:path'
/**
* Create an authentication fixture.
*
* Given our current database setup and testing approach, this solution was
* chosen based on the recommendations at https://playwright.dev/docs/auth
*/
export * from '@playwright/test'
// eslint-disable-next-line @typescript-eslint/ban-types
export const test = baseTest.extend<{}, { workerStorageState: string }>({
// Use the same storage state for all tests in this worker.
storageState: ({ workerStorageState }, use) => use(workerStorageState),
// Authenticate once per worker with a worker-scoped fixture.
workerStorageState: [
// eslint-disable-next-line @typescript-eslint/explicit-function-return-type
async ({ browser }, use) => {
// Use parallelIndex as a unique identifier for each worker.
const id = test.info().parallelIndex
const fileName = path.resolve(test.info().project.outputDir, `.auth/${id}.json`)
const { PASSWORD, USERNAME } = process.env
expect(PASSWORD).toBeTruthy()
expect(USERNAME).toBeTruthy()
// This is really only needed to typescript, it doesn't know expect() is narrowing
if (!PASSWORD || !USERNAME) {
return
}
if (fs.existsSync(fileName)) {
// Reuse existing authentication state if any.
await use(fileName)
return
}
// Important: make sure we authenticate in a clean environment by unsetting storage state.
// eslint-disable-next-line no-undefined
const page = await browser.newPage({ storageState: undefined })
await page.goto(`${config.use?.baseURL ?? '/'}login`)
await page.locator('[name=username]').fill(USERNAME)
await page.locator('[name=password]').fill(PASSWORD)
await page.getByText('Log In').click()
await expect(page.locator('_react=NavigationMenu')).toBeVisible()
await page.context().storageState({ path: fileName })
await page.close()
await use(fileName)
},
{ scope: 'worker' }
]
})
an example test:
import { expect, test } from '../../fixtures'
import { ModalPOM } from '../../poms/components/modal.pom'
import { NumericLogsPOM } from '../poms/numeric-logs.pom'
test.describe('calculation details modal', () => {
test('opens from a numeric logs main grid sequence', async ({ page }) => {
const numericLogs = new NumericLogsPOM(page)
await numericLogs.goto()
// Expand tree nodes so that the necessary node exists
await numericLogs.navigateViaTree(['USNS Evers', 'Engine', 'Daily Vessel Report', 'Ship Information'])
// Right click first cell that has calculation details
await numericLogs.mainGrid.centerContainer.cell(4, 0).click({
button: 'right'
})
// Click the context menu link
await page.getByText('Calculation Details...').click()
const modal = new ModalPOM(page.locator('_react=CalculationDetailsModal'))
await modal.isVisible()
// Verify modal title
await expect(modal.header).toContainText('Calculation')
})
})
Steps
- Run tests
- CTRL+C quit before the suite is done.
Expected
All processes close.
Actual
See pic above. We have to hit "End Task" in task manager for these to close properly.
@viveleroi Unfortunately, we are not able to reproduce the issue, and we haven't seen similar complaints in the recent past. Most likely, there is something about your environment that triggers this issue - antivirus, extensions, etc.
I'll keep this issue open to see whether someone else experiences this problem and could help us with figuring out the culprit.
I will try to find more about how to reproduce it. Several users did add on to my discord question saying they had seen the same problem but they had less info than I did.
We are also facing similar issue as pointed out in all 3 below bug requests, unfortunately we are not able to share a code repo where the issue can be reproduced. https://github.com/microsoft/playwright/issues/26794 https://github.com/microsoft/playwright/issues/26671 https://github.com/microsoft/playwright/issues/26656 We are still trying to debug on what is causing the issue. When we downgraded the version to 1.33 and ran the tests again, it all works fine and the issue is not seen. From what we noticed, version 1.34 and above if any test case fails the worker/thread doesn't get closed until we manually kill the nodes in activity monitor but the browser closes and the test run is stuck.
It happens for us all the time when we run the npm script via intellij and then stop it using intellij button in the middle of the test runs we are running tests in a parallel mode and we are using windows 11
@Smrtnyk Unfortunately, we cannot figure out whether the bug is in Playwright or in IntelliJ integration without a repro. Can you share detailed repro instructions with us?
@dgozman I will try to assemble a clear repro instruction tomorrow and let you know
@dgozman I just tried it out and how it happened for me is in this sequence
- open the project in intelliJ
- go to package.json
- find the test script (for us is like this
"test:playwright:stable:chromium:local": "npm run test:playwright:stable:local -- -- --project=chromium --debug") - on the left side there is a play button, press it
- select debugger option to run the script in debug mode
- when chromium opens and stops before executing test where you need to press play kill the test script using stop button in the intelliJ on top
Had to do this 2 or 3 times to accumulate orphan chromium processes in the task manager
@Smrtnyk Perhaps the issue is related to --debug option. Let me take a look.
I see this when debugging in webstorm. The only way to kill the debugger processes is to kill webstorm.
Facing the same issue here with Windows on Parallels. Thought nothing could make the M1 warm but the always alive Chromium processes running in the background, even after quitting VSCode, ended up sucking a lot of battery and heating up the machine.
I am experiencing the same issue on Mac arm64. It takes a long time (around 5min) cpu usage reducing after pressing ctrl+c. My observation is that the longer the test, the more the number of nodes increases, and the longer it takes for the CPU to calm down.
No --debug or --headed.
For me it was all Chromium instead of node that you are seeing.
Hello Team. Same issue on Windows OS.
async with SEMAPHOR:
# Process the wallet asynchronously
settings = Settings()
now = int(time.time())
async with async_playwright() as p:
if wallet.proxy:
proxy = await Helper.setup_proxy(wallet.proxy)
args = [
f"--disable-extensions-except={EXTENTION_PATH}",
f"--load-extension={EXTENTION_PATH}",
"--no-sandbox", # Add any other arguments you need
'--lang=en-US', # Set language to English (United States)
]
if HEADLESS:
args.append(f"--headless=new")
context = await p.chromium.launch_persistent_context(
'',
headless=False,
args=args,
proxy=proxy,
)
// Code
await context.close()
async with LOCK:
pass
Please could you advise why the chromium driver still running even after context.close() ?
Thanks
I tried setting workers to be 1, and turning off parallels, but still getting this error. Seems to be related to --debug, because I only get it when running with that flag.
I'm on macOS too.
this is happening to me a lot when running on terminal
this is my command:
npm run test:frontend:ui -- client-management.spec.js
any working solutions for this?
what is the best practice, because even when closing on the "X" button it happens sometimes and I don't know why
Sometimes it consumes 27gb of RAM on my PC because there are many processes open and I have to restart my PC to keep working