Files
template-app/test/workspace-pages.test.ts
2026-11-14 09:56:45 +00:00

141 lines
5.6 KiB
TypeScript

// SPDX-License-Identifier: LicenseRef-ConusAI-Template-1.0
// A module's pages above a project (F10): `/<org>/<workspace>/modules/<module>/...`. Signed out is a redirect,
// a workspace the person cannot see is the same 404 as one that does not exist, a missing permission is a 403
// before `load` runs, and `load` receives the query string. `e2e/workspace-pages.spec.ts` proves the same four
// answers over a build on a real Postgres; these say which line gives each one.
import { AppError } from "@conusai-kit/core"
import { isNotFound, isRedirect } from "@tanstack/react-router"
import { beforeEach, describe, expect, it, vi } from "vitest"
const mocks = vi.hoisted(() => ({
requestContext: vi.fn(),
callByName: vi.fn(),
setResponseHeader: vi.fn<(name: string, value: string) => void>(),
load: vi.fn(),
call: vi.fn(),
}))
vi.mock("@tanstack/react-start/server", () => ({
getRequest: () => {
throw new Error("the tests pass their request")
},
setResponseStatus: vi.fn(),
setResponseHeader: mocks.setResponseHeader,
}))
vi.mock("../apps/web/src/server/request", () => ({ requestContext: mocks.requestContext }))
vi.mock("../apps/web/src/server/app", () => ({
webRuntime: () => ({
observe: { config: { errors: false } },
selfServiceOrganizations: "members",
registry: {
modules: [{ id: "reports" }, { id: "unpaged" }],
nav: [{ module: "reports", scope: "workspace", href: "reports/a/b", label: "workspace.overview" }],
},
env: { deployment: {} },
}),
}))
vi.mock("../apps/web/src/server/tenancy-calls", () => ({ mayCreateOrganization: () => false }))
vi.mock("../apps/web/src/server/platform-theme", () => ({ organizationThemeStyle: async () => undefined }))
vi.mock("@conusai-kit/host/http", () => ({ csrfSessionId: () => "session", csrfToken: () => "token" }))
vi.mock("@app/modules/pages", () => ({
modulePages: {},
workspacePages: {
reports: async () => ({ permission: "reports.report.read", load: mocks.load }),
},
}))
const { workspacePageData } = await import("../apps/web/src/server/page")
const person = { actor: { kind: "user" } }
const signedIn = () =>
mocks.requestContext.mockResolvedValue({
caller: { call: mocks.call, callByName: mocks.callByName },
container: {},
principal: person,
user: { email: "ada@example.test", displayName: "Ada", locale: "en" },
})
const request = (): Request =>
new Request("http://app.test/a/b/modules/reports", { headers: { "x-path": "/a/b/modules/reports" } })
const params = { org: "a", workspace: "b", module: "reports" }
const refusal = (status: number): AppError =>
new AppError({ type: "/problems/refused", code: "refused", title: "Refused", status })
const thrown = (run: Promise<unknown>): Promise<unknown> =>
run.then(
() => "resolved",
(error: unknown) => error,
)
beforeEach(() => {
vi.clearAllMocks()
mocks.callByName.mockResolvedValue({ permissions: ["reports.report.read"], revision: "r1" })
mocks.load.mockResolvedValue({ title: "Reports" })
})
describe("a workspace page's loader", () => {
it("sends a signed-out visitor to sign in, before reading anything", async () => {
mocks.requestContext.mockResolvedValue({ caller: {}, container: {}, principal: null })
expect(isRedirect(await thrown(workspacePageData(params, request())))).toBe(true)
expect(mocks.load).not.toHaveBeenCalled()
})
it("hands `load` the workspace, the path below the module and the query string", async () => {
signedIn()
const data = await workspacePageData({ ...params, _splat: "a/b", search: "?q=one&tab=two&q=three" }, request())
expect(mocks.load).toHaveBeenCalledWith(
expect.objectContaining({
slugs: { org: "a", workspace: "b" },
segments: ["a", "b"],
search: { q: "three", tab: "two" },
}),
)
expect(data).toMatchObject({
module: "reports",
title: "Workspace overview · Example App",
segments: ["a", "b"],
locale: "en",
data: { title: "Reports" },
scope: { org: "a", workspace: "b" },
seed: { permissions: ["reports.report.read"] },
})
})
it("answers 404 for a workspace of another organization, before `load`", async () => {
signedIn()
mocks.callByName.mockRejectedValue(refusal(404))
expect(isNotFound(await thrown(workspacePageData(params, request())))).toBe(true)
expect(mocks.load).not.toHaveBeenCalled()
})
it("answers 403 to a member without the page's permission, before `load`", async () => {
signedIn()
mocks.callByName.mockResolvedValue({ permissions: [], revision: "r1" })
const error = (await thrown(workspacePageData(params, request()))) as { data?: unknown }
expect(isNotFound(error)).toBe(true)
expect(error.data).toEqual({ forbidden: true })
expect(mocks.setResponseHeader).toHaveBeenCalledWith("x-conusai-page-status", "403")
expect(mocks.load).not.toHaveBeenCalled()
})
it.each([
["no registered module", { ...params, module: "nothing" }],
["a registered module with no workspace page", { ...params, module: "unpaged" }],
["a name every object has", { ...params, module: "constructor" }],
])("answers 404 for %s", async (_name, wanted) => {
signedIn()
expect(isNotFound(await thrown(workspacePageData(wanted, request())))).toBe(true)
expect(mocks.load).not.toHaveBeenCalled()
})
it.each([
[404, undefined],
[403, { forbidden: true }],
])("turns the module's own %i into that page", async (status, expected) => {
signedIn()
mocks.load.mockRejectedValue(refusal(status))
const error = (await thrown(workspacePageData(params, request()))) as { data?: unknown }
expect(isNotFound(error)).toBe(true)
expect(error.data).toEqual(expected)
})
})