2026-09-03 19:29:38 +08:00
|
|
|
|
import { readFileSync, readdirSync } from 'node:fs'
|
|
|
|
|
|
import { extname, join, relative } from 'node:path'
|
|
|
|
|
|
import { describe, expect, it } from 'vitest'
|
|
|
|
|
|
|
|
|
|
|
|
const modulesRoot = join(process.cwd(), 'src/modules')
|
|
|
|
|
|
|
|
|
|
|
|
function vueFiles(directory: string): string[] {
|
|
|
|
|
|
return readdirSync(directory, { withFileTypes: true }).flatMap((entry) => {
|
|
|
|
|
|
const path = join(directory, entry.name)
|
|
|
|
|
|
if (entry.isDirectory()) return vueFiles(path)
|
|
|
|
|
|
return extname(entry.name) === '.vue' ? [path] : []
|
|
|
|
|
|
})
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
function isPermanentlyReadOnly(tag: string): boolean {
|
|
|
|
|
|
return (
|
|
|
|
|
|
/\s(?:disabled|readonly)(?=\s|\/>|>)/.test(tag) ||
|
|
|
|
|
|
/\s(?:disabled|readonly)=(?:""|'')/.test(tag) ||
|
|
|
|
|
|
/\s(?::|v-bind:)(?:disabled|readonly)=(?:"true"|'true')/.test(tag)
|
|
|
|
|
|
)
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
function hasMeaningfulPlaceholder(tag: string): boolean {
|
|
|
|
|
|
const value = tag.match(/\s(?::|v-bind:)?placeholder\s*=\s*(["'])(.*?)\1/)?.[2]
|
|
|
|
|
|
return Boolean(value?.trim())
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
describe('正式业务模块输入提示合同', () => {
|
2026-10-20 14:52:06 +08:00
|
|
|
|
it('查询条件的可编辑输入框保留 placeholder', () => {
|
2026-09-03 19:29:38 +08:00
|
|
|
|
const violations: string[] = []
|
|
|
|
|
|
|
|
|
|
|
|
for (const file of vueFiles(modulesRoot)) {
|
|
|
|
|
|
const source = readFileSync(file, 'utf8').replace(/<!--[\s\S]*?-->/g, (comment) =>
|
|
|
|
|
|
comment.replace(/[^\n]/g, ' '),
|
|
|
|
|
|
)
|
|
|
|
|
|
const openingTag = /<el-input(?:-number)?\b(?:"[^"]*"|'[^']*'|[^'">])*>/g
|
|
|
|
|
|
|
|
|
|
|
|
for (const match of source.matchAll(openingTag)) {
|
|
|
|
|
|
const tag = match[0]
|
2026-10-20 14:52:06 +08:00
|
|
|
|
if (!/\s(?:v-model(?::[\w-]+)?(?:\.[\w-]+)*|:model-value)="[^"]*(?:filter|query|search|keyword)/i.test(tag)) continue
|
2026-09-03 19:29:38 +08:00
|
|
|
|
if (isPermanentlyReadOnly(tag)) continue
|
|
|
|
|
|
if (hasMeaningfulPlaceholder(tag)) continue
|
|
|
|
|
|
|
|
|
|
|
|
const line = source.slice(0, match.index).split('\n').length
|
|
|
|
|
|
violations.push(
|
|
|
|
|
|
`${relative(modulesRoot, file).replaceAll('\\', '/')}:${line} ${tag.replace(/\s+/g, ' ')}`,
|
|
|
|
|
|
)
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
expect(violations, `缺少 placeholder:\n${violations.join('\n')}`).toEqual([])
|
|
|
|
|
|
})
|
|
|
|
|
|
})
|