mirror of
https://github.com/truewhile/MeBox.git
synced 2026-11-15 20:34:23 +08:00
101 lines
4.7 KiB
TypeScript
101 lines
4.7 KiB
TypeScript
import {
|
|
ALL_GROUP_ID,
|
|
UNGROUPED_GROUP_ID,
|
|
attachBookToGroup,
|
|
buildBookGroupTabs,
|
|
dedupeBookGroups,
|
|
detachBooksFromGroup,
|
|
filterBooksByGroup,
|
|
groupIdOfBook,
|
|
normalizeBookGroups,
|
|
reorderBookGroups,
|
|
resolveSelectedGroupId,
|
|
} from './readerBookGroups'
|
|
|
|
// 书架分组的纯逻辑回归测试(对齐 utils/libraryTags.test.ts 的写法)。
|
|
|
|
function check(name: string, condition: boolean) {
|
|
if (!condition) throw new Error(`readerBookGroups: ${name}`)
|
|
}
|
|
|
|
const book = (id: string) => ({ id })
|
|
|
|
// ── 清洗 ──
|
|
check('non-array → empty', normalizeBookGroups(null).length === 0)
|
|
check('blank name dropped', normalizeBookGroups([{ name: ' ', book_ids: ['a'] }]).length === 0)
|
|
check('name trimmed and ids deduped', (() => {
|
|
const got = normalizeBookGroups([{ name: ' 科幻 ', book_ids: ['a', '', 'a', 'b'] }])
|
|
return got.length === 1 && got[0].name === '科幻' && got[0].book_ids.join(',') === 'a,b'
|
|
})())
|
|
check('same name (case-insensitive) merged', (() => {
|
|
const got = normalizeBookGroups([
|
|
{ name: 'SciFi', book_ids: ['a'] },
|
|
{ name: 'scifi', book_ids: ['b'] },
|
|
])
|
|
return got.length === 1 && got[0].book_ids.join(',') === 'a,b'
|
|
})())
|
|
|
|
// ── 一书一组 ──
|
|
const claimed = dedupeBookGroups([
|
|
{ name: '一', book_ids: ['a', 'b'] },
|
|
{ name: '二', book_ids: ['b', 'c'] },
|
|
{ name: '三', book_ids: ['c'] },
|
|
])
|
|
check('dedupe keeps first claim', claimed[0].book_ids.join(',') === 'a,b')
|
|
// 二 原本 [b,c]:b 被 一 抢走后仍留住 c;三 的 [c] 已被 二 认领,因此变空
|
|
check('dedupe keeps the still-unclaimed member', claimed[1].book_ids.join(',') === 'c')
|
|
check('dedupe empties the group whose books were all claimed', claimed[2].book_ids.length === 0)
|
|
|
|
// ── 过滤 ──
|
|
const books = [book('a'), book('b'), book('c'), book('d')]
|
|
const groups = [{ name: '科幻', book_ids: ['a', 'b'] }, { name: '在读', book_ids: ['c'] }]
|
|
const ids = (list: { id: string }[]) => list.map((x) => x.id).join(',')
|
|
|
|
check('ALL returns everything', ids(filterBooksByGroup(books, groups, ALL_GROUP_ID)) === 'a,b,c,d')
|
|
check('empty id behaves like ALL', ids(filterBooksByGroup(books, groups, '')) === 'a,b,c,d')
|
|
check('group filter keeps members in input order', ids(filterBooksByGroup(books, groups, '科幻')) === 'a,b')
|
|
check('unknown group → empty', filterBooksByGroup(books, groups, '不存在').length === 0)
|
|
check('ungrouped returns unclaimed books', ids(filterBooksByGroup(books, groups, UNGROUPED_GROUP_ID)) === 'd')
|
|
check('stale ids in a group are ignored', (() => {
|
|
const withStale = [{ name: '科幻', book_ids: ['a', 'ghost'] }]
|
|
return ids(filterBooksByGroup(books, withStale, '科幻')) === 'a'
|
|
})())
|
|
|
|
// ── 分组栏 ──
|
|
const noGroups = buildBookGroupTabs(books, [])
|
|
check('no groups → only 全部', noGroups.length === 1 && noGroups[0].id === ALL_GROUP_ID && noGroups[0].count === 4)
|
|
|
|
const tabs = buildBookGroupTabs(books, groups)
|
|
check('tabs = 全部 + groups + 未分组(未分组殿后)', tabs.map((t) => t.name).join(',') === '全部,科幻,在读,未分组')
|
|
check('全部 counts all books', tabs[0].count === 4)
|
|
check('ungrouped is the last tab', tabs[tabs.length - 1].kind === 'ungrouped')
|
|
check('未分组 counts unclaimed', tabs[3].count === 1)
|
|
check('group counts only existing books', tabs[1].count === 2 && tabs[2].count === 1)
|
|
check('stale ids do not inflate counts', (() => {
|
|
const withStale = buildBookGroupTabs(books, [{ name: '科幻', book_ids: ['a', 'ghost', 'ghost2'] }])
|
|
return withStale[1].count === 1 && withStale[2].count === 3
|
|
})())
|
|
|
|
// ── 排序与选中回落 ──
|
|
check(
|
|
'reorder follows the given order',
|
|
reorderBookGroups(groups, ['在读', '科幻']).map((g) => g.name).join(',') === '在读,科幻',
|
|
)
|
|
check(
|
|
'reorder appends unknown names (never drops)',
|
|
reorderBookGroups(groups, ['在读']).map((g) => g.name).join(',') === '在读,科幻',
|
|
)
|
|
check('selected group falls back to ALL when removed', resolveSelectedGroupId(groups, '已删除') === ALL_GROUP_ID)
|
|
check('selected group kept when it exists', resolveSelectedGroupId(groups, '科幻') === '科幻')
|
|
check('ungrouped selection is preserved', resolveSelectedGroupId(groups, UNGROUPED_GROUP_ID) === UNGROUPED_GROUP_ID)
|
|
|
|
// ── 归组 ──
|
|
const moved = attachBookToGroup(groups, 'c', '科幻')
|
|
check('attach moves the book out of its old group', moved[1].book_ids.length === 0)
|
|
check('attach appends to the target group', moved[0].book_ids.join(',') === 'a,b,c')
|
|
check('attach to unknown group is a no-op', attachBookToGroup(groups, 'c', '不存在') === groups)
|
|
check('detach returns the book to ungrouped', detachBooksFromGroup(groups, ['a'])[0].book_ids.join(',') === 'b')
|
|
check('groupIdOfBook finds the owner', groupIdOfBook(groups, 'c') === '在读' && groupIdOfBook(groups, 'd') === '')
|
|
|
|
console.log('readerBookGroups.test.ts ok')
|