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 });