L0–L7 folders stack on scroll with aligned max depth, labeled tabs that stay consistent when L7 joins the rail, Cal embeds, preview screenshots, Playwright tests, and updated README. Co-authored-by: Cursor <cursoragent@cursor.com>
119 lines
3.7 KiB
JavaScript
119 lines
3.7 KiB
JavaScript
/**
|
|
* Automated scroll/blur tests for stack-folder.
|
|
* Run: node scripts/test-stack-scroll.mjs
|
|
*/
|
|
import { chromium } from 'playwright';
|
|
|
|
const URL = process.env.STACK_URL || 'http://localhost:5173/stack-folder/';
|
|
const VIEWPORT = { width: 1280, height: 800 };
|
|
|
|
function fail(msg) {
|
|
console.error('FAIL:', msg);
|
|
process.exitCode = 1;
|
|
}
|
|
|
|
function pass(msg) {
|
|
console.log('PASS:', msg);
|
|
}
|
|
|
|
async function readState(page) {
|
|
return page.evaluate(() => {
|
|
const stick =
|
|
parseFloat(getComputedStyle(document.documentElement).fontSize) * 3;
|
|
const l7Tab = document.querySelector('.f7 .tab');
|
|
const l0 = document.querySelector('.f0');
|
|
const l0Body = document.querySelector('.f0 .body');
|
|
const folderBlur = l0?.style.getPropertyValue('--stack-blur') || '';
|
|
const filter = l0Body ? getComputedStyle(l0Body).filter : '';
|
|
const blurMatch = filter.match(/blur\(([\d.]+)px\)/);
|
|
const blurPx = blurMatch ? parseFloat(blurMatch[1]) : 0;
|
|
return {
|
|
scrollY: window.scrollY,
|
|
docMax: document.documentElement.scrollHeight - innerHeight,
|
|
l7TabTop: l7Tab?.getBoundingClientRect().top ?? null,
|
|
stick,
|
|
l0Covered: l0?.classList.contains('is-covered'),
|
|
l0BlurPx: blurPx,
|
|
folderBlur,
|
|
l0Filter: filter,
|
|
runway: getComputedStyle(document.querySelector('.mount')).getPropertyValue(
|
|
'--stack-runway',
|
|
),
|
|
};
|
|
});
|
|
}
|
|
|
|
async function main() {
|
|
const browser = await chromium.launch();
|
|
const page = await browser.newPage({ viewport: VIEWPORT });
|
|
await page.goto(URL, { waitUntil: 'networkidle' });
|
|
await page.waitForTimeout(400);
|
|
|
|
const top = await readState(page);
|
|
if (top.l0Covered || top.l0BlurPx > 0.1) {
|
|
fail(`L0 blurred at top (covered=${top.l0Covered}, blur=${top.l0BlurPx})`);
|
|
} else {
|
|
pass('L0 clear at scroll top');
|
|
}
|
|
|
|
await page.evaluate(() => window.scrollTo(0, 999999));
|
|
await page.waitForTimeout(350);
|
|
const end = await readState(page);
|
|
|
|
if (end.l7TabTop === null) fail('L7 tab missing');
|
|
else if (Math.abs(end.l7TabTop - end.stick) > 8) {
|
|
fail(`L7 tab not on stick: top=${end.l7TabTop} stick=${end.stick} scrollY=${end.scrollY}`);
|
|
} else {
|
|
pass(`L7 on stick at max scroll (y=${end.scrollY}, tabTop=${end.l7TabTop.toFixed(1)})`);
|
|
}
|
|
|
|
if (end.l0BlurPx < 2) {
|
|
fail(`L0 not blurred when stacked (blur=${end.l0BlurPx})`);
|
|
} else {
|
|
pass(`L0 blurred when stacked (blur=${end.l0BlurPx}px)`);
|
|
}
|
|
|
|
const midY = Math.floor(end.scrollY * 0.45);
|
|
await page.evaluate((y) => window.scrollTo(0, y), midY);
|
|
await page.waitForTimeout(200);
|
|
const mid = await readState(page);
|
|
if (mid.l0BlurPx > 2) {
|
|
fail(`L0 still heavily blurred mid-scroll out (blur=${mid.l0BlurPx} at y=${mid.scrollY})`);
|
|
} else {
|
|
pass(`L0 unfades when scrolling out (blur=${mid.l0BlurPx}px at y=${mid.scrollY})`);
|
|
}
|
|
|
|
await page.evaluate(() => window.scrollTo(0, 0));
|
|
await page.waitForTimeout(350);
|
|
const back = await readState(page);
|
|
if (back.l0Covered || back.l0BlurPx > 0.1) {
|
|
fail(`L0 still blurred after scroll to top (covered=${back.l0Covered}, blur=${back.l0BlurPx})`);
|
|
} else {
|
|
pass('L0 unfades after scroll back to top');
|
|
}
|
|
|
|
const maxY = end.scrollY;
|
|
await page.evaluate((y) => window.scrollTo(0, y), maxY);
|
|
await page.waitForTimeout(200);
|
|
await page.evaluate(() => window.scrollTo(0, 0));
|
|
await page.waitForTimeout(350);
|
|
const back2 = await readState(page);
|
|
if (back2.l0Covered || back2.l0BlurPx > 0.1) {
|
|
fail(`L0 stuck after down-up cycle (blur=${back2.l0BlurPx})`);
|
|
} else {
|
|
pass('L0 unfades after full down-up cycle');
|
|
}
|
|
|
|
await browser.close();
|
|
if (process.exitCode) {
|
|
console.log('\nTests failed.');
|
|
process.exit(1);
|
|
}
|
|
console.log('\nAll tests passed.');
|
|
}
|
|
|
|
main().catch((e) => {
|
|
console.error(e);
|
|
process.exit(1);
|
|
});
|