Skip to content
File

Blob: tests/e2e/specs/15-offline-doc-sync-navigation.spec.ts

typescript112 lines
1import type { Page as PlaywrightPage } from "@playwright/test";
2import { test, expect, createTestPage, expectNoChangeFor } from "../fixtures/bland-test";
3 
4function collectDocSyncErrors(page: PlaywrightPage): string[] {
5 const errors: string[] = [];
6 page.on("console", (message) => {
7 const text = message.text();
8 if (message.type() === "error" && text.includes("/parties/doc-sync/")) {
9 errors.push(text);
10 }
11 });
12 return errors;
13}
14 
15function collectSnapshotRequests(page: PlaywrightPage, pageId: string): string[] {
16 const requests: string[] = [];
17 page.on("request", (request) => {
18 const url = new URL(request.url());
19 if (url.pathname.endsWith(`/pages/${pageId}/snapshot`)) {
20 requests.push(request.url());
21 }
22 });
23 return requests;
24}
25 
26test.describe("offline doc sync navigation", () => {
27 test("authenticated offline navigation parks doc sync on the destination page", async ({
28 authenticatedPage: { page, accessToken },
29 e2eWorkspace,
30 }) => {
31 const [pageA, pageB] = await Promise.all([
32 createTestPage(page, accessToken, "Offline Source", e2eWorkspace),
33 createTestPage(page, accessToken, "Offline Target", e2eWorkspace),
34 ]);
35 
36 const docSyncErrors = collectDocSyncErrors(page);
37 
38 await page.goto(`/${pageA.workspaceSlug}/${pageA.pageId}`);
39 await page.locator(".tiptap").waitFor({ timeout: 30_000 });
40 await expect(page.getByText("Connected")).toBeVisible({ timeout: 15_000 });
41 
42 await page.goto(`/${pageB.workspaceSlug}/${pageB.pageId}`);
43 await page.locator(".tiptap").waitFor({ timeout: 30_000 });
44 await expect(page.getByText("Connected")).toBeVisible({ timeout: 15_000 });
45 
46 await page.goto(`/${pageA.workspaceSlug}/${pageA.pageId}`);
47 await page.locator(".tiptap").waitFor({ timeout: 30_000 });
48 await expect(page.getByText("Connected")).toBeVisible({ timeout: 15_000 });
49 
50 await page.context().setOffline(true);
51 await expect(page.getByText("Offline. Your edits are saved locally and will sync when you're back.")).toBeVisible({
52 timeout: 10_000,
53 });
54 
55 const errorsBeforeNavigation = docSyncErrors.length;
56 const sidebarLink = page.locator("aside nav a").filter({ hasText: "Offline Target" }).first();
57 await sidebarLink.click();
58 
59 await page.waitForURL(`**/${pageB.pageId}`, { timeout: 15_000 });
60 await page.locator(".tiptap").waitFor({ timeout: 30_000 });
61 await expect(page.getByText("Offline", { exact: true })).toBeVisible({ timeout: 10_000 });
62 
63 await expectNoChangeFor(() => docSyncErrors.length, 2_000);
64 expect(docSyncErrors.length).toBe(errorsBeforeNavigation);
65 
66 await page.context().setOffline(false);
67 });
68 
69 test("authenticated offline navigation to an uncached page stays unavailable without snapshot bootstrap attempts", async ({
70 authenticatedPage: { page, accessToken },
71 e2eWorkspace,
72 }) => {
73 const [pageA, pageB] = await Promise.all([
74 createTestPage(page, accessToken, "Offline Cached Source", e2eWorkspace),
75 createTestPage(page, accessToken, "Offline Uncached Target", e2eWorkspace),
76 ]);
77 
78 const docSyncErrors = collectDocSyncErrors(page);
79 const snapshotRequests = collectSnapshotRequests(page, pageB.pageId);
80 
81 await page.goto(`/${pageA.workspaceSlug}/${pageA.pageId}`);
82 await page.locator(".tiptap").waitFor({ timeout: 30_000 });
83 await expect(page.getByText("Connected")).toBeVisible({ timeout: 15_000 });
84 
85 await page.context().setOffline(true);
86 await expect(page.getByText("Offline. Your edits are saved locally and will sync when you're back.")).toBeVisible({
87 timeout: 10_000,
88 });
89 
90 const snapshotRequestsBeforeNavigation = snapshotRequests.length;
91 const errorsBeforeNavigation = docSyncErrors.length;
92 const sidebarLink = page.locator("aside nav a").filter({ hasText: "Offline Uncached Target" }).first();
93 await sidebarLink.click();
94 
95 await page.waitForURL(`**/${pageB.pageId}`, { timeout: 15_000 });
96 await expect(page.getByText("This page isn't available offline yet.")).toBeVisible({ timeout: 10_000 });
97 await expect(page.locator(".tiptap")).toHaveCount(0);
98 
99 await expectNoChangeFor(
100 () => ({
101 docSyncErrors: docSyncErrors.length,
102 snapshotRequests: snapshotRequests.length,
103 }),
104 2_000,
105 );
106 expect(snapshotRequests.length).toBe(snapshotRequestsBeforeNavigation);
107 expect(docSyncErrors.length).toBe(errorsBeforeNavigation);
108 
109 await page.context().setOffline(false);
110 });
111});