mirror of
https://github.com/john-okeefe/foliate-js.git
synced 2026-09-09 11:29:14 -04:00
Add tests for EPUB CFI
This commit is contained in:
@@ -10,7 +10,6 @@ Features:
|
||||
- Does not depend on or include any library for unzipping; bring your own Zip library
|
||||
- Does not require loading whole file into memory
|
||||
- Does not care about older browsers
|
||||
- No tests :(
|
||||
- No continuous scrolling mode :(
|
||||
|
||||
## Demo
|
||||
|
||||
+1
-2
@@ -244,9 +244,8 @@ const partsToNode = (node, parts) => {
|
||||
let sum = 0
|
||||
for (const n of node) {
|
||||
const { length } = n.nodeValue
|
||||
if (sum + length > offset) return { node: n, offset: offset - sum }
|
||||
if (sum + length >= offset) return { node: n, offset: offset - sum }
|
||||
sum += length
|
||||
if (n === node[node.length - 1]) return { node: n, offset: length - 1 }
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,202 @@
|
||||
import * as CFI from '../epubcfi.js'
|
||||
|
||||
const parser = new DOMParser()
|
||||
const XML = str => parser.parseFromString(str, 'application/xml')
|
||||
const XHTML = str => parser.parseFromString(str, 'application/xhtml+xml')
|
||||
|
||||
{
|
||||
// example from EPUB CFI spec
|
||||
const opf = XML(`<?xml version="1.0"?>
|
||||
|
||||
<package version="2.0"
|
||||
unique-identifier="bookid"
|
||||
xmlns="http://www.idpf.org/2007/opf"
|
||||
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
||||
xmlns:opf="http://www.idpf.org/2007/opf">
|
||||
|
||||
<metadata>
|
||||
<dc:title>…</dc:title>
|
||||
<dc:identifier id="bookid">…</dc:identifier>
|
||||
<dc:creator>…</dc:creator>
|
||||
<dc:language>en</dc:language>
|
||||
</metadata>
|
||||
|
||||
<manifest>
|
||||
<item id="toc"
|
||||
properties="nav"
|
||||
href="toc.xhtml"
|
||||
media-type="application/xhtml+xml"/>
|
||||
<item id="titlepage"
|
||||
href="titlepage.xhtml"
|
||||
media-type="application/xhtml+xml"/>
|
||||
<item id="chapter01"
|
||||
href="chapter01.xhtml"
|
||||
media-type="application/xhtml+xml"/>
|
||||
<item id="chapter02"
|
||||
href="chapter02.xhtml"
|
||||
media-type="application/xhtml+xml"/>
|
||||
<item id="chapter03"
|
||||
href="chapter03.xhtml"
|
||||
media-type="application/xhtml+xml"/>
|
||||
<item id="chapter04"
|
||||
href="chapter04.xhtml"
|
||||
media-type="application/xhtml+xml"/>
|
||||
</manifest>
|
||||
|
||||
<spine>
|
||||
<itemref id="titleref" idref="titlepage"/>
|
||||
<itemref id="chap01ref" idref="chapter01"/>
|
||||
<itemref id="chap02ref" idref="chapter02"/>
|
||||
<itemref id="chap03ref" idref="chapter03"/>
|
||||
<itemref id="chap04ref" idref="chapter04"/>
|
||||
</spine>
|
||||
|
||||
</package>`)
|
||||
|
||||
const page = XHTML(`<html xmlns="http://www.w3.org/1999/xhtml">
|
||||
<head>
|
||||
<title>…</title>
|
||||
</head>
|
||||
|
||||
<body id="body01">
|
||||
<p>…</p>
|
||||
<p>…</p>
|
||||
<p>…</p>
|
||||
<p>…</p>
|
||||
<p id="para05">xxx<em>yyy</em>0123456789</p>
|
||||
<p>…</p>
|
||||
<p>…</p>
|
||||
<img id="svgimg" src="foo.svg" alt="…"/>
|
||||
<p>…</p>
|
||||
<p>…</p>
|
||||
</body>
|
||||
</html>`)
|
||||
|
||||
// the exact same page with some text nodes removed, CDATA sections added,
|
||||
// and characters changed to entities
|
||||
const page2 = XHTML(`<html xmlns="http://www.w3.org/1999/xhtml">
|
||||
<head>
|
||||
<title>…</title>
|
||||
</head>
|
||||
<body id="body01">
|
||||
<p>…</p><p>…</p><p>…</p><p>…</p>
|
||||
<p id="para05">xxx<em>yyy</em><![CDATA[0123]]>45<![CDATA[67]]>89</p>
|
||||
<p>…</p>
|
||||
<p>…</p>
|
||||
<img id="svgimg" src="foo.svg" alt="…"/>
|
||||
<p>…</p>
|
||||
<p>…</p>
|
||||
</body>
|
||||
</html>`)
|
||||
|
||||
const a = opf.getElementById('chap01ref')
|
||||
const b = CFI.toElement(opf, CFI.parse('/6/4[chap01ref]')[0])
|
||||
const c = CFI.toElement(opf, CFI.parse('/6/4')[0])
|
||||
console.assert(a === b)
|
||||
console.assert(a === c)
|
||||
|
||||
const test = page => {
|
||||
for (const cfi of [
|
||||
'/4[body01]/10[para05]/3:10',
|
||||
'/4[body01]/16[svgimg]',
|
||||
'/4[body01]/10[para05]/1:0',
|
||||
'/4[body01]/10[para05]/2/1:0',
|
||||
'/4[body01]/10[para05]/2/1:3',
|
||||
]) {
|
||||
const range = CFI.toRange(page, CFI.parse(cfi))
|
||||
const a = CFI.fromRange(range)
|
||||
const b = `epubcfi(${cfi})`
|
||||
console.assert(a === b, `expected ${b}, got ${a}`)
|
||||
}
|
||||
for (let i = 0; i < 10; i++) {
|
||||
const cfi = `/4/10,/3:${i},/3:${i+1}`
|
||||
const range = CFI.toRange(page, CFI.parse(cfi))
|
||||
const n = `${i}`
|
||||
console.assert(range.toString() === n, `expted ${n}, got ${range}`)
|
||||
}
|
||||
}
|
||||
test(page)
|
||||
test(page2)
|
||||
}
|
||||
|
||||
{
|
||||
// special characters in ID assertions
|
||||
const opf = XML(`<?xml version="1.0"?>
|
||||
<package version="2.0"
|
||||
unique-identifier="bookid"
|
||||
xmlns="http://www.idpf.org/2007/opf"
|
||||
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
||||
xmlns:opf="http://www.idpf.org/2007/opf">
|
||||
<metadata></metadata>
|
||||
<manifest></manifest>
|
||||
<spine>
|
||||
<itemref id="titleref" idref="titlepage"/>
|
||||
<itemref id="chap0]!/1ref^" idref="chapter01"/>
|
||||
<itemref id="chap02ref" idref="chapter02"/>
|
||||
<itemref id="chap03ref" idref="chapter03"/>
|
||||
<itemref id="chap04ref" idref="chapter04"/>
|
||||
</spine>
|
||||
</package>`)
|
||||
|
||||
const a = opf.getElementById('chap0]!/1ref^')
|
||||
const b = CFI.toElement(opf, CFI.parse('/6/4[chap0^]!/1ref^^]')[0])
|
||||
console.assert(a === b)
|
||||
|
||||
const page = XHTML(`<html xmlns="http://www.w3.org/1999/xhtml">
|
||||
<head>
|
||||
<title>…</title>
|
||||
</head>
|
||||
<body id="body0]!/1^">
|
||||
<p>…</p>
|
||||
<p>…</p>
|
||||
<p>…</p>
|
||||
<p>…</p>
|
||||
<p id="para]/0,/5">xxx<em>yyy</em>0123456789</p>
|
||||
<p>…</p>
|
||||
<p>…</p>
|
||||
<img id="s][vgimg" src="foo.svg" alt="…"/>
|
||||
<p>…</p>
|
||||
<p>…</p>
|
||||
</body>
|
||||
</html>`)
|
||||
|
||||
for (const cfi of [
|
||||
'/4[body0^]!/1^^]/10[para^]/0^,/5]/3:10',
|
||||
'/4[body0^]!/1^^]/16[s^]^[vgimg]',
|
||||
'/4[body0^]!/1^^]/10[para^]/0^,/5]/1:0',
|
||||
'/4[body0^]!/1^^]/10[para^]/0^,/5]/2/1:0',
|
||||
'/4[body0^]!/1^^]/10[para^]/0^,/5]/2/1:3',
|
||||
]) {
|
||||
const range = CFI.toRange(page, CFI.parse(cfi))
|
||||
const a = CFI.fromRange(range)
|
||||
const b = `epubcfi(${cfi})`
|
||||
console.assert(a === b, `expected ${b}, got ${a}`)
|
||||
}
|
||||
for (let i = 0; i < 10; i++) {
|
||||
const cfi = `/4[body0^]!/1^^]/10[para^]/0^,^/5],/3:${i},/3:${i+1}`
|
||||
const range = CFI.toRange(page, CFI.parse(cfi))
|
||||
const n = `${i}`
|
||||
console.assert(range.toString() === n, `expted ${n}, got ${range}`)
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
for (const [a, b, c] of [
|
||||
['/6/4!/10', '/6/4!/10', 0],
|
||||
['/6/4!/2/3:0', '/6/4!/2', 1],
|
||||
['/6/4!/2/4/6/8/10/3:0', '/6/4!/4', -1],
|
||||
[
|
||||
'/6/4[chap0^]!/1ref^^]!/4[body01^^]/10[para^]^,05^^]',
|
||||
'/6/4!/4/10',
|
||||
0,
|
||||
],
|
||||
[
|
||||
'/6/4[chap0^]!/1ref^^]!/4[body01^^],/10[para^]^,05^^],/15:10[foo^]]',
|
||||
'/6/4!/4/12',
|
||||
-1,
|
||||
],
|
||||
]) {
|
||||
const x = CFI.compare(a, b)
|
||||
console.assert(x === c, `compare ${a} and ${b}, expected ${c}, got ${x}`)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
<!DOCTYPE html>
|
||||
<meta charset="utf-8">
|
||||
<meta name="color-scheme" content="light dark">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<script src="tests.js" type="module"></script>
|
||||
@@ -0,0 +1 @@
|
||||
import './epubcfi-tests.js'
|
||||
Reference in New Issue
Block a user