This commit is contained in:
@@ -0,0 +1,91 @@
|
||||
// SPDX-License-Identifier: LicenseRef-ConusAI-Template-1.0
|
||||
// The tenant logos in a browser (plan item AT2.6, spec §10.1, §10.3): one uploaded logo stands in for the
|
||||
// other theme, an image larger than a stored logo may be is resized and not refused, the organization's and
|
||||
// the app's share images are drawn, and a damaged PNG is refused with nothing stored.
|
||||
|
||||
import { expect, test } from "@playwright/test"
|
||||
import { dimensions, markPng, noisePng } from "../test/support/images"
|
||||
import { makeOrganization, signIn, unique } from "./support/app"
|
||||
|
||||
test("one uploaded logo is used on both themes, a larger image is resized, and the share images are drawn", async ({
|
||||
page,
|
||||
browser,
|
||||
}) => {
|
||||
await signIn(page, `${unique("admin")}@example.test`)
|
||||
const { org } = await makeOrganization(page, "logos")
|
||||
|
||||
// only the dark theme's logo, and larger than a stored logo may be: it is resized, not refused
|
||||
await page.goto(`/${org}/branding`)
|
||||
const upload = noisePng(700, 300)
|
||||
expect(upload.length).toBeGreaterThan(256 * 1024)
|
||||
await page
|
||||
.getByLabel("Logo for the dark theme")
|
||||
.setInputFiles({ name: "logo.png", mimeType: "image/png", buffer: upload })
|
||||
await page.getByRole("button", { name: "Save the brand" }).click()
|
||||
await expect(page.getByAltText("Current logo")).toHaveCount(1)
|
||||
// the page says what each theme shows
|
||||
await expect(page.getByText("Light theme: the dark logo, because this theme has none of its own")).toBeVisible()
|
||||
await expect(page.getByText("Dark theme: its own logo")).toBeVisible()
|
||||
|
||||
// the organization layout draws that logo for both themes, at 1x and 2x
|
||||
const html = await (await page.request.get(`/${org}`)).text()
|
||||
const source = `src="/${org}/brand/logo/dark\\?v=[0-9a-f]{16}&s=1x"`
|
||||
expect(html).toMatch(new RegExp(`class="app-logo app-logo-dark"[^>]*${source}`))
|
||||
expect(html).toMatch(new RegExp(`class="app-logo app-logo-light"[^>]*${source}`))
|
||||
// The page advertises the app's public share image: the organization's own (`/{org}/brand/og`)
|
||||
// answers a member only, so a crawler could never fetch it, and an organization stays
|
||||
// indistinguishable from a missing one to anyone outside it.
|
||||
expect(html).toMatch(/<meta property="og:image" content="http:\/\/127.0.0.1:\d+\/api\/brand\/og"/)
|
||||
expect(html).not.toContain(`/${org}/brand/og"`)
|
||||
|
||||
// the stored sizes are the standard ones, and nothing but a PNG is served
|
||||
const one = await page.request.get(`/${org}/brand/logo/dark?s=1x`)
|
||||
expect(one.headers()["content-type"]).toBe("image/png")
|
||||
expect(dimensions(await one.body()).width).toBeLessThanOrEqual(420)
|
||||
const two = await page.request.get(`/${org}/brand/logo/dark?s=2x`)
|
||||
expect(dimensions(await two.body()).width).toBeLessThanOrEqual(840)
|
||||
expect((await page.request.get(`/${org}/brand/logo/light`)).status()).toBe(404)
|
||||
|
||||
// the organization's share image: a member gets it, a stranger gets what a missing organization gets
|
||||
const card = await page.request.get(`/${org}/brand/og`)
|
||||
expect(card.status()).toBe(200)
|
||||
expect(card.headers()["content-type"]).toBe("image/png")
|
||||
expect(dimensions(await card.body())).toEqual({ width: 1200, height: 630 })
|
||||
const stranger = await browser.newPage()
|
||||
await signIn(stranger, `${unique("stranger")}@example.test`)
|
||||
expect((await stranger.request.get(`/${org}/brand/og`)).status()).toBe(404)
|
||||
await stranger.close()
|
||||
|
||||
// the app's own share image is public: a link preview is fetched without a session
|
||||
const anonymous = await browser.newContext()
|
||||
const appCard = await anonymous.request.get(`${new URL(page.url()).origin}/api/brand/og`)
|
||||
expect(appCard.status()).toBe(200)
|
||||
expect(dimensions(await appCard.body())).toEqual({ width: 1200, height: 630 })
|
||||
await anonymous.close()
|
||||
|
||||
// removing the logo removes it from the page and from the store
|
||||
await page.goto(`/${org}/branding`)
|
||||
await page.getByRole("button", { name: "Remove the dark logo" }).click()
|
||||
await expect(page.getByText("Dark theme: the app's logo, because no logo is set")).toBeVisible()
|
||||
// the page reloads itself once the removal is stored
|
||||
const reloaded = page.waitForEvent("load")
|
||||
await page.getByRole("button", { name: "Save the brand" }).click()
|
||||
await reloaded
|
||||
await expect(page.getByAltText("Current logo")).toHaveCount(0)
|
||||
expect((await page.request.get(`/${org}/brand/logo/dark`)).status()).toBe(404)
|
||||
})
|
||||
|
||||
test("a PNG that is damaged is refused and nothing is stored", async ({ page }) => {
|
||||
await signIn(page, `${unique("admin")}@example.test`)
|
||||
const { org } = await makeOrganization(page, "damaged")
|
||||
const png = Buffer.from(markPng(300, 80))
|
||||
png[30] = (png[30] ?? 0) ^ 0xff
|
||||
await page.goto(`/${org}/branding`)
|
||||
await page
|
||||
.getByLabel("Logo for the light theme")
|
||||
.setInputFiles({ name: "logo.png", mimeType: "image/png", buffer: png })
|
||||
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)
|
||||
})
|
||||
Reference in New Issue
Block a user