import assert from 'node:assert/strict'; import { readFile } from 'node:fs/promises'; import test from 'node:test'; const source = (path) => readFile(new URL(`../${path}`, import.meta.url), 'utf8'); test('login decorations cannot intercept touch input', async () => { const login = await source('src/pages/login/Login.vue'); const loginBox = await source('src/pages/login/LoginBox.vue'); assert.match(login, /&::before[\s\S]*?pointer-events:\s*none/); assert.match(login, /&::after[\s\S]*?pointer-events:\s*none/); assert.match(loginBox, /html-type="submit"/); assert.match(loginBox, /class="login-submit/); }); test('legacy WebViews do not depend on the :has selector to finish mounting', async () => { const html = await source('index.html'); const main = await source('src/main.ts'); assert.doesNotMatch(html, /:has\(/); assert.match(html, /#stepin-app\.app-mounted/); assert.match(main, /classList\.add\('app-mounted'\)/); }); test('mobile overlays stay above fixed navigation', async () => { const nav = await source('src/components/layout/MobileBottomNav.vue'); assert.match(nav, /z-index:\s*900/); assert.match(nav, /:z-index="1300"/); assert.match(nav, /destroy-on-close/); }); test('every primary mobile page provides an explicit title', async () => { const routes = await source('src/router/routes.ts'); for (const title of ['数据看板', '同步记录', '任务中心', '关注博主', '抖音授权', '系统设置', '系统日志']) { assert.match(routes, new RegExp(`mobileTitle: '${title}'`)); } }); test('failed migration record removal is explicit, responsive, and never starts sync automatically', async () => { const modal = await source('src/components/FailedMigrationRecordRemovalModal.vue'); assert.match(modal, /影响全部历史迁移任务/); assert.match(modal, /旧本地文件、已上传的 WebDAV 文件/); assert.match(modal, /下一次正常同步/); assert.match(modal, /RemoveStorageMigrationFailedRecords/); assert.doesNotMatch(modal, /StartTaskSync/); assert.match(modal, /max-height:\s*calc\(100dvh - 190px\)/); assert.match(modal, /grid-template-columns:\s*1fr 1fr/); }); test('live monitoring and email notification stay per-blogger and touch friendly', async () => { const follow = await source('src/pages/followd/index.vue'); const settings = await source('src/pages/set/AppSet.vue'); assert.match(follow, /直播监测/); assert.match(follow, /开播邮件/); assert.match(follow, /UpdateFollowLiveMonitor/); assert.match(follow, /UpdateFollowLiveEmail/); assert.match(follow, /每 5 分钟自动检查/); assert.match(follow, /\.live-monitor-row[\s\S]*?min-height:\s*44px/); assert.match(follow, /\.live-refresh-btn[\s\S]*?width:\s*44px/); assert.match(follow, /@media \(max-width:\s*768px\)[\s\S]*?\.card-main-content\s*\{[\s\S]*?display:\s*grid/); assert.match(follow, /\.card-content\s*\{[\s\S]*?display:\s*contents/); assert.match(follow, /\.live-monitor-row\s*\{[\s\S]*?grid-column:\s*1 \/ -1/); assert.match(follow, /\.card-path-sync-container\s*\{[\s\S]*?grid-template-columns:\s*minmax\(0, 1fr\) max-content/); assert.match(follow, /\.sync-switch-wrapper\s*\{[\s\S]*?flex-shrink:\s*0/); assert.match(settings, /邮箱通知/); assert.match(settings, /发送测试邮件/); assert.match(settings, /SSL\/TLS(常用 465)/); assert.match(settings, /STARTTLS(常用 587)/); }); test('storage capability detection is always visible and touch friendly in task center', async () => { const tasks = await source('src/pages/tasks/index.vue'); assert.match(tasks, /class="storage-probe-action"/); assert.match(tasks, /summary\.storageHealth\?\.status === 1 \? '重新检测存储' : '检测存储'/); assert.match(tasks, /CloudSyncOutlined/); assert.doesNotMatch(tasks, /v-if="summary\.storageHealth\?\.status === 1"[\s\S]{0,500}#action/); assert.match(tasks, /\.storage-probe-action\s*\{[\s\S]*?grid-column:\s*1 \/ -1/); assert.match(tasks, /\.task-actions :deep\(\.ant-btn\)\s*\{[\s\S]*?min-height:\s*44px/); });