Smail/test/frontend/config-pagination.test.mjs
smail b32a41151b
Some checks are pending
Security Checks / security (push) Waiting to run
修复证书路径输入光标跳转
2026-09-08 10:06:51 +08:00

93 lines
4.7 KiB
JavaScript

import assert from 'node:assert/strict'
import test from 'node:test'
import { readFileSync } from 'node:fs'
import { compileFunction } from 'node:vm'
import { createRequire } from 'node:module'
const require = createRequire(new URL('../../frontend/package.json', import.meta.url))
const { computed, ref, watch, nextTick, effectScope } = require('vue')
const root = new URL('../../frontend/src/views/components/ConfigSettings/', import.meta.url)
const paginationSource = readFileSync(new URL('ConfigPagination/ConfigPagination.js', root), 'utf8')
const usePagination = compileFunction(
paginationSource.replace(/^import .*$/gm, '').replace('export function', 'function') + '\nreturn usePagination',
['computed', 'ref', 'watch']
)(computed, ref, watch)
test('列表分页保留源数据、绝对索引及最后一页边界', () => {
const scope = effectScope()
scope.run(() => {
const items = ref(Array.from({ length: 14 }, (_, index) => ({ id: index })))
const page = usePagination(items, 6)
assert.equal(page.pageCount.value, 3)
page.currentPage.value = 2.8
assert.equal(page.currentPage.value, 2)
assert.deepEqual(page.pageItems.value.map(item => item.index), [6, 7, 8, 9, 10, 11])
assert.equal(page.pageItems.value[0].entry, items.value[6])
page.currentPage.value = 999
assert.equal(page.pageItems.value.length, 2)
items.value.splice(12)
assert.equal(page.currentPage.value, 2)
items.value.push({ id: 14 })
assert.equal(page.currentPage.value, 2)
page.currentPage.value = -1
assert.equal(page.currentPage.value, 1)
assert.equal(items.value.length, 13)
items.value = []
assert.equal(page.pageCount.value, 1)
assert.deepEqual(page.pageItems.value, [])
})
scope.stop()
})
test('网络和文本列表跨页编辑、添加、删除仅更新目标条目', async () => {
const fieldSource = readFileSync(new URL('ConfigField/ConfigField.js', root), 'utf8')
const useField = compileFunction(
fieldSource.replace(/^import .*$/gm, '').replace(/^export \{.*$/gm, '').replace('export function', 'function') + '\nreturn useField',
['computed', 'nextTick', 'ref', 'watch', 'fieldKey', 'usePagination']
)(computed, nextTick, ref, watch, field => field.path.join('.'), usePagination)
for (const type of ['list', 'network']) {
const scope = effectScope()
const value = ref(type === 'list' ? ['a.example', 'b.example', 'c.example'] : [
{ scheme: 'https', host: 'a.example', port: 443 },
{ scheme: 'https', host: 'b.example', port: 443 },
{ scheme: 'https', host: 'c.example', port: 443 }
])
const props = { field: { path: ['targets'], type }, get value() { return value.value } }
const page = scope.run(() => useField(props, (_event, _path, updated) => { value.value = updated }))
const first = structuredClone(type === 'list' ? value.value[0] : { ...value.value[0] })
page.currentPage.value = 2
const index = page.pageItems.value[0].index
if (type === 'list') page.updateEntry(index, 'changed.example')
else page.updateTarget(index, 'host', 'changed.example')
assert.equal(type === 'list' ? value.value[2] : value.value[2].host, 'changed.example')
assert.deepEqual(type === 'list' ? value.value[0] : { ...value.value[0] }, first)
await page.addEntry()
assert.equal(page.currentPage.value, page.pageCount.value)
assert.equal(value.value.length, 4)
page.removeEntry(3)
page.removeEntry(2)
assert.equal(page.currentPage.value, 1)
assert.equal(value.value.length, 2)
scope.stop()
}
})
test('基础分区保留完整表单,分页和展开菜单模板均能编译', () => {
const { parse, compileScript, compileStyle } = require('@vue/compiler-sfc')
for (const name of ['ConfigPagination', 'ConfigForm', 'ConfigField', 'ConfigMaintenance']) {
const text = readFileSync(new URL(`${name}/${name}.vue`, root), 'utf8')
const { descriptor, errors } = parse(text)
assert.deepEqual(errors, [])
assert.doesNotThrow(() => compileScript(descriptor, { id: name, inlineTemplate: true }))
assert.doesNotMatch(text, /&#x[0-9a-f]+;/i)
}
const form = readFileSync(new URL('ConfigForm/ConfigForm.vue', root), 'utf8')
assert.doesNotMatch(form, /v-show="section !== 'base' \|\| activeGroup === group.id"/)
assert.doesNotMatch(form, /activeGroup|watch\(\(\) => props.errors/)
const css = readFileSync(new URL('ConfigForm/ConfigForm.css', root), 'utf8')
assert.deepEqual(compileStyle({ source: css, id: 'data-v-form', scoped: true }).errors, [])
const menu = readFileSync(new URL('../SettingsSubmenu/SettingsSubmenu.vue', root), 'utf8')
assert.match(menu, /v-if="item.children\?\.length"\s+class="settings-tertiary-list"/)
assert.doesNotMatch(menu, /v-if="item.children\?\.some/)
})