Recently Written · git

subplz-web

git clone https://github.com/equwal/subplz-web

Log | Files | Refs


tests/engine/mobi.test.mjs (2325 bytes)

1 /* Kindle books are read in the browser, by foliate-js. The same public-domain
2  * book (The Yellow Wallpaper, Project Gutenberg #1952) as a MOBI/KF8 file and
3  * as an epub must give the same text.
4  *
5  *   node --test tests/engine
6  */
7 import test from 'node:test';
8 import assert from 'node:assert/strict';
9 import { readFileSync } from 'node:fs';
10 import { fileURLToPath } from 'node:url';
11 import path from 'node:path';
12 import { JSDOM } from 'jsdom';
13 
14 // foliate-js unescapes HTML entities through a <textarea>, so it wants a document too.
15 const { window } = new JSDOM('');
16 Object.assign(globalThis, { DOMParser: window.DOMParser, XMLSerializer: window.XMLSerializer, document: window.document });
17 
18 const { readBook } = await import('../../frontend/engine/book.js');
19 
20 const here = path.dirname(fileURLToPath(import.meta.url));
21 const fixture = (name) => new File([readFileSync(path.join(here, 'fixtures', name))], name);
22 const bare = (s) => s.replace(/\s+/g, '').toLowerCase();
23 
24 test('a Kindle file gives the paragraphs of the book, as the epub does', async () => {
25   const mobi = await readBook(fixture('yellow-wallpaper.mobi'));
26   const epub = await readBook(fixture('yellow-wallpaper.epub'));
27   assert.ok(epub.paragraphs.length > 100, `epub has ${epub.paragraphs.length} paragraphs`);
28   assert.ok(mobi.paragraphs.length > 100, `mobi has ${mobi.paragraphs.length} paragraphs`);
29 
30   // The story itself, not the licence, which each edition words its own way.
31   const story = (p) => p.filter((t) => t.length > 40);
32   const inMobi = new Set(story(mobi.paragraphs).map(bare));
33   const shared = story(epub.paragraphs).filter((t) => inMobi.has(bare(t))).length;
34   const share = shared / story(epub.paragraphs).length;
35   assert.ok(share > 0.9, `${(share * 100).toFixed(0)}% of the epub's paragraphs are in the mobi`);
36 
37   // In reading order: the first line of the story before its last.
38   const first = mobi.paragraphs.findIndex((t) => /very seldom that mere ordinary people/.test(t));
39   const last = mobi.paragraphs.findIndex((t) => /had to creep over him every time/.test(t));
40   assert.ok(first >= 0 && last > first, `first line at ${first}, last at ${last}`);
41 });
42 
43 test('a Kindle file that is not one is refused, not read as text', async () => {
44   await assert.rejects(readBook(new File([new Uint8Array(100)], 'broken.azw3')));
45 });