TypeScript + Playwright/Puppeteer测试标签通信

huangapple go评论90阅读模式
英文:

TypeScript + Playwright/Puppeteer Test Tab communication

问题

以下是您提供的文本的中文翻译:

我尝试使用 Playwright 来测试我的演示 broadcastChannel 模块,但我无法将 Class 传递给 evaluate,或者将类实例注入到 Window。以下是最小可复现的示例。

utils.ts

export class Utils {
  public static formatTime() { return new Date().toLocaleString(); }
  public static generateRandomStr() { /* 生成随机字符串的逻辑 */ }
}

index.ts

import { Utils } from "./utils";

export class Channel<T extends string> {
  private uniqueID = Utils.generateRandomStr();
  private channel: BroadcastChannel;

  constructor(name: T) {
    this.channel = new BroadcastChannel(name);
    this.onMessage();
  }

  private onMessage() {
    this.channel.addEventListener("message", e => {
      console.log(e.data);
    });
  }

  public sendMsg(msg: any) {
    this.channel.postMessage({
      id: this.uniqueID,
      time: Utils.formatTime(),
      msg,
    });
  }

  public sendHelloWorld() {
    this.sendMsg("Hello World");
  }

  public sendByeWorld() {
    this.sendMsg("Bye World");
  }
}

根据 @Sheldon Oliveira 的回答和 这个 Stack Overflow 回答,我了解到没有简单的方法来将 Class 或类实例传递给 evaluate。也许我选择了错误的方法来测试标签间通信,我希望有更好的解决方案。

以下是我尝试的两种不同方法。

import { test, expect, Page } from '@playwright/test';
import { Channel } from '../src/index';

let page1: Page, page2: Page;
test.beforeEach(async ({ context }) => {
  [page1, page2] = await Promise.all([context.newPage(), context.newPage()]);
  await Promise.all([page1.goto("https://www.google.com"), page2.goto("https://www.google.com")]);
});

test.afterEach(async ({ context }) => {
  await Promise.all([page1.close(), page2.close()]);
  await context.close();
});

test('sendHelloWorld', async () => {
  await Promise.all([
    await page1.evaluate((strClass) => {
      eval("window.Channel = " + strClass);
      window.channel = new window.Channel();
      //                     ^ReferenceError: _Utils is not defined
    }, Channel.toString()),
    await page2.evaluate((strClass) => {
      eval("window.Channel = " + strClass);
      window.channel = new window.Channel();
      //                     ^ReferenceError: _Utils is not defined
    }, Channel.toString()),
  ]);

  const msgPromise = page2.waitForEvent("console");
  await page1.evaluate(() => {
    window.channel.sendHelloWorld();
  });
  const msg = await msgPromise;
  expect((await msg.args()[0].jsonValue()).msg).toBe("Hello World");
});

test('sendByeWorld', async () => {
  await Promise.all([
    await page1.evaluate((channel) => {
      window.channel = channel;
      //             ^ no prototype method sendByeWorld
    }, new Channel()),
    await page2.evaluate((channel) => {
      window.channel = channel;
      //             ^ no prototype method sendByeWorld
    }, new Channel()),
  ]);

  const msgPromise = page2.waitForEvent("console");
  await page1.evaluate(() => {
    window.channel.sendHelloWorld();
  });
  const msg = await msgPromise;
  expect((await msg.args()[0].jsonValue()).msg).toBe("Bye World");
});

期望的控制台技巧来自于 Playwright 文档

我还添加了 "puppeteer" 标签,因为我认为无论使用哪个框架,问题都是相同的。

英文:

I tried to test my demo broadcastChannel module with playwright. But I cannot pass Class to evaluate or inject class instance to Window. Below is the minimal reproducible example.

utils.ts

export class Utils {
  public static formatTime() { return new Date().toLocaleString(); }
  public static generateRandomStr() { /* generate random str logic*/ }
}

index.ts

import { Utils } from &quot;./utils&quot;;

export class Channel&lt;T extends string&gt; {
  private uniqueID = Utils.generateRandomStr();
  private channel: BroadcastChannel;

  constructor(name: T) {
    this.channel = new BroadcastChannel(name);
    this.onMessage();
  }

  private onMessage() {
    this.channel.addEventListener(&quot;message&quot;, e =&gt; {
      console.log(e.data);
    });
  }

  public sendMsg(msg: any) {
    this.channel.postMessage({
      id: this.uniqueID,
      time: Utils.formatTime(),
      msg,
    });
  }

  public sendHelloWorld() {
    this.sendMsg(&quot;Hello World&quot;);
  }

  public sendByeWorld() {
    this.sendMsg(&quot;Bye World&quot;);
  }
}

According to @Sheldon Oliveira's answer and this stackoverflow answer, I got that there's no easy way to pass Class or class instance to evaluate. Maybe I went for the wrong way to test tab communication, I hope there's a better solution.

Below are the two different approches I tried.

import { test, expect, Page } from &#39;@playwright/test&#39;;
import { Channel } from &#39;../src/index&#39;;

let page1: Page, page2: Page;
test.beforeEach(async ({ context }) =&gt; {
  [page1, page2] = await Promise.all([context.newPage(), context.newPage()]);
  await Promise.all([page1.goto(&quot;https://www.google.com&quot;), page2.goto(&quot;https://www.google.com&quot;)]);
});

test.afterEach(async ({ context }) =&gt; {
  await Promise.all([page1.close(), page2.close()]);
  await context.close();
});

test(&#39;sendHelloWorld&#39;, async () =&gt; {
  await Promise.all([
    await page1.evaluate((strClass) =&gt; {
      eval(&quot;window.Channel = &quot; + strClass);
      window.channel = new window.Channel();
      //                     ^ReferenceError: _Utils is not defined
    }, Channel.toString()),
    await page2.evaluate((strClass) =&gt; {
      eval(&quot;window.Channel = &quot; + strClass);
      window.channel = new window.Channel();
      //                     ^ReferenceError: _Utils is not defined
    }, Channel.toString()),
  ]);

  const msgPromise = page2.waitForEvent(&quot;console&quot;);
  await page1.evaluate(() =&gt; {
    window.channel.sendHelloWorld();
  });
  const msg = await msgPromise;
  expect((await msg.args()[0].jsonValue()).msg).toBe(&quot;Hello World&quot;);
});

test(&#39;sendByeWorld&#39;, async () =&gt; {
  await Promise.all([
    await page1.evaluate((channel) =&gt; {
      window.channel = channel;
      //             ^ no prototype method sendByeWorld
    }, new Channel()),
    await page2.evaluate((channel) =&gt; {
      window.channel = channel;
      //             ^ no prototype method sendByeWorld
    }, new Channel()),
  ]);

  const msgPromise = page2.waitForEvent(&quot;console&quot;);
  await page1.evaluate(() =&gt; {
    window.channel.sendHelloWorld();
  });
  const msg = await msgPromise;
  expect((await msg.args()[0].jsonValue()).msg).toBe(&quot;Bye World&quot;);
});

The expect console trick is from playwright documentation.

I also added puppeteer tag, cause I think the problem is the same regardless of the framework.

答案1

得分: 1

TLDR; 你不能将类作为评估的参数使用。

文档:
此参数可以是可序列化的值和JSHandle或ElementHandle实例的混合。

一个类不是可序列化的值。

英文:

TLDR; you can not use a class as a parameter for evaluate.

docs:
This argument can be a mix of Serializable values and JSHandle or ElementHandle instances.

A class is not a serializable value.

huangapple
  • 本文由 发表于 2023年6月6日 00:53:01
  • 转载请务必保留本文链接:https://go.coder-hub.com/76408515.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定