Remember story sorting in local storage

This commit is contained in:
tim
2026-10-01 16:08:46 +02:00
parent e4a17029c1
commit 34ff432aac
2 changed files with 81 additions and 2 deletions
+66 -2
View File
@@ -1,8 +1,17 @@
import assert from 'node:assert/strict';
import { test } from 'node:test';
import { beforeEach, test } from 'node:test';
import { click, field, find, input, install, nodes, server, settle } from './flow-helpers.mjs';
const { app } = install();
const { app, toasts } = install();
const SORT_KEY = 'bindery.story-sort';
beforeEach(() => {
const values = new Map();
window.localStorage = {
getItem: (key) => values.get(key) ?? null,
setItem: (key, value) => values.set(key, value),
};
toasts.replaceChildren();
});
const { storiesPage } = await import('../../bindery/static/js/stories.js');
const { refreshJobs, startPolling, stopPolling } = await import('../../bindery/static/js/jobs.js');
const { fmtDate } = await import('../../bindery/static/js/lib.js');
@@ -112,6 +121,61 @@ for (const [label, key] of [['Author', 'author'], ['Site', 'site'], ['Status', '
});
}
test('sorting saves the column and direction and restores both when the list reopens', async (t) => {
const list = [
story(1, 'Alpha', { author: 'Zed', site: 'z.test', status: 'ok', chapter_count: 2,
last_changed: '2026-01-01T00:00:00Z', last_checked: '2026-01-03T00:00:00Z' }),
story(2, 'Beta', { author: 'Anne', site: 'a.test', status: 'error', chapter_count: 10,
last_changed: '2026-01-02T00:00:00Z', last_checked: '2026-01-02T00:00:00Z' }),
];
await mount(t, list);
for (const [label, column, direction, order] of [
['Last change', 'last_changed', -1, ['Beta', 'Alpha']],
['Last change', 'last_changed', 1, ['Alpha', 'Beta']],
['Last check', 'last_checked', -1, ['Alpha', 'Beta']],
['Last check', 'last_checked', 1, ['Beta', 'Alpha']],
['Title', 'title', 1, ['Alpha', 'Beta']],
['Title', 'title', -1, ['Beta', 'Alpha']],
['Author', 'author', 1, ['Beta', 'Alpha']],
['Author', 'author', -1, ['Alpha', 'Beta']],
['Site', 'site', 1, ['Beta', 'Alpha']],
['Site', 'site', -1, ['Alpha', 'Beta']],
['Status', 'status', 1, ['Beta', 'Alpha']],
['Status', 'status', -1, ['Alpha', 'Beta']],
['Chapters', 'chapter_count', -1, ['Beta', 'Alpha']],
['Chapters', 'chapter_count', 1, ['Alpha', 'Beta']],
]) {
await click(app, label);
assert.deepEqual(JSON.parse(window.localStorage.getItem(SORT_KEY)), { column, direction });
await mount(t, list);
assert.deepEqual(titles(), order);
assert.equal(heading(label).attributes['aria-sort'], direction === 1 ? 'ascending' : 'descending');
}
});
for (const saved of ['not JSON', 'null', '{}', '{"column":"message","direction":-1}',
'{"column":"last_changed","direction":0}', '{"column":"last_checked","direction":"-1"}']) {
test(`invalid saved sorting falls back to title order: ${saved}`, async (t) => {
window.localStorage.setItem(SORT_KEY, saved);
await mount(t, [story(2, 'Beta'), story(1, 'Alpha')]);
assert.deepEqual(titles(), ['Alpha', 'Beta']);
assert.equal(heading('Title').attributes['aria-sort'], 'ascending');
await click(app, 'Title');
assert.deepEqual(JSON.parse(window.localStorage.getItem(SORT_KEY)), { column: 'title', direction: -1 });
});
}
test('sorting works when local storage cannot be read or written', async (t) => {
window.localStorage.getItem = () => { throw new Error('Storage blocked'); };
window.localStorage.setItem = () => { throw new Error('Storage full'); };
await mount(t, [story(2, 'Beta'), story(1, 'Alpha')]);
assert.deepEqual(titles(), ['Alpha', 'Beta']);
await click(app, 'Title');
assert.deepEqual(titles(), ['Beta', 'Alpha']);
assert.equal(heading('Title').attributes['aria-sort'], 'descending');
assert.equal(toasts.textContent, '');
});
test('date sorting compares instants, handles invalid dates and breaks ties by title and id', async (t) => {
await mount(t, [
story(7, 'Missing', { last_changed: 'not a date' }),