英文:
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 "./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");
}
}
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 '@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");
});
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.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论