4

I have a website that communicates through postMessage commands

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

When using playwright, I'm trying to do an "on message" handler

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>)

console output

Im here
setting return event MessageEvent
event was {}

I cannot put my expects in the page.evaluate() code, because then it's executing within the context of the javascript tag that it's injecting, and not within the context of the spec.

Matt Westlake
  • 3,499
  • 7
  • 39
  • 80
  • You're going to have to have your listener add something to the page or console log something (in the page context) and then you'll have to pick up that change in your test – Ben Gooding Sep 22 '21 at 23:07
  • This test is also closer to a unit test that you ideally shouldn't be testing this here. It's much more common to test the result of an action in browser tests and how the DOM is impacted by an action. – Ben Gooding Sep 22 '21 at 23:10

2 Answers2

3

I wanted to eventually get it to an expect, rather an evaluate or a waitForEvent.

Sadly I couldn't find anything for listening to a message event. Instead we just go for console.info.

This solution wraps the page.on('console', ...) into an async function that we can simply use as a 'resolve if success, hang if failure' 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();

This way, expect will fail the test if it times out waiting for our 'Hello, World!' message!

Crayon
  • 46
  • 5
1
async def print_args(msg):
    values = []
    for arg in msg.args:
        values.append(await arg.json_value())
    print(values)

page.on("console", print_args)
await page.evaluate("console.log('hello', 5, {foo: 'bar'})")
Alf
  • 19
  • 3
  • 2
    While this code snippet may solve the problem, it doesn't explain why or how it answers the question. Please [include an explanation for your code](//meta.stackexchange.com/q/114762/269535), as that really helps to improve the quality of your post. Remember that you are answering the question for readers in the future, and those people might not know the reasons for your code suggestion. – Luca Kiebel Jan 07 '22 at 12:20