Wide or tall tables, code blocks and display equations overflowed the reading column and a scroll gesture over them turned the page instead of scrolling the content (#4400). - Wrap tables and display equations in a horizontally/vertically scrollable container; route touch + wheel along the box's scrollable axis so it scrolls the box and never turns the page, even at the edge (both axes). - A box that fits its column is marked fit (overflow:visible) so it never clips or captures gestures; the fit decision is measured once after layout via a self-disconnecting ResizeObserver, so it never relayerizes during a page turn. - The scroll wrapper carries a new cfi-skip attribute that makes it transparent to CFI: epubcfi.js hoists a cfi-skip node's children into its parent (unlike cfi-inert which drops the subtree), and xcfi.ts mirrors this for CFI<->XPointer so existing highlights, bookmarks and KOSync positions inside a wrapped table or equation still resolve. The sanitizer whitelists cfi-skip. - Bump foliate-js submodule (cfi-skip support + raf fallback for large sections). Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
5.8 KiB
name, description, metadata
| name | description | metadata | ||||||
|---|---|---|---|---|---|---|---|---|
| tauri-parser-parity-tests | How to test the native Rust EPUB/MOBI parser against foliate-js in the Tauri WebView suite, plus the dcterms:modified→published parity gotcha |
|
PR #4369 added native Rust EPUB/MOBI parsers (src-tauri/src/epub_parser.rs, mobi_parser.rs, shared parser_common.rs) with foliate-js fallback. Parity between the two parsers is the key risk.
Cross-language parity test harness (src/__tests__/tauri/epub-parser-parity.tauri.test.ts):
- The
.tauri.test.tssuite (run byscripts/test-tauri.sh/pnpm test:tauri, included only byvitest.tauri.config.mts) is the ONLY env where both parsers coexist: Rust viainvoke()(./tauri-invoke.ts), foliate-js viaDocumentLoader. Defaultpnpm testexcludes**/*.tauri.test.ts. - Rust commands read an absolute on-disk path → build it from
process.env.CWD(injected by the tauri config = readest-app dir):${CWD}/src/__tests__/fixtures/data/<name>. The JS side fetches the SAME file via a Vite URL:new URL('../fixtures/data/<name>', import.meta.url).href(same pattern aspaginator-stabilization.browser.test.ts). - Compare via the app's own normalizers so you compare user-visible values, not raw parser shapes:
formatTitle,formatAuthors(authors, lang)(Rust givesstring[], foliate givesstring|Contributor|array— both through formatAuthors),getPrimaryLanguage,formatPublisher,formatDescription. - Cover is presence-only: Rust downscales/re-encodes the cover to a ~512px JPEG (
parser_common::maybe_resize_cover), so bytes never match foliate's original — assert(rust.coverBase64 != null) === ((await js.getCover()) != null). - Description needs whitespace-collapse: foliate collapses an internal source newline to a space; Rust preserves the raw
\n. CompareformatDescription(x).replace(/\s+/g,' ').trim(). - Section
hrefis undefined on foliateSectionItem— compare sections by{id,size,linear}only.linearcan benull. - Strongest test = open the same EPUB twice through
DocumentLoader(with{nativeFilePath}→ Rust prefetch, and without → pure zip.js) and assert identicalmetadata, sections,toc, andcomputeBookNavoutput (toc tree + per-section fragment CFIs/sizes). This validatesparse_epub_fullprefetch + the parallelized TOC pipeline are behavior-preserving in one shot.isTauriAppPlatform()is true in the webview (.env.taurisetsNEXT_PUBLIC_APP_PLATFORM=tauri) so the prefetch fires. - No
.mobi/.azw3fixture exists anywhere in the repo → MOBI parity is NOT covered by this harness; add a Kindle fixture to extend. Rust MOBI is covered bymobi_parser's own unit tests. - Offline verification trick (no chromedriver/tauri-driver locally): dump foliate
book.metadatavia a temp node vitest test, dump Rustparse_epub_metadata_sync/compute_partial_md5via a temp#[cfg(test)]mod usingenv!("CARGO_MANIFEST_DIR")/../src/__tests__/fixtures/data, diff. Lets you lock every expected value before CI runs the WebView suite.
Running the .tauri.test.ts suite locally on macOS (no chromedriver/tauri-driver needed — tauri-plugin-webdriver 0.2 embeds an axum WebDriver server with a platform/macos.rs WKWebView impl, default port 4445):
pnpm test:taurirunsscripts/test-tauri.sh: startsnext devon :3000,tauri dev --features webdriver --no-watch, waits for :4445/status, thenvitest --config vitest.tauri.config.mts. Its cleanup doeslsof -ti :3000 | xargs kill— so it KILLS whatever is on :3000 (e.g. another worktree's dev server). To avoid that, copy the script and shiftDEV_PORT/next dev -p/--config devUrlto a free port (e.g. 3100). Run viapnpm exec bash <script>so npm bins (dotenv,next,tauri,vitest) resolve, not the Rubydotenvgem.- Single-instance blocker:
tauri_plugin_single_instance(lib.rs, keyed on bundle idcom.bilingify.readest) is registered unconditionally for desktop and is NOT gated by the webdriver feature. If/Applications/Readest.app(or any Readest instance) is running, the test instance forwards to it and exits → "Tauri exited before WebDriver ready." Quit Readest.app before running. CI doesn't hit this (Linux, no Readest installed). vitest.tauri.config.mtsneeds anoptimizeDepsblock mirroringvitest.browser.config.mts(include the CJS depsjs-md5/@zip.js/zip.js/franc-min/iso-639-*/etc.; exclude@pdfjs/pdf.min.mjs+ the turso wasm). Without it, esbuild's dep-scan can't resolve foliate-js'simport '@pdfjs/pdf.min.mjs', skips pre-bundling, and any tauri test that imports@/libs/documentfails to load with "Importing a module script failed" on a cold.vitecache. (A warm cache from a prior run masks it.)- Pre-existing failure, not parity-related:
tauri-app-service.tauri.test.tshas 13 failures on macOS —forbidden path: …/src/__tests__/fixtures/data/sample-alice.epub … allow-open permission. importBook's library-copy goes through the fs plugin (scoped), andcapabilities-extra/webdriver.jsondoesn't grant the fixtures dir. Confirmed identical at base config (no regression from parity work). Parser parity tests avoid this: RustFile::openis unscoped, and the JS side fetches fixtures via a Vite URL. To fix separately, add the fixtures path to the webdriver capability scope.
Parity gotcha found + fixed: Rust handle_meta mapped dc:date AND dcterms:modified → published. foliate keeps dcterms:modified as a separate modified field and leaves published empty. EPUB3 mandates dcterms:modified, so native-imported EPUB3 books with no <dc:date> got a bogus publication date. Fix: map only "date" (not "modified") to published. Regression tests: epub_parser::tests::{dcterms_modified_does_not_populate_published, dc_date_populates_published}.