Skip to content

Commit b084f11

Browse files
committed
test(org): measure Home's layout shift, overlap on a phone, and People's loading gutter
1 parent a5a4072 commit b084f11

2 files changed

Lines changed: 112 additions & 6 deletions

File tree

‎apps/sim/scripts/org-redesign-e2e/checks/home.ts‎

Lines changed: 81 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -523,7 +523,7 @@ export async function homeChecks() {
523523
})
524524
})
525525
await check(
526-
'Workspaces holds its rows while its read is out, and keeps them if it fails',
526+
'Workspaces shows its rows while their activity is out, and keeps them if it fails',
527527
async () => {
528528
await withSession(MEMBER, async ({ page }) => {
529529
const release = createDeferred<void>()
@@ -535,10 +535,10 @@ export async function homeChecks() {
535535
})
536536
await page.goto(orgUrl('/home'))
537537
const region = homeRegion(page, 'Workspaces')
538-
await expect(region.getByRole('status', { name: 'Loading workspaces' })).toBeVisible()
539-
await expect(
540-
region.getByRole('status', { name: 'Loading workspaces' }).locator('> span')
541-
).toHaveCount(2)
538+
// The names are already known, so the rows paint at once, with what they report still out.
539+
await expect(region.getByRole('link', { name: MEMBER_ADMIN_WORKSPACE.name })).toBeVisible()
540+
await expect(region.getByText('…', { exact: true }).first()).toBeVisible()
541+
await expect(region.getByText('Up to date')).toHaveCount(0)
542542
release.resolve()
543543
await expect(
544544
region.getByText('What these workspaces are doing could not load.')
@@ -553,6 +553,82 @@ export async function homeChecks() {
553553
},
554554
{ allow: [/500 GET .*workspace-activity/] }
555555
)
556+
for (const [persona, viewer] of [
557+
['member', MEMBER],
558+
['builder', ADMIN],
559+
] as const) {
560+
await check(`a ${persona}'s Home does not jump from its skeleton to its content`, async () => {
561+
await withSession(viewer, async (session) => {
562+
const { page } = session
563+
await page.addInitScript(() => {
564+
let shift = 0
565+
new PerformanceObserver((entries) => {
566+
for (const entry of entries.getEntries() as (PerformanceEntry & {
567+
value: number
568+
hadRecentInput: boolean
569+
})[]) {
570+
if (!entry.hadRecentInput) shift += entry.value
571+
}
572+
}).observe({ type: 'layout-shift', buffered: true })
573+
;(window as Window & { homeLayoutShift?: () => number }).homeLayoutShift = () => shift
574+
})
575+
await page.goto(orgUrl('/home'))
576+
await expect(page.getByRole('main')).toBeVisible()
577+
await session.settle()
578+
const shift = await page.evaluate(() =>
579+
(window as Window & { homeLayoutShift?: () => number }).homeLayoutShift?.()
580+
)
581+
// 0.25 is where Core Web Vitals calls a page's shift poor.
582+
assert.ok((shift ?? 0) < 0.25, `Home shifted by ${shift} as it loaded`)
583+
})
584+
})
585+
}
586+
await check("Home's sections stack without overlapping on a phone", async () => {
587+
await withSession(
588+
ADMIN,
589+
async (session) => {
590+
const { page } = session
591+
await page.goto(orgUrl('/home'))
592+
await expect(homeRegion(page, 'Workspaces')).toBeVisible()
593+
await session.settle()
594+
const seeMore = homeRegion(page, 'Workspaces').getByRole('button', { name: 'See more' })
595+
if ((await seeMore.count()) > 0) await seeMore.click()
596+
const boxes = await page
597+
.getByRole('main')
598+
.getByRole('region')
599+
.evaluateAll((regions) =>
600+
regions
601+
.filter((region) => region.matches('section'))
602+
.map((region) => {
603+
const { top, bottom } = region.getBoundingClientRect()
604+
const contentBottom = Math.max(
605+
bottom,
606+
...[...region.children].map((child) => child.getBoundingClientRect().bottom)
607+
)
608+
return { name: region.getAttribute('aria-label'), top, bottom: contentBottom }
609+
})
610+
.sort((a, b) => a.top - b.top)
611+
)
612+
assert.ok(boxes.length > 1, 'Home has no sections to compare')
613+
for (const [index, box] of boxes.slice(1).entries()) {
614+
const above = boxes[index]
615+
assert.ok(
616+
above.bottom <= box.top + 1,
617+
`${above.name} runs ${Math.round(above.bottom - box.top)}px into ${box.name}`
618+
)
619+
}
620+
},
621+
{ viewport: PHONE }
622+
)
623+
})
624+
await check('Home, Search and Settings each name one main region', async () => {
625+
await withSession(ADMIN, async ({ page }) => {
626+
for (const path of ['/home', '/search', '/settings/general']) {
627+
await page.goto(orgUrl(path))
628+
await expect(page.getByRole('main')).toHaveCount(1)
629+
}
630+
})
631+
})
556632
await check('Customize Home hides Workspaces across a reload, and stops its read', async () => {
557633
await onHome(MEMBER, async (session) => {
558634
const { page } = session

‎apps/sim/scripts/org-redesign-e2e/checks/people.ts‎

Lines changed: 31 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import assert from 'node:assert/strict'
22
import { resolve } from 'node:path'
3+
import { createDeferred } from '@sim/testing/helpers/deferred'
34
import { fullPageScreenshot } from '@/scripts/org-redesign-e2e/checks/walk'
45
import { ADMIN, MEMBER } from '@/scripts/org-redesign-e2e/fixture'
56
import {
@@ -13,7 +14,7 @@ import {
1314
section,
1415
withSession,
1516
} from '@/scripts/org-redesign-e2e/harness'
16-
import { navLabels } from '@/scripts/org-redesign-e2e/locators'
17+
import { mainNav, navLabels } from '@/scripts/org-redesign-e2e/locators'
1718

1819
export async function peoplePhaseChecks() {
1920
section('People with its flag on')
@@ -22,6 +23,35 @@ export async function peoplePhaseChecks() {
2223
assert.deepEqual(await navLabels(page), ['Home', 'Search', 'People', 'Integrations', 'Links'])
2324
})
2425
})
26+
await check('loading rows sit on the page gutter the heading and content use', async () => {
27+
await withSession(MEMBER, async ({ page }) => {
28+
const hold = createDeferred<void>()
29+
const isPeoplePayload = (address: URL) =>
30+
address.pathname.endsWith('/people') && address.searchParams.has('_rsc')
31+
await page.route(isPeoplePayload, async (route) => {
32+
if (route.request().headers()['next-router-prefetch'] === undefined) await hold.promise
33+
await route.continue()
34+
})
35+
const prefetched = page.waitForResponse(
36+
async (response) =>
37+
isPeoplePayload(new URL(response.url())) &&
38+
(await response.text().catch(() => '')).includes('Loading people')
39+
)
40+
await page.goto(orgUrl('/home'))
41+
await prefetched
42+
await mainNav(page).getByRole('link', { name: 'People', exact: true }).click()
43+
const rows = page.getByRole('status', { name: 'Loading people…' })
44+
await expect(rows).toBeVisible()
45+
const heading = await page.getByRole('heading', { name: 'People', exact: true }).boundingBox()
46+
const loading = await rows.boundingBox()
47+
hold.resolve()
48+
assert.ok(heading && loading, 'Nothing to measure')
49+
assert.ok(
50+
Math.abs(heading.x - loading.x) <= 1,
51+
`Loading rows start at ${loading.x}, the heading at ${heading.x}`
52+
)
53+
})
54+
})
2555
await check(
2656
'a member reads that an organization admin connects the directory, and no name',
2757
async () => {

0 commit comments

Comments
 (0)