80 lines
4.8 KiB
TypeScript
80 lines
4.8 KiB
TypeScript
// SPDX-License-Identifier: LicenseRef-ConusAI-Template-1.0
|
|
// Real Postgres and the app worker's invitation relay; provider services remain simulated.
|
|
import { expect, test } from "@playwright/test"
|
|
import { call, callRaw, makeOrganization, signIn, unique, violations } from "./support/app"
|
|
|
|
type Inbox = { items: Array<{ id: string; code: string; readAt: string | null }>; unreadCount: number }
|
|
|
|
test("invitations reach only their recipients and the shared inbox marks them read", async ({
|
|
page,
|
|
browser,
|
|
}, testInfo) => {
|
|
await page.setViewportSize({ width: 1280, height: 900 })
|
|
await signIn(page, `${unique("inbox-owner")}@example.test`)
|
|
const { org } = await makeOrganization(page)
|
|
const email = `${unique("inbox-viewer")}@example.test`
|
|
const invitation = await call<{ token: string }>(page, "tenancy.member.invite", { org, email, role: "viewer" })
|
|
const context = await browser.newContext({ baseURL: new URL(page.url()).origin })
|
|
try {
|
|
const member = await context.newPage()
|
|
await signIn(member, email)
|
|
await call(member, "tenancy.invitation.accept", { org, token: invitation.token })
|
|
await expect
|
|
.poll(async () => (await call<Inbox>(page, "notifications.inbox.list", { org })).items.map((item) => item.code))
|
|
.toContain("invitation_accepted")
|
|
await expect
|
|
.poll(async () => (await call<Inbox>(member, "notifications.inbox.list", { org })).items.map((item) => item.code))
|
|
.toContain("member_joined")
|
|
const ownerInbox = await call<Inbox>(page, "notifications.inbox.list", { org })
|
|
const accepted = ownerInbox.items.find((item) => item.code === "invitation_accepted")
|
|
if (!accepted) throw new Error("the invitation relay did not deliver its notification")
|
|
const csrf = await member.locator('meta[name="csrf-token"]').getAttribute("content")
|
|
const denial = await callRaw(member.request, csrf ?? "", "notifications.inbox.markRead", { org, id: accepted.id })
|
|
expect(denial.status()).toBe(404)
|
|
expect(
|
|
(await call<Inbox>(page, "notifications.inbox.list", { org })).items.find((item) => item.id === accepted.id)
|
|
?.readAt,
|
|
).toBeNull()
|
|
await page.goto(`/${org}`)
|
|
await page.locator('[data-guide="frame.notifications"]').click()
|
|
const panel = page.locator('[data-slot="side-panel"]')
|
|
await expect(panel.getByText("An invitation was accepted", { exact: true })).toBeVisible()
|
|
await panel.getByRole("button", { name: "Mark as read: An invitation was accepted", exact: true }).click()
|
|
await expect
|
|
.poll(
|
|
async () =>
|
|
(await call<Inbox>(page, "notifications.inbox.list", { org })).items.find((item) => item.id === accepted.id)
|
|
?.readAt,
|
|
)
|
|
.not.toBeNull()
|
|
const bar = await page.locator('[data-slot="frame-bar"]').boundingBox()
|
|
const head = await panel.locator(".kit-side-panel-head").boundingBox()
|
|
expect(head?.y ?? -1).toBeGreaterThanOrEqual((bar?.y ?? 0) + (bar?.height ?? 0))
|
|
await page.getByRole("button", { name: "Reject analytics and replay", exact: true }).click({ trial: true })
|
|
expect(await violations(page)).toEqual([])
|
|
await page.screenshot({ path: testInfo.outputPath("notification-invitation-desktop.png") })
|
|
await panel.getByRole("button", { name: "Close", exact: true }).click()
|
|
await expect(page.locator('[data-guide="frame.notifications"]')).toBeFocused()
|
|
await page.setViewportSize({ width: 768, height: 900 })
|
|
await page.locator('[data-guide="frame.notifications"]').click()
|
|
const overlay = page.locator('[data-slot="side-panel"]')
|
|
// The entrance animation must not make the document wider at the overlay breakpoint.
|
|
expect(await page.evaluate(() => document.documentElement.scrollWidth <= innerWidth)).toBe(true)
|
|
const overlayHead = await overlay.locator(".kit-side-panel-head").boundingBox()
|
|
const overlayBar = await page.locator('[data-slot="frame-bar"]').boundingBox()
|
|
expect(overlayHead?.y ?? -1).toBeGreaterThanOrEqual((overlayBar?.y ?? 0) + (overlayBar?.height ?? 0))
|
|
await page.getByRole("button", { name: "Reject analytics and replay", exact: true }).click({ trial: true })
|
|
await page.screenshot({ path: testInfo.outputPath("notification-invitation-tablet.png") })
|
|
await overlay.getByRole("button", { name: "Close", exact: true }).click()
|
|
await page.setViewportSize({ width: 360, height: 800 })
|
|
await page.locator('[data-guide="frame.notifications"]').click()
|
|
const sheet = page.getByRole("dialog")
|
|
await expect(sheet.getByRole("button", { name: "Close", exact: true })).toBeVisible()
|
|
expect(await page.evaluate(() => document.documentElement.scrollWidth <= innerWidth)).toBe(true)
|
|
await page.screenshot({ path: testInfo.outputPath("notification-invitation-mobile.png") })
|
|
await sheet.getByRole("button", { name: "Close", exact: true }).click()
|
|
} finally {
|
|
await context.close()
|
|
}
|
|
})
|