test(desktop): scale zoom visual expectations

Co-authored-by: Taylor Ho <taylorkmho@gmail.com>
Signed-off-by: Taylor Ho <taylorkmho@gmail.com>
This commit is contained in:
npub14vtk7pvazqrq9639qu7e560wnqtl0d53ca4gjuvq6jzf3k2el23qqlwa7f
2026-07-02 01:35:09 -07:00
co-authored by Taylor Ho
parent 7e241e2372
commit f2cd060c19
9 changed files with 109 additions and 23 deletions
@@ -90,13 +90,13 @@ test("estimateRowHeight: bare URL line adds a preview card", () => {
assert.ok(withUrl > withoutUrl + 50, `url ${withUrl} vs ${withoutUrl}`);
});
test("timelineRowReserveStyle: message item yields containIntrinsicSize", () => {
test("timelineRowReserveStyle: message item yields rem containIntrinsicSize", () => {
const style = timelineRowReserveStyle({
kind: "message",
key: "k",
entry: { message: msg({ body: "hi" }), summary: null },
});
assert.match(String(style.containIntrinsicSize), /^auto \d+px$/);
assert.match(String(style.containIntrinsicSize), /^auto \d+(?:\.\d+)?rem$/);
});
test("timelineRowReserveStyle: divider is short fixed height", () => {
@@ -105,5 +105,5 @@ test("timelineRowReserveStyle: divider is short fixed height", () => {
key: "k",
headingTimestamp: 0,
});
assert.equal(style.containIntrinsicSize, "auto 32px");
assert.equal(style.containIntrinsicSize, "auto 2rem");
});
@@ -175,5 +175,5 @@ export function timelineRowReserveStyle(
: item.kind === "system"
? estimateRowHeight(item.entry.message)
: DIVIDER_HEIGHT;
return { containIntrinsicSize: `auto ${height}px` };
return { containIntrinsicSize: `auto ${height / 16}rem` };
}
+12 -4
View File
@@ -6,6 +6,7 @@ import {
installMockBridge,
openChannelBrowser,
} from "../helpers/bridge";
import { expectedScaledPx } from "../helpers/css";
const GENERAL_CHANNEL_ID = "9a1657ac-f7aa-5db0-b632-d8bbeb6dfb50";
const MOCK_IDENTITY_PUBKEY = "deadbeef".repeat(8);
@@ -276,7 +277,9 @@ async function expectSameLeftInset(
throw new Error(`Could not measure ${firstTestId} against ${secondTestId}`);
}
expect(Math.abs(firstBox.x - secondBox.x)).toBeLessThanOrEqual(4);
expect(Math.abs(firstBox.x - secondBox.x)).toBeLessThanOrEqual(
await expectedScaledPx(page.getByTestId(firstTestId), 4),
);
}
async function expectIntroSpacedAboveDayDivider(
@@ -325,9 +328,14 @@ async function expectIntroActionCardLayout(
}
expect(actionBox.height).toBeGreaterThan(actionBox.width);
expect(Math.round(actionBox.width)).toBe(220);
expect(Math.round(iconBox.width)).toBe(48);
expect(Math.round(iconBox.height)).toBe(48);
expect(Math.round(actionBox.width)).toBe(
Math.round(await expectedScaledPx(page.getByTestId(actionTestId), 220)),
);
const expectedIconSize = Math.round(
await expectedScaledPx(page.getByTestId(`${actionTestId}-icon`), 48),
);
expect(Math.round(iconBox.width)).toBe(expectedIconSize);
expect(Math.round(iconBox.height)).toBe(expectedIconSize);
const introIconRadius = await page
.getByTestId("message-channel-intro-icon")
.evaluate((element) => window.getComputedStyle(element).borderRadius);
+39 -4
View File
@@ -1,6 +1,7 @@
import { expect, test } from "@playwright/test";
import { installMockBridge } from "../helpers/bridge";
import { expectedScaledPx } from "../helpers/css";
// Custom-emoji end-to-end guard.
//
@@ -271,14 +272,34 @@ test("reacting with a custom emoji renders via the loopback media proxy", async
}),
)
.toBe("0");
const expectedInlineReactionButtonWidth = Math.round(
await expectedScaledPx(inlineAddReactionButton, 40),
);
const minExpectedInlineReactionButtonHeight = Math.round(
await expectedScaledPx(inlineAddReactionButton, 28),
);
await expect
.poll(() =>
inlineAddReactionButton.evaluate((button) => {
const rect = button.getBoundingClientRect();
return `${Math.round(rect.width)}x${Math.round(rect.height)}`;
return {
height: Math.round(rect.height),
width: Math.round(rect.width),
};
}),
)
.toBe("40x28");
.toEqual({
height: expect.any(Number),
width: expectedInlineReactionButtonWidth,
});
await expect
.poll(() =>
inlineAddReactionButton.evaluate((button) => {
const rect = button.getBoundingClientRect();
return Math.round(rect.height);
}),
)
.toBeGreaterThanOrEqual(minExpectedInlineReactionButtonHeight);
await expect
.poll(() =>
inlineAddReactionButton.evaluate((button) => {
@@ -292,10 +313,24 @@ test("reacting with a custom emoji renders via the loopback media proxy", async
.poll(() =>
inlineAddReactionButton.evaluate((button) => {
const rect = button.getBoundingClientRect();
return `${Math.round(rect.width)}x${Math.round(rect.height)}`;
return {
height: Math.round(rect.height),
width: Math.round(rect.width),
};
}),
)
.toBe("40x28");
.toEqual({
height: expect.any(Number),
width: expectedInlineReactionButtonWidth,
});
await expect
.poll(() =>
inlineAddReactionButton.evaluate((button) => {
const rect = button.getBoundingClientRect();
return Math.round(rect.height);
}),
)
.toBeGreaterThanOrEqual(minExpectedInlineReactionButtonHeight);
// Toggle the reaction back off: click the pill, which fires remove_reaction
// -> emits a kind:5 deletion targeting the reaction event. The pill must
+1 -1
View File
@@ -47,7 +47,7 @@ test("upload a file and see a FileCard in the timeline", async ({ page }) => {
// escapes the webview to the OS browser and hits a corporate CDN page.
const card = page.getByTestId("file-card").last();
await expect(card).toBeVisible();
await expectCornerRadiusPx(card, 16);
await expectCornerRadiusPx(card, 16, { scaleWithRootFont: true });
await expectSmoothCorners(card);
await expect(card).toContainText("quarterly-report.pdf");
@@ -126,8 +126,10 @@ test("image bundle lightbox navigates as a gallery", async ({ page }) => {
const triggers = row.getByTestId("message-image-lightbox-trigger");
await expect(triggers).toHaveCount(3);
await expectCornerRadiusPx(triggers.first(), 16);
await expectCornerRadiusPx(triggers.first().locator("img"), 16);
await expectCornerRadiusPx(triggers.first(), 16, { scaleWithRootFont: true });
await expectCornerRadiusPx(triggers.first().locator("img"), 16, {
scaleWithRootFont: true,
});
await expectSmoothCorners(triggers.first().locator("img"));
await triggers.first().click();
@@ -137,7 +139,7 @@ test("image bundle lightbox navigates as a gallery", async ({ page }) => {
const lightboxSurface = page
.locator("[data-image-lightbox-frame] > div > div")
.first();
await expectCornerRadiusPx(lightboxSurface, 16);
await expectCornerRadiusPx(lightboxSurface, 16, { scaleWithRootFont: true });
await expectSmoothCorners(lightboxSurface);
await expect(
page.getByRole("button", { name: "Previous image" }),
@@ -174,6 +176,7 @@ test("image bundle lightbox navigates as a gallery", async ({ page }) => {
await expectCornerRadiusPx(
page.locator("[data-image-lightbox-frame] > div > div").first(),
16,
{ scaleWithRootFont: true },
);
expect(Math.abs(closingFrameBox.x - currentThumbnailBox.x)).toBeLessThan(2);
+22 -4
View File
@@ -1,7 +1,11 @@
import { expect, test, type Locator } from "@playwright/test";
import { installMockBridge, TEST_IDENTITIES } from "../helpers/bridge";
import { expectCornerRadiusPx, expectSmoothCorners } from "../helpers/css";
import {
expectCornerRadiusPx,
expectSmoothCorners,
expectedScaledPx,
} from "../helpers/css";
import { openSettings } from "../helpers/settings";
async function expectThreadReplyUnobscured(row: Locator) {
@@ -179,7 +183,7 @@ test("supported link previews keep the message link visible", async ({
).toBeVisible();
const previewCard = row.locator('[data-link-preview="github-pull-request"]');
await expect(previewCard).toBeVisible();
await expectCornerRadiusPx(previewCard, 16);
await expectCornerRadiusPx(previewCard, 16, { scaleWithRootFont: true });
await expectSmoothCorners(previewCard);
});
@@ -233,7 +237,9 @@ test("copy a rendered code block and paste it back as code", async ({
const codeBlock = page.locator("[data-code-block]");
await expect(codeBlock).toHaveCount(1);
await expectCornerRadiusPx(codeBlock.locator("pre"), 16);
await expectCornerRadiusPx(codeBlock.locator("pre"), 16, {
scaleWithRootFont: true,
});
await expectSmoothCorners(codeBlock.locator("pre"));
const copyButton = page.getByLabel("Copy code block");
@@ -647,7 +653,19 @@ test("opens a single-level thread panel with inline expansion", async ({
return `${Math.round(rect.width)}x${Math.round(rect.height)}`;
}),
)
.toBe("24x24");
.toBe(
`${Math.round(
await expectedScaledPx(
rootSummaryRow.getByTestId("message-thread-summary-participant"),
24,
),
)}x${Math.round(
await expectedScaledPx(
rootSummaryRow.getByTestId("message-thread-summary-participant"),
24,
),
)}`,
);
const summaryGeometry = await measureThreadSummaryGeometry(rootSummaryRow);
expect(
Math.abs(summaryGeometry.authorLeft - summaryGeometry.bodyLeft),
+1 -1
View File
@@ -253,7 +253,7 @@ test("video upload previews use poster frames and inline videos open review mode
const inlinePlayer = page.getByTestId("video-player").last();
const inlineSurface = inlinePlayer.locator("[data-smooth-corners]").first();
await expectCornerRadiusPx(inlineSurface, 16);
await expectCornerRadiusPx(inlineSurface, 16, { scaleWithRootFont: true });
await expectSmoothCorners(inlineSurface);
const inlineVideo = inlinePlayer.locator("video");
await inlinePlayer.getByRole("button", { name: "Play video" }).click();
+24 -2
View File
@@ -1,8 +1,25 @@
import { expect, type Locator } from "@playwright/test";
export async function currentRootFontScale(locator: Locator) {
const rootFontSize = await locator.evaluate(() =>
Number.parseFloat(
window.getComputedStyle(document.documentElement).fontSize,
),
);
return Number.isFinite(rootFontSize) ? rootFontSize / 16 : 1;
}
export async function expectedScaledPx(
locator: Locator,
pxAtDefaultScale: number,
) {
return pxAtDefaultScale * (await currentRootFontScale(locator));
}
export async function expectCornerRadiusPx(
locator: Locator,
expectedRadiusPx: number,
options: { scaleWithRootFont?: boolean } = {},
) {
const measurement = await locator.evaluate((element) => {
const style = window.getComputedStyle(element);
@@ -65,13 +82,18 @@ export async function expectCornerRadiusPx(
className: element.getAttribute("class") ?? "",
radius,
rawRadius,
rootFontSize,
};
});
const expected = options.scaleWithRootFont
? expectedRadiusPx * (measurement.rootFontSize / 16)
: expectedRadiusPx;
expect(
measurement.radius,
`Expected ${expectedRadiusPx}px corner radius, got ${measurement.rawRadius} on class "${measurement.className}".`,
).toBeCloseTo(expectedRadiusPx, 0);
`Expected ${expected}px corner radius, got ${measurement.rawRadius} on class "${measurement.className}".`,
).toBeCloseTo(expected, 0);
}
export async function expectSmoothCorners(