Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions gh-pages-template/assets/js/item_detail.js
Original file line number Diff line number Diff line change
Expand Up @@ -197,6 +197,8 @@ function renderGameList(container, games) {
const coverUrl = data?.cover ? igdbImageUrl(data.cover.url, "t_cover_small_2x") : null;
renderGameCard(row, id, name, coverUrl);
});
}).catch(error => {
console.error("Failed to render game list", error);
});
}
}
Expand Down
42 changes: 17 additions & 25 deletions gh-pages-template/assets/js/item_loader.js
Original file line number Diff line number Diff line change
Expand Up @@ -550,7 +550,7 @@ function run_search() {
if (!r.ok) throw new Error(`Bucket not found`)
return r.json()
})
.then(bucket_data => {
.then(async bucket_data => {
loading.remove()

// Filter results by name (case-insensitive)
Expand All @@ -577,30 +577,22 @@ function run_search() {
search_container.appendChild(row)

// Fetch platform names to display
fetch(`${base_url}/platforms/all.json`)
.then(r => r.json())
.then(allPlatforms => {
// Fetch full game data for each match
const gamePromises = matches.slice(0, 60).map(([id, _game]) =>
fetchGameData(id, _game.name)
)

Promise.all(gamePromises).then(results => {
renderSearchResults(results, row, allPlatforms)
addMoreResultsNote(search_container, matches.length, 60)
})
})
.catch(() => {
// Fallback if platforms can't be loaded - still fetch game data
const gamePromises = matches.slice(0, 60).map(([id, _game]) =>
fetchGameData(id, _game.name)
)

Promise.all(gamePromises).then(results => {
renderSearchResults(results, row, null)
addMoreResultsNote(search_container, matches.length, 60)
})
})
let allPlatforms
try {
const response = await fetch(`${base_url}/platforms/all.json`)
allPlatforms = await response.json()
} catch {
// Fallback if platforms can't be loaded - still fetch game data
allPlatforms = null
}

// Fetch full game data for each match
const gamePromises = matches.slice(0, 60).map(([id, _game]) =>
fetchGameData(id, _game.name)
)
const results = await Promise.all(gamePromises)
renderSearchResults(results, row, allPlatforms)
addMoreResultsNote(search_container, matches.length, 60)
})
.catch(err => {
loading.remove()
Expand Down
19 changes: 19 additions & 0 deletions tests/item_detail.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -284,6 +284,25 @@ describe('item_detail.js', () => {
expect(container.querySelector('.row')).not.toBeNull();
});

test('handles errors while rendering fetched game cards', async () => {
globalThis.fetch = jest.fn().mockResolvedValue({
ok: true,
json: () => Promise.resolve({ name: 'Fetched Game' }),
});
const error = new Error('Rendering failed');
const errorSpy = jest.spyOn(console, 'error').mockImplementation(() => {});
const container = document.createElement('div');
renderGameList(container, [42]);
jest.spyOn(container.querySelector('.row'), 'appendChild').mockImplementation(() => {
throw error;
});

await new Promise(r => setTimeout(r, 0));

expect(errorSpy).toHaveBeenCalledWith('Failed to render game list', error);
expect(errorSpy).toHaveBeenCalledTimes(1);
});

test('renders game without cover (fetch returns not-ok)', async () => {
globalThis.fetch = jest.fn().mockResolvedValue({ ok: false });
const container = document.createElement('div');
Expand Down
54 changes: 54 additions & 0 deletions tests/item_loader.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -516,5 +516,59 @@ describe('item_loader.js', () => {
const container = document.getElementById('search-container');
expect(container.textContent).toContain('Mario');
});

test('renders results even when platform JSON parsing fails', async () => {
document.getElementById('search_term').value = 'mario';
globalThis.fetch = jest.fn()
.mockResolvedValueOnce({
ok: true,
json: () => Promise.resolve({ '10': { name: 'Mario' } }),
})
.mockResolvedValueOnce({
json: () => Promise.reject(new Error('Invalid platform JSON')),
})
.mockResolvedValue({
ok: true,
json: () => Promise.resolve({ name: 'Mario', platforms: [6] }),
});

run_search();
await flushPromises();

const container = document.getElementById('search-container');
expect(container.querySelector('a').textContent).toContain('Mario');
expect(container.textContent).not.toContain('Search failed');
expect(globalThis.fetch).toHaveBeenCalledTimes(3);
});

test.each([true, false])('shows rendering errors with platform fetch success=%s', async platformsAvailable => {
document.getElementById('search_term').value = 'halo';
globalThis.fetch = jest.fn()
.mockResolvedValueOnce({
ok: true,
json: () => Promise.resolve({ '1': { name: 'Halo' } }),
});
if (platformsAvailable) {
globalThis.fetch.mockResolvedValueOnce({ json: () => Promise.resolve({}) });
} else {
globalThis.fetch.mockRejectedValueOnce(new Error('Platform fetch failed'));
}
globalThis.fetch.mockResolvedValue({
ok: true,
json: () => Promise.resolve({ name: 'Halo' }),
});
const createElement = document.createElement.bind(document);
jest.spyOn(document, 'createElement').mockImplementation(tagName => {
if (tagName === 'a') throw new Error('Rendering failed');
return createElement(tagName);
});

run_search();
await flushPromises();

const container = document.getElementById('search-container');
expect(container.querySelector('.text-danger').textContent).toBe('Search failed: Rendering failed');
expect(globalThis.fetch).toHaveBeenCalledTimes(3);
});
});
});
Loading