diff --git a/electron/main.ts b/electron/main.ts index ed96173..be93b2e 100644 --- a/electron/main.ts +++ b/electron/main.ts @@ -392,6 +392,7 @@ app ) await online.initialize() if (quitting || startupWindow.isDestroyed()) return + void online.restoreGames() let previousConnection = online.state.connection const logConnection = () => { const status = online!.state.connection diff --git a/electron/online.test.ts b/electron/online.test.ts index 8b7b538..3b51777 100644 --- a/electron/online.test.ts +++ b/electron/online.test.ts @@ -938,6 +938,97 @@ test('вход, подготовка и зашифрованные секрет assert.equal(c.state.channels.main.desired, 'playing') }) +test('без сохранённого входа запуск не запрашивает список игр', async (t) => { + const f = await fixture(t) + await f.online.restoreGames() + assert.equal(f.requests.length, 0) +}) + +for (const expired of [false, true]) { + test(`после восстановления входа список игр загружается без кнопки; токен просрочен: ${expired}`, async (t) => { + const f = await fixture(t) + await f.login() + await f.online.close() + f.requests.length = 0 + f.model.accessExpired = expired + const restored = new Online( + join(f.directory, 'connection'), f.store, new Controller(() => {}), f.secrets, f.fetchAPI, + ) + t.after(() => restored.close()) + await restored.initialize() + await restored.restoreGames() + assert.deepEqual(restored.snapshot().games, [{ id: 7, name: 'Дело', status: 'finish' }]) + assert.equal(restored.snapshot().error, '') + assert.equal(restored.snapshot().busy, false) + assert.equal(restored.snapshot().screenId, null) + assert.equal(f.model.refreshCalls, expired ? 1 : 0) + assert.ok(f.requests.every(r => ['/api/games', '/api/auth/refresh'].includes(r.path))) + }) +} + +test('запуск без сети сохраняет доступ к локальной игре; список можно обновить позже', async (t) => { + const f = await fixture(t) + await f.login() + await f.online.act({ action: 'select-game', gameId: 7 }) + await f.online.close() + f.model.offline = true + const controller = new Controller(() => {}) + const restored = new Online( + join(f.directory, 'connection'), f.store, controller, f.secrets, f.fetchAPI, + ) + t.after(() => restored.close()) + await restored.initialize() + const selected = controller.state.catalog.key + assert.ok(selected) + await restored.restoreGames() + assert.match(restored.snapshot().error, /Сервер недоступен/) + assert.equal(restored.snapshot().busy, false) + controller.output(true) + assert.equal(controller.state.outputOpen, true) + assert.equal(controller.state.catalog.key, selected) + f.model.offline = false + await restored.act({ action: 'games' }) + assert.equal(restored.snapshot().games.length, 1) + assert.equal(restored.snapshot().error, '') +}) + +for (const fails of [false, true]) { + test(`ответ фоновой загрузки после выхода игнорируется; ошибка: ${fails}`, async (t) => { + const f = await fixture(t) + await f.login() + await f.online.close() + let release!: () => void + let started!: () => void + const gate = new Promise(resolve => { release = resolve }) + const requestStarted = new Promise(resolve => { started = resolve }) + const fetchAPI: typeof fetch = async (input, init) => { + const response = await f.fetchAPI(input, init) + const read = response.json.bind(response) + response.json = async () => { + started() + await gate + if (fails) throw new Error('network failure') + return read() + } + return response + } + const restored = new Online( + join(f.directory, 'connection'), f.store, new Controller(() => {}), f.secrets, fetchAPI, + ) + t.after(() => restored.close()) + await restored.initialize() + const pending = restored.restoreGames() + await requestStarted + assert.equal(restored.snapshot().busy, false) + await restored.act({ action: 'logout' }) + release() + await pending + assert.equal(restored.snapshot().authenticated, false) + assert.deepEqual(restored.snapshot().games, []) + assert.equal(restored.snapshot().error, '') + }) +} + test('без хранилища ОС вход только в памяти; выход не останавливает показ', async (t) => { const f = await fixture(t, false) await f.prepare() diff --git a/electron/online.ts b/electron/online.ts index 90fbd5d..375f375 100644 --- a/electron/online.ts +++ b/electron/online.ts @@ -1668,8 +1668,20 @@ export class Online { await this.refreshMaterials() await this.poll() } + async restoreGames() { + if (!this.credentials) return + const generation = this.generation + try { + await this.loadGames() + } catch (error) { + if (generation === this.generation && !this.state.error) + this.state.error = error instanceof Error ? error.message : 'Не удалось загрузить список игр' + } + } private async loadGames() { + const generation = this.generation const result = await this.client().GetGames({}) + if (generation !== this.generation) throw new SessionChangedError() if (!Array.isArray(result.games ?? [])) throw new Error('Некорректный список игр') this.state.games = ((result.games ?? []) as unknown[]).map((value) => { const g = object(value)