forked from akai/readest
9dc41e7adf
Add a 'Reference Pages' reading progress style that shows physical book page numbers in the footer progress info: - When the book carries a page list (EPUB3 nav page-list or EPUB2 NCX pageList — foliate-js already parses both and resolves the current pageItem on relocate; it was just never consumed), display the current page label and use the highest numeric label as the total, so a trailing roman-numeral index page can't corrupt the total (#672). - When the book has none, a per-book 'Reference Page Count' input appears; the reading fraction is mapped linearly onto the entered count (#4542). The count is saved per book only and never propagates to global view settings. - Falls back to percentage display when neither source is available. Verified with the sample books from #672: Caleb's Crossing (EPUB3 page-list, 419 pages) and Count Zero (EPUB2 NCX pageList/page-map, 346 pages — chapter 2 lands exactly on page 22 per its page-map), plus a stripped no-pagelist copy for the manual-count path (175/350 at 50%). Closes #672 Closes #4542 Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
135 lines
4.2 KiB
TypeScript
135 lines
4.2 KiB
TypeScript
import { describe, expect, it } from 'vitest';
|
|
import { getReferencePageInfo } from '@/utils/progress';
|
|
|
|
const makePageList = (labels: string[]) => labels.map((label) => ({ label, href: '#' }));
|
|
|
|
describe('getReferencePageInfo', () => {
|
|
describe('with a page list from the book (issue #672)', () => {
|
|
it('uses the current page item label and the highest numeric label as total', () => {
|
|
const pageList = makePageList(['1', '2', '3', '4', '5']);
|
|
const info = getReferencePageInfo({
|
|
pageList,
|
|
pageItem: { label: '3' },
|
|
fraction: 0.5,
|
|
referencePageCount: 0,
|
|
});
|
|
expect(info).toEqual({ current: '3', total: 5 });
|
|
});
|
|
|
|
it('keeps non-numeric current labels (front matter roman numerals) as-is', () => {
|
|
const pageList = makePageList(['i', 'ii', '1', '2', '3']);
|
|
const info = getReferencePageInfo({
|
|
pageList,
|
|
pageItem: { label: 'ii' },
|
|
fraction: 0.1,
|
|
referencePageCount: 0,
|
|
});
|
|
expect(info).toEqual({ current: 'ii', total: 3 });
|
|
});
|
|
|
|
it('ignores trailing non-numeric labels when computing the total', () => {
|
|
// Reported in #672: a book ends with an index page labeled "XII" after
|
|
// page 553 — the total must stay 553, not "XII".
|
|
const pageList = makePageList(['551', '552', '553', 'XII']);
|
|
const info = getReferencePageInfo({
|
|
pageList,
|
|
pageItem: { label: '552' },
|
|
fraction: 0.6,
|
|
referencePageCount: 0,
|
|
});
|
|
expect(info).toEqual({ current: '552', total: 553 });
|
|
});
|
|
|
|
it('falls back to the entry count when no label is numeric', () => {
|
|
const pageList = makePageList(['i', 'ii', 'iii']);
|
|
const info = getReferencePageInfo({
|
|
pageList,
|
|
pageItem: { label: 'iii' },
|
|
fraction: 0.9,
|
|
referencePageCount: 0,
|
|
});
|
|
expect(info).toEqual({ current: 'iii', total: 3 });
|
|
});
|
|
|
|
it('counts pages in nested subitems', () => {
|
|
const pageList = [
|
|
{ label: '1', href: '#', subitems: makePageList(['2', '3']) },
|
|
{ label: '4', href: '#' },
|
|
];
|
|
const info = getReferencePageInfo({
|
|
pageList,
|
|
pageItem: { label: '2' },
|
|
fraction: 0.3,
|
|
referencePageCount: 0,
|
|
});
|
|
expect(info).toEqual({ current: '2', total: 4 });
|
|
});
|
|
|
|
it('estimates the current page linearly when no page item is resolved yet', () => {
|
|
// e.g. on the cover, before the first page anchor
|
|
const pageList = makePageList(['1', '2', '3', '4', '5', '6', '7', '8', '9', '10']);
|
|
const info = getReferencePageInfo({
|
|
pageList,
|
|
pageItem: null,
|
|
fraction: 0.45,
|
|
referencePageCount: 0,
|
|
});
|
|
expect(info).toEqual({ current: '5', total: 10 });
|
|
});
|
|
|
|
it('prefers the page list over a user-entered page count', () => {
|
|
const pageList = makePageList(['1', '2', '3']);
|
|
const info = getReferencePageInfo({
|
|
pageList,
|
|
pageItem: { label: '2' },
|
|
fraction: 0.5,
|
|
referencePageCount: 999,
|
|
});
|
|
expect(info).toEqual({ current: '2', total: 3 });
|
|
});
|
|
});
|
|
|
|
describe('with a user-entered page count (issue #4542)', () => {
|
|
it('maps the reading fraction linearly onto the page count', () => {
|
|
const info = getReferencePageInfo({
|
|
pageList: null,
|
|
pageItem: null,
|
|
fraction: 0.5,
|
|
referencePageCount: 350,
|
|
});
|
|
expect(info).toEqual({ current: '175', total: 350 });
|
|
});
|
|
|
|
it('shows page 1 at the start of the book', () => {
|
|
const info = getReferencePageInfo({
|
|
pageList: undefined,
|
|
pageItem: null,
|
|
fraction: 0,
|
|
referencePageCount: 350,
|
|
});
|
|
expect(info).toEqual({ current: '1', total: 350 });
|
|
});
|
|
|
|
it('shows the last page at the end of the book', () => {
|
|
const info = getReferencePageInfo({
|
|
pageList: [],
|
|
pageItem: null,
|
|
fraction: 1,
|
|
referencePageCount: 350,
|
|
});
|
|
expect(info).toEqual({ current: '350', total: 350 });
|
|
});
|
|
});
|
|
|
|
it('returns null when the book has no page list and no page count is set', () => {
|
|
expect(
|
|
getReferencePageInfo({
|
|
pageList: null,
|
|
pageItem: null,
|
|
fraction: 0.5,
|
|
referencePageCount: 0,
|
|
}),
|
|
).toBeNull();
|
|
});
|
|
});
|