feat(images): report broken covers and achievement icons
Build, Test & Push Frontend / quality-check (push) Successful in 15s
Build, Test & Push Frontend / docker (push) Successful in 22s

This commit is contained in:
lobo committed 2026-09-26 12:07:47 +02:00
1 parent ca75e6a530
commit 42ed0afe51
9 files changed
+173 -9

No files matched your search

+28
View File
@@ -0,0 +1,28 @@
import {apiFetch} from './api.js';
import {hasSession} from './auth.js';
export const COVER = 'COVER';
export const ACHIEVEMENT_ICONS = 'ACHIEVEMENT_ICONS';
const reported = new Set();
/**
* Tells Core that a Steam picture did not load, for its nightly repair. Once per game and kind per page load, only
* with a session. Errors are ignored: the placeholder already shows.
*/
export function reportBrokenImage(appid, kind) {
if (!Number.isInteger(appid) || appid <= 0 || !hasSession()) return;
const key = `${kind}:${appid}`;
if (reported.has(key)) return;
reported.add(key);
apiFetch(`${import.meta.env.VITE_CORE_API_URL}/api/library/broken-images`, {
method: 'POST',
headers: {'Content-Type': 'application/json'},
body: JSON.stringify({appid, kind}),
}).catch(() => {});
}
// For tests only.
export function resetBrokenImageReports() {
reported.clear();
}
+50
View File
@@ -0,0 +1,50 @@
// @vitest-environment jsdom
import {afterEach, beforeEach, describe, expect, it, vi} from 'vitest';
import {ACHIEVEMENT_ICONS, COVER, reportBrokenImage, resetBrokenImageReports} from './brokenImages.js';
beforeEach(() => {
localStorage.setItem('tbp_session', '1');
globalThis.fetch = vi.fn(async () => ({ok: true, status: 204}));
});
afterEach(() => {
localStorage.clear();
resetBrokenImageReports();
});
describe('reportBrokenImage', () => {
it('posts the game and kind once per page load', () => {
reportBrokenImage(620, COVER);
reportBrokenImage(620, COVER);
reportBrokenImage(620, ACHIEVEMENT_ICONS);
expect(fetch).toHaveBeenCalledTimes(2);
const [url, init] = fetch.mock.calls[0];
expect(url).toContain('/api/library/broken-images');
expect(init.method).toBe('POST');
expect(JSON.parse(init.body)).toEqual({appid: 620, kind: 'COVER'});
});
it('sends nothing without a session', () => {
localStorage.clear();
reportBrokenImage(620, COVER);
expect(fetch).not.toHaveBeenCalled();
});
it.each([-1234, 0, undefined, '620'])('sends nothing for the key %s', (appid) => {
reportBrokenImage(appid, COVER);
expect(fetch).not.toHaveBeenCalled();
});
it('ignores a failed request', async () => {
globalThis.fetch = vi.fn(async () => {
throw new TypeError('network');
});
expect(() => reportBrokenImage(620, COVER)).not.toThrow();
await Promise.resolve();
});
});