~tomasgroth/openlp/portable-path

« back to all changes in this revision

Viewing changes to tests/functional/openlp_core/lib/test_htmlbuilder.py

  • Committer: Tomas Groth
  • Date: 2019-04-30 19:02:42 UTC
  • mfrom: (2829.2.32 openlp)
  • Revision ID: tomasgroth@yahoo.dk-20190430190242-6zwjk8724tyux70m
trunk

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
"""
2
 
Package to test the openlp.core.lib.htmlbuilder module.
3
 
"""
4
 
from unittest import TestCase
5
 
from unittest.mock import MagicMock, patch
6
 
 
7
 
from PyQt5 import QtCore, QtWebKit
8
 
 
9
 
from openlp.core.common.settings import Settings
10
 
from openlp.core.lib.htmlbuilder import build_html, build_background_css, build_lyrics_css, build_lyrics_outline_css, \
11
 
    build_lyrics_format_css, build_footer_css, webkit_version, build_chords_css
12
 
from openlp.core.lib.theme import HorizontalType, VerticalType
13
 
from tests.helpers.testmixin import TestMixin
14
 
 
15
 
HTML = r"""
16
 
    <!DOCTYPE html>
17
 
    <html>
18
 
    <head>
19
 
    <title>OpenLP Display</title>
20
 
    <style>
21
 
    *{
22
 
        margin: 0;
23
 
        padding: 0;
24
 
        border: 0;
25
 
        overflow: hidden;
26
 
        -webkit-user-select: none;
27
 
    }
28
 
    body {
29
 
        ;
30
 
    }
31
 
    .size {
32
 
        position: absolute;
33
 
        left: 0px;
34
 
        top: 0px;
35
 
        width: 100%;
36
 
        height: 100%;
37
 
    }
38
 
    #black {
39
 
        z-index: 8;
40
 
        background-color: black;
41
 
        display: none;
42
 
    }
43
 
    #bgimage {
44
 
        z-index: 1;
45
 
    }
46
 
    #image {
47
 
        z-index: 2;
48
 
    }
49
 
    plugin CSS
50
 
    #footer {
51
 
        position: absolute;
52
 
        z-index: 6;
53
 
        dummy: dummy;
54
 
    }
55
 
    /* lyric css */
56
 
    sup {
57
 
        font-size: 0.6em;
58
 
        vertical-align: top;
59
 
        position: relative;
60
 
        top: -0.3em;
61
 
    }
62
 
    /* Chords css */
63
 
    .chordline {
64
 
      line-height: 1.0em;
65
 
    }
66
 
    .chordline span.chord span {
67
 
      position: relative;
68
 
    }
69
 
    .chordline span.chord span strong {
70
 
      position: absolute;
71
 
      top: -0.8em;
72
 
      left: 0;
73
 
      font-size: 75%;
74
 
      font-weight: normal;
75
 
      line-height: normal;
76
 
      display: none;
77
 
    }
78
 
    .firstchordline {
79
 
        line-height: 1.0em;
80
 
    }
81
 
    .ws {
82
 
        display: none;
83
 
        white-space: pre-wrap;
84
 
    }
85
 
    </style>
86
 
    <script>
87
 
        var timer = null;
88
 
        var transition = false;
89
 
        plugin JS
90
 
 
91
 
        function show_image(src){
92
 
            var img = document.getElementById('image');
93
 
            img.src = src;
94
 
            if(src == '')
95
 
                img.style.display = 'none';
96
 
            else
97
 
                img.style.display = 'block';
98
 
        }
99
 
 
100
 
        function show_blank(state){
101
 
            var black = 'none';
102
 
            var lyrics = '';
103
 
            switch(state){
104
 
                case 'theme':
105
 
                    lyrics = 'hidden';
106
 
                    break;
107
 
                case 'black':
108
 
                    black = 'block';
109
 
                    break;
110
 
                case 'desktop':
111
 
                    break;
112
 
            }
113
 
            document.getElementById('black').style.display = black;
114
 
            document.getElementById('lyricsmain').style.visibility = lyrics;
115
 
            document.getElementById('image').style.visibility = lyrics;
116
 
            document.getElementById('footer').style.visibility = lyrics;
117
 
        }
118
 
 
119
 
        function show_footer(footertext){
120
 
            document.getElementById('footer').innerHTML = footertext;
121
 
        }
122
 
 
123
 
        function show_text(new_text){
124
 
            var match = /-webkit-text-fill-color:[^;\"]+/gi;
125
 
            if(timer != null)
126
 
                clearTimeout(timer);
127
 
            /*
128
 
            QtWebkit bug with outlines and justify causing outline alignment
129
 
            problems. (Bug 859950) Surround each word with a <span> to workaround,
130
 
            but only in this scenario.
131
 
            */
132
 
            var txt = document.getElementById('lyricsmain');
133
 
            if(window.getComputedStyle(txt).textAlign == 'justify'){
134
 
                if(window.getComputedStyle(txt).webkitTextStrokeWidth != '0px'){
135
 
                    new_text = new_text.replace(/(\s|&nbsp;)+(?![^<]*>)/g,
136
 
                        function(match) {
137
 
                            return '</span>' + match + '<span>';
138
 
                        });
139
 
                    new_text = '<span>' + new_text + '</span>';
140
 
                }
141
 
            }
142
 
            text_fade('lyricsmain', new_text);
143
 
        }
144
 
 
145
 
        function text_fade(id, new_text){
146
 
            /*
147
 
            Show the text.
148
 
            */
149
 
            var text = document.getElementById(id);
150
 
            if(text == null) return;
151
 
            if(!transition){
152
 
                text.innerHTML = new_text;
153
 
                return;
154
 
            }
155
 
            // Fade text out. 0.1 to minimize the time "nothing" is shown on the screen.
156
 
            text.style.opacity = '0.1';
157
 
            // Fade new text in after the old text has finished fading out.
158
 
            timer = window.setTimeout(function(){_show_text(text, new_text)}, 400);
159
 
        }
160
 
 
161
 
        function _show_text(text, new_text) {
162
 
            /*
163
 
            Helper function to show the new_text delayed.
164
 
            */
165
 
            text.innerHTML = new_text;
166
 
            text.style.opacity = '1';
167
 
            // Wait until the text is completely visible. We want to save the timer id, to be able to call
168
 
            // clearTimeout(timer) when the text has changed before finishing fading.
169
 
            timer = window.setTimeout(function(){timer = null;}, 400);
170
 
        }
171
 
 
172
 
        function show_text_completed(){
173
 
            return (timer == null);
174
 
        }
175
 
    </script>
176
 
    </head>
177
 
    <body>
178
 
    <img id="bgimage" class="size" style="display:none;" />
179
 
    <img id="image" class="size" style="display:none;" />
180
 
    plugin HTML
181
 
    <div class="lyricstable"><div id="lyricsmain" style="opacity:1" class="lyricscell lyricsmain"></div></div>
182
 
    <div id="footer" class="footer"></div>
183
 
    <div id="black" class="size"></div>
184
 
    </body>
185
 
    </html>
186
 
    """
187
 
BACKGROUND_CSS_RADIAL = 'background: -webkit-gradient(radial, 5 50%, 100, 5 50%, 5, from(#000000), to(#FFFFFF)) fixed'
188
 
LYRICS_CSS = """
189
 
    .lyricstable {
190
 
        z-index: 5;
191
 
        position: absolute;
192
 
        display: table;
193
 
        left: 10px; top: 20px;
194
 
    }
195
 
    .lyricscell {
196
 
        display: table-cell;
197
 
        word-wrap: break-word;
198
 
        -webkit-transition: opacity 0.4s ease;
199
 
        lyrics_format_css
200
 
    }
201
 
    .lyricsmain {
202
 
        text-shadow: #000000 5px 5px;
203
 
    }
204
 
    """
205
 
LYRICS_OUTLINE_CSS = ' -webkit-text-stroke: 0.125em #000000; -webkit-text-fill-color: #FFFFFF; '
206
 
LYRICS_FORMAT_CSS = """
207
 
    word-wrap: break-word;
208
 
    text-align: justify;
209
 
    vertical-align: bottom;
210
 
    font-family: Arial;
211
 
    font-size: 40pt;
212
 
    color: #FFFFFF;
213
 
    line-height: 108%;
214
 
    margin: 0;
215
 
    padding: 0;
216
 
    padding-bottom: 0.5em;
217
 
    padding-left: 2px;
218
 
    width: 1580px;
219
 
    height: 810px;
220
 
    font-style: italic;
221
 
    font-weight: bold;
222
 
    """
223
 
FOOTER_CSS_BASE = """
224
 
    left: 10px;
225
 
    bottom: 0px;
226
 
    width: 1260px;
227
 
    font-family: Arial;
228
 
    font-size: 12pt;
229
 
    color: #FFFFFF;
230
 
    text-align: left;
231
 
    white-space: %s;
232
 
    """
233
 
FOOTER_CSS = FOOTER_CSS_BASE % ('nowrap')
234
 
FOOTER_CSS_WRAP = FOOTER_CSS_BASE % ('normal')
235
 
FOOTER_CSS_INVALID = ''
236
 
CHORD_CSS_ENABLED = """
237
 
    .chordline {
238
 
      line-height: 2.0em;
239
 
    }
240
 
    .chordline span.chord span {
241
 
      position: relative;
242
 
    }
243
 
    .chordline span.chord span strong {
244
 
      position: absolute;
245
 
      top: -0.8em;
246
 
      left: 0;
247
 
      font-size: 75%;
248
 
      font-weight: normal;
249
 
      line-height: normal;
250
 
      display: inline;
251
 
    }
252
 
    .firstchordline {
253
 
        line-height: 2.1em;
254
 
    }
255
 
    .ws {
256
 
        display: inline;
257
 
        white-space: pre-wrap;
258
 
    }"""
259
 
 
260
 
__default_settings__ = {
261
 
    'songs/mainview chords': False,
262
 
    'songs/enable chords': True
263
 
}
264
 
 
265
 
 
266
 
class Htmbuilder(TestCase, TestMixin):
267
 
    """
268
 
    Test the functions in the Htmlbuilder module
269
 
    """
270
 
    def setUp(self):
271
 
        """
272
 
        Create the UI
273
 
        """
274
 
        self.build_settings()
275
 
        Settings().extend_default_settings(__default_settings__)
276
 
 
277
 
    def tearDown(self):
278
 
        """
279
 
        Delete all the C++ objects at the end so that we don't have a segfault
280
 
        """
281
 
        self.destroy_settings()
282
 
 
283
 
    def test_build_html(self):
284
 
        """
285
 
        Test the build_html() function
286
 
        """
287
 
        # GIVEN: Mocked arguments and function.
288
 
        with patch('openlp.core.lib.htmlbuilder.build_background_css') as mocked_build_background_css, \
289
 
                patch('openlp.core.lib.htmlbuilder.build_footer_css') as mocked_build_footer_css, \
290
 
                patch('openlp.core.lib.htmlbuilder.build_lyrics_css') as mocked_build_lyrics_css:
291
 
            # Mocked function.
292
 
            mocked_build_background_css.return_value = ''
293
 
            mocked_build_footer_css.return_value = 'dummy: dummy;'
294
 
            mocked_build_lyrics_css.return_value = ''
295
 
            # Mocked arguments.
296
 
            item = MagicMock()
297
 
            item.bg_image_bytes = None
298
 
            screen = MagicMock()
299
 
            is_live = False
300
 
            background = None
301
 
            plugin = MagicMock()
302
 
            plugin.get_display_css.return_value = 'plugin CSS'
303
 
            plugin.get_display_javascript.return_value = 'plugin JS'
304
 
            plugin.get_display_html.return_value = 'plugin HTML'
305
 
            plugins = [plugin]
306
 
 
307
 
            # WHEN: Create the html.
308
 
            html = build_html(item, screen, is_live, background, plugins=plugins)
309
 
 
310
 
            # THEN: The returned html should match.
311
 
            assert html == HTML, 'The returned html should match'
312
 
 
313
 
    def test_build_background_css_radial(self):
314
 
        """
315
 
        Test the build_background_css() function with a radial background
316
 
        """
317
 
        # GIVEN: Mocked arguments.
318
 
        item = MagicMock()
319
 
        item.theme_data.background_start_color = '#000000'
320
 
        item.theme_data.background_end_color = '#FFFFFF'
321
 
        width = 10
322
 
 
323
 
        # WHEN: Create the css.
324
 
        css = build_background_css(item, width)
325
 
 
326
 
        # THEN: The returned css should match.
327
 
        assert BACKGROUND_CSS_RADIAL == css, 'The background css should be equal.'
328
 
 
329
 
    def test_build_lyrics_css(self):
330
 
        """
331
 
        Test the build_lyrics_css() function
332
 
        """
333
 
        # GIVEN: Mocked method and arguments.
334
 
        with patch('openlp.core.lib.htmlbuilder.build_lyrics_format_css') as mocked_build_lyrics_format_css, \
335
 
                patch('openlp.core.lib.htmlbuilder.build_lyrics_outline_css') as mocked_build_lyrics_outline_css:
336
 
            mocked_build_lyrics_format_css.return_value = 'lyrics_format_css'
337
 
            mocked_build_lyrics_outline_css.return_value = ''
338
 
            item = MagicMock()
339
 
            item.main = QtCore.QRect(10, 20, 10, 20)
340
 
            item.theme_data.font_main_shadow = True
341
 
            item.theme_data.font_main_shadow_color = '#000000'
342
 
            item.theme_data.font_main_shadow_size = 5
343
 
 
344
 
            # WHEN: Create the css.
345
 
            css = build_lyrics_css(item)
346
 
 
347
 
            # THEN: The css should be equal.
348
 
            assert LYRICS_CSS == css, 'The lyrics css should be equal.'
349
 
 
350
 
    def test_build_lyrics_outline_css(self):
351
 
        """
352
 
        Test the build_lyrics_outline_css() function
353
 
        """
354
 
        # GIVEN: The mocked theme data.
355
 
        theme_data = MagicMock()
356
 
        theme_data.font_main_outline = True
357
 
        theme_data.font_main_outline_size = 2
358
 
        theme_data.font_main_color = '#FFFFFF'
359
 
        theme_data.font_main_outline_color = '#000000'
360
 
 
361
 
        # WHEN: Create the css.
362
 
        css = build_lyrics_outline_css(theme_data)
363
 
 
364
 
        # THEN: The css should be equal.
365
 
        assert LYRICS_OUTLINE_CSS == css, 'The outline css should be equal.'
366
 
 
367
 
    def test_build_lyrics_format_css(self):
368
 
        """
369
 
        Test the build_lyrics_format_css() function
370
 
        """
371
 
        # GIVEN: Mocked arguments.
372
 
        theme_data = MagicMock()
373
 
        theme_data.display_horizontal_align = HorizontalType.Justify
374
 
        theme_data.display_vertical_align = VerticalType.Bottom
375
 
        theme_data.font_main_name = 'Arial'
376
 
        theme_data.font_main_size = 40
377
 
        theme_data.font_main_color = '#FFFFFF'
378
 
        theme_data.font_main_italics = True
379
 
        theme_data.font_main_bold = True
380
 
        theme_data.font_main_line_adjustment = 8
381
 
        width = 1580
382
 
        height = 810
383
 
 
384
 
        # WHEN: Get the css.
385
 
        css = build_lyrics_format_css(theme_data, width, height)
386
 
 
387
 
        # THEN: They should be equal.
388
 
        assert LYRICS_FORMAT_CSS == css, 'The lyrics format css should be equal.'
389
 
 
390
 
    def test_build_footer_css(self):
391
 
        """
392
 
        Test the build_footer_css() function
393
 
        """
394
 
        # GIVEN: Create a theme.
395
 
        item = MagicMock()
396
 
        item.footer = QtCore.QRect(10, 921, 1260, 103)
397
 
        item.theme_data.font_footer_name = 'Arial'
398
 
        item.theme_data.font_footer_size = 12
399
 
        item.theme_data.font_footer_color = '#FFFFFF'
400
 
        height = 1024
401
 
 
402
 
        # WHEN: create the css with default settings.
403
 
        css = build_footer_css(item, height)
404
 
 
405
 
        # THEN: THE css should be the same.
406
 
        assert FOOTER_CSS == css, 'The footer strings should be equal.'
407
 
 
408
 
    def test_build_footer_css_wrap(self):
409
 
        """
410
 
        Test the build_footer_css() function
411
 
        """
412
 
        # GIVEN: Create a theme.
413
 
        item = MagicMock()
414
 
        item.footer = QtCore.QRect(10, 921, 1260, 103)
415
 
        item.theme_data.font_footer_name = 'Arial'
416
 
        item.theme_data.font_footer_size = 12
417
 
        item.theme_data.font_footer_color = '#FFFFFF'
418
 
        height = 1024
419
 
 
420
 
        # WHEN: Settings say that footer should wrap
421
 
        Settings().setValue('themes/wrap footer', True)
422
 
        css = build_footer_css(item, height)
423
 
 
424
 
        # THEN: Footer should wrap
425
 
        assert FOOTER_CSS_WRAP == css, 'The footer strings should be equal.'
426
 
 
427
 
    def test_build_footer_invalid(self):
428
 
        """
429
 
        Test the build_footer_css() function
430
 
        """
431
 
        # GIVEN: Create a theme.
432
 
        css = []
433
 
        item = MagicMock()
434
 
        item.theme_data = None
435
 
        item.footer = 'FAIL'
436
 
        height = 1024
437
 
 
438
 
        # WHEN: Settings say that footer should wrap
439
 
        css.append(build_footer_css(item, height))
440
 
        item.theme_data = 'TEST'
441
 
        item.footer = None
442
 
        css.append(build_footer_css(item, height))
443
 
 
444
 
        # THEN: Footer should wrap
445
 
        assert FOOTER_CSS_INVALID == css[0], 'The footer strings should be blank.'
446
 
        assert FOOTER_CSS_INVALID == css[1], 'The footer strings should be blank.'
447
 
 
448
 
    def test_webkit_version(self):
449
 
        """
450
 
        Test the webkit_version() function
451
 
        """
452
 
        # GIVEN: Webkit
453
 
        webkit_ver = float(QtWebKit.qWebKitVersion())
454
 
        # WHEN: Retrieving the webkit version
455
 
        # THEN: Webkit versions should match
456
 
        assert webkit_version() == webkit_ver, "The returned webkit version doesn't match the installed one"
457
 
 
458
 
    def test_build_chords_css(self):
459
 
        """
460
 
        Test the build_chords_css() function
461
 
        """
462
 
        # GIVEN: A setting that activates chords on the mainview
463
 
        Settings().setValue('songs/enable chords', True)
464
 
        Settings().setValue('songs/mainview chords', True)
465
 
 
466
 
        # WHEN: Building the chord CSS
467
 
        chord_css = build_chords_css()
468
 
 
469
 
        # THEN: The build css should look as expected
470
 
        assert CHORD_CSS_ENABLED == chord_css, 'The chord CSS should look as expected'