127 lines
4.3 KiB
TypeScript
127 lines
4.3 KiB
TypeScript
/**
|
||
* 16-assets: 资源完整性——静态扫描 + 运行时路径校验
|
||
* 原测试: Node fs 扫描 → 保留同样的扫描逻辑
|
||
*/
|
||
import { describe, it, expect } from 'vitest'
|
||
import { existsSync, readFileSync, readdirSync } from 'fs'
|
||
import { join, resolve } from 'path'
|
||
import { fileURLToPath } from 'url'
|
||
import { WALLPAPERS } from '../../src/composables/useSettings'
|
||
|
||
const __filename = fileURLToPath(import.meta.url)
|
||
const __dirname = join(__filename, '..')
|
||
const ROOT = resolve(__dirname, '../..')
|
||
const PUBLIC = join(ROOT, 'public')
|
||
|
||
describe('16-assets — 资源完整性', () => {
|
||
// ===== 静态扫描:收集所有 assets/ 引用 =====
|
||
function collectAssetRefs(): string[] {
|
||
const refs = new Set<string>()
|
||
const srcDir = join(ROOT, 'src')
|
||
const files = walkFiles(srcDir).concat([join(ROOT, 'index.html')])
|
||
|
||
for (const file of files) {
|
||
try {
|
||
const content = readFileSync(file, 'utf-8')
|
||
for (const m of content.matchAll(/(?:src|href|icon)=["']\/?(assets\/[^"']+?)["']/g)) {
|
||
refs.add(m[1])
|
||
}
|
||
} catch { /* ignore */ }
|
||
}
|
||
return [...refs].sort()
|
||
}
|
||
|
||
function walkFiles(dir: string): string[] {
|
||
const result: string[] = []
|
||
try {
|
||
for (const e of readdirSync(dir, { withFileTypes: true })) {
|
||
const full = join(dir, e.name)
|
||
if (e.isDirectory() && e.name !== 'node_modules') result.push(...walkFiles(full))
|
||
else if (/\.(ts|vue|css|html|js)$/.test(e.name)) result.push(full)
|
||
}
|
||
} catch { /* ignore */ }
|
||
return result
|
||
}
|
||
|
||
// ===== WALLPAPERS =====
|
||
it('所有 WALLPAPERS src 指向的文件存在', () => {
|
||
const missing: string[] = []
|
||
for (const w of WALLPAPERS) {
|
||
const p = join(PUBLIC, w.src)
|
||
if (!existsSync(p)) missing.push(w.src)
|
||
}
|
||
expect(missing).toEqual([])
|
||
})
|
||
|
||
it('bigsur 暗色变体文件存在', () => {
|
||
const bigsur = WALLPAPERS.find(w => w.id === 'bigsur')!
|
||
const p = join(PUBLIC, bigsur.dark!)
|
||
expect(existsSync(p)).toBe(true)
|
||
})
|
||
|
||
// ===== 关键图标 =====
|
||
const requiredIcons = [
|
||
'finder.png', 'folder.svg', 'txt.svg', 'trash.svg', 'trash-full.svg',
|
||
'settings.png', 'safari.png', 'calendar.png', 'calculator.png',
|
||
'terminal.png', 'music.png', 'photos.png', 'notes.png', 'appstore.png',
|
||
'launchpad.png', 'messages.png', 'facetime.png', 'maps.png', 'mail.png',
|
||
'preview.svg', 'quicktime.svg', 'activitymonitor.svg', 'textedit.svg',
|
||
]
|
||
|
||
it('关键应用图标存在', () => {
|
||
const missing: string[] = []
|
||
for (const icon of requiredIcons) {
|
||
const p = join(PUBLIC, 'assets/icons', icon)
|
||
if (!existsSync(p)) missing.push(icon)
|
||
}
|
||
expect(missing).toEqual([])
|
||
})
|
||
|
||
// ===== 音频和视频目录 =====
|
||
it('assets/audio 目录存在且含 mp3 文件', () => {
|
||
const audioDir = join(PUBLIC, 'assets/audio')
|
||
expect(existsSync(audioDir)).toBe(true)
|
||
const files = readdirSync(audioDir).filter((f: string) => f.endsWith('.mp3'))
|
||
expect(files.length).toBeGreaterThanOrEqual(6)
|
||
})
|
||
|
||
it('assets/video 目录存在且含 mp4/webm 文件', () => {
|
||
const videoDir = join(PUBLIC, 'assets/video')
|
||
expect(existsSync(videoDir)).toBe(true)
|
||
const files = readdirSync(videoDir)
|
||
expect(files.length).toBeGreaterThanOrEqual(6)
|
||
})
|
||
|
||
// ===== 静态引用完整性 =====
|
||
it('代码中引用的 assets 路径指向存在的文件', () => {
|
||
const refs = collectAssetRefs()
|
||
const missing: string[] = []
|
||
for (const ref of refs) {
|
||
// 跳过模板变量(如 ${src})
|
||
if (ref.includes('${') || ref.includes('{')) continue
|
||
const p = join(PUBLIC, ref)
|
||
if (!existsSync(p)) missing.push(ref)
|
||
}
|
||
expect(missing).toEqual([])
|
||
})
|
||
|
||
// ===== Store 图标 =====
|
||
const storeIcons = ['bear.png', 'typora.png', 'vscode.png', 'keynote.png', 'podcasts.png', 'news.png', 'github.png', 'tv.png']
|
||
it('Store 应用图标存在', () => {
|
||
const missing: string[] = []
|
||
for (const icon of storeIcons) {
|
||
if (!existsSync(join(PUBLIC, 'assets/icons', icon))) missing.push(icon)
|
||
}
|
||
expect(missing).toEqual([])
|
||
})
|
||
|
||
// ===== 其他资源 =====
|
||
it('avatar.svg 存在', () => {
|
||
expect(existsSync(join(PUBLIC, 'assets/icons/avatar.svg'))).toBe(true)
|
||
})
|
||
|
||
it('pdf.svg 存在', () => {
|
||
expect(existsSync(join(PUBLIC, 'assets/icons/pdf.svg'))).toBe(true)
|
||
})
|
||
})
|