11import { shallowMount , flushPromises } from '@vue/test-utils'
22import App from '../../src/App.vue'
33
4+ // mock XMLHttpRequest for templates loading
5+ global . XMLHttpRequest = class XMLHttpRequest {
6+ private responseText : string
7+ private status : number
8+
9+ open ( method , templatePath ) {
10+ const templateName = templatePath . split ( '/' ) . pop ( )
11+
12+ switch ( templateName ) {
13+ case 'cover-template.html' :
14+ this . status = 200
15+ this . responseText = `<script type="x-tmpl-mustache">
16+ <div class="content-container">
17+ <div class="logo">
18+ <img src="{{{ metadata.logo }}}" alt="Logo">
19+ </div>
20+
21+ <div class="content">
22+ <h1>{{{ title }}}</h1>
23+
24+ <p>
25+ By: {{{ metadata.presenter }}}
26+ </p>
27+
28+ </div>
29+ </div>
30+ </script>
31+ `
32+ break
33+ case 'title-content-template.html' :
34+ this . status = 200
35+ this . responseText = `<script type="x-tmpl-mustache">
36+ <div class="content-container">
37+ <div class="title">
38+ <h1>
39+
40+ {{{ title }}}
41+
42+ </h1>
43+ <div class="logo">
44+ <img src="{{{ metadata.logo }}}" alt="Logo">
45+ </div>
46+ </div>
47+
48+ <div class="content-wrapper">
49+ <div class="content">
50+
51+ {{{ content }}}
52+
53+ </div>
54+ </div>
55+ </div>
56+ <footer>
57+ <div class="footer-content">{{{ metadata.footer }}}</div>
58+ <div class="custom-slide-number"></div>
59+ </footer>
60+ </script>
61+ `
62+ break
63+ case 'title-content-image-template.html' :
64+ this . status = 200
65+ this . responseText = `<script type="x-tmpl-mustache">
66+ <div class="content-container">
67+ <div class="title">
68+ <h1>
69+
70+ {{{ title }}}
71+
72+ </h1>
73+ <div class="logo">
74+ <img src="{{{ metadata.logo }}}" alt="Logo">
75+ </div>
76+ </div>
77+
78+ <div class="content-wrapper">
79+ <div class="content">
80+
81+ {{{ content }}}
82+
83+ </div>
84+ </div>
85+ </div>
86+ <footer>
87+ <div class="footer-content">{{{ metadata.footer }}}</div>
88+ <div class="custom-slide-number"></div>
89+ </footer>
90+ </script>
91+ `
92+ break
93+ case 'about-us-template.html' :
94+ this . status = 200
95+ this . responseText = `<script type="x-tmpl-mustache">
96+ <div class="content-container">
97+ <div class="title">
98+ <h1>
99+
100+ {{{ title }}}
101+
102+ </h1>
103+ <div class="logo">
104+ <img src="{{{ metadata.logo }}}" alt="Logo">
105+ </div>
106+ </div>
107+
108+ <div class="content-wrapper">
109+ <div class="content">
110+ <div class="about-us-text">
111+
112+ {{{ content }}}
113+
114+ </div>
115+ <div class="info-section">
116+
117+ {{#metadata.aboutUs}}
118+ <div class="info-box">
119+ <h3>{{ title }}</h3>
120+ <p>{{ text }}</p>
121+ </div>
122+ <div class="divider"></div>
123+ {{/metadata.aboutUs}}
124+
125+ </div>
126+
127+ </div>
128+ </div>
129+ </div>
130+ <footer>
131+ <div class="footer-content">{{{ metadata.footer }}}</div>
132+ <div class="custom-slide-number"></div>
133+ </footer>
134+ </script>
135+ `
136+ break
137+ default :
138+ this . status = 404
139+ return '<p>Template for slide "' + templateName + '" not found.</p>'
140+ }
141+ }
142+
143+ send ( ) {
144+ return
145+ }
146+ }
147+
4148// mock modules
5149vi . mock ( '@ownclouders/web-pkg' , ( ) => ( {
6150 useAppDefaults : vi . fn ( ) . mockImplementation ( ( ) => ( {
@@ -35,7 +179,7 @@ vi.mock('@ownclouders/web-pkg', () => ({
35179 AppLoadingSpinner : vi . fn ( )
36180} ) )
37181// global mocks
38- global . fetch = vi . fn ( ) . mockImplementation ( ( ) =>
182+ const defaultFetchMock = vi . fn ( ) . mockImplementation ( ( ) =>
39183 Promise . resolve ( {
40184 text : ( ) =>
41185 Promise . resolve ( `### Slide 1
@@ -96,6 +240,7 @@ code block
96240 )
97241 } )
98242)
243+ global . fetch = defaultFetchMock
99244URL . createObjectURL = vi
100245 . fn ( )
101246 . mockImplementation ( ( ) => 'blob:nodedata:0295bafb-5976-468a-a263-685a8872cb96' )
@@ -113,3 +258,186 @@ function getWrapper() {
113258 propsData : { url : 'https://localhost:9200/slides.md' }
114259 } )
115260}
261+
262+ // eslint-disable-next-line require-await
263+ describe ( 'Template Features' , async ( ) => {
264+ beforeEach ( ( ) => {
265+ global . fetch = defaultFetchMock
266+ } )
267+
268+ it ( 'should return template not found error message' , async ( ) => {
269+ global . fetch = vi . fn ( ) . mockImplementation ( ( ) => {
270+ return Promise . resolve ( {
271+ text : ( ) =>
272+ Promise . resolve ( `---
273+ slide: non-existent
274+ ---
275+ ` ) ,
276+ blob : ( ) => Promise . resolve ( new Blob ( [ ] , { type : 'text/markdown' } ) )
277+ } )
278+ } )
279+ const vm = getWrapper ( )
280+ await flushPromises ( )
281+ expect ( vm . html ( ) ) . toContain ( 'Template for slide "non-existent" not found.' )
282+ } )
283+
284+ it ( 'should return yaml parsing error' , async ( ) => {
285+ const errorSpy = vi . spyOn ( console , 'error' ) . mockImplementation ( ( ) => { } )
286+
287+ global . fetch = vi . fn ( ) . mockResolvedValue (
288+ new Response (
289+ `---
290+ slide: title-content
291+ logo: [invalid yaml
292+ ---
293+ ` ,
294+ {
295+ status : 200 ,
296+ headers : { 'Content-Type' : 'text/markdown' }
297+ }
298+ )
299+ )
300+
301+ const vm = getWrapper ( )
302+ await flushPromises ( )
303+
304+ expect ( errorSpy ) . toHaveBeenCalledWith (
305+ 'Frontmatter parsing error:' ,
306+ expect . objectContaining ( {
307+ name : 'YAMLException' ,
308+ message : expect . stringContaining ( 'unexpected end of the stream within a flow collection' )
309+ } )
310+ )
311+ expect ( vm . html ( ) ) . toContain (
312+ 'YAMLException: unexpected end of the stream within a flow collection at line 3, column 1'
313+ )
314+ } )
315+
316+ it ( 'should render cover template' , async ( ) => {
317+ global . fetch = vi . fn ( ) . mockImplementation ( ( ) => {
318+ return Promise . resolve ( {
319+ text : ( ) =>
320+ Promise . resolve ( `---
321+ slide: title-content
322+ presenter: John Doe
323+ logo: https://external:9200/cat.jpg
324+ ---
325+ # Reveal Js Templates in Web App Presentation Viewer ::slide:cover
326+ ` ) ,
327+ blob : ( ) => Promise . resolve ( new Blob ( [ ] , { type : 'text/markdown' } ) )
328+ } )
329+ } )
330+ const vm = getWrapper ( )
331+ await flushPromises ( )
332+ await vi . waitFor (
333+ ( ) => {
334+ expect ( vm . classes ( 'md-template' ) ) . toBe ( true )
335+ } ,
336+ { timeout : 1000 }
337+ )
338+ expect ( vm . find ( '.reveal .slides' ) . html ( ) ) . toMatchSnapshot ( )
339+ } )
340+
341+ it ( 'should render title content template' , async ( ) => {
342+ global . fetch = vi . fn ( ) . mockImplementation ( ( ) => {
343+ return Promise . resolve ( {
344+ text : ( ) =>
345+ Promise . resolve ( `---
346+ slide: title-content
347+ presenter: John Doe
348+ logo: https://external:9200/cat.jpg
349+ ---
350+ # Title Content Slide
351+
352+ - Introduction to mountain ecosystems
353+ - Basic concepts of quantum encryption
354+ - Exploring culinary traditions in Southeast Asia
355+ - Overview of blockchain consensus mechanisms
356+ - Setting up a personal productivity system
357+ - Writing clean, maintainable JavaScript code
358+ - Understanding modern art movements
359+ - History of aviation and early flight experiments
360+ - Managing team dynamics in remote work
361+ ` ) ,
362+ blob : ( ) => Promise . resolve ( new Blob ( [ ] , { type : 'text/markdown' } ) )
363+ } )
364+ } )
365+ const vm = getWrapper ( )
366+ await flushPromises ( )
367+ await vi . waitFor (
368+ ( ) => {
369+ expect ( vm . classes ( 'md-template' ) ) . toBe ( true )
370+ } ,
371+ { timeout : 1000 }
372+ )
373+ expect ( vm . find ( '.reveal .slides' ) . html ( ) ) . toMatchSnapshot ( )
374+ } )
375+
376+ it ( 'should render title content image template' , async ( ) => {
377+ global . fetch = vi . fn ( ) . mockImplementation ( ( ) => {
378+ return Promise . resolve ( {
379+ text : ( ) =>
380+ Promise . resolve ( `---
381+ slide: title-content
382+ presenter: John Doe
383+ logo: https://external:9200/cat.jpg
384+ ---
385+ # Title Content Image Slide ::slide:title-content-image
386+
387+ - Introduction to mountain ecosystems
388+ - Basic concepts of quantum encryption
389+ - Exploring culinary traditions in Southeast Asia
390+ - Overview of blockchain consensus mechanisms
391+
392+ 
393+ ` ) ,
394+ blob : ( ) => Promise . resolve ( new Blob ( [ ] , { type : 'text/markdown' } ) )
395+ } )
396+ } )
397+ const vm = getWrapper ( )
398+ await flushPromises ( )
399+ await vi . waitFor (
400+ ( ) => {
401+ expect ( vm . classes ( 'md-template' ) ) . toBe ( true )
402+ } ,
403+ { timeout : 1000 }
404+ )
405+ expect ( vm . find ( '.reveal .slides' ) . html ( ) ) . toMatchSnapshot ( )
406+ } )
407+
408+ it ( 'should render about us template' , async ( ) => {
409+ global . fetch = vi . fn ( ) . mockImplementation ( ( ) => {
410+ return Promise . resolve ( {
411+ text : ( ) =>
412+ Promise . resolve ( `---
413+ slide: title-content
414+ presenter: John Doe
415+ logo: https://external:9200/cat.jpg
416+ aboutUs:
417+ - title: WWW
418+ text: www.example.com
419+ - title: LINKEDIN
420+ text: www.linkedin.com/company/example
421+ - title: FACEBOOK
422+ text: www.facebook.com/example
423+ - title: X
424+ text: www.x.com/example
425+ ---
426+ # About Us ::slide:about-us
427+
428+ Some content about us.
429+ ` ) ,
430+ blob : ( ) => Promise . resolve ( new Blob ( [ ] , { type : 'text/markdown' } ) )
431+ } )
432+ } )
433+ const vm = getWrapper ( )
434+ await flushPromises ( )
435+ await vi . waitFor (
436+ ( ) => {
437+ expect ( vm . classes ( 'md-template' ) ) . toBe ( true )
438+ } ,
439+ { timeout : 1000 }
440+ )
441+ expect ( vm . find ( '.reveal .slides' ) . html ( ) ) . toMatchSnapshot ( )
442+ } )
443+ } )
0 commit comments