import { test, expect } from "./fixtures"; import { freshAccount, loginViaUi, pickBottle } from "./helpers"; test.describe("safety and resilience", () => { test("reports and reloads chat history after an offline reconnect", async ({ browser, }) => { const aAccount = freshAccount("135"); const bAccount = freshAccount("134"); const ctxA = await browser.newContext(); const ctxB = await browser.newContext(); const aPage = await ctxA.newPage(); const bPage = await ctxB.newPage(); await loginViaUi(aPage, aAccount); await loginViaUi(bPage, bAccount); // A 投瓶,B 捞瓶并回复建立会话 await aPage.goto("/throw"); await aPage .getByPlaceholder("写下你想让陌生人看到的话…") .fill(`断线恢复瓶子 ${Date.now()}`); await aPage.getByRole("button", { name: /扔进海里/ }).click(); await expect(aPage.getByText(/瓶子已进入审核/)).toBeVisible(); await bPage.goto("/pick"); await pickBottle(bPage); await bPage.locator(".bottle-card textarea").fill("离线前的第一条消息"); await bPage.getByRole("button", { name: /回复并建立会话/ }).click(); await expect(bPage).toHaveURL(/\/conversations\//, { timeout: 15_000 }); // A 进入同一会话 await aPage.goto("/conversations"); await aPage.locator(".conversation").first().click(); await expect(aPage.getByText("离线前的第一条消息")).toBeVisible({ timeout: 15_000, }); // A 离线前再互发一条 await bPage.locator(".composer textarea").fill("离线前的第二条消息"); await bPage.getByRole("button", { name: /发送消息/ }).click(); await expect(aPage.getByText("离线前的第二条消息")).toBeVisible({ timeout: 15_000, }); // 模拟 B 断线(离线),A 发送新消息 await ctxB.setOffline(true); await aPage.locator(".composer textarea").fill("断线期间的第三条消息"); await aPage.getByRole("button", { name: /发送消息/ }).click(); await expect(aPage.getByText("断线期间的第三条消息")).toBeVisible(); // B 恢复在线,重新拉取历史应补全缺失消息 await ctxB.setOffline(false); await bPage.reload(); await expect(bPage.getByText("断线期间的第三条消息")).toBeVisible({ timeout: 20_000, }); await ctxA.close(); await ctxB.close(); }); test("blocks a peer and both sides can no longer send messages", async ({ browser, }) => { const aAccount = freshAccount("138"); const bAccount = freshAccount("139"); const ctxA = await browser.newContext(); const ctxB = await browser.newContext(); const aPage = await ctxA.newPage(); const bPage = await ctxB.newPage(); await loginViaUi(aPage, aAccount); await loginViaUi(bPage, bAccount); await aPage.goto("/throw"); await aPage .getByPlaceholder("写下你想让陌生人看到的话…") .fill(`拉黑测试瓶子 ${Date.now()}`); await aPage.getByRole("button", { name: /扔进海里/ }).click(); await expect(aPage.getByText(/瓶子已进入审核/)).toBeVisible(); await bPage.goto("/pick"); await pickBottle(bPage); await bPage.locator(".bottle-card textarea").fill("建立会话,稍后拉黑"); await bPage.getByRole("button", { name: /回复并建立会话/ }).click(); await expect(bPage).toHaveURL(/\/conversations\//, { timeout: 15_000 }); // A 进入会话并拉黑 await aPage.goto("/conversations"); await aPage.locator(".conversation").first().click(); await expect(aPage.getByText("建立会话,稍后拉黑")).toBeVisible({ timeout: 15_000, }); aPage.once("dialog", (dialog) => void dialog.accept()); await aPage.getByRole("button", { name: /拉黑对方/ }).click(); await expect(aPage.getByText(/已拉黑对方/)).toBeVisible(); // B 发消息应失败 await bPage.locator(".composer textarea").fill("你还在吗?"); await bPage.getByRole("button", { name: /发送消息/ }).click(); await expect(bPage.locator(".bubble.failed").first()).toBeVisible({ timeout: 15_000, }); await ctxA.close(); await ctxB.close(); }); });