microsoft / playwright

Playwright is a framework for Web Testing and Automation. It allows testing Chromium, Firefox and WebKit with a single API.
https://playwright.dev
Apache License 2.0
67.35k stars 3.71k forks source link

[BUG] Playwright can not close the browser when running with Chrome (no-headless mode) #5327

Open honglax opened 3 years ago

honglax commented 3 years ago

Context:

Code Snippet

const playwright = require('playwright')

(async () => {
  const browser = await playwright['chromium'].launch({
    executablePath:
      '/Applications/Google Chrome.app/Contents/MacOS/Google Chrome',
    headless: false,
  })
  const context = await browser.newContext()
  const page = await context.newPage()
  await page.goto('https://google.com', { waitUntil: 'load' })
  const cookies = await page.context().cookies()
  await browser.close()
})()

Describe the bug When running the test with no-headless Chrome (also happens with Chrome Dev, and Chrome Canary), the test is hung at await browser.close() and can not close the browser.

phileba commented 3 years ago

I also got this issue

dgozman commented 3 years ago

Note that Playwright is only guaranteed to work with bundled browsers, so I expect some internal protocol mismatch. That said, if you could generate protocol logs for us, we would look into them for more details. For that, run with two environment variables and copy mylog.txt here.

$ DEBUG=pw:protocol DEBUG_FILE=mylog.txt node myscript.js
honglax commented 3 years ago

Note that Playwright is only guaranteed to work with bundled browsers, so I expect some internal protocol mismatch. That said, if you could generate protocol logs for us, we would look into them for more details. For that, run with two environment variables and copy mylog.txt here.

$ DEBUG=pw:protocol DEBUG_FILE=mylog.txt node myscript.js

Here it is. chrome-bug.txt

dgozman commented 3 years ago

Logs indicate there were no issues while handling the Browser.close command. I also tried this script with Chrome Canary (version 90.0.4412.0) on MacOS 10.15 and everything works.

Did you try playwright v1.6? It roughly corresponds to Chromium 88.0.4316.0, so I expect things to work there. Overall, you can find out the version of bundled browsers in our (release notes)[https://github.com/microsoft/playwright/releases]. We usually expect similar version to work with Playwright, but older versions might break.

pohlt commented 3 years ago

Same issue here for me (await browser.close() hangs):

tommulkins commented 3 years ago

I had the same problem until I downgraded to Playwright v1.6.0. That seems to work with Chrome binary on Mac OS and closes a headfull browser.

jonny7 commented 3 years ago

I also have this with v1.11.0 with headfull, note headless is fine. This is the setup.ts file for my suites:

import { Browser, chromium, Page } from "playwright";

let browser: Browser;
let page: Page

before(async () => {
  browser = await chromium.launch({ headless: false });
});

after(async () => {
  await browser.close();
});

beforeEach(async () => {
  page = await browser.newPage();
});

afterEach(async () => {
  await page.close();
});

export { page, browser }
delimmy commented 3 years ago

+1 same issue with headful on v1.11.0. Process just hangs forever even with calling browser.close(); This should be considered a major bug that impacts all headful workflows imo.

delimmy commented 3 years ago

This is happening when I'm using the "chrome" stable channel (so it launches real Chrome), but I'm not seeing the issue with Chromium.

delimmy commented 3 years ago

Actually, I wasn't closing the browser properly, but it is still odd that the process hangs on chrome stable but not when "channel" is not specified when launching the browser.

mailtogagan commented 3 years ago

I also observed the same issue with Playwright 1.12.3, and Chrome 91.0.44.72.101 (32 bit). Test execution was fast but it looks like browser closure is taking ages before forcefully killing it.

pw:api => browserContext.close started +1ms
  pw:api <= browserContext.close succeeded +40ms
  pw:api => browser.close started +16ms
  pw:browser [pid=54900] <gracefully close start> +3s
  pw:browser [pid=54900] <kill> +30s
  pw:browser [pid=54900] <will force kill> +1ms
  pw:browser [pid=54900] taskkill output: SUCCESS: The process with PID 53636 (child process of PID 54900) has been terminated.
  pw:browser SUCCESS: The process with PID 54480 (child process of PID 54900) has been terminated.
  pw:browser SUCCESS: The process with PID 56632 (child process of PID 54900) has been terminated.
  pw:browser SUCCESS: The process with PID 11660 (child process of PID 54900) has been terminated.
  pw:browser SUCCESS: The process with PID 54480 (child process of PID 54900) has been terminated.
  pw:browser SUCCESS: The process with PID 54900 (child process of PID 54996) has been terminated.
  pw:browser +516ms
  pw:browser [pid=13148] starting temporary directories cleanup +0ms

  1 passed(37s)
ducalpha commented 3 years ago

I got the same problem on Playwright Python 1.15.2. The browser context close() hanged when the context was launched in the headful mode by browser_type.launch_persistent_context() with a record_har_path to record a HAR archive. The context closed normally without the record_har_path (set to None). A workaround is to open a new tab, then close all other tabs, then close the browser context.

TimurNurlygayanov commented 2 years ago

I have the same issue with Playwright 1.19.2 (TypeScript), when I enable channel: "chrome", the tests are failing because browser.close() "hangs" and playwright is not able to start new browser for next test scenario.

NickDuBois commented 2 years ago

We are experiencing the same issues. Locally and within a Github Action.

Node: 14.18.2 playwright/test: 1.21.1 Ubuntu 20.04 I believe this is both Chrome & Firefox.

In Headless: False mode. I can see a browser not close and remain open until the suite complete and all closes as expected.

In headless: True the workflow completes what it can per set workers/browsers, and then when all else is done, the workflow just sits there.

In github, when I cancel to workflow/test, in the post clean up output I see something like this:

Cleaning up orphan processes
Terminate orphan process: pid (10634) (sh)
Terminate orphan process: pid (10647) (node)
Terminate orphan process: pid (10659) (sh)
Terminate orphan process: pid (10660) (node)
Terminate orphan process: pid (10689) (node)
Terminate orphan process: pid (13358) (firefox)
Terminate orphan process: pid (14095) (WebExtensions)

This is what leads me to believe this is also a firefox issue.

beloquintana commented 2 years ago

I have the same issue with chromium. I am using:

The browser.close(); takes too long.

akhilputhiry commented 1 year ago

Facing same issue. browser is not closing and test timesout. For me if I remove recordHar it works properly

aandrzejczuk commented 1 year ago

I have the same issue with chromium browser chrome channel (no issue for msedge). I am using:

await IBrowser.CloseAsync() takes 30 seconds and then times out.

Attaching logs for pw:protocol and pw:browser mylog_protocol.txt

mohsen1 commented 1 year ago

This is still happening for all Chromium based browser macOS:

My setup:

I'm running the following script:

example.js ```ts const { chromium, webkit, firefox } = require('@playwright/test'); async function main() { const msedge = await chromium.launch({ channel: 'msedge', headless: false, }); console.log('\n\n*** MS Edge ***'); await test(msedge); const chrome = await chromium.launch({ channel: 'chrome', headless: false, }); console.log('\n\n*** Chrome ***'); await test(chrome); const chromiumBrowser = await chromium.launch({ channel: 'chrome', headless: false, }); console.log('\n\n*** Chromium ***'); await test(chromiumBrowser); const webkitBrowser = await webkit.launch({ headless: false, }); console.log('\n\n*** Webkit ***'); await test(webkitBrowser); const firefoxBrowser = await firefox.launch({ headless: false, }); console.log('\n\n*** Firefox ***'); await test(firefoxBrowser); } async function test(browser) { const context = await browser.newContext(); const page = await context.newPage(); console.log(new Date().toLocaleTimeString(), 'Navigating to https://example.com'); await page.goto('https://example.com', { waitUntil: 'load' }); console.log(new Date().toLocaleTimeString(), 'Closing browser...'); console.time('Closing browser took'); await browser.close(); console.timeEnd('Closing browser took'); } process.on('unhandledRejection', (reason, promise) => { console.log('Unhandled Rejection at:', reason.stack || reason); }); process.on('uncaughtException', (error) => { console.log('Uncaught Exception thrown', error); }); process.on('exit', (code) => { console.log(new Date().toLocaleTimeString(), `About to exit with code: ${code}`); }); main() .then(() => { console.log(new Date().toLocaleTimeString(), 'Script finished'); }) .catch((err) => { console.error('Error during playwright script', err); }); ```

I'm getting the following output:

*** MS Edge ***
1:51:03 PM Navigating to https://example.com
1:51:12 PM Closing browser...
Closing browser took: 30.035s

*** Chrome ***
1:51:43 PM Navigating to https://example.com
1:51:44 PM Closing browser...
Closing browser took: 30.068s

*** Chromium ***
1:52:14 PM Navigating to https://example.com
1:52:15 PM Closing browser...
Closing browser took: 30.048s

*** Webkit ***
1:52:46 PM Navigating to https://example.com
1:52:46 PM Closing browser...
Closing browser took: 18.404ms

*** Firefox ***
1:52:48 PM Navigating to https://example.com
1:52:48 PM Closing browser...
Closing browser took: 2.054s
1:52:50 PM Script finished
1:52:50 PM About to exit with code: 0

Closing a Chromium browser takes exactly 30 seconds! There must be a timeout somewhere... Other browsers (Firefox and Webkit) are working correctly.

context.close does not help

Closing all contexts before browser.close() does not fix the issue:

  const contexts = await browser.contexts();
  await Promise.all(contexts.map((ctx) => ctx.close()));
  await browser.close();
dgozman commented 1 year ago

@mohsen1 Enterprise policies may interfere with Playwright controlling the browser. We consider this case to be out of scope, so it's unlikely things will improve in this area.

mohsen1 commented 1 year ago

Thank you @dgozman, in a Mac without Chrome Enterprise the close times are around 2 seconds. It's surprising that Chrome Enterprise is impacting all of Chromium based browsers. I understand that this is outside of the scope of Playwright but do you have any suggestions where can I look for possible solutions?

dgozman commented 1 year ago

@mohsen1 There is no known workaround, except for using Playwright's built-in chromium and not passing channel.

mohsen1 commented 1 year ago

In our case, Chromium (without the channel specified) also takes 30 seconds to close.

dgozman commented 1 year ago

@mohsen1 This is interesting. Could you please run with DEBUG=pw:browser* environment variable and share the logs?

mohsen1 commented 1 year ago

Updated the script to remove channel from Chromium

```ts const { chromium, webkit, firefox } = require('@playwright/test'); async function main() { const msedge = await chromium.launch({ channel: 'msedge', headless: false, }); console.log('\n\n*** MS Edge ***'); await test(msedge); const chrome = await chromium.launch({ channel: 'chrome', headless: false, }); console.log('\n\n*** Chrome ***'); await test(chrome); const chromiumBrowser = await chromium.launch({ headless: false, }); console.log('\n\n*** Chromium ***'); await test(chromiumBrowser); const webkitBrowser = await webkit.launch({ headless: false, }); console.log('\n\n*** Webkit ***'); await test(webkitBrowser); const firefoxBrowser = await firefox.launch({ headless: false, }); console.log('\n\n*** Firefox ***'); await test(firefoxBrowser); } async function test(browser) { const context = await browser.newContext(); const page = await context.newPage(); console.log(new Date().toLocaleTimeString(), 'Navigating to https://example.com'); await page.goto('https://example.com', { waitUntil: 'load' }); console.log(new Date().toLocaleTimeString(), 'Closing browser...'); console.time('Closing browser took'); await browser.close(); console.timeEnd('Closing browser took'); } process.on('unhandledRejection', (reason, promise) => { console.log('Unhandled Rejection at:', reason.stack || reason); }); process.on('uncaughtException', (error) => { console.log('Uncaught Exception thrown', error); }); process.on('exit', (code) => { console.log(new Date().toLocaleTimeString(), `About to exit with code: ${code}`); }); main() .then(() => { console.log(new Date().toLocaleTimeString(), 'Script finished'); }) .catch((err) => { console.error('Error during playwright script', err); }); ```

Command: DEBUG=pw:browser* DEBUG_FILE=pw.log node pw.js

Output:

``` *** MS Edge *** 5:46:15 PM Navigating to https://example.com 5:46:17 PM Closing browser... Closing browser took: 30.056s *** Chrome *** 5:46:47 PM Navigating to https://example.com 5:46:48 PM Closing browser... Closing browser took: 30.048s *** Chromium *** 5:47:21 PM Navigating to https://example.com 5:47:21 PM Closing browser... Closing browser took: 30.048s *** Webkit *** 5:47:55 PM Navigating to https://example.com 5:47:56 PM Closing browser... Closing browser took: 17.823ms *** Firefox *** 5:47:58 PM Navigating to https://example.com 5:47:58 PM Closing browser... Closing browser took: 1.931s 5:48:00 PM Script finished 5:48:00 PM About to exit with code: 0 ```

I did not see any content in my pw.log file. Not sure what I'm doing wrong?

dgozman commented 1 year ago

I did not see any content in my pw.log file. Not sure what I'm doing wrong?

I am not sure. After running your command on my computer, I got pw.log file with some logs.

kaliiiiiiiiii commented 1 year ago

Something like:

async function close(endpoint) {
    let promise
    const ws = new WebSocket(endpoint);
    ws.on('open', function open() {
        ws.send("{'id': 0, 'method': 'Browser.close', 'params':{}");
        ws.close()
        promise = Promise.resolve()
    });
    return await promise
}
const server  = await chromium.launchServer({
        headless: false,
        executablePath: "C:/Program Files/Google/Chrome/Application/chrome.exe",
    })
endpoint = server.wsEndpoint()
await close(endpoint) // patches https://github.com/microsoft/playwright/issues/5327

however raises the following after closing

>SyntaxError: Unexpected token ' in JSON at position 1
 >    at JSON.parse (<anonymous>)
 >    at i.<anonymous> (C:\Users\aurin\source\repos\playwright test\playwright test\node_modules\playwright-core\lib\remote\playwrightConnection.js:65:48)
 >    at process.processTicksAndRejections (node:internal/process/task_queues:95:5)

( not sure how to catch that error)