Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[BUG] PDF Viewer does not work in headless Chromium and leads to download the file instead #6342

Closed
klhex opened this issue Apr 27, 2021 · 8 comments
Labels
browser-chromium P3-collecting-feedback upstream This is a bug in something playwright depends on, like a browser.

Comments

@klhex
Copy link
Contributor

klhex commented Apr 27, 2021

Context:

  • Playwright Version: 1.10.0
  • Operating System: MacOS 11.3
  • Node.js version: 15.11.0
  • Browser: Chromium (works fine w/ Firefox)

Code Snippet

  1. Create the following HTML file in your local webserver's DocumentRoot:
<a href="test.pdf" target="_blank">Link to PDF file</a>
  1. Put a PDF file with the name "test.pdf" in the same directory.

  2. Run the following PW code:

const { chromium } = require('playwright');

(async () => {
  const browser = await chromium.launch({
    headless: true,
  });
  const context = await browser.newContext();
  const page = await context.newPage();
  await page.setViewportSize({
    width: 1200,
    height: 1200,
  });

  await page.goto('localhost/a.html');

  const [pagePDF] = await Promise.all([
    context.waitForEvent('page'),
    await page.click('text=Link to PDF file'),
  ]);

  await pagePDF.waitForLoadState();
  console.log('Load state reached.');
  await browser.close();
})();

Describe the bug

After 30 seconds you will get the following error message:

/[...]/node_modules/playwright/lib/client/waiter.js:57
            const result = await Promise.race([promise, ...this._failures]);
                           ^
TimeoutError: page.waitForLoadState: Timeout 30000ms exceeded.
=========================== logs ===========================
  "networkidle" event fired
============================================================
Note: use DEBUG=pw:api environment variable to capture Playwright logs.
    at /[...]/node_modules/playwright/lib/client/waiter.js:49:51
    at Waiter.waitForPromise (/[...]/node_modules/playwright/lib/client/waiter.js:57:28)
    at /[...]/node_modules/playwright/lib/client/frame.js:150:13
    at Frame._wrapApiCall (/[...]/node_modules/playwright/lib/client/channelOwner.js:77:28)

Process finished with exit code 1

The expected behavior is instead to get the following console log messages:

Load state reached.

Process finished with exit code 0

The above code works fine when run with Chromium in headful mode or when run with Firefox either in headless or headful mode.

@mxschmitt
Copy link
Member

mxschmitt commented Apr 28, 2021

Seems like it's because Firefox internal PDF viewer does emit a load event and Firefox tries to display the PDF file. Disabling the internal PDF viewer or adding the download attribute to the link element should lead to a more consistent behavior over all the browsers.

Related #3509 (comment)

@klhex
Copy link
Contributor Author

klhex commented Apr 28, 2021

Chromium emits a load event as well, but only in headful mode it appears. So would this have to be considered as an upstream bug?

Also, disabling the internal PDF viewer is not a good option for me since I write e2e tests which simulate the average customer's behavior/setup. And the average customer (user) does not disable the browser's internal PDF view.

And changing the application's functionality in order to make test code work ("adding the download attribute to the link element") would be considered as an anti-pattern in my team.

@mxschmitt
Copy link
Member

mxschmitt commented Apr 29, 2021

Oh I thought its Firefox related, my mistake. After digging more into it I found out that on headless Chromium the PDF viewer does not get used and it leads to download events instead, this is an upstream issue on Chromium side. To workaround that you could start it headful with xvfb-run when you are using it on Linux, that should work or use the download event too for your use-case. Related issue.

See here for running Playwright with headful with Linux: https://playwright.dev/docs/next/ci#running-headed

@klhex
Copy link
Contributor Author

klhex commented Apr 29, 2021

Yes, indeed, I am usually running my tests in headful mode. However, since headless is a bit faster and since I am running my tests within a CI/CD environment, I wanted to switch to headless. But I guess I will have to postpone this for a while. ;-)

@mxschmitt mxschmitt changed the title [BUG] page.waitForLoadState() does not resolve when linked file is opened in new tab [BUG] PDF Viewer does not work in headed Chromium and leads to download the file instead Apr 29, 2021
@mxschmitt mxschmitt added browser-chromium P3-collecting-feedback upstream This is a bug in something playwright depends on, like a browser. and removed triaging labels Apr 29, 2021
@mxschmitt
Copy link
Member

Someone found another work around: microsoft/playwright-python#675 (comment)

@klhex
Copy link
Contributor Author

klhex commented Jun 1, 2021

@mxschmitt Thanks for the link to the workaround, that does the job so far. Although, from an e2e (simulating the end-user/customer experience) point of view, being able to use the internal PDF viewer in headless mode as well (like Firefox seems to do it) would better reflect the real-world scenario whereby the end-user/customer would also (in most cases) use the internal PDF viewer.

BTW: I think you mixed headed and headless when adjusting this issue's title. PDF viewer works fine in headed Chromium but does not get used in headless Chromium.

@mxschmitt mxschmitt changed the title [BUG] PDF Viewer does not work in headed Chromium and leads to download the file instead [BUG] PDF Viewer does not work in headless Chromium and leads to download the file instead Jun 1, 2021
@corradin
Copy link

corradin commented Sep 28, 2021

Thanks! You really helped me out working towards a suitable solution in my scenario (I am not able to change the source code, since I have no access to it.)

import { test, expect } from '@playwright/test';

test('Download PDF', async ({ context, page, browserName, headless }) => {
    if (browserName === 'chromium' && headless === true) {
        // Chromium handles pdf downloads differently in headless mode, see: https://github.com/microsoft/playwright/issues/6342
        const [download] = await Promise.all([
            page.waitForEvent('download'), // wait for download to start
            page.click('button')
        ]);
        const endsWithPdf = download.suggestedFilename().endsWith('.pdf');
        expect(endsWithPdf).toBe(true);
    }
    else if ((browserName === 'chromium' && headless === false) || browserName === 'firefox' || browserName === 'webkit') {
        // Clicking the download button opens a new page
        const [newPage] = await Promise.all([
            context.waitForEvent('page'),
            page.click('button')
        ]);

        // Subscribe to 'response' events of the new page
        newPage.on('response', response =>
            expect(response.headers()['content-type']).toBe('application/pdf')
        );

        // Reload to fire the request/response again
        await newPage.reload();
    }
});

@klhex
Copy link
Contributor Author

klhex commented Sep 28, 2021

As explained by @mxschmitt and also earlier in an issue related to Puppeteer, Chromium does not use/support the internal PDF viewer in headless mode by design. It's not a bug and it's not planned to be changed.

This issue contains enough workarounds I'd say, therefore I am closing this issue.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
browser-chromium P3-collecting-feedback upstream This is a bug in something playwright depends on, like a browser.
Projects
None yet
Development

No branches or pull requests

3 participants