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 }; } async function newDoc(a: { page: any; invoke: any }, title: string) { await a.page.getByRole('button', { name: /新建文档/ }).click(); await a.page.getByPlaceholder(/输入文档标题/).fill(title); await a.page.getByPlaceholder(/输入文档标题/).press('Enter'); await expect(a.page.getByTestId('tabbar').getByText(title, { exact: true })).toBeVisible({ timeout: 10000 }); } // FR-E7:Alt+↑/↓ 上移/下移当前顶层块 test('FR-E7:Alt+↑/↓ 上移/下移当前块', async () => { const tmp = await fs.mkdtemp(nodePath.join(os.tmpdir(), 'hn-e7-')); const userData = nodePath.join(tmp, 'userdata'); const vault = nodePath.join(tmp, 'vault'); 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 newDoc(a, '块操作'); const editor = a.page.locator('.ProseMirror'); await editor.click(); await a.page.keyboard.type('first'); await a.page.keyboard.press('Enter'); await a.page.keyboard.type('second'); // 光标在第二段 'second' -> Alt+↑ 上移 -> 顺序变 second, first await a.page.keyboard.press('Alt+ArrowUp'); let p0 = (await a.page.locator('.ProseMirror p').nth(0).textContent() ?? '').trim(); let p1 = (await a.page.locator('.ProseMirror p').nth(1).textContent() ?? '').trim(); expect(p0).toBe('second'); expect(p1).toBe('first'); // Alt+↓ 下移回去 -> first, second await a.page.keyboard.press('Alt+ArrowDown'); p0 = (await a.page.locator('.ProseMirror p').nth(0).textContent() ?? '').trim(); p1 = (await a.page.locator('.ProseMirror p').nth(1).textContent() ?? '').trim(); expect(p0).toBe('first'); expect(p1).toBe('second'); await a.app.close(); }); // FR-E11:查找全命中高亮 + 替换 + 全部替换 test('FR-E11:查找高亮 + 替换 + 全部替换', async () => { const tmp = await fs.mkdtemp(nodePath.join(os.tmpdir(), 'hn-e11a-')); const userData = nodePath.join(tmp, 'userdata'); const vault = nodePath.join(tmp, 'vault'); 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 newDoc(a, '查找替换'); await a.page.locator('.ProseMirror').click(); await a.page.keyboard.type('foo bar foo baz foo'); await a.page.getByPlaceholder('查找').fill('foo'); await a.page.getByRole('button', { name: '查找' }).click(); await expect(a.page.locator('.ProseMirror .find-match')).toHaveCount(3, { timeout: 5000 }); await expect(a.page.locator('.ProseMirror .find-match-active')).toHaveCount(1); await a.page.getByPlaceholder('替换').fill('Y'); await a.page.getByRole('button', { name: '替换', exact: true }).click(); await expect(a.page.locator('.ProseMirror .find-match')).toHaveCount(2, { timeout: 5000 }); await a.page.getByPlaceholder('替换').fill('X'); await a.page.getByRole('button', { name: '全部替换' }).click(); await expect(a.page.locator('.ProseMirror .find-match')).toHaveCount(0, { timeout: 5000 }); const body = await a.page.evaluate(() => document.querySelector('.ProseMirror')?.textContent ?? ''); expect(body).not.toContain('foo'); await a.app.close(); }); // FR-E11:整词 + 区分大小写选项 test('FR-E11:整词 + 区分大小写选项', async () => { const tmp = await fs.mkdtemp(nodePath.join(os.tmpdir(), 'hn-e11b-')); const userData = nodePath.join(tmp, 'userdata'); const vault = nodePath.join(tmp, 'vault'); 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 newDoc(a, '查找选项'); await a.page.locator('.ProseMirror').click(); await a.page.keyboard.type('foo foobar foo'); await a.page.getByPlaceholder('查找').fill('foo'); await a.page.getByRole('button', { name: '查找' }).click(); await expect(a.page.locator('.ProseMirror .find-match')).toHaveCount(3, { timeout: 5000 }); // 整词 -> 排除 foobar -> 2 await a.page.getByTestId('fr-word').check(); await expect(a.page.locator('.ProseMirror .find-match')).toHaveCount(2, { timeout: 5000 }); // 区分大小写 + 查 'Foo' -> 0 await a.page.getByTestId('fr-case').check(); await a.page.getByPlaceholder('查找').fill('Foo'); await a.page.getByRole('button', { name: '查找' }).click(); await expect(a.page.locator('.ProseMirror .find-match')).toHaveCount(0, { timeout: 5000 }); await a.app.close(); });