Skip to content

Commit 80441ef

Browse files
committed
add tests
Signed-off-by: Prajwol Amatya <prajwolamatya11@gmail.com>
1 parent eb5f5ee commit 80441ef

File tree

3 files changed

+480
-4
lines changed

3 files changed

+480
-4
lines changed

src/App.vue

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -121,7 +121,7 @@ onMounted(async () => {
121121
})
122122
123123
if (reveal.isReady()) {
124-
applyTemplateIfNeeded()
124+
await applyTemplateIfNeeded()
125125
addCustomSlideNumber()
126126
updateImageStructure()
127127
fitContent()
@@ -399,16 +399,19 @@ function setFontColor() {
399399
el.style.color = color
400400
})
401401
}
402-
function applyTemplateIfNeeded() {
402+
async function applyTemplateIfNeeded() {
403403
const [markdown, frontMatter] = separateFrontmatterAndMarkdown()
404404
loadTemplate = !!(frontMatter.metadata?.slide || markdown.match(headingSlideRegex))
405405
406406
if (loadTemplate) {
407407
// dynamically import CSS file only when needed
408408
presentationViewerRef.value.classList.add('md-template')
409-
import('./css/templates.css')
409+
await import('./css/templates.css')
410410
setFontColor()
411+
await new Promise((resolve) => requestAnimationFrame(resolve))
412+
await new Promise((resolve) => setTimeout(resolve, 100))
411413
}
414+
return loadTemplate
412415
}
413416
async function updateLogoUrl() {
414417
const frontMatter = separateFrontmatterAndMarkdown()[1]

tests/unit/App.spec.ts

Lines changed: 319 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,150 @@
11
import { shallowMount, flushPromises } from '@vue/test-utils'
22
import 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
5149
vi.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
99244
URL.createObjectURL = vi
100245
.fn()
101246
.mockImplementation(() => 'blob:nodedata:0295bafb-5976-468a-a263-685a8872cb96')
@@ -113,3 +258,176 @@ 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+
global.fetch = vi.fn().mockImplementation(() => {
287+
return Promise.resolve({
288+
text: () =>
289+
Promise.resolve(`---
290+
slide: title-content
291+
logo: [invalid yaml
292+
---
293+
`),
294+
blob: () => Promise.resolve(new Blob([], { type: 'text/markdown' }))
295+
})
296+
})
297+
298+
const vm = getWrapper()
299+
await flushPromises()
300+
expect(errorSpy).toHaveBeenCalled()
301+
expect(vm.html()).toContain(
302+
'YAMLException: unexpected end of the stream within a flow collection at line 3, column 1'
303+
)
304+
})
305+
306+
it('should render cover template', async () => {
307+
global.fetch = vi.fn().mockImplementation(() => {
308+
return Promise.resolve({
309+
text: () =>
310+
Promise.resolve(`---
311+
slide: title-content
312+
presenter: John Doe
313+
logo: https://external:9200/cat.jpg
314+
---
315+
# Reveal Js Templates in Web App Presentation Viewer ::slide:cover
316+
`),
317+
blob: () => Promise.resolve(new Blob([], { type: 'text/markdown' }))
318+
})
319+
})
320+
const vm = getWrapper()
321+
await flushPromises()
322+
await vi.waitFor(
323+
() => {
324+
expect(vm.classes('md-template')).toBe(true)
325+
},
326+
{ timeout: 1000 }
327+
)
328+
expect(vm.find('.reveal .slides').html()).toMatchSnapshot()
329+
})
330+
331+
it('should render title content template', async () => {
332+
global.fetch = vi.fn().mockImplementation(() => {
333+
return Promise.resolve({
334+
text: () =>
335+
Promise.resolve(`---
336+
slide: title-content
337+
presenter: John Doe
338+
logo: https://external:9200/cat.jpg
339+
---
340+
# Title Content Slide
341+
342+
- Introduction to mountain ecosystems
343+
- Basic concepts of quantum encryption
344+
- Exploring culinary traditions in Southeast Asia
345+
- Overview of blockchain consensus mechanisms
346+
- Setting up a personal productivity system
347+
- Writing clean, maintainable JavaScript code
348+
- Understanding modern art movements
349+
- History of aviation and early flight experiments
350+
- Managing team dynamics in remote work
351+
`),
352+
blob: () => Promise.resolve(new Blob([], { type: 'text/markdown' }))
353+
})
354+
})
355+
const vm = getWrapper()
356+
await flushPromises()
357+
await vi.waitFor(
358+
() => {
359+
expect(vm.classes('md-template')).toBe(true)
360+
},
361+
{ timeout: 1000 }
362+
)
363+
expect(vm.find('.reveal .slides').html()).toMatchSnapshot()
364+
})
365+
366+
it('should render title content image template', async () => {
367+
global.fetch = vi.fn().mockImplementation(() => {
368+
return Promise.resolve({
369+
text: () =>
370+
Promise.resolve(`---
371+
slide: title-content
372+
presenter: John Doe
373+
logo: https://external:9200/cat.jpg
374+
---
375+
# Title Content Image Slide ::slide:title-content-image
376+
377+
- Introduction to mountain ecosystems
378+
- Basic concepts of quantum encryption
379+
- Exploring culinary traditions in Southeast Asia
380+
- Overview of blockchain consensus mechanisms
381+
382+
![cat](https://external:9200/cat.jpg)
383+
`),
384+
blob: () => Promise.resolve(new Blob([], { type: 'text/markdown' }))
385+
})
386+
})
387+
const vm = getWrapper()
388+
await flushPromises()
389+
await vi.waitFor(
390+
() => {
391+
expect(vm.classes('md-template')).toBe(true)
392+
},
393+
{ timeout: 1000 }
394+
)
395+
expect(vm.find('.reveal .slides').html()).toMatchSnapshot()
396+
})
397+
398+
it('should render about us template', async () => {
399+
global.fetch = vi.fn().mockImplementation(() => {
400+
return Promise.resolve({
401+
text: () =>
402+
Promise.resolve(`---
403+
slide: title-content
404+
presenter: John Doe
405+
logo: https://external:9200/cat.jpg
406+
aboutUs:
407+
- title: WWW
408+
text: www.example.com
409+
- title: LINKEDIN
410+
text: www.linkedin.com/company/example
411+
- title: FACEBOOK
412+
text: www.facebook.com/example
413+
- title: X
414+
text: www.x.com/example
415+
---
416+
# About Us ::slide:about-us
417+
418+
Some content about us.
419+
`),
420+
blob: () => Promise.resolve(new Blob([], { type: 'text/markdown' }))
421+
})
422+
})
423+
const vm = getWrapper()
424+
await flushPromises()
425+
await vi.waitFor(
426+
() => {
427+
expect(vm.classes('md-template')).toBe(true)
428+
},
429+
{ timeout: 1000 }
430+
)
431+
expect(vm.find('.reveal .slides').html()).toMatchSnapshot()
432+
})
433+
})

0 commit comments

Comments
 (0)