118 lines
3.2 KiB
JavaScript
118 lines
3.2 KiB
JavaScript
const assert = require('node:assert/strict')
|
||
const test = require('node:test')
|
||
|
||
const {
|
||
getVoiceFailureHint,
|
||
getVoiceLessonStatusText,
|
||
getVoicePrimaryAction,
|
||
getVoicePrimaryActionText
|
||
} = require('../.test-dist/pages/feedback/voice-state.js')
|
||
const { runWithSessionRefresh } = require('../.test-dist/pages/feedback/voice-actions.js')
|
||
|
||
function session(overrides = {}) {
|
||
return {
|
||
id: 'session-1',
|
||
profileId: null,
|
||
feedbackDate: '2026-07-22',
|
||
content: '',
|
||
status: 'active',
|
||
lessonCount: 1,
|
||
totalDurationMs: 2800,
|
||
processingSegmentCount: 0,
|
||
failedSegmentCount: 0,
|
||
needsGeneration: false,
|
||
lessons: [],
|
||
createdAt: 0,
|
||
updatedAt: 0,
|
||
...overrides
|
||
}
|
||
}
|
||
|
||
test('failed segments take priority over processing and generation', () => {
|
||
const value = session({
|
||
failedSegmentCount: 2,
|
||
processingSegmentCount: 1,
|
||
needsGeneration: true
|
||
})
|
||
|
||
const action = getVoicePrimaryAction(value)
|
||
assert.equal(action, 'retry')
|
||
assert.equal(getVoicePrimaryActionText(action, value.failedSegmentCount), '重试转录(2)')
|
||
})
|
||
|
||
test('voice primary action follows retry, processing, then generation states', () => {
|
||
assert.equal(getVoicePrimaryAction(session({ failedSegmentCount: 1 })), 'retry')
|
||
assert.equal(getVoicePrimaryAction(session({ processingSegmentCount: 1 })), 'processing')
|
||
assert.equal(getVoicePrimaryAction(session({ needsGeneration: true })), 'generate')
|
||
})
|
||
|
||
test('no clear speech failures provide an actionable explanation', () => {
|
||
const value = session({
|
||
failedSegmentCount: 1,
|
||
lessons: [
|
||
{
|
||
segments: [
|
||
{
|
||
id: 'segment-1',
|
||
sequence: 1,
|
||
durationMs: 2800,
|
||
status: 'failed',
|
||
errorMessage: 'FunASR HTTP 422: no clear speech was recognized'
|
||
}
|
||
]
|
||
}
|
||
]
|
||
})
|
||
|
||
const hint = getVoiceFailureHint(value)
|
||
assert.match(hint, /录音过短、静音或环境噪声/)
|
||
assert.match(hint, /放弃该失败录音/)
|
||
})
|
||
|
||
test('ready lessons explain whether their transcript can be added again', () => {
|
||
const lesson = {
|
||
status: 'ready',
|
||
appliedDirectly: false,
|
||
includedInGeneration: false
|
||
}
|
||
|
||
assert.equal(getVoiceLessonStatusText(lesson), '可加入输入框')
|
||
assert.equal(getVoiceLessonStatusText({ ...lesson, appliedDirectly: true }), '可重新加入')
|
||
assert.equal(getVoiceLessonStatusText({ ...lesson, appliedDirectly: true }, true), '已在输入框')
|
||
assert.equal(getVoiceLessonStatusText({ ...lesson, includedInGeneration: true }), '已生成反馈')
|
||
})
|
||
|
||
test('session refresh runs after a failed API action', async () => {
|
||
const expected = new Error('generation rejected')
|
||
let refreshCount = 0
|
||
|
||
await assert.rejects(
|
||
runWithSessionRefresh(
|
||
async () => {
|
||
throw expected
|
||
},
|
||
async () => {
|
||
refreshCount += 1
|
||
}
|
||
),
|
||
expected
|
||
)
|
||
assert.equal(refreshCount, 1)
|
||
})
|
||
|
||
test('refresh failure does not replace the original API error', async () => {
|
||
const expected = new Error('generation rejected with request id')
|
||
|
||
await assert.rejects(
|
||
runWithSessionRefresh(
|
||
async () => {
|
||
throw expected
|
||
},
|
||
async () => {
|
||
throw new Error('refresh failed')
|
||
}
|
||
),
|
||
expected
|
||
)
|
||
})
|