Skip to content

Commit 997a4ac

Browse files
test(e2e): Settings section drill-in/out + visible motion evidence
Exercises the SettingsCard layout='position' fix: opens a section, captures a mid-transition frame + the settled full-width detail, asserts the drill-in (back affordance + col-span-full width) and clean drill-out back to the grid. Screenshots (collapsed / opening-midframe / open / collapsed-again) are PR evidence + a vision check that the text is no longer scale-distorted during the open animation.
1 parent 58d1348 commit 997a4ac

5 files changed

Lines changed: 101 additions & 0 deletions

File tree

217 KB
Loading
246 KB
Loading
219 KB
Loading
183 KB
Loading
Lines changed: 101 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,101 @@
1+
/**
2+
* Settings section open/close (accordion drill-in) — behaviour + visible evidence.
3+
*
4+
* Guards the SettingsCard motion fix: opening a section used to scale/zoom the card
5+
* (full framer `layout` animates size via transform: scale(), distorting the text);
6+
* it is now `layout="position"` so content stays crisp and the body height animation
7+
* does the vertical growth. A static screenshot can't show the animation's smoothness,
8+
* but it CAN prove the end states render correctly (crisp, full-width detail, clean
9+
* return to the grid) and that the drill-in/out behaviour works. Captures collapsed →
10+
* mid-transition → open → closed into e2e/screenshots/ for the PR + a vision pass.
11+
*
12+
* Core build (OFFGRID_PRO=0), fresh temp profile, seeded demo data.
13+
*/
14+
import { test, expect, _electron as electron, type ElectronApplication, type Page } from '@playwright/test'
15+
import os from 'os'
16+
import path from 'path'
17+
import fs from 'fs'
18+
19+
let app: ElectronApplication
20+
let page: Page
21+
let userDataDir: string
22+
23+
const shot = async (name: string): Promise<void> => {
24+
await page.screenshot({ path: `e2e/screenshots/${name}.png` })
25+
}
26+
27+
test.beforeAll(async () => {
28+
userDataDir = fs.mkdtempSync(path.join(os.tmpdir(), 'offgrid-settings-motion-'))
29+
app = await electron.launch({
30+
args: ['.'],
31+
env: {
32+
...process.env,
33+
OFFGRID_USER_DATA: userDataDir,
34+
OFFGRID_PRO: '0',
35+
OFFGRID_SEED: 'force',
36+
NODE_ENV: 'production'
37+
}
38+
})
39+
page = await app.firstWindow()
40+
await page.waitForLoadState('domcontentloaded')
41+
// Dismiss onboarding if present.
42+
for (let i = 0; i < 8; i++) {
43+
const btn = page.getByRole('button', { name: /Continue|Start using Off Grid/i })
44+
if (!(await btn.isVisible().catch(() => false))) break
45+
await btn.click().catch(() => {})
46+
await page.waitForTimeout(300)
47+
}
48+
try {
49+
await page.getByRole('button', { name: 'Expand sidebar' }).click({ timeout: 4000 })
50+
} catch {
51+
/* already open */
52+
}
53+
await page.getByRole('button', { name: 'Settings', exact: true }).first().click()
54+
await page.waitForTimeout(800)
55+
})
56+
57+
test.afterAll(async () => {
58+
await app?.close()
59+
try {
60+
fs.rmSync(userDataDir, { recursive: true, force: true })
61+
} catch {
62+
/* ignore */
63+
}
64+
})
65+
66+
test('drills into a Settings section and back without distorting the card', async () => {
67+
// Collapsed grid: several section cards are visible as a scannable master list.
68+
const setupCard = page.getByRole('button', { name: /Setup & health/ }).first()
69+
await expect(setupCard).toBeVisible()
70+
await expect(page.getByRole('heading', { name: 'Setup & health' })).toBeVisible()
71+
await shot('settings-collapsed')
72+
73+
// Open it. Capture a mid-transition frame (during the open animation) then the
74+
// settled detail — so a vision pass can confirm the text is not scaled/zoomed.
75+
await setupCard.click()
76+
await page.waitForTimeout(90)
77+
await shot('settings-opening-midframe')
78+
await page.waitForTimeout(900)
79+
80+
// Open state = the L2 detail: a "back to all settings" affordance appears and the
81+
// card owns the full grid width. The section body content is now visible.
82+
await expect(page.getByText(/All settings/i).first()).toBeVisible()
83+
await shot('settings-section-open')
84+
85+
// The opened card takes the full grid width (col-span-full) — assert it spans most
86+
// of the viewport, proving the detail morph landed rather than staying a grid cell.
87+
const openCard = page.getByRole('heading', { name: 'Setup & health' }).locator('..').locator('..')
88+
const box = await openCard.boundingBox()
89+
const viewport = page.viewportSize()
90+
if (box && viewport) {
91+
expect(box.width).toBeGreaterThan(viewport.width * 0.7)
92+
}
93+
94+
// Close it — the back affordance collapses the detail and the grid returns.
95+
await page.getByText(/All settings/i).first().click()
96+
await page.waitForTimeout(900)
97+
await expect(page.getByText(/All settings/i)).toHaveCount(0)
98+
// Sibling sections are back in the grid (drill-out complete).
99+
await expect(page.getByRole('heading', { name: 'Setup & health' })).toBeVisible()
100+
await shot('settings-collapsed-again')
101+
})

0 commit comments

Comments
 (0)