Add Greatest of All Time manga dashboard

This commit is contained in:
Codex committed 2026-10-09 10:10:34 +02:00
1 parent c8dd46d0fb
commit 5df94db9a3
16 files changed
+2717 -1

No files matched your search

+30
View File
@@ -0,0 +1,30 @@
'use strict';
const test = require('node:test');
const assert = require('node:assert/strict');
const { loadCatalog, findOwned, countsFor, scoreFor } = require('../greatest');
test('curated manga catalog has unique identities and ordered five-year periods', () => {
const catalog = loadCatalog();
assert.equal(catalog.periods.length, 13);
assert.equal(catalog.entries.length, 106);
for (const [index, period] of catalog.periods.entries()) {
const entries = catalog.entries.filter(entry => entry.period === period);
assert.ok(entries.length >= 5 && entries.length <= 10);
assert.deepEqual(entries.map(entry => entry.rank), entries.map((_, rank) => rank + 1));
assert.ok(entries.every(entry => entry.periodOrder === index));
}
assert.equal(catalog.entries.find(entry => entry.title === 'Phoenix').year, 1954);
assert.equal(catalog.entries.find(entry => entry.title === 'JoJo\'s Bizarre Adventure').year, 1987);
});
test('library matching and vote aggregation stay independent of scraper data', () => {
const catalog = loadCatalog();
const berserk = catalog.entries.find(entry => entry.title === 'Berserk');
const manga = { id: 'local-Berserk', title: 'Berserk', meta: {} };
assert.equal(findOwned(berserk, [manga]), manga);
assert.equal(findOwned(berserk, [{ id: 'different', title: 'Berserker', meta: {} }]), null);
const votes = { ada: { [berserk.id]: 'flame' }, bob: { [berserk.id]: 'good' }, gone: { [berserk.id]: 'skip' } };
const counts = countsFor(berserk.id, votes, { ada: {}, bob: {} });
assert.deepEqual(counts, { flame: 1, good: 1, neutral: 0, skip: 0 });
assert.equal(scoreFor(counts), 4);
});
+26
View File
@@ -47,6 +47,32 @@ test('three services integrate collections, auth, custom covers and private heal
let inventory;
for (let n = 0; n < 50; n++) { inventory = await (await request('/api/library')).json(); if (inventory.mangas.length === 2) break; await delay(100); }
assert.equal(inventory.mangas.length, 2);
assert.equal((await fetch(base + '/api/greatest')).status, 401);
const greatest = await (await request('/api/greatest')).json();
assert.equal(greatest.entries.length, 106);
const firstPick = greatest.entries[0];
assert.equal(firstPick.myRating, null);
assert.equal((await request('/api/greatest/rating', { id: firstPick.id, rating: 'flame' })).status, 200);
assert.equal((await request('/api/greatest/rating', { id: firstPick.id, rating: 'invalid' })).status, 400);
assert.equal((await request('/api/greatest/rating', { id: 'missing', rating: 'good' })).status, 404);
const afterRating = (await (await request('/api/greatest')).json()).entries[0];
assert.equal(afterRating.myRating, 'flame');
assert.equal(afterRating.votes.flame, 1);
assert.equal(afterRating.score, 3);
assert.equal((await request('/api/admin/user', { name: 'another', password: 'local-test-password' })).status, 200);
const anotherLogin = await fetch(base + '/api/login', { method: 'POST', headers: { 'content-type': 'application/json' }, body: JSON.stringify({ name: 'another', password: 'local-test-password' }) });
assert.equal(anotherLogin.status, 200);
const anotherCookie = anotherLogin.headers.get('set-cookie').split(';')[0];
const anotherRequest = (endpoint, body) => fetch(base + endpoint, { headers: { cookie: anotherCookie, 'content-type': 'application/json' }, ...(body ? { method: 'POST', body: JSON.stringify(body) } : {}) });
assert.equal((await (await anotherRequest('/api/greatest')).json()).entries[0].myRating, null);
assert.equal((await anotherRequest('/api/greatest/rating', { id: firstPick.id, rating: 'good' })).status, 200);
const communityPick = (await (await request('/api/greatest')).json()).entries[0];
assert.equal(communityPick.myRating, 'flame');
assert.deepEqual(communityPick.votes, { flame: 1, good: 1, neutral: 0, skip: 0 });
assert.equal((await request('/api/greatest/rating', { id: firstPick.id, rating: null })).status, 200);
assert.equal((await (await request('/api/greatest')).json()).entries[0].votes.flame, 0);
assert.equal((await request('/api/admin/user/delete', { name: 'another' })).status, 200);
assert.equal((await (await request('/api/greatest')).json()).entries[0].votes.good, 0);
const ids = inventory.mangas.map(m => m.id);
const catalog = { revision: 0, collections: [{ id: 'demo', title: 'Demo collection', members: ids.map((mangaId, index) => ({ mangaId, readingOrder: index + 1, role: index ? 'sequel' : 'main' })), coverMode: 'mosaic' }], relations: [{ from: ids[0], to: ids[1], type: 'sequel', state: 'confirmed' }] };
let response = await request('/api/admin/catalog', catalog); assert.equal(response.status, 200); const saved = await response.json();