forked from openlp/openlp
parent
c8b328c4f5
commit
47261d206b
@ -235,119 +235,6 @@ function _createStyle(selector, rules) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* An audio player with a play list
|
|
||||||
*/
|
|
||||||
var AudioPlayer = function (audioElement) {
|
|
||||||
this._audioElement = null;
|
|
||||||
this._eventListeners = {};
|
|
||||||
this._playlist = [];
|
|
||||||
this._currentTrack = null;
|
|
||||||
this._canRepeat = false;
|
|
||||||
this._state = AudioState.Stopped;
|
|
||||||
this.createAudioElement();
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Call all listeners associated with this event
|
|
||||||
* @private
|
|
||||||
* @param {object} event - The event that was emitted
|
|
||||||
*/
|
|
||||||
AudioPlayer.prototype._callListener = function (event) {
|
|
||||||
if (this._eventListeners.hasOwnProperty(event.type)) {
|
|
||||||
this._eventListeners[event.type].forEach(function (listener) {
|
|
||||||
listener(event);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
console.warn("Received unknown event \"" + event.type + "\", doing nothing.");
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Create the <audio> element that is used to play the audio
|
|
||||||
*/
|
|
||||||
AudioPlayer.prototype.createAudioElement = function () {
|
|
||||||
this._audioElement = document.createElement("audio");
|
|
||||||
this._audioElement.addEventListener("ended", this.onEnded);
|
|
||||||
this._audioElement.addEventListener("ended", this._callListener);
|
|
||||||
this._audioElement.addEventListener("timeupdate", this._callListener);
|
|
||||||
this._audioElement.addEventListener("volumechange", this._callListener);
|
|
||||||
this._audioElement.addEventListener("durationchange", this._callListener);
|
|
||||||
this._audioElement.addEventListener("loadeddata", this._callListener);
|
|
||||||
document.addEventListener("complete", function(event) {
|
|
||||||
document.body.appendChild(this._audioElement);
|
|
||||||
});
|
|
||||||
};
|
|
||||||
AudioPlayer.prototype.addEventListener = function (eventType, listener) {
|
|
||||||
this._eventListeners[eventType] = this._eventListeners[eventType] || [];
|
|
||||||
this._eventListeners[eventType].push(listener);
|
|
||||||
};
|
|
||||||
AudioPlayer.prototype.onEnded = function (event) {
|
|
||||||
this.nextTrack();
|
|
||||||
};
|
|
||||||
AudioPlayer.prototype.setCanRepeat = function (canRepeat) {
|
|
||||||
this._canRepeat = canRepeat;
|
|
||||||
};
|
|
||||||
AudioPlayer.prototype.clearTracks = function () {
|
|
||||||
this._playlist = [];
|
|
||||||
};
|
|
||||||
AudioPlayer.prototype.addTrack = function (track) {
|
|
||||||
this._playlist.push(track);
|
|
||||||
};
|
|
||||||
AudioPlayer.prototype.nextTrack = function () {
|
|
||||||
if (!!this._currentTrack) {
|
|
||||||
var trackIndex = this._playlist.indexOf(this._currentTrack);
|
|
||||||
if ((trackIndex + 1 >= this._playlist.length) && this._canRepeat) {
|
|
||||||
this.play(this._playlist[0]);
|
|
||||||
}
|
|
||||||
else if (trackIndex + 1 < this._playlist.length) {
|
|
||||||
this.play(this._playlist[trackIndex + 1]);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
this.stop();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else if (this._playlist.length > 0) {
|
|
||||||
this.play(this._playlist[0]);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
console.warn("No tracks in playlist, doing nothing.");
|
|
||||||
}
|
|
||||||
};
|
|
||||||
AudioPlayer.prototype.play = function () {
|
|
||||||
if (arguments.length > 0) {
|
|
||||||
this._currentTrack = arguments[0];
|
|
||||||
this._audioElement.src = this._currentTrack;
|
|
||||||
this._audioElement.play();
|
|
||||||
this._state = AudioState.Playing;
|
|
||||||
}
|
|
||||||
else if (this._state == AudioState.Paused) {
|
|
||||||
this._audioElement.play();
|
|
||||||
this._state = AudioState.Playing;
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
console.warn("No track currently paused and no track specified, doing nothing.");
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Pause
|
|
||||||
*/
|
|
||||||
AudioPlayer.prototype.pause = function () {
|
|
||||||
this._audioElement.pause();
|
|
||||||
this._state = AudioState.Paused;
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Stop playing
|
|
||||||
*/
|
|
||||||
AudioPlayer.prototype.stop = function () {
|
|
||||||
this._audioElement.pause();
|
|
||||||
this._audioElement.src = "";
|
|
||||||
this._state = AudioState.Stopped;
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The Display object is what we use from OpenLP
|
* The Display object is what we use from OpenLP
|
||||||
*/
|
*/
|
||||||
@ -392,9 +279,9 @@ var Display = {
|
|||||||
init: function (options) {
|
init: function (options) {
|
||||||
// Set defaults for undefined values
|
// Set defaults for undefined values
|
||||||
options = options || {};
|
options = options || {};
|
||||||
var isDisplay = options.isDisplay || false;
|
let isDisplay = options.isDisplay || false;
|
||||||
var doItemTransitions = options.doItemTransitions || false;
|
let doItemTransitions = options.doItemTransitions || false;
|
||||||
var hideMouse = options.hideMouse || false;
|
let hideMouse = options.hideMouse || false;
|
||||||
// Now continue to initialisation
|
// Now continue to initialisation
|
||||||
if (!isDisplay) {
|
if (!isDisplay) {
|
||||||
document.body.classList.add('checkerboard');
|
document.body.classList.add('checkerboard');
|
||||||
@ -1248,9 +1135,26 @@ var Display = {
|
|||||||
*/
|
*/
|
||||||
setScale: function(scale) {
|
setScale: function(scale) {
|
||||||
document.body.style.zoom = scale+"%";
|
document.body.style.zoom = scale+"%";
|
||||||
|
},
|
||||||
|
/**
|
||||||
|
* In order to check if a font exists, we need a container to do
|
||||||
|
* calculations on. This method creates that container and caches
|
||||||
|
* some width values so that we don't have to do this step every
|
||||||
|
* time we check if a font exists.
|
||||||
|
*/
|
||||||
|
_prepareFontContainer: function() {
|
||||||
|
Display._fontContainer = document.createElement("span");
|
||||||
|
Display._fontContainer.id = "does-font-exist";
|
||||||
|
Display._fontContainer.innerHTML = Array(100).join("wi");
|
||||||
|
Display._fontContainer.style.cssText = [
|
||||||
|
"position: absolute",
|
||||||
|
"width: auto",
|
||||||
|
"font-size: 128px",
|
||||||
|
"left: -999999px"
|
||||||
|
].join(" !important;");
|
||||||
|
document.body.appendChild(Display._fontContainer);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
new QWebChannel(qt.webChannelTransport, function (channel) {
|
new QWebChannel(qt.webChannelTransport, function (channel) {
|
||||||
window.mediaWatcher = channel.objects.mediaWatcher;
|
|
||||||
window.displayWatcher = channel.objects.displayWatcher;
|
window.displayWatcher = channel.objects.displayWatcher;
|
||||||
});
|
});
|
||||||
|
@ -49,7 +49,8 @@ class WebEnginePage(QtWebEngineWidgets.QWebEnginePage):
|
|||||||
"""
|
"""
|
||||||
# The JS log has the entire file location, which we don't really care about
|
# The JS log has the entire file location, which we don't really care about
|
||||||
app_dir = AppLocation.get_directory(AppLocation.AppDir).parent
|
app_dir = AppLocation.get_directory(AppLocation.AppDir).parent
|
||||||
source_id = source_id.replace('file://{app_dir}/'.format(app_dir=app_dir), '')
|
if str(app_dir) in source_id:
|
||||||
|
source_id = source_id.replace('file://{app_dir}/'.format(app_dir=app_dir), '')
|
||||||
# Log the JS messages to the Python logger
|
# Log the JS messages to the Python logger
|
||||||
log.log(LOG_LEVELS[level], '{source_id}:{line_number} {message}'.format(source_id=source_id,
|
log.log(LOG_LEVELS[level], '{source_id}:{line_number} {message}'.format(source_id=source_id,
|
||||||
line_number=line_number,
|
line_number=line_number,
|
||||||
|
@ -25,9 +25,11 @@ import json
|
|||||||
import logging
|
import logging
|
||||||
import os
|
import os
|
||||||
import copy
|
import copy
|
||||||
|
import re
|
||||||
|
|
||||||
from PyQt5 import QtCore, QtWebChannel, QtWidgets
|
from PyQt5 import QtCore, QtWebChannel, QtWidgets
|
||||||
|
|
||||||
|
from openlp.core.common import is_win
|
||||||
from openlp.core.common.applocation import AppLocation
|
from openlp.core.common.applocation import AppLocation
|
||||||
from openlp.core.common.enum import ServiceItemType
|
from openlp.core.common.enum import ServiceItemType
|
||||||
from openlp.core.common.i18n import translate
|
from openlp.core.common.i18n import translate
|
||||||
@ -39,70 +41,10 @@ from openlp.core.display.screens import ScreenList
|
|||||||
from openlp.core.ui import HideMode
|
from openlp.core.ui import HideMode
|
||||||
|
|
||||||
|
|
||||||
|
FONT_FOUNDRY = re.compile(r'(.*?) \[(.*?)\]')
|
||||||
log = logging.getLogger(__name__)
|
log = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
class MediaWatcher(QtCore.QObject):
|
|
||||||
"""
|
|
||||||
A class to watch media events in the display and emit signals for OpenLP
|
|
||||||
"""
|
|
||||||
progress = QtCore.pyqtSignal(float)
|
|
||||||
duration = QtCore.pyqtSignal(float)
|
|
||||||
volume = QtCore.pyqtSignal(float)
|
|
||||||
playback_rate = QtCore.pyqtSignal(float)
|
|
||||||
ended = QtCore.pyqtSignal(bool)
|
|
||||||
muted = QtCore.pyqtSignal(bool)
|
|
||||||
|
|
||||||
@QtCore.pyqtSlot(float)
|
|
||||||
def update_progress(self, time):
|
|
||||||
"""
|
|
||||||
Notify about the current position of the media
|
|
||||||
"""
|
|
||||||
log.warning(time)
|
|
||||||
self.progress.emit(time)
|
|
||||||
|
|
||||||
@QtCore.pyqtSlot(float)
|
|
||||||
def update_duration(self, time):
|
|
||||||
"""
|
|
||||||
Notify about the duration of the media
|
|
||||||
"""
|
|
||||||
log.warning(time)
|
|
||||||
self.duration.emit(time)
|
|
||||||
|
|
||||||
@QtCore.pyqtSlot(float)
|
|
||||||
def update_volume(self, level):
|
|
||||||
"""
|
|
||||||
Notify about the volume of the media
|
|
||||||
"""
|
|
||||||
log.warning(level)
|
|
||||||
level = level * 100
|
|
||||||
self.volume.emit(level)
|
|
||||||
|
|
||||||
@QtCore.pyqtSlot(float)
|
|
||||||
def update_playback_rate(self, rate):
|
|
||||||
"""
|
|
||||||
Notify about the playback rate of the media
|
|
||||||
"""
|
|
||||||
log.warning(rate)
|
|
||||||
self.playback_rate.emit(rate)
|
|
||||||
|
|
||||||
@QtCore.pyqtSlot(bool)
|
|
||||||
def has_ended(self, is_ended):
|
|
||||||
"""
|
|
||||||
Notify that the media has ended playing
|
|
||||||
"""
|
|
||||||
log.warning(is_ended)
|
|
||||||
self.ended.emit(is_ended)
|
|
||||||
|
|
||||||
@QtCore.pyqtSlot(bool)
|
|
||||||
def has_muted(self, is_muted):
|
|
||||||
"""
|
|
||||||
Notify that the media has been muted
|
|
||||||
"""
|
|
||||||
log.warning(is_muted)
|
|
||||||
self.muted.emit(is_muted)
|
|
||||||
|
|
||||||
|
|
||||||
class DisplayWatcher(QtCore.QObject):
|
class DisplayWatcher(QtCore.QObject):
|
||||||
"""
|
"""
|
||||||
This facilitates communication from the Display object in the browser back to the Python
|
This facilitates communication from the Display object in the browser back to the Python
|
||||||
@ -153,14 +95,12 @@ class DisplayWindow(QtWidgets.QWidget, RegistryProperties, LogMixin):
|
|||||||
self.display_path = display_base_path / 'display.html'
|
self.display_path = display_base_path / 'display.html'
|
||||||
self.checkerboard_path = display_base_path / 'checkerboard.png'
|
self.checkerboard_path = display_base_path / 'checkerboard.png'
|
||||||
self.openlp_splash_screen_path = display_base_path / 'openlp-splash-screen.png'
|
self.openlp_splash_screen_path = display_base_path / 'openlp-splash-screen.png'
|
||||||
self.set_url(QtCore.QUrl.fromLocalFile(path_to_str(self.display_path)))
|
|
||||||
self.channel = QtWebChannel.QWebChannel(self)
|
self.channel = QtWebChannel.QWebChannel(self)
|
||||||
self.media_watcher = MediaWatcher(self)
|
|
||||||
self.channel.registerObject('mediaWatcher', self.media_watcher)
|
|
||||||
self.display_watcher = DisplayWatcher(self)
|
self.display_watcher = DisplayWatcher(self)
|
||||||
self.channel.registerObject('displayWatcher', self.display_watcher)
|
self.channel.registerObject('displayWatcher', self.display_watcher)
|
||||||
self.webview.page().setWebChannel(self.channel)
|
self.webview.page().setWebChannel(self.channel)
|
||||||
self.display_watcher.initialised.connect(self.on_initialised)
|
self.display_watcher.initialised.connect(self.on_initialised)
|
||||||
|
self.set_url(QtCore.QUrl.fromLocalFile(path_to_str(self.display_path)))
|
||||||
self.is_display = False
|
self.is_display = False
|
||||||
self.scale = 1
|
self.scale = 1
|
||||||
self.hide_mode = None
|
self.hide_mode = None
|
||||||
@ -175,6 +115,19 @@ class DisplayWindow(QtWidgets.QWidget, RegistryProperties, LogMixin):
|
|||||||
if len(ScreenList()) > 1 or self.settings.value('core/display on monitor'):
|
if len(ScreenList()) > 1 or self.settings.value('core/display on monitor'):
|
||||||
self.show()
|
self.show()
|
||||||
|
|
||||||
|
def _fix_font_name(self, font_name):
|
||||||
|
"""
|
||||||
|
Do some font machinations to see if we can fix the font name
|
||||||
|
"""
|
||||||
|
# Some fonts on Windows that end in "Bold" are made into a base font that is bold
|
||||||
|
if is_win() and font_name.endswith(' Bold'):
|
||||||
|
font_name = font_name.split(' Bold')[0]
|
||||||
|
# Some fonts may have the Foundry name in their name. Remove the foundry name
|
||||||
|
match = FONT_FOUNDRY.match(font_name)
|
||||||
|
if match:
|
||||||
|
font_name = match.group(1)
|
||||||
|
return font_name
|
||||||
|
|
||||||
def deregister_display(self):
|
def deregister_display(self):
|
||||||
"""
|
"""
|
||||||
De-register this displays callbacks in the registry to be able to remove it
|
De-register this displays callbacks in the registry to be able to remove it
|
||||||
@ -424,6 +377,9 @@ class DisplayWindow(QtWidgets.QWidget, RegistryProperties, LogMixin):
|
|||||||
theme_copy.background_end_color = '#590909'
|
theme_copy.background_end_color = '#590909'
|
||||||
theme_copy.background_main_color = '#090909'
|
theme_copy.background_main_color = '#090909'
|
||||||
theme_copy.background_footer_color = '#090909'
|
theme_copy.background_footer_color = '#090909'
|
||||||
|
# Do some font-checking, see https://gitlab.com/openlp/openlp/-/issues/39
|
||||||
|
theme_copy.font_main_name = self._fix_font_name(theme.font_main_name)
|
||||||
|
theme_copy.font_footer_name = self._fix_font_name(theme.font_footer_name)
|
||||||
exported_theme = theme_copy.export_theme(is_js=True)
|
exported_theme = theme_copy.export_theme(is_js=True)
|
||||||
self.run_javascript('Display.setTheme({theme});'.format(theme=exported_theme), is_sync=is_sync)
|
self.run_javascript('Display.setTheme({theme});'.format(theme=exported_theme), is_sync=is_sync)
|
||||||
|
|
||||||
|
@ -199,9 +199,12 @@ class ThemeManager(QtWidgets.QWidget, RegistryBase, Ui_ThemeManager, LogMixin, R
|
|||||||
|
|
||||||
:rtype: None
|
:rtype: None
|
||||||
"""
|
"""
|
||||||
|
xml_file_paths = [p for p in AppLocation.get_section_data_path('themes').glob('*/*.xml')]
|
||||||
|
# Exit early if there are no themes to upgrade
|
||||||
|
if not xml_file_paths:
|
||||||
|
return
|
||||||
# Wait for 2 seconds to allow some other things to start processing first
|
# Wait for 2 seconds to allow some other things to start processing first
|
||||||
wait_for(lambda: False, timeout=1)
|
wait_for(lambda: False, timeout=1)
|
||||||
xml_file_paths = AppLocation.get_section_data_path('themes').glob('*/*.xml')
|
|
||||||
for xml_file_path in xml_file_paths:
|
for xml_file_path in xml_file_paths:
|
||||||
theme_data = get_text_file_string(xml_file_path)
|
theme_data = get_text_file_string(xml_file_path)
|
||||||
theme = self._create_theme_from_xml(theme_data, self.theme_path)
|
theme = self._create_theme_from_xml(theme_data, self.theme_path)
|
||||||
|
@ -32,7 +32,7 @@ from PyQt5 import QtCore
|
|||||||
# Mock QtWebEngineWidgets
|
# Mock QtWebEngineWidgets
|
||||||
sys.modules['PyQt5.QtWebEngineWidgets'] = MagicMock()
|
sys.modules['PyQt5.QtWebEngineWidgets'] = MagicMock()
|
||||||
|
|
||||||
from openlp.core.display.window import DisplayWindow
|
from openlp.core.display.window import DisplayWindow, DisplayWatcher
|
||||||
from openlp.core.common.enum import ServiceItemType
|
from openlp.core.common.enum import ServiceItemType
|
||||||
from openlp.core.lib.theme import Theme
|
from openlp.core.lib.theme import Theme
|
||||||
from openlp.core.ui import HideMode
|
from openlp.core.ui import HideMode
|
||||||
@ -220,6 +220,69 @@ def test_run_javascript_sync_no_wait(mock_time, mocked_webengine, mocked_addWidg
|
|||||||
mock_time.sleep.assert_not_called()
|
mock_time.sleep.assert_not_called()
|
||||||
|
|
||||||
|
|
||||||
|
@patch('openlp.core.display.window.QtWidgets.QVBoxLayout')
|
||||||
|
@patch('openlp.core.display.webengine.WebEngineView')
|
||||||
|
@patch('openlp.core.display.window.is_win')
|
||||||
|
def test_fix_font_bold_windows(mocked_is_win, mocked_webengine, mocked_layout, mock_settings):
|
||||||
|
"""
|
||||||
|
Test that on Windows, fonts that end with "Bold" are handled
|
||||||
|
"""
|
||||||
|
# GIVEN: A display window and a font name
|
||||||
|
mocked_is_win.return_value = True
|
||||||
|
display_window = DisplayWindow()
|
||||||
|
display_window.is_display = True
|
||||||
|
display_window.run_javascript = MagicMock()
|
||||||
|
font_name = 'Arial Rounded MT Bold'
|
||||||
|
|
||||||
|
# WHEN: The font is processed
|
||||||
|
result = display_window._fix_font_name(font_name)
|
||||||
|
|
||||||
|
# Then the font name should be fixed
|
||||||
|
assert result == 'Arial Rounded MT'
|
||||||
|
|
||||||
|
|
||||||
|
@patch('openlp.core.display.window.QtWidgets.QVBoxLayout')
|
||||||
|
@patch('openlp.core.display.webengine.WebEngineView')
|
||||||
|
@patch('openlp.core.display.window.is_win')
|
||||||
|
def test_fix_font_bold_not_windows(mocked_is_win, mocked_webengine, mocked_layout, mock_settings):
|
||||||
|
"""
|
||||||
|
Test that on NOT Windows, fonts that end with "Bold" are ignored
|
||||||
|
"""
|
||||||
|
# GIVEN: A display window and a font name
|
||||||
|
mocked_is_win.return_value = False
|
||||||
|
display_window = DisplayWindow()
|
||||||
|
display_window.is_display = True
|
||||||
|
display_window.run_javascript = MagicMock()
|
||||||
|
font_name = 'Arial Rounded MT Bold'
|
||||||
|
|
||||||
|
# WHEN: The font is processed
|
||||||
|
result = display_window._fix_font_name(font_name)
|
||||||
|
|
||||||
|
# Then the font name should be fixed
|
||||||
|
assert result == 'Arial Rounded MT Bold'
|
||||||
|
|
||||||
|
|
||||||
|
@patch('openlp.core.display.window.QtWidgets.QVBoxLayout')
|
||||||
|
@patch('openlp.core.display.webengine.WebEngineView')
|
||||||
|
@patch('openlp.core.display.window.is_win')
|
||||||
|
def test_fix_font_foundry(mocked_is_win, mocked_webengine, mocked_layout, mock_settings):
|
||||||
|
"""
|
||||||
|
Test that a font with a foundry name in it has the foundry removed
|
||||||
|
"""
|
||||||
|
# GIVEN: A display window and a font name
|
||||||
|
mocked_is_win.return_value = False
|
||||||
|
display_window = DisplayWindow()
|
||||||
|
display_window.is_display = True
|
||||||
|
display_window.run_javascript = MagicMock()
|
||||||
|
font_name = 'CMG Sans [Foundry]'
|
||||||
|
|
||||||
|
# WHEN: The font is processed
|
||||||
|
result = display_window._fix_font_name(font_name)
|
||||||
|
|
||||||
|
# Then the font name should be fixed
|
||||||
|
assert result == 'CMG Sans'
|
||||||
|
|
||||||
|
|
||||||
@patch('openlp.core.display.window.QtWidgets.QVBoxLayout')
|
@patch('openlp.core.display.window.QtWidgets.QVBoxLayout')
|
||||||
@patch('openlp.core.display.webengine.WebEngineView')
|
@patch('openlp.core.display.webengine.WebEngineView')
|
||||||
def test_set_theme_is_display_video(mocked_webengine, mocked_addWidget, mock_settings, mock_geometry):
|
def test_set_theme_is_display_video(mocked_webengine, mocked_addWidget, mock_settings, mock_geometry):
|
||||||
@ -463,3 +526,18 @@ def test_hide_display_no_display(mocked_screenlist, mocked_webengine, mocked_add
|
|||||||
|
|
||||||
# THEN: Hide mode should still be none
|
# THEN: Hide mode should still be none
|
||||||
assert display_window.hide_mode is None
|
assert display_window.hide_mode is None
|
||||||
|
|
||||||
|
|
||||||
|
def test_display_watcher_set_initialised():
|
||||||
|
"""
|
||||||
|
Test that the initialised signal is emitted
|
||||||
|
"""
|
||||||
|
# GIVEN: A DisplayWatcher instance
|
||||||
|
display_watcher = DisplayWatcher(None)
|
||||||
|
|
||||||
|
# WHEN: setInitialised is called
|
||||||
|
with patch.object(display_watcher, 'initialised') as mocked_initialised:
|
||||||
|
display_watcher.setInitialised(True)
|
||||||
|
|
||||||
|
# THEN: initialised should have been emitted
|
||||||
|
mocked_initialised.emit.assert_called_once_with(True)
|
||||||
|
@ -960,191 +960,3 @@ describe("Display.toggleVideoMute", function () {
|
|||||||
expect(mockVideo.muted).toEqual(false);
|
expect(mockVideo.muted).toEqual(false);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe("AudioPlayer", function () {
|
|
||||||
var audioPlayer, audioElement;
|
|
||||||
|
|
||||||
beforeEach(function () {
|
|
||||||
audioElement = {
|
|
||||||
_eventListeners: {},
|
|
||||||
_playing: false,
|
|
||||||
_paused: false,
|
|
||||||
_stopped: false,
|
|
||||||
src: "",
|
|
||||||
addEventListener: function (eventType, listener) {
|
|
||||||
this._eventListeners[eventType] = this._eventListeners[eventType] || [];
|
|
||||||
this._eventListeners[eventType].push(listener);
|
|
||||||
},
|
|
||||||
play: function () {
|
|
||||||
this._playing = true;
|
|
||||||
this._paused = false;
|
|
||||||
this._stopped = false;
|
|
||||||
},
|
|
||||||
pause: function () {
|
|
||||||
this._playing = false;
|
|
||||||
this._paused = true;
|
|
||||||
this._stopped = false;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
spyOn(document, "createElement").and.returnValue(audioElement);
|
|
||||||
audioPlayer = new AudioPlayer();
|
|
||||||
});
|
|
||||||
|
|
||||||
it("should create an object", function () {
|
|
||||||
expect(audioPlayer).toBeDefined();
|
|
||||||
expect(audioPlayer._audioElement).not.toBeNull();
|
|
||||||
expect(audioPlayer._eventListeners).toEqual({});
|
|
||||||
expect(audioPlayer._playlist).toEqual([]);
|
|
||||||
expect(audioPlayer._currentTrack).toEqual(null);
|
|
||||||
expect(audioPlayer._canRepeat).toEqual(false);
|
|
||||||
expect(audioPlayer._state).toEqual(AudioState.Stopped);
|
|
||||||
});
|
|
||||||
|
|
||||||
it("should call the correct method when _callListener is called", function () {
|
|
||||||
var testCalled = false;
|
|
||||||
function test(event) {
|
|
||||||
testCalled = true;
|
|
||||||
}
|
|
||||||
audioPlayer._eventListeners["test"] = [test];
|
|
||||||
audioPlayer._callListener({"type": "test"});
|
|
||||||
expect(testCalled).toEqual(true);
|
|
||||||
});
|
|
||||||
|
|
||||||
it("should log a warning when _callListener is called for an unknown event", function () {
|
|
||||||
spyOn(console, "warn");
|
|
||||||
audioPlayer._callListener({"type": "test"});
|
|
||||||
expect(console.warn).toHaveBeenCalledWith("Received unknown event \"test\", doing nothing.");
|
|
||||||
});
|
|
||||||
|
|
||||||
it("should add all the correct event listeners", function () {
|
|
||||||
expectedListeners = {
|
|
||||||
"ended": [audioPlayer.onEnded, audioPlayer._callListener],
|
|
||||||
"timeupdate": [audioPlayer._callListener],
|
|
||||||
"volumechange": [audioPlayer._callListener],
|
|
||||||
"durationchange": [audioPlayer._callListener],
|
|
||||||
"loadeddata": [audioPlayer._callListener]
|
|
||||||
};
|
|
||||||
expect(audioElement._eventListeners).toEqual(expectedListeners);
|
|
||||||
});
|
|
||||||
|
|
||||||
it("should add the correct event listener when calling addEventListener", function () {
|
|
||||||
function dummy () {};
|
|
||||||
var expectedListeners = {
|
|
||||||
"test": [dummy]
|
|
||||||
};
|
|
||||||
audioPlayer.addEventListener("test", dummy);
|
|
||||||
expect(audioPlayer._eventListeners).toEqual(expectedListeners);
|
|
||||||
});
|
|
||||||
|
|
||||||
it("should set call nextTrack when the onEnded listener is called", function () {
|
|
||||||
spyOn(audioPlayer, "nextTrack");
|
|
||||||
audioPlayer.onEnded({});
|
|
||||||
expect(audioPlayer.nextTrack).toHaveBeenCalled();
|
|
||||||
});
|
|
||||||
|
|
||||||
it("should set the _canRepeat property when calling setCanRepeat", function () {
|
|
||||||
audioPlayer.setCanRepeat(true);
|
|
||||||
expect(audioPlayer._canRepeat).toEqual(true);
|
|
||||||
});
|
|
||||||
|
|
||||||
it("should clear the playlist when clearTracks is called", function () {
|
|
||||||
audioPlayer._playlist = ["one", "two", "three"];
|
|
||||||
audioPlayer.clearTracks();
|
|
||||||
expect(audioPlayer._playlist).toEqual([]);
|
|
||||||
});
|
|
||||||
|
|
||||||
it("should add a track to the playlist when addTrack is called", function () {
|
|
||||||
audioPlayer._playlist = [];
|
|
||||||
audioPlayer.addTrack("one");
|
|
||||||
expect(audioPlayer._playlist).toEqual(["one"]);
|
|
||||||
});
|
|
||||||
|
|
||||||
it("should move to the first track when canRepeat is true and nextTrack is called", function () {
|
|
||||||
spyOn(audioPlayer, "play");
|
|
||||||
audioPlayer.addTrack("one");
|
|
||||||
audioPlayer.addTrack("two");
|
|
||||||
audioPlayer.setCanRepeat(true);
|
|
||||||
audioPlayer._currentTrack = "two";
|
|
||||||
|
|
||||||
audioPlayer.nextTrack();
|
|
||||||
|
|
||||||
expect(audioPlayer.play).toHaveBeenCalledWith("one");
|
|
||||||
});
|
|
||||||
|
|
||||||
it("should move to the next track when nextTrack is called", function () {
|
|
||||||
spyOn(audioPlayer, "play");
|
|
||||||
audioPlayer.addTrack("one");
|
|
||||||
audioPlayer.addTrack("two");
|
|
||||||
audioPlayer._currentTrack = "one";
|
|
||||||
|
|
||||||
audioPlayer.nextTrack();
|
|
||||||
|
|
||||||
expect(audioPlayer.play).toHaveBeenCalledWith("two");
|
|
||||||
});
|
|
||||||
|
|
||||||
it("should stop when canRepeat is false and nextTrack is called on the last track in the list", function () {
|
|
||||||
spyOn(audioPlayer, "play");
|
|
||||||
spyOn(audioPlayer, "stop");
|
|
||||||
audioPlayer.addTrack("one");
|
|
||||||
audioPlayer.addTrack("two");
|
|
||||||
audioPlayer.setCanRepeat(false);
|
|
||||||
audioPlayer._currentTrack = "two";
|
|
||||||
|
|
||||||
audioPlayer.nextTrack();
|
|
||||||
|
|
||||||
expect(audioPlayer.play).not.toHaveBeenCalled();
|
|
||||||
expect(audioPlayer.stop).toHaveBeenCalled();
|
|
||||||
});
|
|
||||||
|
|
||||||
it("should play the first track when nextTrack is called when no songs are playing", function () {
|
|
||||||
spyOn(audioPlayer, "play");
|
|
||||||
audioPlayer.addTrack("one");
|
|
||||||
audioPlayer.nextTrack();
|
|
||||||
expect(audioPlayer.play).toHaveBeenCalledWith("one");
|
|
||||||
});
|
|
||||||
|
|
||||||
it("should log a warning when nextTrack is called when no songs are in the playlist", function () {
|
|
||||||
spyOn(console, "warn");
|
|
||||||
audioPlayer.nextTrack();
|
|
||||||
expect(console.warn).toHaveBeenCalledWith("No tracks in playlist, doing nothing.");
|
|
||||||
});
|
|
||||||
|
|
||||||
it("should play the track specified when play is called with a filename", function () {
|
|
||||||
audioPlayer.addTrack("one");
|
|
||||||
audioPlayer.play("one");
|
|
||||||
|
|
||||||
expect(audioPlayer._currentTrack).toEqual("one");
|
|
||||||
expect(audioElement._playing).toEqual(true);
|
|
||||||
expect(audioElement.src).toEqual("one");
|
|
||||||
expect(audioPlayer._state).toEqual(AudioState.Playing);
|
|
||||||
});
|
|
||||||
|
|
||||||
it("should continue playing when play is called without a filename and the player is paused", function () {
|
|
||||||
audioPlayer._state = AudioState.Paused;
|
|
||||||
audioPlayer.play();
|
|
||||||
|
|
||||||
expect(audioElement._playing).toEqual(true);
|
|
||||||
expect(audioPlayer._state).toEqual(AudioState.Playing);
|
|
||||||
});
|
|
||||||
|
|
||||||
it("should do nothing when play is called without a filename and the player is not paused", function () {
|
|
||||||
spyOn(console, "warn");
|
|
||||||
audioPlayer._state = AudioState.Playing;
|
|
||||||
audioPlayer.play();
|
|
||||||
|
|
||||||
expect(console.warn).toHaveBeenCalledWith("No track currently paused and no track specified, doing nothing.");
|
|
||||||
});
|
|
||||||
|
|
||||||
it("should pause the current track when pause is called", function () {
|
|
||||||
audioPlayer.pause();
|
|
||||||
expect(audioPlayer._state).toEqual(AudioState.Paused);
|
|
||||||
expect(audioElement._paused).toEqual(true);
|
|
||||||
});
|
|
||||||
|
|
||||||
it("should stop the current track when stop is called", function () {
|
|
||||||
audioPlayer.stop();
|
|
||||||
expect(audioPlayer._state).toEqual(AudioState.Stopped);
|
|
||||||
expect(audioElement._paused).toEqual(true);
|
|
||||||
expect(audioElement.src).toEqual("");
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
Loading…
Reference in New Issue
Block a user