Format expected dates in the page rather than in node (#385)

Co-authored-by: Seydi Charyyev <seydi.charyev@gmail.com>
This commit is contained in:
Seydi Charyyev 2026-08-31 23:18:07 +00:00 committed by hodlbod
parent ee5d71dc1d
commit 88d8bee015
3 changed files with 33 additions and 17 deletions

View file

@ -1109,12 +1109,17 @@ test("US-102 pause a relay and settle the bill", async ({seed, as}) => {
const invoices = history.getByRole("listitem")
// The whole period, not just its start: the two invoices meet at a month boundary, so either
// date on its own reads the same on both of them.
// date on its own reads the same on both of them. Formatted by the browser rather than by node,
// so the locale is the one the app rendered with — see dayLabel in dms.spec.ts.
const period = ({start, end}: {start: number; end: number}) =>
`${new Date(start * 1000).toLocaleDateString()} – ${new Date(end * 1000).toLocaleDateString()}`
page.evaluate(
([from, to]) =>
`${new Date(from * 1000).toLocaleDateString()} – ${new Date(to * 1000).toLocaleDateString()}`,
[start, end],
)
await expect(invoices.first()).toContainText("$9.00")
await expect(invoices.first()).toContainText(period(openPeriod))
await expect(invoices.first()).toContainText(await period(openPeriod))
await expect(invoices.nth(1)).toContainText("$5.00")
await expect(invoices.nth(1)).toContainText(period(paidPeriod))
await expect(invoices.nth(1)).toContainText(await period(paidPeriod))
})

View file

@ -96,10 +96,14 @@ const expectReactionRoundTrip = async (page: Page, scope: Locator, opener: Locat
await expect(pill).toHaveCount(0)
}
// formatTimestamp renders a short date and a short time in the environment's own locale, which node
// shares, so the date half of it is what a spec can name without pinning a format.
const shortDate = (seconds: number) =>
new Intl.DateTimeFormat(undefined, {dateStyle: "short"}).format(new Date(seconds * 1000))
// formatTimestamp renders a short date and a short time, so the date half of it is what a spec can
// name without pinning a format. Formatted by the browser rather than by node, so the locale and the
// timezone are the ones the app rendered with — see dayLabel in dms.spec.ts.
const shortDate = (page: Page, seconds: number) =>
page.evaluate(
ts => new Intl.DateTimeFormat(undefined, {dateStyle: "short"}).format(new Date(ts * 1000)),
seconds,
)
// The card is a div carrying an overlay link, so it is found by its component rather than by a
// role — its own contents include a profile button and the room and action links.
@ -284,7 +288,7 @@ test("US-038 browse, filter, and read articles", async ({seed, as}) => {
await expect(garden).toContainText("Written by")
await expect(garden).toContainText("Alice Anderson")
await expect(garden).toContainText("A short teaser about gardens.")
await expect(garden).toContainText(shortDate(at(4, HOUR)))
await expect(garden).toContainText(await shortDate(page, at(4, HOUR)))
const authors = page
.locator("section")
@ -308,7 +312,7 @@ test("US-038 browse, filter, and read articles", async ({seed, as}) => {
await expect(page.getByRole("heading", {name: "Tending the Garden"}).first()).toBeVisible()
await expect(page.locator('img[src="https://images.test/garden.jpg"]')).toBeVisible()
await expect(page.getByText("A short teaser about gardens.")).toBeVisible()
await expect(page.getByText(/^Published /)).toContainText(shortDate(at(4, HOUR)))
await expect(page.getByText(/^Published /)).toContainText(await shortDate(page, at(4, HOUR)))
const markdown = page.locator(".content-markdown")
@ -658,7 +662,9 @@ test("US-042 start a thread and see it filed under its room", async ({seed, as})
await expect(chairs.getByRole("cell").nth(0)).toContainText("Chair procurement")
await expect(chairs.getByRole("cell").nth(1)).toContainText("Bob Barker")
await expect(chairs.getByRole("cell").nth(2)).toHaveText("2")
await expect(chairs.getByRole("cell").nth(3)).toContainText(shortDate(at(4, HOUR) + 60))
await expect(chairs.getByRole("cell").nth(3)).toContainText(
await shortDate(page, at(4, HOUR) + 60),
)
await pageBar(page).getByRole("button", {name: "Create", exact: true}).click()

View file

@ -63,10 +63,15 @@ const openCard = (card: Locator, title: string) => card.getByText(title).click()
// its progress bar.
const pollOption = (page: Page, label: string) => page.locator(".card-sm").filter({hasText: label})
// dateFormatter in @welshman/lib resolves the environment's own locale, which node shares.
const longDate = (seconds: number) =>
new Intl.DateTimeFormat(undefined, {year: "numeric", month: "long", day: "numeric"}).format(
new Date(seconds * 1000),
// The options mirror dateFormatter in @welshman/lib. Formatted by the browser rather than by node,
// so the locale and the timezone are the ones the app rendered with — see dayLabel in dms.spec.ts.
const longDate = (page: Page, seconds: number) =>
page.evaluate(
ts =>
new Intl.DateTimeFormat(undefined, {year: "numeric", month: "long", day: "numeric"}).format(
new Date(ts * 1000),
),
seconds,
)
// The selections of every poll response this page put on the wire, oldest first. A multiple choice
@ -204,7 +209,7 @@ test("US-046 create and browse a calendar event", async ({seed, as}) => {
const market = card("Winter Market")
await expect(market).toBeVisible()
await expect(market).toContainText(longDate(Math.floor(target.getTime() / 1000)))
await expect(market).toContainText(await longDate(page, Math.floor(target.getTime() / 1000)))
})
test("US-047 manage your own calendar event", async ({seed, as}) => {
@ -249,7 +254,7 @@ test("US-047 manage your own calendar event", async ({seed, as}) => {
const eventCard = page.locator(".card.z-feature")
await expect(page.getByRole("heading", {name: "Harvest Supper", exact: true})).toBeVisible()
await expect(eventCard).toContainText(longDate(at(-2, DAY)))
await expect(eventCard).toContainText(await longDate(page, at(-2, DAY)))
await expect(eventCard).toContainText("The Old Mill")
await expect(eventCard).toContainText("Alice Anderson")