123 lines
5.7 KiB
JavaScript
123 lines
5.7 KiB
JavaScript
import assert from 'node:assert/strict';
|
|
import { test } from 'node:test';
|
|
import { Element, change, click, find, install, nodes, section, server, settle } from './flow-helpers.mjs';
|
|
|
|
const ui = install();
|
|
const { settingsPage } = await import('../../bindery/static/js/settings.js');
|
|
const { storyPage } = await import('../../bindery/static/js/story.js');
|
|
const { storiesPage } = await import('../../bindery/static/js/stories.js');
|
|
|
|
const config = { job_list_max_hours: 720, job_list_max_count: 500 };
|
|
const book = { id: 7, uuid: 'book-7', title: 'Library Title', authors: ['Writer'] };
|
|
const details = (story, extra = {}) => ({
|
|
story: { id: 1, title: 'Story', author: 'Writer', url: 'https://example.com/story', site: 'example.com',
|
|
locked_fields: [], pinned: {}, site_values: {}, auto_update: true, status: 'ok', cover: null,
|
|
calibre_id: null, calibre_uuid: null, ingested_at: null, upload_held: false, needs_approval: false, ...story },
|
|
chapters: [],
|
|
recent_chapters: 0,
|
|
uploads_paused: false,
|
|
book: null,
|
|
...extra,
|
|
});
|
|
|
|
async function renderStory(t, data) {
|
|
const backend = server(t);
|
|
backend.on('GET', 'stories/1', data);
|
|
backend.on('GET', 'stories/1/rules', { rules: [], paragraphs: null, global_paragraphs: false });
|
|
backend.on('GET', 'jobs?story_id=1', []);
|
|
backend.on('GET', 'jobs', []);
|
|
const main = new Element('main');
|
|
const cleanup = storyPage(main, { storyId: '1' }, { metadata_fields: {}, mode_labels: {} });
|
|
t.after(() => cleanup?.());
|
|
await settle();
|
|
const summary = nodes(main).find((el) => el.className === 'card story-summary');
|
|
return { backend, notice: nodes(summary).find((el) => el.className === 'notice') };
|
|
}
|
|
|
|
test('the upload switches load and save on the settings page', async (t) => {
|
|
const backend = server(t);
|
|
backend.on('GET', 'settings', { recent_chapters: 0, hold_new_links: true, pause_uploads: false, personal_ini: '' });
|
|
backend.on('PUT', 'settings', {});
|
|
backend.on('GET', 'rules', { rules: [], paragraphs: false });
|
|
backend.on('GET', 'stories', []);
|
|
const main = new Element('main');
|
|
settingsPage(main, {}, config);
|
|
await settle();
|
|
const card = section(main, 'Uploads to CWA');
|
|
const [hold, pause] = nodes(card).filter((el) => el.attributes.type === 'checkbox');
|
|
assert.deepEqual([hold.checked, pause.checked], [true, false]);
|
|
change(pause, true);
|
|
await click(card, 'Save');
|
|
assert.deepEqual(backend.sent('PUT', 'settings')[0].body, { hold_new_links: true, pause_uploads: true });
|
|
});
|
|
|
|
test('a held book with a new link names the book and sends it on approval', async (t) => {
|
|
const data = details(
|
|
{ calibre_id: 7, calibre_uuid: 'book-7', ingested_at: '2026-01-01', upload_held: true, needs_approval: true },
|
|
{ book },
|
|
);
|
|
const { backend, notice } = await renderStory(t, data);
|
|
assert.match(notice.textContent, /waits for your approval\. The next upload replaces the ePub of library book 7: Library Title · Writer\./);
|
|
backend.on('POST', 'stories/1/approve', { story: data.story, job: { id: 'j1' } });
|
|
await click(notice, 'Approve and send');
|
|
await settle();
|
|
assert.deepEqual(backend.sent('POST', 'stories/1/approve')[0].body, { uuid: 'book-7' });
|
|
assert.match(ui.toasts.textContent, /Approved\. Bindery rebuilds the book and sends it\./);
|
|
});
|
|
|
|
test('a wrong link can go to CWA as a new book instead', async (t) => {
|
|
const data = details(
|
|
{ calibre_id: 7, calibre_uuid: 'book-7', ingested_at: '2026-01-01', upload_held: true, needs_approval: true },
|
|
{ book },
|
|
);
|
|
const { backend, notice } = await renderStory(t, data);
|
|
backend.on('POST', 'stories/1/link', data.story);
|
|
backend.on('POST', 'stories/1/approve', { story: data.story, job: null });
|
|
await click(notice, 'Send as a new book instead');
|
|
await settle();
|
|
assert.deepEqual(
|
|
backend.requests.filter((r) => r.method === 'POST').map((r) => [r.path, r.body]),
|
|
[['stories/1/link', { uuid: null }], ['stories/1/approve', { uuid: null }]],
|
|
);
|
|
});
|
|
|
|
test('a new story asks for approval to go to CWA as a new book', async (t) => {
|
|
const { notice } = await renderStory(t, details({ needs_approval: true }));
|
|
assert.match(notice.textContent, /The next upload goes to CWA as a new book\./);
|
|
assert.ok(find(notice, 'button', 'Approve'));
|
|
assert.equal(find(notice, 'button', 'Send as a new book instead'), undefined);
|
|
});
|
|
|
|
test('a paused upload points to the settings', async (t) => {
|
|
const { notice } = await renderStory(
|
|
t,
|
|
details({ calibre_id: 7, calibre_uuid: 'book-7', ingested_at: '2026-01-01', upload_held: true }, { book, uploads_paused: true }),
|
|
);
|
|
assert.match(notice.textContent, /Uploads to CWA are paused/);
|
|
assert.equal(find(notice, 'a', 'Settings').attributes.href, '#/settings');
|
|
assert.equal(nodes(notice).filter((el) => el.tagName === 'button').length, 0);
|
|
});
|
|
|
|
test('a story that went to CWA without a link shows no approval until Bindery finds its book', async (t) => {
|
|
const { notice } = await renderStory(t, details({ ingested_at: '2026-01-01', needs_approval: true }));
|
|
assert.equal(notice, undefined);
|
|
});
|
|
|
|
test('the story list marks a held book', async (t) => {
|
|
const backend = server(t);
|
|
backend.on('GET', 'stories', [
|
|
{ id: 1, url: 'u1', title: 'Held', author: 'A', site: 's', chapter_count: 1, offsite_count: 0, status: 'ok',
|
|
message: '', upload_held: true },
|
|
{ id: 2, url: 'u2', title: 'Sent', author: 'A', site: 's', chapter_count: 1, offsite_count: 0, status: 'ok',
|
|
message: '', upload_held: false },
|
|
]);
|
|
backend.on('GET', 'jobs', []);
|
|
const main = new Element('main');
|
|
const cleanup = storiesPage(main, {}, { library_available: false });
|
|
t.after(() => cleanup?.());
|
|
await settle();
|
|
const held = nodes(main).filter((el) => el.tagName === 'tr').filter((row) => find(row, 'span', 'held'));
|
|
assert.equal(held.length, 1);
|
|
assert.match(held[0].textContent, /^Held/);
|
|
});
|