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
66.39k stars 3.63k forks source link

[BUG] route.fulfill ignores some response headers #28627

Closed BeejeeDS closed 10 months ago

BeejeeDS commented 10 months ago

System info

Source code

page.route('https://random.com/api/documents/*', async route => {
    await route.fulfill({
            contentType: 'text/csv',
            headers: {
                'Content-Disposition': 'inline; filename=test.csv'
            },
            body: 'This is a test'
        }
    );
});

Expected

I expect the content-type and content-disposition headers to be present in the response.

Actual

The response doesn't contain the content-disposition header, only content-type is present.

Playwright config

export default defineConfig({
    ...nxE2EPreset(__filename, { testDir: './e2e' }),
    retries: 3,
    workers: process.env.CI ? 1 : 2,
    fullyParallel: true,
    reporter: [['playwright-trx-reporter', { outputFile: 'e2e-xxx-results.trx' }]],
    timeout: 5 * 60 * 1000,
    use: {
        baseURL,
        trace: 'on-first-retry',
        screenshot: 'only-on-failure',
        video: 'on' //retain-on-failure --> na de POC
    },
    projects: [
        { name: 'setup', testMatch: /.*\.setup\.ts/ },
        {
            name: 'chromium',
            use: {
                ...devices['Desktop Chrome']
            },
            dependencies: ['setup']
        }
        // {
        //     name: 'firefox',
        //     use: {
        //         ...devices['Desktop Firefox'],
        //     },
        //     dependencies: ['setup'],
        // },
        // {
        //     name: 'webkit',
        //     use: {
        //         ...devices['Desktop Safari'],
        //     },
        //     dependencies: ['setup'],
        // },
    ],
    /* Run your local dev server before starting the tests */
    webServer: [
        {
            command: 'npm run start',
            url: baseURL,
            reuseExistingServer: !process.env.CI,
            timeout: 300000
        }
    ]
});
mxschmitt commented 10 months ago

I tried the following which works for me, what am I doing different?

import { chromium } from 'playwright';

(async () => {
  const browser = await chromium.launch({
    headless: false,
  });
  const context = await browser.newContext();
  const page = await context.newPage();

  await page.goto('https://example.com');
  await page.route('**/foo.pdf', async route => {
    await route.fulfill({
      contentType: 'text/csv',
      headers: {
        'Content-Disposition': 'inline; filename=test.csv'
      },
      body: 'This is a test'
    }
    );
  });
  console.log('Received download: ' + JSON.stringify(await page.evaluate(() => fetch('https://example.com/foo.pdf').then(r => ([...r.headers.entries()])))));

  // Teardown
  await context.close();
  await browser.close();
})();
BeejeeDS commented 10 months ago

@mxschmitt I've added your example to my project but the result stays the same, there are 2 headers (content-length and content-type) but no content-disposition header.

I've added my playwright.config to my original message.

This is the test I've created based on your code sample:


    test('Mxschmitt test.', async () => {
        const browser = await chromium.launch({
            headless: false,
        });
        const context = await browser.newContext();
        const page = await context.newPage();

        await page.route('**/foo.pdf', async route => {
            await route.fulfill({
                    contentType: 'text/csv',
                    headers: {
                        'Content-Disposition': 'inline; filename=test.csv'
                    },
                    body: 'This is a test'
                }
            );
        });

        // @ts-ignore
        console.log('Received download: ' + JSON.stringify(await page.evaluate(() => fetch('https://example.com/foo.pdf').then(r => ([...r.headers.entries()])))));
        // Returns Received download: [["content-length","14"],["content-type","text/csv"]]

        // Teardown
        await context.close();
        await browser.close();
    });`
mxschmitt commented 10 months ago

If you do await page.goto('https://example.com'); before the page.route, it works as intended. Would that work for you? Seems like something what Chromium is internally doing.

mxschmitt commented 10 months ago

Closing as per above. Please re-file for further bug reports / feature requests.