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
62.73k stars 3.38k forks source link

[Feature]: I would like waitForResponse to be available on BrowserContext #30851

Closed eveltman-twinfield closed 2 weeks ago

eveltman-twinfield commented 2 weeks ago

🚀 Feature Request

Hi, I'd like waitForResponse to be available on BrowserContext. Right now we can only use it on Page level.

Example

Since it wasn't available, this our own implementation. (Note: I omitted the actual usage in the test, but triggerAction is a function which clicks somewhere in the application to open a popup)

export class ExternalDocumentViewerPage extends ClassicPage {
    // <snip>
    static async monitorApiRequestLoadsInvoice(
        invoiceId: string,
        context: BrowserContext,
        triggerAction: () => Promise<void>): Promise<void> {

        const identityUrl = process.env.API_URL.toLowerCase() + '/api/sales/invoices/' + invoiceId;
        const response = await BrowserContextApiMonitor.waitForResponse(context, identityUrl, triggerAction);

        expect(response.ok()).toBe(true);
    }
}

// browserContextApiMonitor.ts
import { BrowserContext, Response } from "@playwright/test";

export class BrowserContextApiMonitor {
    static async waitForResponse(
        context: BrowserContext,
        expectedUrl: string,
        triggerAction: () => Promise<void>): Promise<Response> {

        let responseHandler: (response: Response) => void;

        const responsePromise = new Promise<Response>(resolve => {
            responseHandler = (response: Response) => {
                if (response.url() == expectedUrl) {
                    resolve(response);
                }
            };

            context.on('response', responseHandler);
        });

        await triggerAction();

        const response = await responsePromise;
        context.off('response', responseHandler);

        return response;
    }
}

Motivation

Today I was assisting a QA with verifying that a popup page was making a successful API request (to load a PDF in an iframe). We were checking not only that the URL (requested by the application) contained the expected invoice id but also that the request would return a success response. Since the API request is done in a popup, we could not use page.waitForResponse. By the time the popup event occurs and by the time we handled that event and attached a response handler to it, the API response may already have been received.

I initially was convinced we could just do context.waitForResponse but it didn't exist.

yury-s commented 2 weeks ago

You can use context.waitForEvent('response') for that:

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

test("context wait for response", async ({ page, context }) => {
  const responsePromise = context.waitForEvent('response', (response) => response.url().includes('playwright-logo.svg'));
  await page.goto("https://playwright.dev");
  const response = await responsePromise;
  expect(response.status()).toBe(200);
});