javascript - hvordan kan jeg få playwright til at lytte til beskedbegivenheder


Jeg har et websted, der kommunikerer gennem postMessage-kommandoer

 window.parent.postMessage(communication, this.origin);

Når jeg bruger Playwright, prøver jeg at lave en "on message"-håndtering

function eventListener(returnEvent: any): any {
  window.addEventListener('message', function(event: MessageEvent) {
    console.log('Im here');
    if (event.data.messageId === 'payment-method') {
      console.log('setting return event', event);
      returnEvent = event;
      return returnEvent;
    }
  });
}

...

  let returnEvent: any = {};
  await page.evaluate(eventListener, returnEvent);
  await creditCardDetailsPage.fillFormValid();
  await page.waitForTimeout(5000); //give time for message event to fire
  console.log('event was', returnEvent);
  await expect(returnEvent).toEqual(<MY DATA OBJECT HERE>)

konsoloutput

Im here
setting return event MessageEvent
event was {}

Jeg kan ikke sætte mine forventninger i page.evaluate()-koden, fordi den så kører inden for konteksten af det javascript-tag, som den injicerer, og ikke inden for konteksten af spec'en.

Svar #1

Jeg ville til sidst få det til et expect, snarere end et evaluate eller et waitForEvent.

Desværre kunne jeg ikke finde noget til at lytte til en beskedhændelse. I stedet går vi bare efter console.info.

Denne løsning pakker page.on('console', ...) ind i en asynkron funktion, som vi simpelthen kan bruge som en 'opfyld hvis succes, hæng hvis fejl'-switch

// Return true if the page emits 'Hello, World!' to the console info level
async function waitForMessageAsync(): Promise<boolean> {
  return new Promise(function (resolve) {
    page.on('console', (msg) => {
      if (msg.type() === 'log' && msg.text() === 'Hello, World!') {
        resolve(true);
      }
    });
  });
}

await expect(await waitForMessageAsync).toBeTruthy();

På denne måde vil expect fejle testen, hvis den timeout'er, mens den venter på vores 'Hello, World!' besked! 🥳

Svar #2

Man kan påstå kald til window.parent.postMessage eller andre browser-API'er ved at overskrive browser-API-implementeringen for at skubbe metodekald ind i Node.js-hukommelsen ved hjælp af exposeFunction og addInitScript. Derefter kan påstande udføres asynkront på disse kald ved hjælp af expect.poll.

For eksempel:

import { test, expect, Page } from "@playwright/test";

const setupMockPostMessage = async (page: Page) => {
  // Store calls to postMessage
  let calls: any[] = [];

  // Expose function for pushing messages to the Node.js environment
  const logCall = (args: any[]) => {
    calls.push(args);
  };
  await page.exposeFunction("logCall", logCall);

  // Inject script into the page to intercept postMessage calls
  await page.addInitScript(() => {
    window.parent.postMessage = (...args) => {
      logCall(args);
    };
  });
  
  // Return object for performing assertions
  return {
    calls,
    lastCalledWith: () => calls.at(-1),
    callTimes: () => calls.length,
    clear: () => (calls = []),
  };
};

test("iframe message channel", async ({ page }) => {
  const mockPostMessage = await setupMockPostMessage(page);
  await page.goto("/");

  await expect.poll(mockPostMessage.lastCalledWith).toEqual(["LOAD", "*"]);
  await expect.poll(mockPostMessage.callTimes).toEqual(1);
});