Some checks are pending
CI / Lint bridge (Biome) (push) Waiting to run
CI / Type-check bridge (push) Blocked by required conditions
CI / Tests unit bridge (push) Blocked by required conditions
CI / Tests integration bridge (push) Blocked by required conditions
CI / Security scan (push) Waiting to run
CI / Docker build + healthcheck (push) Blocked by required conditions
E2E Playwright / Playwright e2e (chromium) (push) Waiting to run
7 scenarios covering the full bridge+DocAdenice+Baserow chain: auth login, database-view insert, inline edit persistence, SSE realtime update (no reload), RBAC write-denied, kanban drag-drop, calendar reschedule. Includes docker-compose.e2e.yml (Postgres+Redis+Baserow+bridge+DocAdenice), playwright.config.ts (3 projects: chromium/firefox/webkit), auth+baserow+cleanup fixtures, global setup (API login + Baserow seed), and GitHub Actions e2e.yml. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
104 lines
3.8 KiB
TypeScript
104 lines
3.8 KiB
TypeScript
/**
|
|
* Scenario: database-view-realtime-sse
|
|
*
|
|
* Verifies the full SSE end-to-end flow:
|
|
* 1. Browser A has a page open with a database-view (grid).
|
|
* 2. An external API call (via Baserow API + bridge webhook) modifies a row.
|
|
* 3. Browser A sees the updated value without any manual reload.
|
|
*
|
|
* This test exercises the full chain:
|
|
* Baserow webhook -> bridge handler -> Redis Streams -> SSE endpoint
|
|
* -> useDatabaseRealtimeUpdates hook -> React Query invalidation -> re-render.
|
|
*
|
|
* SSE propagation timing: the bridge polls Redis every 100ms (default). We
|
|
* allow up to 15 seconds for the update to appear — well above the 99th
|
|
* percentile for local network.
|
|
*/
|
|
|
|
import { test, expect } from "@playwright/test";
|
|
import * as fs from "fs";
|
|
import * as path from "path";
|
|
import type { BaserowSeed } from "../fixtures/baserow";
|
|
import { updateRowViaBaserowApi } from "../fixtures/baserow";
|
|
|
|
const BASE_URL = process.env.E2E_DOCMOST_URL ?? "http://localhost:5173";
|
|
const SEED_FILE = path.resolve(__dirname, "../.auth/baserow-seed.json");
|
|
|
|
const SSE_TEST_VALUE = "SSE Updated Task";
|
|
|
|
test.describe("database-view realtime SSE", () => {
|
|
let seed: BaserowSeed;
|
|
|
|
test.beforeAll(() => {
|
|
if (!fs.existsSync(SEED_FILE)) {
|
|
throw new Error(`Seed file not found at ${SEED_FILE}.`);
|
|
}
|
|
seed = JSON.parse(fs.readFileSync(SEED_FILE, "utf-8")) as BaserowSeed;
|
|
});
|
|
|
|
test(
|
|
"row update via Baserow API propagates to open browser via SSE without reload",
|
|
async ({ page, request }) => {
|
|
// Open a page with a database-view node (depends on insert spec having run).
|
|
await page.goto(BASE_URL);
|
|
|
|
const tableRenderer = page
|
|
.getByTestId("table-renderer")
|
|
.or(page.locator("[data-node-type='database-view'] table"))
|
|
.first();
|
|
|
|
const rendererVisible = await tableRenderer
|
|
.isVisible({ timeout: 5_000 })
|
|
.catch(() => false);
|
|
|
|
if (!rendererVisible) {
|
|
test.skip(
|
|
true,
|
|
"No database-view page found. Run database-view-insert spec first.",
|
|
);
|
|
return;
|
|
}
|
|
|
|
// Confirm the initial value is present.
|
|
await expect(page.getByText("Task Beta")).toBeVisible({ timeout: 10_000 });
|
|
|
|
// Verify the SSE connection is established by checking the bridge events endpoint.
|
|
// We do this by intercepting XHR/fetch — but SSE uses EventSource natively.
|
|
// Instead we monitor the page console for the SSE open event log.
|
|
// The bridge logs "SSE client connected" on open — this doesn't leak to the client.
|
|
// We rely purely on the observable DOM change after the Baserow API call.
|
|
|
|
// Wait a moment to ensure the SSE connection is established and stable.
|
|
await page.waitForTimeout(2_000);
|
|
|
|
// Modify row 2 ("Task Beta") via the Baserow API directly.
|
|
// This bypasses the bridge write path — Baserow will emit a webhook to the bridge,
|
|
// which publishes to Redis Streams, which the SSE connection picks up.
|
|
const rowIdToUpdate = seed.rowIds[1]; // Task Beta
|
|
|
|
await updateRowViaBaserowApi(request, seed.token, seed.tableId, rowIdToUpdate, {
|
|
[seed.primaryFieldName]: SSE_TEST_VALUE,
|
|
});
|
|
|
|
// The browser should receive the SSE event and invalidate the React Query cache,
|
|
// causing the table to re-render with the updated value — WITHOUT a page reload.
|
|
await expect(page.getByText(SSE_TEST_VALUE)).toBeVisible({
|
|
timeout: 15_000,
|
|
});
|
|
|
|
// The old value "Task Beta" must be gone from the rendered table.
|
|
await expect(page.getByRole("cell", { name: "Task Beta" })).toBeHidden({
|
|
timeout: 5_000,
|
|
});
|
|
},
|
|
);
|
|
|
|
test.afterAll(async ({ request }) => {
|
|
// Restore "Task Beta".
|
|
if (!seed?.rowIds[1]) return;
|
|
|
|
await updateRowViaBaserowApi(request, seed.token, seed.tableId, seed.rowIds[1], {
|
|
[seed.primaryFieldName]: "Task Beta",
|
|
});
|
|
});
|
|
});
|