ConusAI vendor v0.1.0-dev.20261027+9b1131fb
ci / gate (push) Waiting to run

This commit is contained in:
ConusAI vendor v0.1.0-dev.20261027+9b1131fb
2026-10-27 18:05:17 +00:00
commit 3102273ec7
538 changed files with 80203 additions and 0 deletions
+111
View File
@@ -0,0 +1,111 @@
// SPDX-License-Identifier: LicenseRef-ConusAI-Template-1.0
// The tenant brand in a browser (plan item AT2.6): an administrator chooses colours and a logo and a
// member sees them, legibly in both themes, from the first byte; a member who is not an administrator
// cannot change them; and a logo that could run script is refused.
import { expect, test } from "@playwright/test"
import { call, callRaw, makeOrganization, signIn, unique, violations } from "./support/app"
const channels = (color: string): [number, number, number] => {
const match = /rgba?\((\d+),\s*(\d+),\s*(\d+)/.exec(color)
if (!match) throw new Error(`not an rgb colour: ${color}`)
return [Number(match[1]), Number(match[2]), Number(match[3])]
}
const luminance = ([r, g, b]: [number, number, number]): number => {
const lin = (v: number) => (v / 255 <= 0.03928 ? v / 255 / 12.92 : ((v / 255 + 0.055) / 1.055) ** 2.4)
return 0.2126 * lin(r) + 0.7152 * lin(g) + 0.0722 * lin(b)
}
const contrast = (a: string, b: string): number => {
const [hi, lo] = [luminance(channels(a)), luminance(channels(b))].sort((x, y) => y - x) as [number, number]
return (hi + 0.05) / (lo + 0.05)
}
test("a tenant brand applies in both themes, from the first paint", async ({ page }) => {
await signIn(page, `${unique("admin")}@example.test`)
const { org } = await makeOrganization(page, "brand")
await page.goto(`/${org}/branding`)
await expect(page.getByRole("heading", { level: 1, name: "Brand" })).toBeVisible()
await page.getByLabel("Display name").fill("Acme Review")
await page.getByLabel("Primary colour").fill("#1a1a8c")
await page.getByLabel("Accent colour").fill("#f4c430")
// the preview answers before anything is saved, and says what the contrast is
await expect(page.getByText(/Text on the colour: \d+\.\d to 1/).first()).toBeVisible()
expect(await violations(page)).toEqual([])
// Saving reloads the page (brand-form.tsx); wait for that reload, so a later navigation of the
// test's own never races it ("interrupted by another navigation").
const reloaded = page.waitForEvent("load")
await page.getByRole("button", { name: "Save the brand" }).click()
await reloaded
await expect(page.getByLabel("Primary colour")).toHaveValue("#1a1a8c")
// server-rendered: the raw HTML of the organization page already carries the brand layer
const html = await (await page.request.get(`/${org}`)).text()
expect(html).toMatch(/data-tenant-brand="#1a1a8c"[^>]*style="[^"]*--brand-accent:#1a1a8c/)
expect(html).toContain("Acme Review")
// No vendor name in what the tenant's users get. Asset URLs under /_next/ are left out: a dev
// server names its chunks after the package they start in (".../_next/static/chunks/..._%40conusai-kit_brand_...js"),
// while a production build hashes them.
expect(html.replace(/\/_next\/[^"'\s\\]+/g, "").toLowerCase()).not.toContain("conusai")
// the primary action's fill is the brand's colour on both themes, and what is written as text on it reads
await page.goto(`/${org}/branding`)
const panels = page.locator(".brand-preview-panel")
await expect(panels).toHaveCount(2)
for (const [index, theme] of ["dark", "light"].entries()) {
const panel = panels.nth(index)
const action = panel.locator(".brand-preview-action")
const fill = await action.evaluate((el) => getComputedStyle(el).backgroundColor)
const onFill = await action.evaluate((el) => getComputedStyle(el).color)
expect(channels(fill), theme).toEqual([0x1a, 0x1a, 0x8c])
expect(contrast(fill, onFill), `${theme} text on the fill`).toBeGreaterThanOrEqual(4.5)
const link = panel.locator(".brand-preview-link")
const ink = await link.evaluate((el) => getComputedStyle(el).color)
const page_ = await panel.evaluate((el) => getComputedStyle(el).backgroundColor)
expect(contrast(ink, page_), `${theme} link on the page`).toBeGreaterThanOrEqual(4.5)
}
// one element carries the theme class: the light preview, and nothing else on the page
expect(await page.evaluate(() => document.querySelectorAll(".light").length)).toBeLessThanOrEqual(1)
})
test("a member who is not an administrator reads the brand and cannot change it", async ({ page, browser }) => {
const adminEmail = `${unique("admin")}@example.test`
await signIn(page, adminEmail)
const { org } = await makeOrganization(page, "perm")
const memberEmail = `${unique("member")}@example.test`
const invite = await call<{ token: string }>(page, "tenancy.member.invite", {
org,
email: memberEmail,
role: "member",
})
const member = await browser.newPage()
await signIn(member, memberEmail)
await call(member, "tenancy.invitation.accept", { org, token: invite.token })
await member.goto(`/${org}/branding`)
await expect(member.getByText("Only administrators can change the brand.")).toBeVisible()
await expect(member.getByRole("button", { name: "Save the brand" })).toHaveCount(0)
// and the procedure answers them as it answers a stranger
const csrf = (await member.locator('meta[name="csrf-token"]').getAttribute("content")) ?? ""
const refused = await callRaw(member.request, csrf, "branding.brand.set", { org, primary: "#000000" })
expect(refused.status()).toBe(404)
await member.close()
})
test("a logo that carries a script is refused, and nothing is stored", async ({ page }) => {
await signIn(page, `${unique("admin")}@example.test`)
const { org } = await makeOrganization(page, "logo")
await page.goto(`/${org}/branding`)
await page.getByLabel("Logo for the light theme").setInputFiles({
name: "logo.svg",
mimeType: "image/svg+xml",
buffer: Buffer.from('<svg xmlns="http://www.w3.org/2000/svg"><script>alert(1)</script></svg>'),
})
await page.getByRole("button", { name: "Save the brand" }).click()
await expect(page.locator(".app-error")).toContainText("That did not work")
await page.reload()
await expect(page.getByAltText("Current logo")).toHaveCount(0)
})