55import type { WebViewProps } from '@papi/core' ;
66import type { SerializedVerseRef } from '@sillsdev/scripture' ;
77import { render , screen } from '@testing-library/react' ;
8- import { InterlinearXmlParser } from 'parsers/interlinearXmlParser' ;
9-
10- /** Mock parser to allow overriding constructor behavior per test. */
11- jest . mock ( 'parsers/interlinearXmlParser' , ( ) => {
12- const actual = jest . requireActual < typeof import ( 'parsers/interlinearXmlParser' ) > (
13- 'parsers/interlinearXmlParser' ,
14- ) ;
15- return {
16- InterlinearXmlParser : jest . fn ( ) . mockImplementation ( ( ) => new actual . InterlinearXmlParser ( ) ) ,
17- } ;
18- } ) ;
8+ import { useProjectData } from '@papi/frontend/react' ;
199
2010/**
2111 * Load the WebView module; it assigns the component to globalThis.webViewComponent. This pattern is
@@ -32,94 +22,86 @@ if (!InterlinearizerWebView) throw new Error('webViewComponent not loaded');
3222/** Minimal SerializedVerseRef for hook mock return. */
3323const defaultScrRef : SerializedVerseRef = { book : 'GEN' , chapterNum : 1 , verseNum : 1 } ;
3424
35- /** Full WebViewProps for tests; interlinearizer component ignores hooks/update. */
36- const testWebViewProps : WebViewProps = {
37- id : 'test-id' ,
38- webViewType : 'interlinearizer.mainWebView' ,
39- useWebViewState : < T , > ( _key : string , defaultValue : T ) : [ T , ( v : T ) => void , ( ) => void ] => [
40- defaultValue ,
41- ( ) => { } ,
42- ( ) => { } ,
43- ] ,
44- useWebViewScrollGroupScrRef : ( ) : [
45- SerializedVerseRef ,
46- ( r : SerializedVerseRef ) => void ,
47- number | undefined ,
48- ( id : number | undefined ) => void ,
49- ] => [ defaultScrRef , ( ) => { } , undefined , ( ) => { } ] ,
50- updateWebViewDefinition : ( ) => true ,
51- } ;
25+ /** Builds a minimal WebViewProps for tests. */
26+ function makeProps ( projectId ?: string ) : WebViewProps {
27+ return {
28+ id : 'test-id' ,
29+ webViewType : 'interlinearizer.mainWebView' ,
30+ projectId,
31+ useWebViewState : < T , > ( _key : string , defaultValue : T ) : [ T , ( v : T ) => void , ( ) => void ] => [
32+ defaultValue ,
33+ ( ) => { } ,
34+ ( ) => { } ,
35+ ] ,
36+ useWebViewScrollGroupScrRef : ( ) : [
37+ SerializedVerseRef ,
38+ ( r : SerializedVerseRef ) => void ,
39+ number | undefined ,
40+ ( id : number | undefined ) => void ,
41+ ] => [ defaultScrRef , ( ) => { } , undefined , ( ) => { } ] ,
42+ updateWebViewDefinition : ( ) => true ,
43+ } ;
44+ }
45+
46+ /** Configures useProjectData to return the given BookUSJ value this render. */
47+ function mockBookData ( value : unknown ) : void {
48+ jest . mocked ( useProjectData ) . mockImplementation ( ( ) => ( {
49+ BookUSJ : ( ) => [ value , jest . fn ( ) , false ] ,
50+ } ) ) ;
51+ }
5252
5353describe ( 'InterlinearizerWebView' , ( ) => {
54+ beforeEach ( ( ) => {
55+ mockBookData ( undefined ) ;
56+ } ) ;
57+
5458 it ( 'renders the heading "Interlinearizer"' , ( ) => {
55- render ( < InterlinearizerWebView { ...testWebViewProps } /> ) ;
59+ render ( < InterlinearizerWebView { ...makeProps ( ) } /> ) ;
5660
5761 expect ( screen . getByRole ( 'heading' , { name : / i n t e r l i n e a r i z e r / i } ) ) . toBeInTheDocument ( ) ;
5862 } ) ;
5963
60- it ( 'renders the description mentioning test-data XML ' , ( ) => {
61- render ( < InterlinearizerWebView { ...testWebViewProps } /> ) ;
64+ it ( 'shows a prompt to open from a project when no projectId is provided ' , ( ) => {
65+ render ( < InterlinearizerWebView { ...makeProps ( ) } /> ) ;
6266
63- expect (
64- screen . getByText ( / r a w j s o n o f t h e m o d e l p a r s e d f r o m / i, { exact : false } ) ,
65- ) . toBeInTheDocument ( ) ;
66- expect ( screen . getByText ( / t e s t - d a t a \/ I n t e r l i n e a r _ e n _ M A T \. x m l / i) ) . toBeInTheDocument ( ) ;
67+ expect ( screen . getByText ( / o p e n t h i s w e b v i e w f r o m a p a r a t e x t p r o j e c t / i) ) . toBeInTheDocument ( ) ;
6768 } ) ;
6869
69- it ( 'parses the bundled test XML and displays parsed JSON' , ( ) => {
70- render ( < InterlinearizerWebView { ...testWebViewProps } /> ) ;
70+ it ( 'shows the book and projectId when a project is linked' , ( ) => {
71+ mockBookData ( {
72+ type : 'USJ' ,
73+ version : '3.1' ,
74+ content : [ { type : 'book' , marker : 'id' , code : 'GEN' } ] ,
75+ } ) ;
76+ render ( < InterlinearizerWebView { ...makeProps ( 'test-project-id' ) } /> ) ;
7177
72- expect ( screen . getByText ( / p a r s e d i n t e r l i n e a r d a t a \( j s o n \) / i) ) . toBeInTheDocument ( ) ;
73- expect ( screen . getByText ( / " G l o s s L a n g u a g e " / ) ) . toBeInTheDocument ( ) ;
74- expect ( screen . getByText ( / " B o o k I d " / ) ) . toBeInTheDocument ( ) ;
78+ expect ( screen . getByText ( / t e s t - p r o j e c t - i d / ) ) . toBeInTheDocument ( ) ;
79+ expect ( screen . getByText ( / G E N · p r o j e c t / ) ) . toBeInTheDocument ( ) ;
7580 } ) ;
7681
77- it ( 'displays parsed structure with expected verse data' , ( ) => {
78- render ( < InterlinearizerWebView { ...testWebViewProps } /> ) ;
82+ it ( 'shows Loading when projectId is set but book data has not arrived' , ( ) => {
83+ mockBookData ( undefined ) ;
84+ render ( < InterlinearizerWebView { ...makeProps ( 'test-project-id' ) } /> ) ;
7985
80- expect ( screen . getByText ( / " e n " / ) ) . toBeInTheDocument ( ) ;
81- expect ( screen . getByText ( / " M A T " / ) ) . toBeInTheDocument ( ) ;
86+ expect ( screen . getByText ( 'Loading…' ) ) . toBeInTheDocument ( ) ;
8287 } ) ;
8388
84- it ( 'does not show parse error when XML is valid' , ( ) => {
85- render ( < InterlinearizerWebView { ...testWebViewProps } /> ) ;
89+ it ( 'shows the raw USFM when book data arrives' , ( ) => {
90+ mockBookData ( {
91+ type : 'USJ' ,
92+ version : '3.1' ,
93+ content : [ { type : 'book' , marker : 'id' , code : 'GEN' } ] ,
94+ } ) ;
95+ render ( < InterlinearizerWebView { ...makeProps ( 'test-project-id' ) } /> ) ;
8696
87- expect ( screen . queryByText ( / ^ p a r s e e r r o r $ / i ) ) . not . toBeInTheDocument ( ) ;
97+ expect ( screen . getByText ( / " c o d e " : " G E N " / ) ) . toBeInTheDocument ( ) ;
8898 } ) ;
8999
90- it ( 'displays parse error when parser throws an Error (uses err.message)' , ( ) => {
91- const actual = jest . requireActual < typeof import ( '../parsers/interlinearXmlParser' ) > (
92- '../parsers/interlinearXmlParser' ,
93- ) ;
94- const realInstance = new actual . InterlinearXmlParser ( ) ;
95- const throwingParse = ( ) : never => {
96- throw new Error ( 'Invalid XML structure' ) ;
97- } ;
98- Object . defineProperty ( realInstance , 'parse' , { value : throwingParse , writable : true } ) ;
99- jest . mocked ( InterlinearXmlParser ) . mockImplementationOnce ( ( ) => realInstance ) ;
100-
101- render ( < InterlinearizerWebView { ...testWebViewProps } /> ) ;
102-
103- expect ( screen . getByRole ( 'heading' , { name : / ^ p a r s e e r r o r $ / i } ) ) . toBeInTheDocument ( ) ;
104- expect ( screen . getByText ( / i n v a l i d x m l s t r u c t u r e / i) ) . toBeInTheDocument ( ) ;
105- } ) ;
100+ it ( 'shows an error heading and message when book data is a PlatformError' , ( ) => {
101+ mockBookData ( { isPlatformError : true , message : 'Project not found' } ) ;
102+ render ( < InterlinearizerWebView { ...makeProps ( 'test-project-id' ) } /> ) ;
106103
107- it ( 'displays parse error when parser throws non-Error (uses String(err))' , ( ) => {
108- const actual = jest . requireActual < typeof import ( '../parsers/interlinearXmlParser' ) > (
109- '../parsers/interlinearXmlParser' ,
110- ) ;
111- const realInstance = new actual . InterlinearXmlParser ( ) ;
112- const throwingParse = ( ) : never => {
113- // Intentionally throw a non-Error to test the String(err) branch in the catch block.
114- // eslint-disable-next-line no-throw-literal -- testing non-Error handling
115- throw 'plain string error' ;
116- } ;
117- Object . defineProperty ( realInstance , 'parse' , { value : throwingParse , writable : true } ) ;
118- jest . mocked ( InterlinearXmlParser ) . mockImplementationOnce ( ( ) => realInstance ) ;
119-
120- render ( < InterlinearizerWebView { ...testWebViewProps } /> ) ;
121-
122- expect ( screen . getByRole ( 'heading' , { name : / ^ p a r s e e r r o r $ / i } ) ) . toBeInTheDocument ( ) ;
123- expect ( screen . getByText ( 'plain string error' ) ) . toBeInTheDocument ( ) ;
104+ expect ( screen . getByRole ( 'heading' , { name : / e r r o r l o a d i n g b o o k / i } ) ) . toBeInTheDocument ( ) ;
105+ expect ( screen . getByText ( / p r o j e c t n o t f o u n d / i) ) . toBeInTheDocument ( ) ;
124106 } ) ;
125107} ) ;
0 commit comments