up
This commit is contained in:
+77
-1
@@ -331,6 +331,38 @@ async function fixture(t: TestContext, secure = true) {
|
||||
prepare,
|
||||
}
|
||||
}
|
||||
test('подтверждение игры загружает материалы без подключения экрана и сохраняет выбор', async (t) => {
|
||||
const f = await fixture(t)
|
||||
await f.login()
|
||||
await f.online.act({ action: 'select-game', gameId: 7 })
|
||||
const selected = f.online.snapshot()
|
||||
assert.equal(selected.currentGameId, 7)
|
||||
assert.equal(selected.screenId, null)
|
||||
assert.equal(selected.connection, 'disabled')
|
||||
assert.ok(selected.materials.length > 0)
|
||||
assert.ok(selected.materials.every((m) => m.status === 'ready'))
|
||||
assert.equal(f.model.bindings.length, 0)
|
||||
assert.ok(!f.requests.some((r) => r.path === '/api/screens'))
|
||||
const saved = JSON.parse(await readFile(join(f.directory, 'connection', 'selection.json'), 'utf8'))
|
||||
assert.equal(saved.key, f.controller.state.catalog.key)
|
||||
await f.online.act({ action: 'connect' })
|
||||
assert.equal(f.online.snapshot().screenId, '12')
|
||||
assert.equal(f.model.bindings.at(-1)!.gameID, 7)
|
||||
})
|
||||
|
||||
test('ошибка подготовки и открытый показ не подтверждают выбор игры', async (t) => {
|
||||
const f = await fixture(t)
|
||||
await f.login()
|
||||
f.controller.output(true)
|
||||
await assert.rejects(f.online.act({ action: 'select-game', gameId: 7 }), /Закройте показ/)
|
||||
f.controller.output(false)
|
||||
f.model.failMedia = true
|
||||
await assert.rejects(f.online.act({ action: 'select-game', gameId: 7 }))
|
||||
assert.equal(f.controller.state.catalog.key, null)
|
||||
assert.equal(f.online.snapshot().currentGameId, null)
|
||||
assert.equal(f.model.bindings.length, 0)
|
||||
})
|
||||
|
||||
test('чистый профиль предлагает адрес сайта по умолчанию', async (t) => {
|
||||
const f = await fixture(t)
|
||||
assert.equal(f.online.snapshot().server, 'https://evening-detective.crabs-games.art')
|
||||
@@ -446,6 +478,50 @@ for (const gameId of [7, 8]) {
|
||||
})
|
||||
}
|
||||
|
||||
for (const status of [200, 404]) {
|
||||
test(`отключение отвязывает экран, сохраняет игру и переживает перезапуск: ${status}`, async (t) => {
|
||||
const f = await fixture(t)
|
||||
await f.prepare()
|
||||
const key = f.controller.state.catalog.key
|
||||
const materials = f.online.snapshot().materials
|
||||
f.model.unbindError = status === 200 ? 0 : status
|
||||
await f.online.act({ action: 'disconnect' })
|
||||
assert.equal(f.requests.at(-1)!.path, '/api/screens/12/unbind')
|
||||
assert.equal(f.online.snapshot().screenId, null)
|
||||
assert.equal(f.online.snapshot().connection, 'disabled')
|
||||
assert.equal(f.controller.state.catalog.key, key)
|
||||
assert.deepEqual(f.online.snapshot().materials, materials)
|
||||
const saved = JSON.parse(f.secrets.decrypt(await readFile(join(f.directory, 'connection', 'credentials.bin'))))
|
||||
assert.equal(saved.screen, null)
|
||||
const restored = new Online(join(f.directory, 'connection'), f.store, new Controller(() => {}), f.secrets, f.fetchAPI)
|
||||
t.after(() => restored.close())
|
||||
await restored.initialize()
|
||||
assert.equal(restored.snapshot().screenId, null)
|
||||
assert.equal(restored.snapshot().currentGameId, 7)
|
||||
const count = f.requests.length
|
||||
await f.online.act({ action: 'disconnect' })
|
||||
assert.equal(f.requests.length, count)
|
||||
})
|
||||
}
|
||||
|
||||
for (const failure of ['server', 'network'] as const) {
|
||||
test(`ошибка отключения сохраняет сессию для повтора: ${failure}`, async (t) => {
|
||||
const f = await fixture(t)
|
||||
await f.prepare()
|
||||
const saved = await readFile(join(f.directory, 'connection', 'credentials.bin'))
|
||||
f.model.unbindError = failure === 'server' ? 500 : 0
|
||||
f.model.offline = failure === 'network'
|
||||
await assert.rejects(f.online.act({ action: 'disconnect' }))
|
||||
assert.equal(f.online.snapshot().screenId, '12')
|
||||
assert.equal(f.online.snapshot().connection, 'waiting')
|
||||
assert.deepEqual(await readFile(join(f.directory, 'connection', 'credentials.bin')), saved)
|
||||
f.model.unbindError = 0
|
||||
f.model.offline = false
|
||||
await f.online.act({ action: 'disconnect' })
|
||||
assert.equal(f.online.snapshot().screenId, null)
|
||||
})
|
||||
}
|
||||
|
||||
test('ошибка отвязки сохраняет прежнюю сессию и не создаёт новую', async (t) => {
|
||||
const f = await fixture(t)
|
||||
await f.prepare()
|
||||
@@ -1001,7 +1077,7 @@ test('назначение звука подтверждается сервер
|
||||
const unbindsBeforeDisconnect = f.requests.filter((r) => r.path.endsWith('/unbind')).length
|
||||
await f.online.act({ action: 'disconnect' })
|
||||
assert.equal(f.online.state.soundEnabled, null)
|
||||
assert.equal(f.requests.filter((r) => r.path.endsWith('/unbind')).length, unbindsBeforeDisconnect)
|
||||
assert.equal(f.requests.filter((r) => r.path.endsWith('/unbind')).length, unbindsBeforeDisconnect + 1)
|
||||
})
|
||||
|
||||
test('план: сохранение и удаление на сервере, восстановление копии офлайн, ошибки валидации и доступа', async (t) => {
|
||||
|
||||
+18
-2
@@ -1171,6 +1171,7 @@ export class Online {
|
||||
login: 'Вход на сервер…',
|
||||
logout: 'Выход из аккаунта…',
|
||||
games: 'Загрузка списка игр…',
|
||||
'select-game': 'Подготовка выбранной игры…',
|
||||
connect: 'Подключение игры…',
|
||||
disconnect: 'Отключение игры…',
|
||||
download: 'Загрузка материалов игры…',
|
||||
@@ -1278,10 +1279,13 @@ export class Online {
|
||||
return
|
||||
case 'games':
|
||||
return this.loadGames()
|
||||
case 'select-game':
|
||||
case 'download':
|
||||
case 'check': {
|
||||
const gameId = integer(data.gameId, 1)
|
||||
const key = packageKey(this.state.server, gameId)
|
||||
if (action === 'select-game' && this.controller.state.outputOpen)
|
||||
throw new Error('Закройте показ перед выбором игры')
|
||||
if (
|
||||
action === 'download' &&
|
||||
key === this.controller.state.catalog.key &&
|
||||
@@ -1289,7 +1293,7 @@ export class Online {
|
||||
)
|
||||
throw new Error('Закройте показ перед обновлением выбранной игры')
|
||||
if (!this.credentials) throw new ApiError(401)
|
||||
this.downloadKey = action === 'download' ? key : null
|
||||
this.downloadKey = action === 'check' ? null : key
|
||||
this.cancellation = new AbortController()
|
||||
this.state.canCancel = true
|
||||
try {
|
||||
@@ -1324,7 +1328,8 @@ export class Online {
|
||||
},
|
||||
})
|
||||
this.state.packagesRevision++
|
||||
if (key === this.controller.state.catalog.key) {
|
||||
if (action === 'select-game' || key === this.controller.state.catalog.key) {
|
||||
if (action === 'select-game') this.detach()
|
||||
this.controller.selectPackage(pkg)
|
||||
this.plan = {
|
||||
steps: structuredClone(this.controller.state.catalog.steps),
|
||||
@@ -1335,6 +1340,10 @@ export class Online {
|
||||
await this.atomic(`plan-${key}.json`, this.cachedPlan)
|
||||
this.previous = null
|
||||
await this.restoreResults(key)
|
||||
if (action === 'select-game') {
|
||||
await this.saveSelection()
|
||||
await this.persistSecrets()
|
||||
}
|
||||
}
|
||||
this.state.notice = 'Материалы проверены и готовы к показу'
|
||||
} finally {
|
||||
@@ -1406,6 +1415,13 @@ export class Online {
|
||||
return
|
||||
}
|
||||
case 'disconnect':
|
||||
if (this.screen) {
|
||||
try {
|
||||
await this.client().UnbindScreen({ screenID: this.screen.id })
|
||||
} catch (error) {
|
||||
if (!(error instanceof ApiError && error.status === 404)) throw error
|
||||
}
|
||||
}
|
||||
this.detach()
|
||||
await this.persistSecrets()
|
||||
return
|
||||
|
||||
Reference in New Issue
Block a user