"""Shared offline format preparation and progressive text acceptance.""" import unittest from urllib.parse import urlencode from tests import test_reader_pdf_followup as support from tests.test_reader_performance import epub_with_navigation class ReaderFormatPipelineTests(unittest.TestCase): setUpClass = classmethod(support.PdfFollowupTests.setUpClass.__func__) setUp = support.PdfFollowupTests.setUp def test_body_consumption_overlaps_delayed_format_engines(self): self.page.add_init_script('''(()=>{ const native=fetch;window.__bodyReads=0; window.fetch=(input,init)=>native(input,init).then(response=>{ if(!String(input).includes('/api/reader-content?'))return response; const source=response.body.getReader(); return new Response(new ReadableStream({async pull(controller){ __bodyReads++;const part=await source.read(); if(part.done){source.releaseLock();controller.close();}else controller.enqueue(part.value); },cancel(reason){return source.cancel(reason);}} ,{highWaterMark:0}),{status:response.status,headers:response.headers}); }); })();''') engines={ 'marked':'window.marked={parse:t=>"
"+t+"
"};', 'purify':'window.DOMPurify={addHook(){},removeHook(){},sanitize:(t,o)=>{if(!o?.RETURN_DOM_FRAGMENT)return t;const n=document.createElement("template");n.innerHTML=t;return n.content;}};', } documents=[('md',b'Markdown body','text/plain'),('html',b'HTML body
','text/html'), ('epub',epub_with_navigation(),'application/epub+zip')] for ext,body,mime in documents: with self.subTest(format=ext): held=[] def engine(route):held.append(route) pattern='**/static/foliate-reader/view.js*' if ext=='epub' else '**/static/vendor/*.js' self.page.route(pattern,engine) self.page.route('**/api/reader-content**',lambda route,_request,body=body,mime=mime:route.fulfill( content_type=mime,body=body,headers={'Access-Control-Allow-Origin':'*'})) url=f'https://huggingface.co/datasets/VoiceOfML/Test/resolve/main/pipeline.{ext}' self.page.goto(self.origin+support.PREFIX+'/static/reader.html?'+urlencode(dict(url=url,ext=ext)),wait_until='domcontentloaded') self.page.wait_for_function('()=>window.__bodyReads>1',timeout=1500) self.assertTrue(held) self.assertNotEqual(self.page.locator('html').get_attribute('data-reader-phase'),'ready') self.page.unroute(pattern,engine) for route in held: if ext=='epub':route.continue_() else: name=next(key for key in engines if '/'+key+'.' in route.request.url) route.fulfill(content_type='text/javascript',body=engines[name]) self.page.wait_for_function("()=>document.documentElement.dataset.readerPhase==='ready'") text=(self.page.frame_locator('.html-frame').locator('body').inner_text() if ext=='html' else self.page.locator('.reader-section-body').first.inner_text() if ext=='epub' else self.page.locator('#content').inner_text()) self.assertTrue(text.strip()) def test_streaming_text_replaces_loading_feedback_before_response_finishes(self): self.page.add_init_script(r'''(()=>{ const native=fetch;window.fetch=(input,init)=>String(input).includes('/api/reader-content?') ?Promise.resolve(new Response(new ReadableStream({start(c){ c.enqueue(new TextEncoder().encode('Readable text\n')); window.__finishText=()=>{c.enqueue(new TextEncoder().encode('Tail text'));c.close();}; }}),{headers:{'Content-Type':'text/plain'}})):native(input,init); })();''') self.page.goto(self.origin+support.PREFIX+'/static/reader.html?'+urlencode(dict( url='https://huggingface.co/datasets/VoiceOfML/Test/resolve/main/stream.txt',ext='txt')),wait_until='domcontentloaded') self.page.wait_for_function("()=>document.querySelector('.reader-text')?.textContent==='Readable text\\n'") self.assertEqual(self.page.locator('.reader-loading-indicator').count(),0) self.assertNotEqual(self.page.locator('html').get_attribute('data-reader-phase'),'ready') self.page.evaluate('window.__finishText()') self.page.wait_for_function("()=>document.documentElement.dataset.readerPhase==='ready'") self.assertEqual(self.page.locator('.reader-text').text_content(),'Readable text\nTail text') def test_engine_failure_cancels_a_stalled_body_without_waiting_for_download(self): self.page.add_init_script(r'''(()=>{ const native=fetch;window.fetch=(input,init)=>String(input).includes('/api/reader-content?') ?Promise.resolve(new Response(new ReadableStream({start(c){c.enqueue(new TextEncoder().encode('