Merge branch 'master' of gitlab.com:openlp/openlp

This commit is contained in:
Tim 2019-10-26 09:25:26 +01:00
commit 2d2576aa6c
No known key found for this signature in database
GPG Key ID: 3D454289AF831A6D
15 changed files with 3000 additions and 176 deletions

View File

@ -3,20 +3,25 @@ stages:
- test - test
- deploy - deploy
lint: lint-python:
stage: lint stage: lint
image: openlp/debian image: openlp/debian
before_script:
- sh scripts/generate_resources.sh
script: script:
- sh scripts/generate_resources.sh
- flake8 - flake8
lint-javascript:
stage: lint
image: openlp/angular
script:
- yarn install
- yarn lint
test-debian: test-debian:
stage: test stage: test
image: openlp/debian image: openlp/debian
before_script:
- sh scripts/generate_resources.sh
script: script:
- sh scripts/generate_resources.sh
- xvfb-run -s '-screen 0 1024x768x24' pytest-3 --color=no --disable-warnings --cov openlp --cov-report term - xvfb-run -s '-screen 0 1024x768x24' pytest-3 --color=no --disable-warnings --cov openlp --cov-report term
- mv .coverage linux.coverage - mv .coverage linux.coverage
artifacts: artifacts:
@ -26,35 +31,31 @@ test-debian:
test-ubuntu: test-ubuntu:
stage: test stage: test
image: openlp/ubuntu image: openlp/ubuntu
before_script:
- sh scripts/generate_resources.sh
script: script:
- sh scripts/generate_resources.sh
- xvfb-run -s '-screen 0 1024x768x24' pytest-3 --color=no --disable-warnings - xvfb-run -s '-screen 0 1024x768x24' pytest-3 --color=no --disable-warnings
test-fedora: test-fedora:
stage: test stage: test
image: openlp/fedora image: openlp/fedora
before_script:
- sh scripts/generate_resources.sh
script: script:
- sh scripts/generate_resources.sh
- xvfb-run -s '-screen 0 1024x768x24' pytest-3 --color=no --disable-warnings - xvfb-run -s '-screen 0 1024x768x24' pytest-3 --color=no --disable-warnings
test-arch: test-arch:
stage: test stage: test
image: openlp/arch image: openlp/arch
before_script:
- sh scripts/generate_resources.sh
script: script:
- sh scripts/generate_resources.sh
- xvfb-run -s '-screen 0 1024x768x24' pytest --color=no --disable-warnings - xvfb-run -s '-screen 0 1024x768x24' pytest --color=no --disable-warnings
test-macos: test-macos:
stage: test stage: test
tags: tags:
- macos - macos
before_script: script:
- export PATH=/opt/local/bin:$PATH - export PATH=/opt/local/bin:$PATH
- sh scripts/generate_resources.sh - sh scripts/generate_resources.sh
script:
- python3 -m pytest --color=no --disable-warnings --cov openlp - python3 -m pytest --color=no --disable-warnings --cov openlp
- mv .coverage macos.coverage - mv .coverage macos.coverage
artifacts: artifacts:
@ -76,6 +77,13 @@ test-windows:
only: only:
- master@openlp/openlp - master@openlp/openlp
test-display:
stage: test
image: openlp/angular
script:
- yarn install
- yarn test --browsers ChromiumHeadlessCI
pages: pages:
stage: deploy stage: deploy
image: openlp/debian image: openlp/debian

View File

@ -64,6 +64,12 @@ module.exports = function(config) {
// start these browsers // start these browsers
// available browser launchers: https://npmjs.org/browse/keyword/karma-launcher // available browser launchers: https://npmjs.org/browse/keyword/karma-launcher
browsers: ["Chromium"], browsers: ["Chromium"],
customLaunchers: {
ChromiumHeadlessCI: {
base: 'ChromiumHeadless',
flags: ['--no-sandbox']
}
},
// Continuous Integration mode // Continuous Integration mode
// if true, Karma captures browsers, runs the tests and exits // if true, Karma captures browsers, runs the tests and exits

View File

@ -25,10 +25,18 @@ sup {
.reveal .slides > section, .reveal .slides > section,
.reveal .slides > section > section { .reveal .slides > section > section {
padding: 0; padding: 0;
max-height: 100%;
} }
.reveal > .backgrounds > .present { .reveal .slides > section.text-slides {
visibility: hidden !important; /* Need to override reveal styles to use our text aligment */
display: flex !important;
flex-direction: column;
word-wrap: break-word;
}
.reveal .slides > section p {
margin: 0;
} }
#global-background { #global-background {

View File

@ -28,19 +28,19 @@ var GradientType = {
* Horizontal alignment enumeration * Horizontal alignment enumeration
*/ */
var HorizontalAlign = { var HorizontalAlign = {
Left: "left", Left: 0,
Right: "right", Right: 1,
Center: "center", Center: 2,
Justify: "justify" Justify: 3
}; };
/** /**
* Vertical alignment enumeration * Vertical alignment enumeration
*/ */
var VerticalAlign = { var VerticalAlign = {
Top: "top", Top: 0,
Middle: "middle", Middle: 1,
Bottom: "bottom" Bottom: 2
}; };
/** /**
@ -85,7 +85,7 @@ var AlertLocation = {
var AlertState = { var AlertState = {
Displaying: "displaying", Displaying: "displaying",
NotDisplaying: "notDisplaying" NotDisplaying: "notDisplaying"
} };
/** /**
* Alert delay enumeration * Alert delay enumeration
@ -94,7 +94,7 @@ var AlertDelay = {
FiftyMilliseconds: 50, FiftyMilliseconds: 50,
OneSecond: 1000, OneSecond: 1000,
OnePointFiveSeconds: 1500 OnePointFiveSeconds: 1500
} };
/** /**
* Return an array of elements based on the selector query * Return an array of elements based on the selector query
@ -180,12 +180,13 @@ function _fromCamelCase(text) {
* @param {Object} rules - The rules to apply to the style * @param {Object} rules - The rules to apply to the style
*/ */
function _createStyle(selector, rules) { function _createStyle(selector, rules) {
var style;
var id = selector.replace("#", "").replace(" .", "-").replace(".", "-").replace(" ", "_"); var id = selector.replace("#", "").replace(" .", "-").replace(".", "-").replace(" ", "_");
if ($("style#" + id).length != 0) { if ($("style#" + id).length != 0) {
var style = $("style#" + id)[0]; style = $("style#" + id)[0];
} }
else { else {
var style = document.createElement("style"); style = document.createElement("style");
document.getElementsByTagName("head")[0].appendChild(style); document.getElementsByTagName("head")[0].appendChild(style);
style.type = "text/css"; style.type = "text/css";
style.id = id; style.id = id;
@ -348,7 +349,7 @@ var Display = {
* Start up reveal and do any other initialisation * Start up reveal and do any other initialisation
*/ */
init: function () { init: function () {
Reveal.initialize(this._revealConfig); Reveal.initialize(Display._revealConfig);
}, },
/** /**
* Reinitialise Reveal * Reinitialise Reveal
@ -368,13 +369,17 @@ var Display = {
*/ */
clearSlides: function () { clearSlides: function () {
$(".slides")[0].innerHTML = ""; $(".slides")[0].innerHTML = "";
this._slides = {}; Display._slides = {};
}, },
/** /**
* Checks if the present slide content fits within the slide * Checks if the present slide content fits within the slide
*/ */
doesContentFit: function () { doesContentFit: function () {
var currSlide = $(".slides")[0]; var currSlide = $("section.text-slides");
if (currSlide.length === 0) {
currSlide = $(".slides");
}
currSlide = currSlide[0];
console.debug("scrollHeight: " + currSlide.scrollHeight + ", clientHeight: " + currSlide.clientHeight); console.debug("scrollHeight: " + currSlide.scrollHeight + ", clientHeight: " + currSlide.clientHeight);
return currSlide.clientHeight >= currSlide.scrollHeight; return currSlide.clientHeight >= currSlide.scrollHeight;
}, },
@ -475,10 +480,10 @@ var Display = {
var alertText = $('#alert-text')[0]; var alertText = $('#alert-text')[0];
// create styles for the alerts from the settings // create styles for the alerts from the settings
_createStyle("#alert-background.settings", { _createStyle("#alert-background.settings", {
backgroundColor: settings["backgroundColor"], backgroundColor: settings.backgroundColor,
fontFamily: "'" + settings["fontFace"] + "'", fontFamily: "'" + settings.fontFace + "'",
fontSize: settings["fontSize"].toString() + "pt", fontSize: settings.fontSize.toString() + "pt",
color: settings["fontColor"] color: settings.fontColor
}); });
alertBackground.classList.add("settings"); alertBackground.classList.add("settings");
alertBackground.classList.replace("hide", "show"); alertBackground.classList.replace("hide", "show");
@ -593,33 +598,35 @@ var Display = {
* Add a slide. If the slide exists but the HTML is different, update the slide. * Add a slide. If the slide exists but the HTML is different, update the slide.
* @param {string} verse - The verse number, e.g. "v1" * @param {string} verse - The verse number, e.g. "v1"
* @param {string} text - The HTML for the verse, e.g. "line1<br>line2" * @param {string} text - The HTML for the verse, e.g. "line1<br>line2"
* @param {string} footer_text - The HTML for the footer" * @param {string} footer_text - The HTML for the footer
* @param {bool} reinit - True if React should reinit when creating a new slide
*/ */
addTextSlide: function (verse, text, footerText) { addTextSlide: function (verse, text, footerText, reinit=true) {
var slide;
var html = _prepareText(text); var html = _prepareText(text);
if (this._slides.hasOwnProperty(verse)) { if (Display._slides.hasOwnProperty(verse)) {
var slide = $("#" + verse)[0]; slide = $("#" + verse)[0];
if (slide.innerHTML != html) { if (slide.innerHTML != html) {
slide.innerHTML = html; slide.innerHTML = html;
} }
} else {
var parent = $("section.text-slides");
if (parent.length === 0) {
Display._createTextContainer();
parent = $("section.text-slides");
} }
else { parent = parent[0];
var slidesDiv = $(".slides")[0]; slide = document.createElement("section");
var slide = document.createElement("section");
slide.setAttribute("id", verse); slide.setAttribute("id", verse);
slide.innerHTML = html; slide.innerHTML = html;
slidesDiv.appendChild(slide); parent.appendChild(slide);
var slides = $(".slides > section"); Display._slides[verse] = parent.children.length - 1;
this._slides[verse] = slides.length - 1;
if (footerText) { if (footerText) {
$(".footer")[0].innerHTML = footerText; $(".footer")[0].innerHTML = footerText;
} }
if (reinit) {
Display.reinit();
} }
if ((arguments.length > 3) && (arguments[3] === true)) {
this.reinit();
}
else if (arguments.length == 3) {
this.reinit();
} }
}, },
/** /**
@ -634,6 +641,19 @@ var Display = {
Display.reinit(); Display.reinit();
Display.goToSlide(0); Display.goToSlide(0);
}, },
/**
* Create the <section> that will contain text slides (vertical slides in react)
*/
_createTextContainer: function () {
var slideContainer = document.createElement("section");
slideContainer.classList = "text-slides";
var slidesDiv = $(".slides")[0];
slidesDiv.appendChild(slideContainer);
// Apply the current theme to the new container
if (!!Display._theme) {
Display.setTheme(Display._theme);
}
},
/** /**
* Set image slides * Set image slides
* @param {Object[]} slides - A list of images to add as JS objects [{"path": "url/to/file"}] * @param {Object[]} slides - A list of images to add as JS objects [{"path": "url/to/file"}]
@ -641,18 +661,20 @@ var Display = {
setImageSlides: function (slides) { setImageSlides: function (slides) {
Display.clearSlides(); Display.clearSlides();
var slidesDiv = $(".slides")[0]; var slidesDiv = $(".slides")[0];
var parentSection = document.createElement("section");
slides.forEach(function (slide, index) { slides.forEach(function (slide, index) {
var section = document.createElement("section"); var section = document.createElement("section");
section.setAttribute("id", index); section.setAttribute("id", index);
section.setAttribute("data-background", "#000"); section.setAttribute("data-background", "#000");
section.setAttribute("style", "height: 100%; width: 100%;"); section.setAttribute("style", "height: 100%; width: 100%;");
var img = document.createElement('img'); var img = document.createElement('img');
img.src = slide["path"]; img.src = slide.path;
img.setAttribute("style", "max-width: 100%; max-height: 100%; margin: 0; position: absolute; top: 50%; left: 50%; transform: translate(-50%, -50%);"); img.setAttribute("style", "max-width: 100%; max-height: 100%; margin: 0; position: absolute; top: 50%; left: 50%; transform: translate(-50%, -50%);");
section.appendChild(img); section.appendChild(img);
slidesDiv.appendChild(section); parentSection.appendChild(section);
Display._slides[index.toString()] = index; Display._slides[index.toString()] = index;
}); });
slidesDiv.appendChild(parentSection);
Display.reinit(); Display.reinit();
}, },
/** /**
@ -660,11 +682,11 @@ var Display = {
* @param {Object} video - The video to show as a JS object: {"path": "url/to/file"} * @param {Object} video - The video to show as a JS object: {"path": "url/to/file"}
*/ */
setVideo: function (video) { setVideo: function (video) {
this.clearSlides(); Display.clearSlides();
var section = document.createElement("section"); var section = document.createElement("section");
section.setAttribute("data-background", "#000"); section.setAttribute("data-background", "#000");
var videoElement = document.createElement("video"); var videoElement = document.createElement("video");
videoElement.src = video["path"]; videoElement.src = video.path;
videoElement.preload = "auto"; videoElement.preload = "auto";
videoElement.setAttribute("id", "video"); videoElement.setAttribute("id", "video");
videoElement.setAttribute("style", "height: 100%; width: 100%;"); videoElement.setAttribute("style", "height: 100%; width: 100%;");
@ -690,7 +712,7 @@ var Display = {
}); });
section.appendChild(videoElement); section.appendChild(videoElement);
$(".slides")[0].appendChild(section); $(".slides")[0].appendChild(section);
this.reinit(); Display.reinit();
}, },
/** /**
* Play a video * Play a video
@ -780,21 +802,21 @@ var Display = {
* @param slide The slide number or name, e.g. "v1", 0 * @param slide The slide number or name, e.g. "v1", 0
*/ */
goToSlide: function (slide) { goToSlide: function (slide) {
if (this._slides.hasOwnProperty(slide)) { if (Display._slides.hasOwnProperty(slide)) {
Reveal.slide(this._slides[slide]); Reveal.slide(0, Display._slides[slide]);
} }
else { else {
Reveal.slide(slide); Reveal.slide(0, slide);
} }
}, },
/** /**
* Go to the next slide in the list * Go to the next slide in the list
*/ */
next: Reveal.next, next: Reveal.nextFragment,
/** /**
* Go to the previous slide in the list * Go to the previous slide in the list
*/ */
prev: Reveal.prev, prev: Reveal.prevFragment,
/** /**
* Blank the screen * Blank the screen
*/ */
@ -833,59 +855,59 @@ var Display = {
* @param fontSize The font size in pts * @param fontSize The font size in pts
*/ */
calculateLineCount: function (fontSize) { calculateLineCount: function (fontSize) {
var p = $(".slides > section > p"); var p = $(".slides > section > section > p");
if (p.length == 0) { if (p.length == 0) {
this.addSlide("v1", "Arky arky"); Display.addSlide("v1", "Arky arky");
p = $(".slides > section > p"); p = $(".slides > section > section > p");
} }
p = p[0]; p = p[0];
p.style.fontSize = "" + fontSize + "pt"; p.style.fontSize = "" + fontSize + "pt";
var d = $(".slides")[0]; var d = $(".slides > section")[0];
var lh = parseFloat(_getStyle(p, "line-height")); var lh = parseFloat(_getStyle(p, "line-height"));
var dh = parseFloat(_getStyle(d, "height")); var dh = parseFloat(_getStyle(d, "height"));
return Math.floor(dh / lh); return Math.floor(dh / lh);
}, },
setTheme: function (theme) { setTheme: function (theme) {
this._theme = theme; Display._theme = theme;
// Set the background // Set the background
var globalBackground = $("#global-background")[0]; var globalBackground = $("#global-background")[0];
var backgroundStyle = {}; var backgroundStyle = {};
var backgroundHtml = ""; var backgroundHtml = "";
switch (theme.background_type) { switch (theme.background_type) {
case BackgroundType.Transparent: case BackgroundType.Transparent:
backgroundStyle["background"] = "transparent"; backgroundStyle.background = "transparent";
break; break;
case BackgroundType.Solid: case BackgroundType.Solid:
backgroundStyle["background"] = theme.background_color; backgroundStyle.background = theme.background_color;
break; break;
case BackgroundType.Gradient: case BackgroundType.Gradient:
switch (theme.background_direction) { switch (theme.background_direction) {
case GradientType.Horizontal: case GradientType.Horizontal:
backgroundStyle["background"] = _buildLinearGradient("left top", "left bottom", backgroundStyle.background = _buildLinearGradient("left top", "left bottom",
theme.background_start_color, theme.background_start_color,
theme.background_end_color); theme.background_end_color);
break; break;
case GradientType.Vertical: case GradientType.Vertical:
backgroundStyle["background"] = _buildLinearGradient("left top", "right top", backgroundStyle.background = _buildLinearGradient("left top", "right top",
theme.background_start_color, theme.background_start_color,
theme.background_end_color); theme.background_end_color);
break; break;
case GradientType.LeftTop: case GradientType.LeftTop:
backgroundStyle["background"] = _buildLinearGradient("left top", "right bottom", backgroundStyle.background = _buildLinearGradient("left top", "right bottom",
theme.background_start_color, theme.background_start_color,
theme.background_end_color); theme.background_end_color);
break; break;
case GradientType.LeftBottom: case GradientType.LeftBottom:
backgroundStyle["background"] = _buildLinearGradient("left bottom", "right top", backgroundStyle.background = _buildLinearGradient("left bottom", "right top",
theme.background_start_color, theme.background_start_color,
theme.background_end_color); theme.background_end_color);
break; break;
case GradientType.Circular: case GradientType.Circular:
backgroundStyle["background"] = _buildRadialGradient(window.innerWidth / 2, theme.background_start_color, backgroundStyle.background = _buildRadialGradient(window.innerWidth / 2, theme.background_start_color,
theme.background_end_color); theme.background_end_color);
break; break;
default: default:
backgroundStyle["background"] = "#000"; backgroundStyle.background = "#000";
} }
break; break;
case BackgroundType.Image: case BackgroundType.Image:
@ -898,76 +920,104 @@ var Display = {
console.warn(backgroundHtml); console.warn(backgroundHtml);
break; break;
default: default:
backgroundStyle["background"] = "#000"; backgroundStyle.background = "#000";
} }
globalBackground.style.cssText = ""; globalBackground.style.cssText = "";
for (var key in backgroundStyle) { for (var bgKey in backgroundStyle) {
if (backgroundStyle.hasOwnProperty(key)) { if (backgroundStyle.hasOwnProperty(bgKey)) {
globalBackground.style.setProperty(key, backgroundStyle[key]); globalBackground.style.setProperty(bgKey, backgroundStyle[bgKey]);
} }
} }
if (!!backgroundHtml) { if (!!backgroundHtml) {
globalBackground.innerHTML = backgroundHtml; globalBackground.innerHTML = backgroundHtml;
} }
// set up the main area // set up the main area
mainStyle = { mainStyle = {};
"word-wrap": "break-word",
/*"margin": "0",
"padding": "0"*/
};
if (!!theme.font_main_outline) { if (!!theme.font_main_outline) {
mainStyle["-webkit-text-stroke"] = "" + theme.font_main_outline_size + "pt " + mainStyle["-webkit-text-stroke"] = "" + theme.font_main_outline_size + "pt " +
theme.font_main_outline_color; theme.font_main_outline_color;
mainStyle["-webkit-text-fill-color"] = theme.font_main_color; mainStyle["-webkit-text-fill-color"] = theme.font_main_color;
} }
// These need to be fixed, in the Python they use a width passed in as a parameter
mainStyle.width = theme.font_main_width + "px";
mainStyle.height = theme.font_main_height + "px";
mainStyle.top = "" + theme.font_main_y + "px";
mainStyle.left = "" + theme.font_main_x + "px";
mainStyle.color = theme.font_main_color;
mainStyle["font-family"] = theme.font_main_name; mainStyle["font-family"] = theme.font_main_name;
mainStyle["font-size"] = "" + theme.font_main_size + "pt"; mainStyle["font-size"] = "" + theme.font_main_size + "pt";
mainStyle["font-style"] = !!theme.font_main_italics ? "italic" : ""; mainStyle["font-style"] = !!theme.font_main_italics ? "italic" : "";
mainStyle["font-weight"] = !!theme.font_main_bold ? "bold" : ""; mainStyle["font-weight"] = !!theme.font_main_bold ? "bold" : "";
mainStyle["color"] = theme.font_main_color;
mainStyle["line-height"] = "" + (100 + theme.font_main_line_adjustment) + "%"; mainStyle["line-height"] = "" + (100 + theme.font_main_line_adjustment) + "%";
mainStyle["text-align"] = theme.display_horizontal_align; // Using text-align-last because there is a <br> seperating each line
if (theme.display_horizontal_align != HorizontalAlign.Justify) { switch (theme.display_horizontal_align) {
mainStyle["white-space"] = "pre-wrap"; case HorizontalAlign.Justify:
mainStyle["text-align"] = "justify";
mainStyle["text-align-last"] = "justify";
break;
case HorizontalAlign.Center:
mainStyle["text-align"] = "center";
mainStyle["text-align-last"] = "center";
break;
case HorizontalAlign.Left:
mainStyle["text-align"] = "left";
mainStyle["text-align-last"] = "left";
break;
case HorizontalAlign.Right:
mainStyle["text-align"] = "right";
mainStyle["text-align-last"] = "right";
break;
default:
mainStyle["text-align"] = "center";
mainStyle["text-align-last"] = "center";
}
switch (theme.display_vertical_align) {
case VerticalAlign.Middle:
mainStyle["justify-content"] = "center";
break;
case VerticalAlign.Top:
mainStyle["justify-content"] = "flex-start";
break;
case VerticalAlign.Bottom:
mainStyle["justify-content"] = "flex-end";
// This gets around the webkit scroll height bug
mainStyle["padding-bottom"] = "" + (theme.font_main_size / 8) + "px";
break;
default:
mainStyle["justify-content"] = "center";
} }
mainStyle["vertical-align"] = theme.display_vertical_align;
if (theme.hasOwnProperty('font_main_shadow_size')) { if (theme.hasOwnProperty('font_main_shadow_size')) {
mainStyle["text-shadow"] = theme.font_main_shadow_color + " " + theme.font_main_shadow_size + "px " + mainStyle["text-shadow"] = theme.font_main_shadow_color + " " + theme.font_main_shadow_size + "pt " +
theme.font_main_shadow_size + "px"; theme.font_main_shadow_size + "pt";
} }
mainStyle["padding-bottom"] = theme.display_vertical_align == VerticalAlign.Bottom ? "0.5em" : "0"; var slidesDiv = $("section.text-slides");
mainStyle["padding-left"] = !!theme.font_main_outline ? "" + (theme.font_main_outline_size * 2) + "pt" : "0"; if (slidesDiv.length > 0) {
// These need to be fixed, in the Python they use a width passed in as a parameter slidesDiv = slidesDiv[0];
mainStyle["position"] = "absolute";
mainStyle["width"] = "" + (window.innerWidth - (theme.font_main_outline_size * 4)) + "px";
mainStyle["height"] = "" + (window.innerHeight - (theme.font_main_outline_size * 4)) + "px";
mainStyle["left"] = "" + theme.font_main_x + "px";
mainStyle["top"] = "" + theme.font_main_y + "px";
var slidesDiv = $(".slides")[0];
slidesDiv.style.cssText = ""; slidesDiv.style.cssText = "";
for (var key in mainStyle) { for (var mainKey in mainStyle) {
if (mainStyle.hasOwnProperty(key)) { if (mainStyle.hasOwnProperty(mainKey)) {
slidesDiv.style.setProperty(key, mainStyle[key]); slidesDiv.style.setProperty(mainKey, mainStyle[mainKey]);
}
} }
} }
// Set up the footer // Set up the footer
footerStyle = { footerStyle = {
"text-align": "left" "text-align": "left"
}; };
footerStyle["position"] = "absolute"; footerStyle.position = "absolute";
footerStyle["left"] = "" + theme.font_footer_x + "px"; footerStyle.left = "" + theme.font_footer_x + "px";
footerStyle["top"] = "" + theme.font_footer_y + "px"; footerStyle.top = "" + theme.font_footer_y + "px";
footerStyle["bottom"] = "" + (window.innerHeight - theme.font_footer_y - theme.font_footer_height) + "px"; footerStyle.width = "" + theme.font_footer_width + "px";
footerStyle["width"] = "" + theme.font_footer_width + "px"; footerStyle.height = "" + theme.font_footer_height + "px";
footerStyle.color = theme.font_footer_color;
footerStyle["font-family"] = theme.font_footer_name; footerStyle["font-family"] = theme.font_footer_name;
footerStyle["font-size"] = "" + theme.font_footer_size + "pt"; footerStyle["font-size"] = "" + theme.font_footer_size + "pt";
footerStyle["color"] = theme.font_footer_color;
footerStyle["white-space"] = theme.font_footer_wrap ? "normal" : "nowrap"; footerStyle["white-space"] = theme.font_footer_wrap ? "normal" : "nowrap";
var footer = $(".footer")[0]; var footer = $(".footer")[0];
footer.style.cssText = ""; footer.style.cssText = "";
for (var key in footerStyle) { for (var footerKey in footerStyle) {
if (footerStyle.hasOwnProperty(key)) { if (footerStyle.hasOwnProperty(footerKey)) {
footer.style.setProperty(key, footerStyle[key]); footer.style.setProperty(footerKey, footerStyle[footerKey]);
} }
} }
}, },

View File

@ -169,6 +169,7 @@ class Theme(object):
jsn = get_text_file_string(json_path) jsn = get_text_file_string(json_path)
self.load_theme(jsn) self.load_theme(jsn)
self.background_filename = None self.background_filename = None
self.background_source = None
self.version = 2 self.version = 2
def expand_json(self, var, prev=None): def expand_json(self, var, prev=None):

View File

@ -67,7 +67,7 @@ class Ui_PrintServiceDialog(object):
self.toolbar.addWidget(self.options_button) self.toolbar.addWidget(self.options_button)
self.toolbar.addSeparator() self.toolbar.addSeparator()
self.plain_copy = self.toolbar.addAction(UiIcons().clone, self.plain_copy = self.toolbar.addAction(UiIcons().clone,
translate('OpenLP.PrintServiceForm', 'Copy')) translate('OpenLP.PrintServiceForm', 'Copy as Text'))
self.html_copy = self.toolbar.addAction(UiIcons().clone, self.html_copy = self.toolbar.addAction(UiIcons().clone,
translate('OpenLP.PrintServiceForm', 'Copy as HTML')) translate('OpenLP.PrintServiceForm', 'Copy as HTML'))
self.toolbar.addSeparator() self.toolbar.addSeparator()
@ -139,7 +139,7 @@ class Ui_PrintServiceDialog(object):
self.zoom_in_button.setToolTip(translate('OpenLP.PrintServiceForm', 'Zoom In')) self.zoom_in_button.setToolTip(translate('OpenLP.PrintServiceForm', 'Zoom In'))
self.options_button.setText(translate('OpenLP.PrintServiceForm', 'Options')) self.options_button.setText(translate('OpenLP.PrintServiceForm', 'Options'))
self.title_label.setText(translate('OpenLP.PrintServiceForm', 'Title:')) self.title_label.setText(translate('OpenLP.PrintServiceForm', 'Title:'))
self.footer_label.setText(translate('OpenLP.PrintServiceForm', 'Custom Footer Text:')) self.footer_label.setText(translate('OpenLP.PrintServiceForm', 'Service Note Text:'))
self.options_group_box.setTitle(translate('OpenLP.PrintServiceForm', 'Other Options')) self.options_group_box.setTitle(translate('OpenLP.PrintServiceForm', 'Other Options'))
self.slide_text_check_box.setText(translate('OpenLP.PrintServiceForm', 'Include slide text if available')) self.slide_text_check_box.setText(translate('OpenLP.PrintServiceForm', 'Include slide text if available'))
self.page_break_after_text.setText(translate('OpenLP.PrintServiceForm', 'Add page break before each text item')) self.page_break_after_text.setText(translate('OpenLP.PrintServiceForm', 'Add page break before each text item'))

View File

@ -197,7 +197,7 @@ class PrintServiceForm(QtWidgets.QDialog, Ui_PrintServiceDialog, RegistryPropert
if self.footer_text_edit.toPlainText(): if self.footer_text_edit.toPlainText():
div = self._add_element('div', parent=html_data.body, class_id='customNotes') div = self._add_element('div', parent=html_data.body, class_id='customNotes')
self._add_element( self._add_element(
'span', translate('OpenLP.ServiceManager', 'Custom Service Notes: '), div, class_id='customNotesTitle') 'span', translate('OpenLP.ServiceManager', 'Service Notes: '), div, class_id='customNotesTitle')
self._add_element('span', html.escape(self.footer_text_edit.toPlainText()), div, class_id='customNotesText') self._add_element('span', html.escape(self.footer_text_edit.toPlainText()), div, class_id='customNotesText')
self.document.setHtml(lxml.html.tostring(html_data).decode()) self.document.setHtml(lxml.html.tostring(html_data).decode())
self.preview_widget.updatePreview() self.preview_widget.updatePreview()

View File

@ -280,7 +280,7 @@ class ThemeForm(QtWidgets.QWizard, Ui_ThemeWizard, RegistryProperties):
Run the wizard. Run the wizard.
""" """
log.debug('Editing theme {name}'.format(name=self.theme.theme_name)) log.debug('Editing theme {name}'.format(name=self.theme.theme_name))
self.temp_background_filename = None self.temp_background_filename = self.theme.background_source
self.update_theme_allowed = False self.update_theme_allowed = False
self.set_defaults() self.set_defaults()
self.update_theme_allowed = True self.update_theme_allowed = True
@ -325,11 +325,11 @@ class ThemeForm(QtWidgets.QWizard, Ui_ThemeWizard, RegistryProperties):
self.setField('background_type', 1) self.setField('background_type', 1)
elif self.theme.background_type == BackgroundType.to_string(BackgroundType.Image): elif self.theme.background_type == BackgroundType.to_string(BackgroundType.Image):
self.image_color_button.color = self.theme.background_border_color self.image_color_button.color = self.theme.background_border_color
self.image_path_edit.path = self.theme.background_filename self.image_path_edit.path = self.theme.background_source
self.setField('background_type', 2) self.setField('background_type', 2)
elif self.theme.background_type == BackgroundType.to_string(BackgroundType.Video): elif self.theme.background_type == BackgroundType.to_string(BackgroundType.Video):
self.video_color_button.color = self.theme.background_border_color self.video_color_button.color = self.theme.background_border_color
self.video_path_edit.path = self.theme.background_filename self.video_path_edit.path = self.theme.background_source
self.setField('background_type', 4) self.setField('background_type', 4)
elif self.theme.background_type == BackgroundType.to_string(BackgroundType.Stream): elif self.theme.background_type == BackgroundType.to_string(BackgroundType.Stream):
self.setField('background_type', 5) self.setField('background_type', 5)
@ -467,6 +467,7 @@ class ThemeForm(QtWidgets.QWizard, Ui_ThemeWizard, RegistryProperties):
:param pathlib.Path new_path: Path to the new image :param pathlib.Path new_path: Path to the new image
:rtype: None :rtype: None
""" """
self.theme.background_source = new_path
self.theme.background_filename = new_path self.theme.background_filename = new_path
self.set_background_page_values() self.set_background_page_values()
@ -477,6 +478,7 @@ class ThemeForm(QtWidgets.QWizard, Ui_ThemeWizard, RegistryProperties):
:param pathlib.Path new_path: Path to the new video :param pathlib.Path new_path: Path to the new video
:rtype: None :rtype: None
""" """
self.theme.background_source = new_path
self.theme.background_filename = new_path self.theme.background_filename = new_path
self.set_background_page_values() self.set_background_page_values()
@ -553,14 +555,14 @@ class ThemeForm(QtWidgets.QWizard, Ui_ThemeWizard, RegistryProperties):
translate('OpenLP.ThemeWizard', 'Theme Name Invalid'), translate('OpenLP.ThemeWizard', 'Theme Name Invalid'),
translate('OpenLP.ThemeWizard', 'Invalid theme name. Please enter one.')) translate('OpenLP.ThemeWizard', 'Invalid theme name. Please enter one.'))
return return
source_path = None
destination_path = None destination_path = None
if self.theme.background_type == BackgroundType.to_string(BackgroundType.Image) or \ if self.theme.background_type == BackgroundType.to_string(BackgroundType.Image) or \
self.theme.background_type == BackgroundType.to_string(BackgroundType.Video): self.theme.background_type == BackgroundType.to_string(BackgroundType.Video):
file_name = self.theme.background_filename.name file_name = self.theme.background_filename.name
destination_path = self.path / self.theme.theme_name / file_name destination_path = self.path / self.theme.theme_name / file_name
source_path = self.theme.background_filename
if not self.edit_mode and not self.theme_manager.check_if_theme_exists(self.theme.theme_name): if not self.edit_mode and not self.theme_manager.check_if_theme_exists(self.theme.theme_name):
return return
self.theme_manager.save_theme(self.theme, source_path, destination_path, self.preview_box.save_screenshot()) # Set the theme background to the cache location
self.theme.background_filename = destination_path
self.theme_manager.save_theme(self.theme, self.preview_box.save_screenshot())
return QtWidgets.QDialog.accept(self) return QtWidgets.QDialog.accept(self)

View File

@ -325,14 +325,11 @@ class ThemeManager(QtWidgets.QWidget, RegistryBase, Ui_ThemeManager, LogMixin, R
:param str new_theme_name: The new theme name of the theme :param str new_theme_name: The new theme name of the theme
:rtype: None :rtype: None
""" """
destination_path = None
source_path = None
if theme_data.background_type == 'image' or theme_data.background_type == 'video': if theme_data.background_type == 'image' or theme_data.background_type == 'video':
destination_path = self.theme_path / new_theme_name / theme_data.background_filename.name theme_data.background_filename = self.theme_path / new_theme_name / theme_data.background_filename.name
source_path = theme_data.background_filename
theme_data.theme_name = new_theme_name theme_data.theme_name = new_theme_name
theme_data.extend_image_filename(self.theme_path) theme_data.extend_image_filename(self.theme_path)
self.save_theme(theme_data, source_path, destination_path) self.save_theme(theme_data)
self.load_themes() self.load_themes()
def on_edit_theme(self, field=None): def on_edit_theme(self, field=None):
@ -648,14 +645,12 @@ class ThemeManager(QtWidgets.QWidget, RegistryBase, Ui_ThemeManager, LogMixin, R
return False return False
return True return True
def save_theme(self, theme, image_source_path=None, image_destination_path=None, image=None): def save_theme(self, theme, image=None):
""" """
Writes the theme to the disk and handles the background image if necessary Writes the theme to the disk and including the background image and thumbnail if necessary
:param Theme theme: The theme data object. :param Theme theme: The theme data object.
:param Path image_source_path: Where the theme image is currently located. :param image: The theme thumbnail. Optionally.
:param Path image_destination_path: Where the Theme Image is to be saved to
:param image: The example image of the theme. Optionally.
:rtype: None :rtype: None
""" """
name = theme.theme_name name = theme.theme_name
@ -667,12 +662,14 @@ class ThemeManager(QtWidgets.QWidget, RegistryBase, Ui_ThemeManager, LogMixin, R
theme_path.write_text(theme_pretty) theme_path.write_text(theme_pretty)
except OSError: except OSError:
self.log_exception('Saving theme to file failed') self.log_exception('Saving theme to file failed')
if image_source_path and image_destination_path: if theme.background_source and theme.background_filename:
if self.old_background_image_path and image_destination_path != self.old_background_image_path: if self.old_background_image_path and theme.background_filename != self.old_background_image_path:
delete_file(self.old_background_image_path) delete_file(self.old_background_image_path)
if image_source_path != image_destination_path: if not theme.background_source.exists():
self.log_warning('Background does not exist, retaining cached background')
elif theme.background_source != theme.background_filename:
try: try:
shutil.copyfile(image_source_path, image_destination_path) shutil.copyfile(theme.background_source, theme.background_filename)
except OSError: except OSError:
self.log_exception('Failed to save theme image') self.log_exception('Failed to save theme image')
if image: if image:

View File

@ -8,6 +8,7 @@
}, },
"dependencies": { "dependencies": {
"jasmine-core": "^2.6.4", "jasmine-core": "^2.6.4",
"jshint": "^2.10.2",
"karma": "^3.1.4", "karma": "^3.1.4",
"karma-chrome-launcher": "^3.1.0", "karma-chrome-launcher": "^3.1.0",
"karma-coverage": "^1.1.2", "karma-coverage": "^1.1.2",
@ -17,7 +18,11 @@
"karma-log-reporter": "0.0.4" "karma-log-reporter": "0.0.4"
}, },
"scripts": { "scripts": {
"test": "karma start --single-run" "test": "karma start --single-run",
"lint": "jshint openlp/core/display/html/display.js"
},
"jshintConfig": {
"esversion": 6
}, },
"author": "OpenLP Developers", "author": "OpenLP Developers",
"license": "GPL-3.0-or-later" "license": "GPL-3.0-or-later"

View File

@ -181,4 +181,4 @@ class TestTheme(TestCase):
assert 0 == theme.display_vertical_align, 'display_vertical_align should be 0' assert 0 == theme.display_vertical_align, 'display_vertical_align should be 0'
assert theme.font_footer_bold is False, 'font_footer_bold should be False' assert theme.font_footer_bold is False, 'font_footer_bold should be False'
assert 'Arial' == theme.font_main_name, 'font_main_name should be "Arial"' assert 'Arial' == theme.font_main_name, 'font_main_name should be "Arial"'
assert 48 == len(theme.__dict__), 'The theme should have 48 attributes' assert 49 == len(theme.__dict__), 'The theme should have 49 attributes'

View File

@ -87,19 +87,22 @@ class TestThemeManager(TestCase):
Test that we don't try to overwrite a theme background image with itself Test that we don't try to overwrite a theme background image with itself
""" """
# GIVEN: A new theme manager instance, with mocked builtins.open, copyfile, # GIVEN: A new theme manager instance, with mocked builtins.open, copyfile,
# theme, create_paths and thememanager-attributes. # theme, create_paths, thememanager-attributes and background
# .filename path is the same as the source path.
theme_manager = ThemeManager(None) theme_manager = ThemeManager(None)
theme_manager.old_background_image = None theme_manager.old_background_image_path = None
theme_manager.update_preview_images = MagicMock() theme_manager.update_preview_images = MagicMock()
theme_manager.theme_path = MagicMock() theme_manager.theme_path = MagicMock()
mocked_theme = MagicMock() mocked_theme = MagicMock()
mocked_theme.theme_name = 'themename' mocked_theme.theme_name = 'themename'
mocked_theme.extract_formatted_xml = MagicMock() mocked_theme.extract_formatted_xml = MagicMock()
mocked_theme.extract_formatted_xml.return_value = 'fake_theme_xml'.encode() mocked_theme.extract_formatted_xml.return_value = 'fake_theme_xml'.encode()
# WHEN: Calling save_theme with path to the same image, but the path written slightly different
file_path_1 = RESOURCE_PATH / 'church.jpg' file_path_1 = RESOURCE_PATH / 'church.jpg'
theme_manager.save_theme(mocked_theme, file_path_1, file_path_1) mocked_theme.background_filename = file_path_1
mocked_theme.background_source = file_path_1
# WHEN: Calling save_theme with both background paths to the same image
theme_manager.save_theme(mocked_theme)
# THEN: The mocked_copyfile should not have been called # THEN: The mocked_copyfile should not have been called
assert mocked_shutil.copyfile.called is False, 'copyfile should not be called' assert mocked_shutil.copyfile.called is False, 'copyfile should not be called'
@ -111,30 +114,118 @@ class TestThemeManager(TestCase):
Test that we do overwrite a theme background image when a new is submitted Test that we do overwrite a theme background image when a new is submitted
""" """
# GIVEN: A new theme manager instance, with mocked builtins.open, copyfile, # GIVEN: A new theme manager instance, with mocked builtins.open, copyfile,
# theme, create_paths and thememanager-attributes. # theme, create_paths, thememanager-attributes and background
# .filename path is the same as the source path.
theme_manager = ThemeManager(None) theme_manager = ThemeManager(None)
theme_manager.old_background_image = None theme_manager.old_background_image_path = None
theme_manager.update_preview_images = MagicMock() theme_manager.update_preview_images = MagicMock()
theme_manager.theme_path = MagicMock() theme_manager.theme_path = MagicMock()
mocked_theme = MagicMock() mocked_theme = MagicMock()
mocked_theme.theme_name = 'themename' mocked_theme.theme_name = 'themename'
mocked_theme.filename = "filename" mocked_theme.background_filename = RESOURCE_PATH / 'church.jpg'
mocked_theme.background_source = RESOURCE_PATH / 'church2.jpg'
# WHEN: Calling save_theme with path to different images # WHEN: Calling save_theme with both background paths to different images
file_path_1 = RESOURCE_PATH / 'church.jpg' theme_manager.save_theme(mocked_theme)
file_path_2 = RESOURCE_PATH / 'church2.jpg'
theme_manager.save_theme(mocked_theme, file_path_1, file_path_2)
# THEN: The mocked_copyfile should not have been called # THEN: The mocked_copyfile should have been called
assert mocked_shutil.copyfile.called is True, 'copyfile should be called' assert mocked_shutil.copyfile.called is True, 'copyfile should be called'
@patch('openlp.core.ui.thememanager.shutil')
@patch('openlp.core.ui.thememanager.delete_file')
@patch('openlp.core.ui.thememanager.create_paths')
def test_save_theme_delete_old_image(self, mocked_create_paths, mocked_delete_file, mocked_shutil):
"""
Test that we do delete a old theme background image when a new is submitted
"""
# GIVEN: A new theme manager instance, with mocked builtins.open,
# theme, create_paths, thememanager-attributes and background
# .filename path is the same as the source path.
theme_manager = ThemeManager(None)
theme_manager.old_background_image_path = RESOURCE_PATH / 'old_church.png'
theme_manager.update_preview_images = MagicMock()
theme_manager.theme_path = MagicMock()
mocked_theme = MagicMock()
mocked_theme.theme_name = 'themename'
mocked_theme.background_filename = RESOURCE_PATH / 'church.jpg'
mocked_theme.background_source = RESOURCE_PATH / 'church2.jpg'
# WHEN: Calling save_theme with both background paths to different images
theme_manager.save_theme(mocked_theme)
# THEN: The mocked_delete_file should have been called to delete the old cached background
assert mocked_delete_file.called is True, 'delete_file should be called'
@patch.object(ThemeManager, 'log_exception')
@patch('openlp.core.ui.thememanager.delete_file')
@patch('openlp.core.ui.thememanager.create_paths')
def test_save_theme_missing_original(self, mocked_paths, mocked_delete, mocked_log_exception):
"""
Test that we revert to the old theme background image if the source is missing
when changing the theme. (User doesn't change background but the original is
missing)
"""
# GIVEN: A new theme manager instance, with invalid files. Setup as if the user
# has left the background the same, or reselected the same path.
# Not using resource dir because I could potentially copy a file
folder_path = Path(mkdtemp())
theme_manager = ThemeManager(None)
theme_manager.old_background_image_path = folder_path / 'old.png'
theme_manager.update_preview_images = MagicMock()
theme_manager.theme_path = MagicMock()
mocked_theme = MagicMock()
mocked_theme.theme_name = 'themename'
mocked_theme.background_filename = folder_path / 'old.png'
mocked_theme.background_source = folder_path / 'non_existent_original.png'
# WHEN: Calling save_theme with a invalid background_filename
# Although all filenames are considered invalid in this test,
# it is important it reverts to the old background path as this in reality is always
# valid unless someone has messed with the cache.
theme_manager.save_theme(mocked_theme)
# THEN: The old background should not have bee deleted
# AND the filename should have been replaced with the old cached background
# AND there is no exception
assert mocked_delete.called is False, 'delete_file should not be called'
assert mocked_theme.background_filename == theme_manager.old_background_image_path, \
'Background path should be reverted'
assert mocked_log_exception.called is False, \
'Should not have been an exception as the file wasn\'t changed'
@patch.object(ThemeManager, 'log_warning')
@patch('openlp.core.ui.thememanager.delete_file')
@patch('openlp.core.ui.thememanager.create_paths')
def test_save_theme_missing_new(self, mocked_paths, mocked_delete, mocked_log_warning):
"""
Test that we log a warning if the new background is missing
"""
# GIVEN: A new theme manager instance, with invalid files. Setup as if the user
# has changed the background to a invalid path.
# Not using resource dir because I could potentially copy a file
folder_path = Path(mkdtemp())
theme_manager = ThemeManager(None)
theme_manager.old_background_image_path = folder_path / 'old.png'
theme_manager.update_preview_images = MagicMock()
theme_manager.theme_path = MagicMock()
mocked_theme = MagicMock()
mocked_theme.theme_name = 'themename'
mocked_theme.background_filename = folder_path / 'new_cached.png'
mocked_theme.background_source = folder_path / 'new_original.png'
# WHEN: Calling save_theme with a invalid background_filename
theme_manager.save_theme(mocked_theme)
# THEN: A warning should have happened due to attempting to copy a missing file
mocked_log_warning.assert_called_once_with('Background does not exist, retaining cached background')
def test_save_theme_special_char_name(self): def test_save_theme_special_char_name(self):
""" """
Test that we can save themes with special characters in the name Test that we can save themes with special characters in the name
""" """
# GIVEN: A new theme manager instance, with mocked theme and thememanager-attributes. # GIVEN: A new theme manager instance, with mocked theme and thememanager-attributes.
theme_manager = ThemeManager(None) theme_manager = ThemeManager(None)
theme_manager.old_background_image = None theme_manager.old_background_image_path = None
theme_manager.update_preview_images = MagicMock() theme_manager.update_preview_images = MagicMock()
theme_manager.theme_path = Path(self.temp_folder) theme_manager.theme_path = Path(self.temp_folder)
mocked_theme = MagicMock() mocked_theme = MagicMock()

View File

@ -22,6 +22,7 @@
This module contains tests for the PdfController This module contains tests for the PdfController
""" """
import os import os
import pytest
from pathlib import Path from pathlib import Path
from shutil import rmtree, which from shutil import rmtree, which
from tempfile import mkdtemp from tempfile import mkdtemp
@ -173,7 +174,13 @@ class TestPdfController(TestCase, TestMixin):
if exe_path: if exe_path:
self.load_pdf(exe_path) self.load_pdf(exe_path)
self.load_pdf_pictures(exe_path) self.load_pdf_pictures(exe_path)
# PyMuPDF
def test_loading_pdf_using_pymupdf(self):
try:
import fitz # noqa: F401
except ImportError:
pytest.skip('PyMuPDF is not installed')
self.load_pdf(None) self.load_pdf(None)
self.load_pdf_pictures(None) self.load_pdf_pictures(None)

View File

@ -147,7 +147,7 @@ describe("The Display object", function () {
Display._slides["v1"] = 0; Display._slides["v1"] = 0;
Display.goToSlide("v1"); Display.goToSlide("v1");
expect(Reveal.slide).toHaveBeenCalledWith(0); expect(Reveal.slide).toHaveBeenCalledWith(0, 0);
}); });
it("should have an alert() method", function () { it("should have an alert() method", function () {
@ -458,8 +458,8 @@ describe("Display.addTextSlide", function () {
Display.addTextSlide(verse, text, footer); Display.addTextSlide(verse, text, footer);
expect(Display._slides[verse]).toEqual(0); expect(Display._slides[verse]).toEqual(0);
expect($(".slides > section").length).toEqual(1); expect($(".slides > section > section").length).toEqual(1);
expect($(".slides > section")[0].innerHTML).toEqual(_prepareText(text)); expect($(".slides > section > section")[0].innerHTML).toEqual(_prepareText(text));
expect(Display.reinit).toHaveBeenCalled(); expect(Display.reinit).toHaveBeenCalled();
}); });
@ -472,8 +472,8 @@ describe("Display.addTextSlide", function () {
Display.addTextSlide(verse, text, footer, false); Display.addTextSlide(verse, text, footer, false);
expect(Display._slides[verse]).toEqual(0); expect(Display._slides[verse]).toEqual(0);
expect($(".slides > section").length).toEqual(1); expect($(".slides > section > section").length).toEqual(1);
expect($(".slides > section")[0].innerHTML).toEqual(_prepareText(text)); expect($(".slides > section > section")[0].innerHTML).toEqual(_prepareText(text));
expect(Display.reinit).not.toHaveBeenCalled(); expect(Display.reinit).not.toHaveBeenCalled();
}); });
@ -484,12 +484,12 @@ describe("Display.addTextSlide", function () {
Display.addTextSlide(verse, "Amazing grace,\nhow sweet the sound", footer, false); Display.addTextSlide(verse, "Amazing grace,\nhow sweet the sound", footer, false);
spyOn(Display, "reinit"); spyOn(Display, "reinit");
Display.addTextSlide(verse, text, true); Display.addTextSlide(verse, text, footer, true);
expect(Display._slides[verse]).toEqual(0); expect(Display._slides[verse]).toEqual(0);
expect($(".slides > section").length).toEqual(1); expect($(".slides > section > section").length).toEqual(1);
expect($(".slides > section")[0].innerHTML).toEqual(_prepareText(text)); expect($(".slides > section > section")[0].innerHTML).toEqual(_prepareText(text));
expect(Display.reinit).toHaveBeenCalled(); expect(Display.reinit).toHaveBeenCalledTimes(0);
}); });
}); });
@ -526,12 +526,12 @@ describe("Display.setTextSlides", function () {
expect(Display.clearSlides).toHaveBeenCalledTimes(1); expect(Display.clearSlides).toHaveBeenCalledTimes(1);
expect(Display._slides["v1"]).toEqual(0); expect(Display._slides["v1"]).toEqual(0);
expect(Display._slides["v2"]).toEqual(1); expect(Display._slides["v2"]).toEqual(1);
expect($(".slides > section").length).toEqual(2); expect($(".slides > section > section").length).toEqual(2);
expect(Display.reinit).toHaveBeenCalledTimes(1); expect(Display.reinit).toHaveBeenCalledTimes(1);
expect(Reveal.slide).toHaveBeenCalledWith(0); expect(Reveal.slide).toHaveBeenCalledWith(0, 0);
}); });
xit("should correctly set outline width (skipped for now)", function () { it("should correctly set outline width", function () {
const slides = [ const slides = [
{ {
"verse": "v1", "verse": "v1",
@ -549,14 +549,68 @@ describe("Display.setTextSlides", function () {
spyOn(Display, "reinit"); spyOn(Display, "reinit");
spyOn(Reveal, "slide"); spyOn(Reveal, "slide");
Display.setTextSlides(slides);
Display.setTheme(theme); Display.setTheme(theme);
Display.setTextSlides(slides);
const slidesDiv = $(".slides")[0]; const slidesDiv = $(".text-slides")[0];
expect(slidesDiv.style['-webkit-text-stroke']).toEqual('42pt red'); expect(slidesDiv.style['-webkit-text-stroke']).toEqual('42pt red');
expect(slidesDiv.style['padding-left']).toEqual('84pt');
expect(slidesDiv.style['-webkit-text-fill-color']).toEqual('yellow'); expect(slidesDiv.style['-webkit-text-fill-color']).toEqual('yellow');
}) })
it("should correctly set text alignment,\
(check the order of alignments in the emuns are the same in both js and python)", function () {
const slides = [
{
"verse": "v1",
"text": "Amazing grace, how sweet the sound\nThat saved a wretch like me\n" +
"I once was lost, but now I'm found\nWas blind but now I see",
"footer": "Public Domain"
}
];
//
const theme = {
'display_horizontal_align': 3,
'display_vertical_align': 1
};
spyOn(Display, "reinit");
spyOn(Reveal, "slide");
Display.setTheme(theme);
Display.setTextSlides(slides);
const slidesDiv = $(".text-slides")[0];
expect(slidesDiv.style['text-align-last']).toEqual('justify');
expect(slidesDiv.style['justify-content']).toEqual('center');
})
it("should correctly set slide size position to theme size when adding a text slide", function () {
const slides = [
{
"verse": "v1",
"text": "Amazing grace, how sweet the sound\nThat saved a wretch like me\n" +
"I once was lost, but now I'm found\nWas blind but now I see",
"footer": "Public Domain"
}
];
//
const theme = {
'font_main_y': 789,
'font_main_x': 1000,
'font_main_width': 1230,
'font_main_height': 4560
};
spyOn(Display, "reinit");
spyOn(Reveal, "slide");
Display.setTheme(theme);
Display.setTextSlides(slides);
const slidesDiv = $(".text-slides")[0];
expect(slidesDiv.style['top']).toEqual('789px');
expect(slidesDiv.style['left']).toEqual('1000px');
expect(slidesDiv.style['width']).toEqual('1230px');
expect(slidesDiv.style['height']).toEqual('4560px');
})
}); });
describe("Display.setImageSlides", function () { describe("Display.setImageSlides", function () {
@ -578,12 +632,12 @@ describe("Display.setImageSlides", function () {
expect(Display.clearSlides).toHaveBeenCalledTimes(1); expect(Display.clearSlides).toHaveBeenCalledTimes(1);
expect(Display._slides["0"]).toEqual(0); expect(Display._slides["0"]).toEqual(0);
expect(Display._slides["1"]).toEqual(1); expect(Display._slides["1"]).toEqual(1);
expect($(".slides > section").length).toEqual(2); expect($(".slides > section > section").length).toEqual(2);
expect($(".slides > section > img").length).toEqual(2); expect($(".slides > section > section > img").length).toEqual(2);
expect($(".slides > section > img")[0].getAttribute("src")).toEqual("file:///openlp1.jpg") expect($(".slides > section > section > img")[0].getAttribute("src")).toEqual("file:///openlp1.jpg")
expect($(".slides > section > img")[0].getAttribute("style")).toEqual("max-width: 100%; max-height: 100%; margin: 0; position: absolute; top: 50%; left: 50%; transform: translate(-50%, -50%);") expect($(".slides > section > section > img")[0].getAttribute("style")).toEqual("max-width: 100%; max-height: 100%; margin: 0; position: absolute; top: 50%; left: 50%; transform: translate(-50%, -50%);")
expect($(".slides > section > img")[1].getAttribute("src")).toEqual("file:///openlp2.jpg") expect($(".slides > section > section > img")[1].getAttribute("src")).toEqual("file:///openlp2.jpg")
expect($(".slides > section > img")[1].getAttribute("style")).toEqual("max-width: 100%; max-height: 100%; margin: 0; position: absolute; top: 50%; left: 50%; transform: translate(-50%, -50%);") expect($(".slides > section > section > img")[1].getAttribute("style")).toEqual("max-width: 100%; max-height: 100%; margin: 0; position: absolute; top: 50%; left: 50%; transform: translate(-50%, -50%);")
expect(Display.reinit).toHaveBeenCalledTimes(1); expect(Display.reinit).toHaveBeenCalledTimes(1);
}); });
}); });

2595
yarn.lock Normal file

File diff suppressed because it is too large Load Diff