3f6ad0c6ad
管理员/普通用户数据已由后端裁剪到本人可访问分组,分组维度对其无意义; 账号归属分组是超管职责,故只有超管需要按分组筛选与区分。 - 隐藏分组筛选+分组列:品牌数据库、查询ASIN、最低价ASIN、去重总数据、 店铺管理、用户密钥 - 仅隐藏分组展示:店铺数据记录(筛选框+卡片行)、图片视频任务(所属分组行)、 撞款检测(明细抽屉分组列+卡片分组标签) - 弹窗分组选择保留展示但锁定:非超管仅1个可访问分组时自动选中并置灰 (查询/最低价ASIN 另联动加载该分组店铺),多分组不锁以免限制用户 - 空数据行 colspan 随列显隐动态化 - 新增 tests/align-group-visibility.test.ts 回归守卫(5 条)
93 lines
4.6 KiB
TypeScript
93 lines
4.6 KiB
TypeScript
import test from 'node:test'
|
|
import assert from 'node:assert/strict'
|
|
import { readSource } from './helpers.ts'
|
|
|
|
/**
|
|
* 分组维度仅超管可见:非超管(管理员/普通用户)数据已由后端裁剪到本人分组,
|
|
* 后台各页的分组筛选与分组列一律不展示,只有超管需要按分组筛选/区分。
|
|
* 参考实现见撞款检测页(DuplicateCheckPage.vue 分组筛选用 isSuper 门控)。
|
|
*/
|
|
|
|
/** 页面 → 分组筛选门控片段(正则不做宽松匹配,确保门控真实存在)。 */
|
|
const FILTER_GATES: Array<[string, RegExp]> = [
|
|
['src/pages/asin/AsinInvalidPage.vue', /v-if="isSuperAdmin"[\s\S]{0,160}<label>分组<\/label>/],
|
|
['src/pages/asin/QueryAsinPage.vue', /v-if="isSuperAdmin"[\s\S]{0,160}<label>分组<\/label>/],
|
|
['src/pages/asin/SkipPricePage.vue', /v-if="isSuperAdmin"[\s\S]{0,160}<label>分组<\/label>/],
|
|
['src/pages/asin/DedupeRegistryPage.vue', /v-if="isSuperAdmin"[\s\S]{0,160}<label>分组<\/label>/],
|
|
['src/pages/shop/ShopManagePage.vue', /v-if="isSuperAdmin"[\s\S]{0,160}<label>分组<\/label>/],
|
|
['src/pages/tasks/ShopDataTasksPage.vue', /v-if="session\.isSuperAdmin"[\s\S]{0,160}模糊搜索分组名/],
|
|
['src/pages/account/UserSecretsPage.vue', /v-if="isSuperAdmin[\s\S]{0,160}<label>分组<\/label>/],
|
|
]
|
|
|
|
/** 页面 → 分组列表头门控片段。 */
|
|
const COLUMN_GATES: Array<[string, RegExp]> = [
|
|
['src/pages/asin/AsinInvalidPage.vue', /<th v-if="isSuperAdmin"[^>]*>分组<\/th>/],
|
|
['src/pages/asin/QueryAsinPage.vue', /<th v-if="isSuperAdmin"[^>]*>分组<\/th>/],
|
|
['src/pages/asin/SkipPricePage.vue', /<th v-if="isSuperAdmin"[^>]*>分组<\/th>/],
|
|
['src/pages/asin/DedupeRegistryPage.vue', /<th v-if="isSuperAdmin"[^>]*>分组<\/th>/],
|
|
['src/pages/shop/ShopManagePage.vue', /<th v-if="isSuperAdmin"[^>]*>分组<\/th>/],
|
|
['src/pages/tasks/DuplicateCheckPage.vue', /<th v-if="isSuper"[^>]*>分组<\/th>/],
|
|
['src/pages/account/UserSecretsPage.vue', /<th v-if="isSuperAdmin"[^>]*>分组<\/th>/],
|
|
]
|
|
|
|
test('align_group_visibility_filter_gated_by_super_admin', () => {
|
|
for (const [file, pattern] of FILTER_GATES) {
|
|
assert.match(readSource(file), pattern, `${file} 分组筛选未按超管门控`)
|
|
}
|
|
})
|
|
|
|
test('align_group_visibility_column_gated_by_super_admin', () => {
|
|
for (const [file, pattern] of COLUMN_GATES) {
|
|
assert.match(readSource(file), pattern, `${file} 分组列未按超管门控`)
|
|
}
|
|
})
|
|
|
|
test('align_group_visibility_no_ungated_group_column_header', () => {
|
|
// 不变量:以上页面的“分组”数据列表头必须带 v-if 门控,防止后续改动误放开。
|
|
// DuplicateCheckPage 的分组说明抽屉内另有一张分组统计表,该抽屉由已门控的按钮打开,
|
|
// 非超管无法触达,故不纳入本不变量(单独在 detail_cards 用例中校验按钮门控)。
|
|
const ungated = /<th(?![^>]*v-if)[^>]*>分组<\/th>/
|
|
for (const [file] of COLUMN_GATES) {
|
|
if (file.endsWith('DuplicateCheckPage.vue')) continue
|
|
assert.doesNotMatch(readSource(file), ungated, `${file} 存在未门控的分组列表头`)
|
|
}
|
|
})
|
|
|
|
test('align_group_visibility_detail_cards_gated', () => {
|
|
// 明细卡片/信息行里的分组展示同样只给超管。
|
|
assert.match(
|
|
readSource('src/pages/tasks/ImageVideoTasksPage.vue'),
|
|
/v-if="session\.isSuperAdmin"[\s\S]{0,120}所属分组/,
|
|
'图片视频任务页「所属分组」未门控',
|
|
)
|
|
assert.match(
|
|
readSource('src/pages/tasks/ShopDataTasksPage.vue'),
|
|
/v-if="session\.isSuperAdmin && row\.groupName"/,
|
|
'店铺数据记录页卡片分组行未门控',
|
|
)
|
|
assert.match(
|
|
readSource('src/pages/tasks/DuplicateCheckPage.vue'),
|
|
/v-if="isSuper && block\.groupName"/,
|
|
'撞款检测页卡片分组标签未门控',
|
|
)
|
|
// 分组说明抽屉整体为分组维度内容:入口按钮与分组范围切换条均已按超管门控。
|
|
const dup = readSource('src/pages/tasks/DuplicateCheckPage.vue')
|
|
assert.match(dup, /<button v-if="isSuper"[^>]*@click="groupInfoVisible = true"/, '分组说明入口未门控')
|
|
assert.match(dup, /<div v-if="isSuper" class="scope-bar"/, '分组撞款范围条未门控')
|
|
})
|
|
|
|
test('align_group_visibility_dialogs_keep_locked_selector', () => {
|
|
// 弹窗分组选择保留展示但非超管锁定(单分组时),不隐藏。
|
|
const lockedPages = [
|
|
'src/pages/asin/QueryAsinPage.vue',
|
|
'src/pages/asin/SkipPricePage.vue',
|
|
'src/pages/asin/DedupeRegistryPage.vue',
|
|
'src/pages/shop/ShopManagePage.vue',
|
|
]
|
|
for (const file of lockedPages) {
|
|
const src = readSource(file)
|
|
assert.match(src, /lockedGroupId/, `${file} 缺少弹窗分组锁定计算`)
|
|
assert.match(src, /:disabled="lockedGroupId != null"/, `${file} 弹窗分组未锁定`)
|
|
}
|
|
})
|