import { test } from 'node:test' import assert from 'node:assert/strict' import { readFileSync } from 'node:fs' import { AVG_MODE_OPTIONS, loadAvgMode, selectAvgMode, settleAvgMode } from '../pages/avgMode' // 「平均值口径」只在录入页改;项目没有组件测试工具,界面部分按仓库既有做法读源码断言。 const ENTRY_SOURCE = readFileSync(new URL('../pages/EntryPage.vue', import.meta.url), 'utf8') const CHANNELS_SOURCE = readFileSync( new URL('../components/baseData/ChannelsSection.vue', import.meta.url), 'utf8', ) test('载入口径时归一化并清掉上一次的保存状态', () => { const state = loadAvgMode(108, 'all') assert.deepEqual(state, { eventId: 108, saved: 'all', mode: 'all', saving: false }) // 旧数据 / 非法值一律按 valid。 assert.equal(loadAvgMode(108, undefined).mode, 'valid') assert.equal(loadAvgMode(null, 'avg').mode, 'valid') }) test('选同一个口径或上一次保存未返回时不发请求', () => { const state = loadAvgMode(108, 'valid') const same = selectAvgMode(state, 'valid') assert.equal(same.shouldSave, false) assert.equal(same.state, state) const changed = selectAvgMode(state, 'all') assert.equal(changed.shouldSave, true) assert.equal(changed.state.mode, 'all') assert.equal(changed.state.saving, true) assert.equal(changed.state.saved, 'valid') // 在途请求只允许一个:保存中再点另一个口径被忽略。 const again = selectAvgMode(changed.state, 'valid') assert.equal(again.shouldSave, false) assert.equal(again.state.mode, 'all') }) test('保存成功记住新口径,失败退回原口径', () => { const pending = selectAvgMode(loadAvgMode(108, 'valid'), 'all').state const ok = settleAvgMode(pending, true) assert.deepEqual(ok, { eventId: 108, saved: 'all', mode: 'all', saving: false }) const failed = settleAvgMode(pending, false) assert.deepEqual(failed, { eventId: 108, saved: 'valid', mode: 'valid', saving: false }) // 没有在途请求时回落是空操作。 const idle = loadAvgMode(108, 'valid') assert.equal(settleAvgMode(idle, true), idle) }) test('口径选项覆盖两种分母口径与「按 0 计入」提示', () => { assert.deepEqual( AVG_MODE_OPTIONS.map((option) => option.value), ['valid', 'all'], ) assert.ok(AVG_MODE_OPTIONS.some((option) => option.label === '只算有效成绩')) assert.ok(AVG_MODE_OPTIONS.some((option) => option.label === '全部报名人员都计入' && option.hint === '没成绩的按 0 计入平均')) }) test('录入页只在存在 aggregate 通道时显示口径选择,并整体回写小项', () => { assert.match(ENTRY_SOURCE, /hasAggregateChannel\(channels\.value\)/) assert.match(ENTRY_SOURCE, /平均值口径/) assert.match(ENTRY_SOURCE, /v-if="showAvgMode"/) // 口径改动走 PUT /api/events/{id},不能混进成绩草稿的保存流程。 assert.match(ENTRY_SOURCE, /client\.events\.update\(event\.id, eventInputOf\(event, \{ avgMode: mode \}\)\)/) }) test('计分通道页支持「按队内平均」与「计入单位总分」开关', () => { assert.match(CHANNELS_SOURCE, /\{ value: 'aggregate', label: '按队内平均' \}/) assert.match(CHANNELS_SOURCE, /团体成绩 = 该单位成员成绩的平均值/) assert.match(CHANNELS_SOURCE, /计入单位总分/) assert.match(CHANNELS_SOURCE, /只记在个人头上/) assert.match(CHANNELS_SOURCE, /countToTeam: row\.countToTeam/) })