← Back to team overview

openlp-core team mailing list archive

[Merge] lp:~raoul-snyman/openlp/info-pane into lp:openlp

 

Raoul Snyman has proposed merging lp:~raoul-snyman/openlp/info-pane into lp:openlp.

Requested reviews:
  OpenLP Core (openlp-core)

For more details, see:
https://code.launchpad.net/~raoul-snyman/openlp/info-pane/+merge/226595

Added the name of the current item to the service controller, and added some more tests
-- 
https://code.launchpad.net/~raoul-snyman/openlp/info-pane/+merge/226595
Your team OpenLP Core is requested to review the proposed merge of lp:~raoul-snyman/openlp/info-pane into lp:openlp.
=== modified file 'openlp/core/ui/slidecontroller.py'
--- openlp/core/ui/slidecontroller.py	2014-05-02 06:33:18 +0000
+++ openlp/core/ui/slidecontroller.py	2014-07-12 23:59:27 +0000
@@ -96,6 +96,8 @@
         """
         This is the generic function to send signal for control widgets, created from within other plugins
         This function is needed to catch the current controller
+
+        :param args: Arguments to send to the plugins
         """
         sender = self.sender().objectName() if self.sender().objectName() else self.sender().text()
         controller = self
@@ -143,11 +145,19 @@
         self.panel_layout = QtGui.QVBoxLayout(self.panel)
         self.panel_layout.setSpacing(0)
         self.panel_layout.setMargin(0)
-        # Type label for the top of the slide controller
+        # Type label at the top of the slide controller
         self.type_label = QtGui.QLabel(self.panel)
         self.type_label.setStyleSheet('font-weight: bold; font-size: 12pt;')
         self.type_label.setAlignment(QtCore.Qt.AlignCenter)
+        if self.is_live:
+            self.type_label.setText(UiStrings().Live)
+        else:
+            self.type_label.setText(UiStrings().Preview)
         self.panel_layout.addWidget(self.type_label)
+        # Info label for the title of the current item, at the top of the slide controller
+        self.info_label = QtGui.QLabel(self.panel)
+        self.info_label.setAlignment(QtCore.Qt.AlignCenter)
+        self.panel_layout.addWidget(self.info_label)
         # Splitter
         self.splitter = QtGui.QSplitter(self.panel)
         self.splitter.setOrientation(QtCore.Qt.Vertical)
@@ -402,12 +412,17 @@
         """
         try:
             from openlp.plugins.songs.lib import VerseType
-            SONGS_PLUGIN_AVAILABLE = True
+            is_songs_plugin_available = True
         except ImportError:
-            SONGS_PLUGIN_AVAILABLE = False
+            class VerseType(object):
+                """
+                This empty class is mostly just to satisfy Python, PEP8 and PyCharm
+                """
+                pass
+            is_songs_plugin_available = False
         sender_name = self.sender().objectName()
         verse_type = sender_name[15:] if sender_name[:15] == 'shortcutAction_' else ''
-        if SONGS_PLUGIN_AVAILABLE:
+        if is_songs_plugin_available:
             if verse_type == 'V':
                 self.current_shortcut = VerseType.translated_tags[VerseType.Verse]
             elif verse_type == 'C':
@@ -777,6 +792,7 @@
         if service_item.is_command():
             Registry().execute(
                 '%s_start' % service_item.name.lower(), [self.service_item, self.is_live, self.hide_mode(), slide_no])
+        self.info_label.setText(self.service_item.title)
         self.slide_list = {}
         if self.is_live:
             self.song_menu.menu().clear()

=== modified file 'tests/functional/openlp_core_ui/test_slidecontroller.py'
--- tests/functional/openlp_core_ui/test_slidecontroller.py	2014-05-07 16:05:27 +0000
+++ tests/functional/openlp_core_ui/test_slidecontroller.py	2014-07-12 23:59:27 +0000
@@ -30,10 +30,13 @@
 Package to test the openlp.core.ui.slidecontroller package.
 """
 from unittest import TestCase
+from openlp.core import Registry
+from openlp.core.lib import ServiceItemAction
 
 from openlp.core.ui import SlideController
+from openlp.core.ui.slidecontroller import WIDE_MENU, NON_TEXT_MENU
 
-from tests.interfaces import MagicMock
+from tests.interfaces import MagicMock, patch
 
 
 class TestSlideController(TestCase):
@@ -42,37 +45,514 @@
         """
         Test the initial slide controller state .
         """
-        # GIVEN: A new slideController instance.
+        # GIVEN: A new SlideController instance.
         slide_controller = SlideController(None)
+
         # WHEN: the default controller is built.
         # THEN: The controller should not be a live controller.
         self.assertEqual(slide_controller.is_live, False, 'The base slide controller should not be a live controller')
 
-    def toggle_blank_test(self):
-        """
-        Test the setting of the display blank icons by display type.
-        """
-        # GIVEN: A new slideController instance.
+    def text_service_item_blank_test(self):
+        """
+        Test that loading a text-based service item into the slide controller sets the correct blank menu
+        """
+        # GIVEN: A new SlideController instance.
         slide_controller = SlideController(None)
         service_item = MagicMock()
         toolbar = MagicMock()
-        toolbar.set_widget_visible = self.dummy_widget_visible
+        toolbar.set_widget_visible = MagicMock()
         slide_controller.toolbar = toolbar
         slide_controller.service_item = service_item
 
-        # WHEN a text based service item is used
+        # WHEN: a text based service item is used
         slide_controller.service_item.is_text = MagicMock(return_value=True)
         slide_controller.set_blank_menu()
 
-        # THEN: then call set up the toolbar to blank the display screen.
-        self.assertEqual(len(self.test_widget), 3, 'There should be three icons to display on the screen')
+        # THEN: the call to set the visible items on the toolbar should be correct
+        toolbar.set_widget_visible.assert_called_with(WIDE_MENU, True)
+
+    def non_text_service_item_blank_test(self):
+        """
+        Test that loading a non-text service item into the slide controller sets the correct blank menu
+        """
+        # GIVEN: A new SlideController instance.
+        slide_controller = SlideController(None)
+        service_item = MagicMock()
+        toolbar = MagicMock()
+        toolbar.set_widget_visible = MagicMock()
+        slide_controller.toolbar = toolbar
+        slide_controller.service_item = service_item
 
         # WHEN a non text based service item is used
         slide_controller.service_item.is_text = MagicMock(return_value=False)
         slide_controller.set_blank_menu()
 
         # THEN: then call set up the toolbar to blank the display screen.
-        self.assertEqual(len(self.test_widget), 2, 'There should be only two icons to display on the screen')
-
-    def dummy_widget_visible(self, widget, visible=True):
-        self.test_widget = widget
+        toolbar.set_widget_visible.assert_called_with(NON_TEXT_MENU, True)
+
+    def receive_spin_delay_test(self):
+        """
+        Test that the spin box is updated accordingly after a call to receive_spin_delay()
+        """
+        with patch('openlp.core.ui.slidecontroller.Settings') as MockedSettings:
+            # GIVEN: A new SlideController instance.
+            mocked_value = MagicMock(return_value=1)
+            MockedSettings.return_value = MagicMock(value=mocked_value)
+            mocked_delay_spin_box = MagicMock()
+            slide_controller = SlideController(None)
+            slide_controller.delay_spin_box = mocked_delay_spin_box
+
+            # WHEN: The receive_spin_delay() method is called
+            slide_controller.receive_spin_delay()
+
+            # THEN: The Settings()value() and delay_spin_box.setValue() methods should have been called correctly
+            mocked_value.assert_called_with('core/loop delay')
+            mocked_delay_spin_box.setValue.assert_called_with(1)
+
+    def toggle_display_blank_test(self):
+        """
+        Check that the toggle_display('blank') method calls the on_blank_display() method
+        """
+        # GIVEN: A new SlideController instance.
+        mocked_on_blank_display = MagicMock()
+        mocked_on_theme_display = MagicMock()
+        mocked_on_hide_display = MagicMock()
+        slide_controller = SlideController(None)
+        slide_controller.on_blank_display = mocked_on_blank_display
+        slide_controller.on_theme_display = mocked_on_theme_display
+        slide_controller.on_hide_display = mocked_on_hide_display
+
+        # WHEN: toggle_display() is called with an argument of "blank"
+        slide_controller.toggle_display('blank')
+
+        # THEN: Only on_blank_display() should have been called with an argument of True
+        mocked_on_blank_display.assert_called_once_with(True)
+        self.assertEqual(0, mocked_on_theme_display.call_count, 'on_theme_display should not have been called')
+        self.assertEqual(0, mocked_on_hide_display.call_count, 'on_hide_display should not have been called')
+
+    def toggle_display_hide_test(self):
+        """
+        Check that the toggle_display('hide') method calls the on_blank_display() method
+        """
+        # GIVEN: A new SlideController instance.
+        mocked_on_blank_display = MagicMock()
+        mocked_on_theme_display = MagicMock()
+        mocked_on_hide_display = MagicMock()
+        slide_controller = SlideController(None)
+        slide_controller.on_blank_display = mocked_on_blank_display
+        slide_controller.on_theme_display = mocked_on_theme_display
+        slide_controller.on_hide_display = mocked_on_hide_display
+
+        # WHEN: toggle_display() is called with an argument of "hide"
+        slide_controller.toggle_display('hide')
+
+        # THEN: Only on_blank_display() should have been called with an argument of True
+        mocked_on_blank_display.assert_called_once_with(True)
+        self.assertEqual(0, mocked_on_theme_display.call_count, 'on_theme_display should not have been called')
+        self.assertEqual(0, mocked_on_hide_display.call_count, 'on_hide_display should not have been called')
+
+    def toggle_display_theme_test(self):
+        """
+        Check that the toggle_display('theme') method calls the on_theme_display() method
+        """
+        # GIVEN: A new SlideController instance.
+        mocked_on_blank_display = MagicMock()
+        mocked_on_theme_display = MagicMock()
+        mocked_on_hide_display = MagicMock()
+        slide_controller = SlideController(None)
+        slide_controller.on_blank_display = mocked_on_blank_display
+        slide_controller.on_theme_display = mocked_on_theme_display
+        slide_controller.on_hide_display = mocked_on_hide_display
+
+        # WHEN: toggle_display() is called with an argument of "theme"
+        slide_controller.toggle_display('theme')
+
+        # THEN: Only on_theme_display() should have been called with an argument of True
+        mocked_on_theme_display.assert_called_once_with(True)
+        self.assertEqual(0, mocked_on_blank_display.call_count, 'on_blank_display should not have been called')
+        self.assertEqual(0, mocked_on_hide_display.call_count, 'on_hide_display should not have been called')
+
+    def toggle_display_desktop_test(self):
+        """
+        Check that the toggle_display('desktop') method calls the on_hide_display() method
+        """
+        # GIVEN: A new SlideController instance.
+        mocked_on_blank_display = MagicMock()
+        mocked_on_theme_display = MagicMock()
+        mocked_on_hide_display = MagicMock()
+        slide_controller = SlideController(None)
+        slide_controller.on_blank_display = mocked_on_blank_display
+        slide_controller.on_theme_display = mocked_on_theme_display
+        slide_controller.on_hide_display = mocked_on_hide_display
+
+        # WHEN: toggle_display() is called with an argument of "desktop"
+        slide_controller.toggle_display('desktop')
+
+        # THEN: Only on_hide_display() should have been called with an argument of True
+        mocked_on_hide_display.assert_called_once_with(True)
+        self.assertEqual(0, mocked_on_blank_display.call_count, 'on_blank_display should not have been called')
+        self.assertEqual(0, mocked_on_theme_display.call_count, 'on_theme_display should not have been called')
+
+    def toggle_display_show_test(self):
+        """
+        Check that the toggle_display('show') method calls all the on_X_display() methods
+        """
+        # GIVEN: A new SlideController instance.
+        mocked_on_blank_display = MagicMock()
+        mocked_on_theme_display = MagicMock()
+        mocked_on_hide_display = MagicMock()
+        slide_controller = SlideController(None)
+        slide_controller.on_blank_display = mocked_on_blank_display
+        slide_controller.on_theme_display = mocked_on_theme_display
+        slide_controller.on_hide_display = mocked_on_hide_display
+
+        # WHEN: toggle_display() is called with an argument of "show"
+        slide_controller.toggle_display('show')
+
+        # THEN: All the on_X_display() methods should have been called with an argument of False
+        mocked_on_blank_display.assert_called_once_with(False)
+        mocked_on_theme_display.assert_called_once_with(False)
+        mocked_on_hide_display.assert_called_once_with(False)
+
+    def live_escape_test(self):
+        """
+        Test that when the live_escape() method is called, the display is set to invisible and any media is stopped
+        """
+        # GIVEN: A new SlideController instance and mocked out display and media_controller
+        mocked_display = MagicMock()
+        mocked_media_controller = MagicMock()
+        Registry.create()
+        Registry().register('media_controller', mocked_media_controller)
+        slide_controller = SlideController(None)
+        slide_controller.display = mocked_display
+
+        # WHEN: live_escape() is called
+        slide_controller.live_escape()
+
+        # THEN: the display should be set to invisible and the media controller stopped
+        mocked_display.setVisible.assert_called_once_with(False)
+        mocked_media_controller.media_stop.assert_called_once_with(slide_controller)
+
+    def service_previous_test(self):
+        """
+        Check that calling the service_previous() method adds the previous key to the queue and processes the queue
+        """
+        # GIVEN: A new SlideController instance.
+        mocked_keypress_queue = MagicMock()
+        mocked_process_queue = MagicMock()
+        slide_controller = SlideController(None)
+        slide_controller.keypress_queue = mocked_keypress_queue
+        slide_controller._process_queue = mocked_process_queue
+
+        # WHEN: The service_previous() method is called
+        slide_controller.service_previous()
+
+        # THEN: The keypress is added to the queue and the queue is processed
+        mocked_keypress_queue.append.assert_called_once_with(ServiceItemAction.Previous)
+        mocked_process_queue.assert_called_once_with()
+
+    def service_next_test(self):
+        """
+        Check that calling the service_next() method adds the next key to the queue and processes the queue
+        """
+        # GIVEN: A new SlideController instance and mocked out methods
+        mocked_keypress_queue = MagicMock()
+        mocked_process_queue = MagicMock()
+        slide_controller = SlideController(None)
+        slide_controller.keypress_queue = mocked_keypress_queue
+        slide_controller._process_queue = mocked_process_queue
+
+        # WHEN: The service_next() method is called
+        slide_controller.service_next()
+
+        # THEN: The keypress is added to the queue and the queue is processed
+        mocked_keypress_queue.append.assert_called_once_with(ServiceItemAction.Next)
+        mocked_process_queue.assert_called_once_with()
+
+    def update_slide_limits_test(self):
+        """
+        Test that calling the update_slide_limits() method updates the slide limits
+        """
+        # GIVEN: A mocked out Settings object, a new SlideController and a mocked out main_window
+        with patch('openlp.core.ui.slidecontroller.Settings') as MockedSettings:
+            mocked_value = MagicMock(return_value=10)
+            MockedSettings.return_value = MagicMock(value=mocked_value)
+            mocked_main_window = MagicMock(advanced_settings_section='advanced')
+            Registry.create()
+            Registry().register('main_window', mocked_main_window)
+            slide_controller = SlideController(None)
+
+            # WHEN: update_slide_limits() is called
+            slide_controller.update_slide_limits()
+
+            # THEN: The value of slide_limits should be 10
+            mocked_value.assert_called_once_with('advanced/slide limits')
+            self.assertEqual(10, slide_controller.slide_limits, 'Slide limits should have been updated to 10')
+
+    def enable_tool_bar_live_test(self):
+        """
+        Check that when enable_tool_bar on a live slide controller is called, enable_live_tool_bar is called
+        """
+        # GIVEN: Mocked out enable methods and a real slide controller which is set to live
+        mocked_enable_live_tool_bar = MagicMock()
+        mocked_enable_preview_tool_bar = MagicMock()
+        slide_controller = SlideController(None)
+        slide_controller.is_live = True
+        slide_controller.enable_live_tool_bar = mocked_enable_live_tool_bar
+        slide_controller.enable_preview_tool_bar = mocked_enable_preview_tool_bar
+        mocked_service_item = MagicMock()
+
+        # WHEN: enable_tool_bar() is called
+        slide_controller.enable_tool_bar(mocked_service_item)
+
+        # THEN: The enable_live_tool_bar() method is called, not enable_preview_tool_bar()
+        mocked_enable_live_tool_bar.assert_called_once_with(mocked_service_item)
+        self.assertEqual(0, mocked_enable_preview_tool_bar.call_count, 'The preview method should not have been called')
+
+    def enable_tool_bar_preview_test(self):
+        """
+        Check that when enable_tool_bar on a preview slide controller is called, enable_preview_tool_bar is called
+        """
+        # GIVEN: Mocked out enable methods and a real slide controller which is set to live
+        mocked_enable_live_tool_bar = MagicMock()
+        mocked_enable_preview_tool_bar = MagicMock()
+        slide_controller = SlideController(None)
+        slide_controller.is_live = False
+        slide_controller.enable_live_tool_bar = mocked_enable_live_tool_bar
+        slide_controller.enable_preview_tool_bar = mocked_enable_preview_tool_bar
+        mocked_service_item = MagicMock()
+
+        # WHEN: enable_tool_bar() is called
+        slide_controller.enable_tool_bar(mocked_service_item)
+
+        # THEN: The enable_preview_tool_bar() method is called, not enable_live_tool_bar()
+        mocked_enable_preview_tool_bar.assert_called_once_with(mocked_service_item)
+        self.assertEqual(0, mocked_enable_live_tool_bar.call_count, 'The live method should not have been called')
+
+    def refresh_service_item_text_test(self):
+        """
+        Test that the refresh_service_item() method refreshes a text service item
+        """
+        # GIVEN: A mock service item and a fresh slide controller
+        mocked_service_item = MagicMock()
+        mocked_service_item.is_text.return_value = True
+        mocked_service_item.is_image.return_value = False
+        mocked_process_item = MagicMock()
+        slide_controller = SlideController(None)
+        slide_controller.service_item = mocked_service_item
+        slide_controller._process_item = mocked_process_item
+        slide_controller.selected_row = 5
+
+        # WHEN: The refresh_service_item method() is called
+        slide_controller.refresh_service_item()
+
+        # THEN: The item should be re-processed
+        mocked_service_item.is_text.assert_called_once_with()
+        self.assertEqual(0, mocked_service_item.is_image.call_count, 'is_image should not have been called')
+        mocked_service_item.render.assert_called_once_with()
+        mocked_process_item.assert_called_once_with(mocked_service_item, 5)
+
+    def refresh_service_item_image_test(self):
+        """
+        Test that the refresh_service_item() method refreshes a image service item
+        """
+        # GIVEN: A mock service item and a fresh slide controller
+        mocked_service_item = MagicMock()
+        mocked_service_item.is_text.return_value = False
+        mocked_service_item.is_image.return_value = True
+        mocked_process_item = MagicMock()
+        slide_controller = SlideController(None)
+        slide_controller.service_item = mocked_service_item
+        slide_controller._process_item = mocked_process_item
+        slide_controller.selected_row = 5
+
+        # WHEN: The refresh_service_item method() is called
+        slide_controller.refresh_service_item()
+
+        # THEN: The item should be re-processed
+        mocked_service_item.is_text.assert_called_once_with()
+        mocked_service_item.is_image.assert_called_once_with()
+        mocked_service_item.render.assert_called_once_with()
+        mocked_process_item.assert_called_once_with(mocked_service_item, 5)
+
+    def refresh_service_item_not_image_or_text_test(self):
+        """
+        Test that the refresh_service_item() method does not refresh a service item if it's neither text or an image
+        """
+        # GIVEN: A mock service item and a fresh slide controller
+        mocked_service_item = MagicMock()
+        mocked_service_item.is_text.return_value = False
+        mocked_service_item.is_image.return_value = False
+        mocked_process_item = MagicMock()
+        slide_controller = SlideController(None)
+        slide_controller.service_item = mocked_service_item
+        slide_controller._process_item = mocked_process_item
+        slide_controller.selected_row = 5
+
+        # WHEN: The refresh_service_item method() is called
+        slide_controller.refresh_service_item()
+
+        # THEN: The item should be re-processed
+        mocked_service_item.is_text.assert_called_once_with()
+        mocked_service_item.is_image.assert_called_once_with()
+        self.assertEqual(0, mocked_service_item.render.call_count, 'The render() method should not have been called')
+        self.assertEqual(0, mocked_process_item.call_count,
+                         'The mocked_process_item() method should not have been called')
+
+    def add_service_item_with_song_edit_test(self):
+        """
+        Test the add_service_item() method when song_edit is True
+        """
+        # GIVEN: A slide controller and a new item to add
+        mocked_item = MagicMock()
+        mocked_process_item =MagicMock()
+        slide_controller = SlideController(None)
+        slide_controller._process_item = mocked_process_item
+        slide_controller.song_edit = True
+        slide_controller.selected_row = 2
+
+        # WHEN: The item is added to the service
+        slide_controller.add_service_item(mocked_item)
+
+        # THEN: The item is processed, the slide number is correct, and the song is not editable (or something)
+        mocked_item.render.assert_called_once_with()
+        self.assertFalse(slide_controller.song_edit, 'song_edit should be False')
+        mocked_process_item.assert_called_once_with(mocked_item, 2)
+
+    def add_service_item_without_song_edit_test(self):
+        """
+        Test the add_service_item() method when song_edit is False
+        """
+        # GIVEN: A slide controller and a new item to add
+        mocked_item = MagicMock()
+        mocked_process_item =MagicMock()
+        slide_controller = SlideController(None)
+        slide_controller._process_item = mocked_process_item
+        slide_controller.song_edit = False
+        slide_controller.selected_row = 2
+
+        # WHEN: The item is added to the service
+        slide_controller.add_service_item(mocked_item)
+
+        # THEN: The item is processed, the slide number is correct, and the song is not editable (or something)
+        mocked_item.render.assert_called_once_with()
+        self.assertFalse(slide_controller.song_edit, 'song_edit should be False')
+        mocked_process_item.assert_called_once_with(mocked_item, 0)
+
+    def replace_service_manager_item_different_items_test(self):
+        """
+        Test that when the service items are not the same, nothing happens
+        """
+        # GIVEN: A slide controller and a new item to add
+        mocked_item = MagicMock()
+        mocked_preview_widget = MagicMock()
+        mocked_process_item = MagicMock()
+        slide_controller = SlideController(None)
+        slide_controller.preview_widget = mocked_preview_widget
+        slide_controller._process_item = mocked_process_item
+        slide_controller.service_item = None
+
+        # WHEN: The service item is replaced
+        slide_controller.replace_service_manager_item(mocked_item)
+
+        # THEN: The service item should not be processed
+        self.assertEqual(0, mocked_process_item.call_count, 'The _process_item() method should not have been called')
+        self.assertEqual(0, mocked_preview_widget.current_slide_number.call_count,
+                         'The preview_widgetcurrent_slide_number.() method should not have been called')
+
+    def replace_service_manager_item_same_item_test(self):
+        """
+        Test that when the service item is the same, the service item is reprocessed
+        """
+        # GIVEN: A slide controller and a new item to add
+        mocked_item = MagicMock()
+        mocked_preview_widget = MagicMock()
+        mocked_preview_widget.current_slide_number.return_value = 7
+        mocked_process_item = MagicMock()
+        slide_controller = SlideController(None)
+        slide_controller.preview_widget = mocked_preview_widget
+        slide_controller._process_item = mocked_process_item
+        slide_controller.service_item = mocked_item
+
+        # WHEN: The service item is replaced
+        slide_controller.replace_service_manager_item(mocked_item)
+
+        # THEN: The service item should not be processed
+        mocked_preview_widget.current_slide_number.assert_called_with()
+        mocked_process_item.assert_called_once_with(mocked_item, 7)
+
+    def on_slide_selected_index_no_service_item_test(self):
+        """
+        Test that when there is no service item, the on_slide_selected_index() method returns immediately
+        """
+        # GIVEN: A mocked service item and a slide controller without a service item
+        mocked_item = MagicMock()
+        slide_controller = SlideController(None)
+        slide_controller.service_item = None
+
+        # WHEN: The method is called
+        slide_controller.on_slide_selected_index([10])
+
+        # THEN: It should have exited early
+        self.assertEqual(0, mocked_item.is_command.call_count, 'The service item should have not been called')
+
+    def on_slide_selected_index_service_item_command_test(self):
+        """
+        Test that when there is a command service item, the command is executed
+        """
+        # GIVEN: A mocked service item and a slide controller with a service item
+        mocked_item = MagicMock()
+        mocked_item.is_command.return_value = True
+        mocked_item.name = 'Mocked Item'
+        mocked_execute = MagicMock()
+        mocked_update_preview = MagicMock()
+        mocked_preview_widget = MagicMock()
+        mocked_slide_selected = MagicMock()
+        Registry.execute = mocked_execute
+        Registry.create()
+        slide_controller = SlideController(None)
+        slide_controller.service_item = mocked_item
+        slide_controller.update_preview = mocked_update_preview
+        slide_controller.preview_widget = mocked_preview_widget
+        slide_controller.slide_selected = mocked_slide_selected
+        slide_controller.is_live = True
+
+        # WHEN: The method is called
+        slide_controller.on_slide_selected_index([9])
+
+        # THEN: It should have sent a notification
+        mocked_item.is_command.assert_called_once_with()
+        mocked_execute.assert_called_once_with('mocked item_slide', [mocked_item, True, 9])
+        mocked_update_preview.assert_called_once_with()
+        self.assertEqual(0, mocked_preview_widget.change_slide.call_count, 'Change slide should not have been called')
+        self.assertEqual(0, mocked_slide_selected.call_count, 'slide_selected should not have been called')
+
+    def on_slide_selected_index_service_item_not_command_test(self):
+        """
+        Test that when there is a service item but it's not a command, the preview widget is updated
+        """
+        # GIVEN: A mocked service item and a slide controller with a service item
+        mocked_item = MagicMock()
+        mocked_item.is_command.return_value = False
+        mocked_item.name = 'Mocked Item'
+        mocked_execute = MagicMock()
+        mocked_update_preview = MagicMock()
+        mocked_preview_widget = MagicMock()
+        mocked_slide_selected = MagicMock()
+        Registry.execute = mocked_execute
+        Registry.create()
+        slide_controller = SlideController(None)
+        slide_controller.service_item = mocked_item
+        slide_controller.update_preview = mocked_update_preview
+        slide_controller.preview_widget = mocked_preview_widget
+        slide_controller.slide_selected = mocked_slide_selected
+
+        # WHEN: The method is called
+        slide_controller.on_slide_selected_index([7])
+
+        # THEN: It should have sent a notification
+        mocked_item.is_command.assert_called_once_with()
+        self.assertEqual(0, mocked_execute.call_count, 'Execute should not have been called')
+        self.assertEqual(0, mocked_update_preview.call_count, 'Update preview should not have been called')
+        mocked_preview_widget.change_slide.assert_called_once_with(7)
+        mocked_slide_selected.assert_called_once_with()


Follow ups