import { test, expect } from '@playwright/test'; import { _electron as electron } from 'playwright'; import { promises as fs } from 'node:fs'; import nodePath from 'node:path'; import os from 'node:os'; const MAIN_JS = nodePath.join(process.cwd(), 'dist-electron/main/index.js'); async function launch(userData: string) { const app = await electron.launch({ args: ['--user-data-dir=' + userData, MAIN_JS] }); const page = await app.firstWindow(); await page.waitForLoadState('domcontentloaded'); const invoke = (ch: string, args: any) => page.evaluate(({ ch, args }) => (globalThis as any).dsh.invoke(ch, args), { ch, args }); return { app, page, invoke }; } // 生成长文档:足够滚动 function longBody(title: string): string { const paras: string[] = []; for (let i = 1; i <= 800; i++) paras.push('段落 ' + i + ' 内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容'); return '# ' + title + '\n\n' + paras.join('\n\n'); } async function scrollerTop(page: any): Promise { return page.evaluate(() => { const sc = document.querySelector('.ProseMirror')?.parentElement as HTMLElement | null; return sc ? sc.scrollTop : -1; }); } async function setScroll(page: any, top: number): Promise { await page.evaluate((t: number) => { const sc = document.querySelector('.ProseMirror')?.parentElement as HTMLElement | null; if (sc) { sc.scrollTop = t; sc.dispatchEvent(new Event('scroll')); } }, top); } test('FR-T5:切换文档恢复各自滚动位置;新文档从顶部', async () => { const tmp = await fs.mkdtemp(nodePath.join(os.tmpdir(), 'hn-scroll-')); const userData = nodePath.join(tmp, 'ud'); const vault = nodePath.join(tmp, 'v'); await fs.mkdir(userData, { recursive: true }); await fs.mkdir(vault, { recursive: true }); const a = await launch(userData); await a.invoke('vault:switch', { root: vault }); await a.invoke('doc:write', { path: 'long.md', meta: {}, body: longBody('长文') }); await a.invoke('doc:write', { path: 'short.md', meta: {}, body: '# 短文\n\n很短的内容。' }); // 打开长文档,滚到中部,等 rAF 记录 await a.page.evaluate((p: string) => (globalThis as any).__STORE__?.getState?.().openDoc(p), 'long.md'); await a.page.waitForTimeout(1200); await setScroll(a.page, 1500); await a.page.waitForTimeout(400); // 切到新文档(短文)——应从顶部开始 await a.page.evaluate((p: string) => (globalThis as any).__STORE__?.getState?.().openDoc(p), 'short.md'); await a.page.waitForTimeout(400); const shortTop = await scrollerTop(a.page); expect(shortTop).toBeLessThanOrEqual(1); // 切回长文档——应恢复到上次滚动位置 await a.page.evaluate((p: string) => (globalThis as any).__STORE__?.getState?.().openDoc(p), 'long.md'); await a.page.waitForTimeout(400); const restored = await scrollerTop(a.page); expect(restored).toBeGreaterThan(1000); await a.app.close(); });