up
This commit is contained in:
@@ -4,6 +4,7 @@ import { Controller, parseCommand } from './controller.js'
|
||||
import { buildScreenPackage } from './screen-package.js'
|
||||
import { packageKey, type LocalPackage } from './package-store.js'
|
||||
import { parseRange } from './range.js'
|
||||
import { applyRemote, parseRemote } from './remote-state.js'
|
||||
import type { Channel, Intent, Report } from '../src/types/screen.js'
|
||||
|
||||
function testPackage(): LocalPackage {
|
||||
@@ -155,6 +156,7 @@ test('музыка независима от основного канала, mu
|
||||
ack(c, 'music', 'playing', 15)
|
||||
send(c, { action: 'select', channel: 'main', sourceId: 'conclusion' })
|
||||
send(c, { action: 'play', channel: 'main' })
|
||||
send(c, { action: 'resume', channel: 'music' })
|
||||
ack(c, 'main', 'playing', 20)
|
||||
const operation = c.state.channels.main.operation
|
||||
send(c, { action: 'volume', channel: 'main', volume: 80 })
|
||||
@@ -257,3 +259,117 @@ test('обновление порядка сохраняет текущий фа
|
||||
send(c, { action: 'next', channel: 'music' })
|
||||
assert.equal(c.state.channels.music.trackIndex, 0)
|
||||
})
|
||||
|
||||
for (const mode of [2, 4] as const) {
|
||||
test(`локальная автопауза: режим ${mode}, шаг и запуск медиа сохраняют позицию музыки`, () => {
|
||||
const entries: Intent[] = [
|
||||
{ action: 'mode', mode },
|
||||
{ action: 'step', index: 0 },
|
||||
{ action: 'select', channel: 'main', sourceId: mode === 2 ? 'intro' : 'conclusion' },
|
||||
{ action: 'play', channel: 'main' },
|
||||
]
|
||||
for (const entry of entries) {
|
||||
const c = controller()
|
||||
c.selectPackage(testPackage())
|
||||
c.setPlan([{ mode, text: '', timerLabel: '', timerDurationSeconds: 30 }])
|
||||
if (entry.action === 'play' && mode === 4) {
|
||||
send(c, { action: 'select', channel: 'main', sourceId: 'conclusion' })
|
||||
send(c, { action: 'mode', mode: 1 })
|
||||
}
|
||||
send(c, { action: 'play', channel: 'music' })
|
||||
ack(c, 'music', 'playing', 17)
|
||||
const seek = c.state.channels.music.seekOperation
|
||||
send(c, entry)
|
||||
assert.equal(c.state.channels.music.desired, 'paused')
|
||||
assert.equal(c.state.channels.music.position, 17)
|
||||
assert.equal(c.state.channels.music.seekOperation, seek)
|
||||
ack(c, 'music', 'paused', 18)
|
||||
send(c, { action: 'mode', mode: mode === 2 ? 4 : 2 })
|
||||
assert.equal(c.state.channels.music.desired, 'paused')
|
||||
send(c, { action: 'text', text: 'Другой экран' })
|
||||
assert.equal(c.state.channels.music.desired, 'playing')
|
||||
assert.equal(c.state.channels.music.position, 18)
|
||||
assert.equal(c.state.channels.music.seekOperation, seek)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
test('ручные команды музыки отменяют автопродолжение до следующего входа', () => {
|
||||
const commands: Intent[] = [
|
||||
{ action: 'pause', channel: 'music' }, { action: 'stop', channel: 'music' },
|
||||
{ action: 'volume', channel: 'music', volume: 40 },
|
||||
{ action: 'loop', channel: 'music', enabled: true },
|
||||
{ action: 'seek', channel: 'music', position: 20 },
|
||||
{ action: 'playlist', channel: 'music', playlistId: 1 },
|
||||
{ action: 'track', channel: 'music', trackIndex: 1 },
|
||||
{ action: 'next', channel: 'music' }, { action: 'resume', channel: 'music' },
|
||||
{ action: 'play', channel: 'music' }, { action: 'replay', channel: 'music' },
|
||||
]
|
||||
for (const command of commands) {
|
||||
const c = controller()
|
||||
c.selectPackage(testPackage())
|
||||
send(c, { action: 'play', channel: 'music' })
|
||||
send(c, { action: 'mode', mode: 2 })
|
||||
send(c, command)
|
||||
const music = structuredClone(c.state.channels.music)
|
||||
send(c, { action: 'mode', mode: 4 })
|
||||
send(c, { action: 'mode', mode: 1 })
|
||||
assert.deepEqual(c.state.channels.music, music, command.action)
|
||||
send(c, { action: 'resume', channel: 'music' })
|
||||
send(c, { action: 'mode', mode: 2 })
|
||||
assert.equal(c.state.channels.music.desired, 'paused')
|
||||
}
|
||||
})
|
||||
|
||||
test('автопауза не запускает остановленную музыку и сбрасывается с сессией, игрой и ошибкой', () => {
|
||||
for (const initial of ['stop', 'pause'] as const) {
|
||||
const c = controller()
|
||||
c.selectPackage(testPackage())
|
||||
send(c, { action: initial, channel: 'music' })
|
||||
send(c, { action: 'mode', mode: 2 })
|
||||
send(c, { action: 'mode', mode: 1 })
|
||||
assert.equal(c.state.channels.music.desired, initial === 'stop' ? 'stopped' : 'paused')
|
||||
}
|
||||
for (const reset of ['session', 'game', 'error']) {
|
||||
const c = controller()
|
||||
c.selectPackage(testPackage())
|
||||
send(c, { action: 'play', channel: 'music' })
|
||||
send(c, { action: 'mode', mode: 2 })
|
||||
if (reset === 'session') { c.output(false); c.output(true) }
|
||||
else if (reset === 'game') c.selectPackage(testPackage())
|
||||
else ack(c, 'music', 'error')
|
||||
send(c, { action: 'mode', mode: 1 })
|
||||
assert.notEqual(c.state.channels.music.desired, 'playing')
|
||||
}
|
||||
})
|
||||
|
||||
test('серверная синхронизация не отменяет локальную паузу; новая музыкальная команда имеет приоритет', () => {
|
||||
const music = { stateVersion: '1', playlist: { ID: 1, tracks: [{ ID: 1 }, { ID: 2 }] }, trackIndex: 0, playback: 2, positionMs: 10000, volume: 80, loop: true }
|
||||
for (const initialMode of [1, 2, 4]) {
|
||||
const snapshots: { mode: number; desired: string }[] = []
|
||||
const c = new Controller((state) => snapshots.push({ mode: state.scene.mode, desired: state.channels.music.desired }))
|
||||
c.selectPackage(testPackage())
|
||||
c.output(true)
|
||||
const start = parseRemote({ mode: initialMode, stateVersion: '1', music })
|
||||
applyRemote(c, parseRemote({}), start)
|
||||
const intro = parseRemote({ mode: 2, stateVersion: '2', music })
|
||||
applyRemote(c, start, intro)
|
||||
assert.equal(c.state.channels.music.desired, 'paused')
|
||||
ack(c, 'music', 'paused', 11)
|
||||
const operation = c.state.channels.music.operation
|
||||
applyRemote(c, intro, intro)
|
||||
assert.equal(c.state.channels.music.operation, operation)
|
||||
assert.equal(snapshots.some(s => [2, 4].includes(s.mode) && s.desired === 'playing'), false)
|
||||
const end = parseRemote({ mode: 1, stateVersion: '3', music })
|
||||
applyRemote(c, intro, end)
|
||||
assert.equal(c.state.channels.music.desired, 'playing')
|
||||
assert.equal(c.state.channels.music.position, 11)
|
||||
const again = parseRemote({ mode: 4, stateVersion: '4', music })
|
||||
applyRemote(c, end, again)
|
||||
const manualPause = parseRemote({ mode: 4, stateVersion: '4', music: { ...music, stateVersion: '2', playback: 3 } })
|
||||
applyRemote(c, again, manualPause)
|
||||
const after = parseRemote({ mode: 1, stateVersion: '5', music: { ...music, stateVersion: '2', playback: 3 } })
|
||||
applyRemote(c, manualPause, after)
|
||||
assert.equal(c.state.channels.music.desired, 'paused')
|
||||
}
|
||||
})
|
||||
|
||||
Reference in New Issue
Block a user