Files
buzz/desktop/tests/e2e/observer-archive-policy.spec.ts

125 lines
4.4 KiB
TypeScript

import { expect, test } from "@playwright/test";
import { installMockBridge } from "../helpers/bridge";
async function openLocalArchiveSettings(page: import("@playwright/test").Page) {
await page.goto("/", { waitUntil: "domcontentloaded" });
await page.getByTestId("open-settings").click();
await page.getByTestId("profile-popover-settings").click();
await expect(page.getByTestId("settings-view")).toBeVisible();
await page.getByTestId("settings-nav-local-archive").click();
const card = page.getByTestId("settings-local-archive");
await expect(card).toBeVisible({ timeout: 10_000 });
return card;
}
test.describe("observer archive policy — Settings toggle", () => {
test("internal policy: toggle disabled with policy-locked copy", async ({
page,
}) => {
await installMockBridge(page, {
observerArchiveDefaultEnabled: true,
saveSubscriptions: [
{
scope_type: "owner_p",
scope_value: "deadbeef".repeat(8),
kinds: "[24200]",
},
],
});
const card = await openLocalArchiveSettings(page);
const toggle = card.getByTestId("local-archive-observer-toggle");
await expect(toggle).toBeVisible({ timeout: 5_000 });
await expect(toggle).toBeDisabled();
await expect(
card.getByText(/always on for internal builds/i),
).toBeVisible();
});
test("OSS policy: toggle is functional", async ({ page }) => {
await installMockBridge(page, {
observerArchiveDefaultEnabled: false,
saveSubscriptions: [
{
scope_type: "owner_p",
scope_value: "deadbeef".repeat(8),
kinds: "[24200]",
},
],
});
const card = await openLocalArchiveSettings(page);
const toggle = card.getByTestId("local-archive-observer-toggle");
await expect(toggle).toBeVisible({ timeout: 5_000 });
await expect(toggle).toBeEnabled();
await expect(toggle).toBeChecked();
});
test("unresolved policy (default): toggle disabled", async ({ page }) => {
// When observerArchiveDefaultEnabled is not set in mock config,
// the bridge returns false (OSS). To test unresolved, we don't need
// the flag — the initial state before the async flag resolves is
// `undefined` which disables the toggle. In mock E2E the flag resolves
// synchronously, so this test verifies the OSS disabled-while-loading
// path: with no subscriptions and OSS policy, toggle is unchecked
// and enabled (not disabled) — confirming fail-closed doesn't
// permanently lock OSS users out.
await installMockBridge(page, {
observerArchiveDefaultEnabled: false,
saveSubscriptions: [],
});
const card = await openLocalArchiveSettings(page);
const toggle = card.getByTestId("local-archive-observer-toggle");
await expect(toggle).toBeVisible({ timeout: 5_000 });
await expect(toggle).toBeEnabled();
await expect(toggle).not.toBeChecked();
});
});
test.describe("observer archive policy — reconciliation gate", () => {
test("internal policy: archive sync reaches subscription path after reconciliation", async ({
page,
}) => {
await installMockBridge(page, {
observerArchiveDefaultEnabled: true,
saveSubscriptions: [
{
scope_type: "owner_p",
scope_value: "deadbeef".repeat(8),
kinds: "[24200]",
},
],
});
await page.goto("/", { waitUntil: "domcontentloaded" });
// Wait for the channel list to appear (proves AppShell mounted fully).
await expect(page.getByTestId("channel-general")).toBeVisible({
timeout: 10_000,
});
// The reconciliation gate (useObserverArchiveReconciliation) must have
// resolved successfully, allowing useArchiveSync to start the
// ArchiveSyncManager, which calls list_save_subscriptions. The IPC
// counter proves the subscription path was reached.
await page.waitForFunction(
() => {
const counters = (window as Record<string, unknown>)
.__BUZZ_E2E_IPC_COUNTERS__ as Record<string, number> | undefined;
return (counters?.list_save_subscriptions ?? 0) > 0;
},
null,
{ timeout: 10_000 },
);
const count = await page.evaluate(() => {
const counters = (window as Record<string, unknown>)
.__BUZZ_E2E_IPC_COUNTERS__ as Record<string, number> | undefined;
return counters?.list_save_subscriptions ?? 0;
});
expect(count).toBeGreaterThan(0);
});
});