beeref/tests/actions/test_mixin.py

372 lines
12 KiB
Python

import os.path
from unittest.mock import patch, MagicMock, call
from PyQt6 import QtWidgets
from beeref.actions import ActionsMixin
from beeref.actions.actions import Action
from beeref.actions.menu_structure import MENU_SEPARATOR
from beeref.utils import ActionList
class FooWidget(QtWidgets.QWidget, ActionsMixin):
settings = MagicMock()
undo_stack = MagicMock()
scene = MagicMock()
def on_foo(self):
pass
def on_bar(self):
pass
def on_action_open_recent_file(self, filename):
pass
@patch('PyQt6.QtGui.QAction.triggered')
@patch('PyQt6.QtGui.QAction.toggled')
@patch('beeref.actions.mixin.menu_structure',
[{'menu': 'Foo', 'items': ['foo']}])
@patch('beeref.actions.mixin.actions',
ActionList([Action(
id='foo',
text='&Foo',
shortcuts=['Ctrl+F'],
callback='on_foo',
)]))
@patch('beeref.config.KeyboardSettings.get_list')
def test_create_actions(kb_mock, toggle_mock, trigger_mock, qapp):
kb_mock.side_effect = lambda group, key, default: default
widget = FooWidget()
widget.build_menu_and_actions()
trigger_mock.connect.assert_called_once_with(widget.on_foo)
toggle_mock.connect.assert_not_called()
assert len(widget.actions()) == 1
qaction = widget.actions()[0]
assert qaction.text() == '&Foo'
assert qaction.shortcut() == 'Ctrl+F'
assert qaction.isEnabled() is True
from beeref.actions.mixin import actions
assert actions['foo'].qaction == qaction
kb_mock.assert_called_once_with('Actions', 'foo', ['Ctrl+F'])
@patch('beeref.actions.mixin.menu_structure',
[{'menu': 'Foo', 'items': ['foo']}])
def test_create_actions_with_shortcut_from_settings(qapp, kbsettings):
with patch('beeref.actions.mixin.actions',
ActionList([Action(
id='foo',
text='&Foo',
shortcuts=['Ctrl+F'],
callback='on_foo')])):
# Create Action inside the test function so that its
# kbsettings get created after the kbsettings fixture changes
# the file path
kbsettings.set_list('Actions', 'foo', ['Alt+O'])
widget = FooWidget()
widget.build_menu_and_actions()
qaction = widget.actions()[0]
assert qaction.shortcuts() == ['Alt+O']
@patch('PyQt6.QtGui.QAction.triggered')
@patch('PyQt6.QtGui.QAction.toggled')
@patch('beeref.actions.mixin.menu_structure',
[{'menu': 'Foo', 'items': ['foo']}])
@patch('beeref.actions.mixin.actions',
ActionList([Action(
id='foo',
text='&Foo',
checkable=True,
callback='on_foo',
)]))
def test_create_actions_checkable(toggle_mock, trigger_mock, qapp):
widget = FooWidget()
widget.build_menu_and_actions()
trigger_mock.connect.assert_not_called()
toggle_mock.connect.assert_called_once_with(widget.on_foo)
assert len(widget.actions()) == 1
qaction = widget.actions()[0]
assert qaction.text() == '&Foo'
assert qaction.isEnabled() is True
assert qaction.isChecked() is False
@patch('PyQt6.QtGui.QAction.triggered')
@patch('PyQt6.QtGui.QAction.toggled')
@patch('beeref.actions.mixin.menu_structure',
[{'menu': 'Foo', 'items': ['foo']}])
@patch('beeref.actions.mixin.actions',
ActionList([Action(
id='foo',
text='&Foo',
checkable=True,
checked=True,
callback='on_foo',
)]))
def test_create_actions_checkable_checked_true(
toggle_mock, trigger_mock, qapp):
widget = FooWidget()
widget.build_menu_and_actions()
trigger_mock.connect.assert_not_called()
toggle_mock.connect.assert_called_once_with(widget.on_foo)
assert len(widget.actions()) == 1
qaction = widget.actions()[0]
assert qaction.text() == '&Foo'
assert qaction.isEnabled() is True
assert qaction.isChecked() is True
@patch.object(FooWidget, 'on_foo')
@patch.object(FooWidget, 'settings')
@patch('PyQt6.QtGui.QAction.toggled')
@patch('beeref.actions.mixin.menu_structure',
[{'menu': 'Foo', 'items': ['foo']}])
@patch('beeref.actions.mixin.actions',
ActionList([Action(
id='foo',
text='&Foo',
checkable=True,
settings='foo/bar',
callback='on_foo',
)]))
def test_create_actions_checkable_with_settings(
toggle_mock, settings_mock, callback_mock, qapp):
widget = FooWidget()
settings_mock.value.return_value = True
widget.build_menu_and_actions()
settings_mock.value.assert_called_once_with(
'foo/bar', False, type=bool)
qaction = widget.actions()[0]
assert qaction.isChecked() is True
assert toggle_mock.connect.call_count == 2
callback_mock.assert_called_once_with(True)
@patch('beeref.actions.mixin.menu_structure',
[{'menu': 'Foo', 'items': ['foo']}])
@patch('beeref.actions.mixin.actions',
ActionList([Action(
id='foo',
text='&Foo',
callback='on_foo',
group='bar',
)]))
def test_create_actions_with_group(qapp):
widget = FooWidget()
widget.build_menu_and_actions()
assert len(widget.actions()) == 1
qaction = widget.actions()[0]
assert widget.bee_actiongroups['bar'] == [qaction]
@patch('beeref.actions.mixin.menu_structure',
[{'menu': 'Foo', 'items': ['foo']}])
@patch('beeref.actions.mixin.actions',
ActionList([Action(
id='foo',
text='&Foo',
callback='on_foo',
)]))
def test_build_menu_and_actions_with_actions(qapp):
widget = FooWidget()
with patch('PyQt6.QtWidgets.QMenu.addAction') as add_mock:
widget.build_menu_and_actions()
assert isinstance(widget.context_menu, QtWidgets.QMenu)
from beeref.actions.mixin import actions
add_mock.assert_called_once_with(actions['foo'].qaction)
@patch('beeref.actions.mixin.menu_structure',
[{'menu': 'Foo', 'items': [MENU_SEPARATOR]}])
@patch('beeref.actions.mixin.actions', ActionList([]))
def test_build_menu_and_actions_with_separator(qapp):
widget = FooWidget()
with patch('PyQt6.QtWidgets.QMenu.addSeparator') as sep_mock:
widget.build_menu_and_actions()
assert isinstance(widget.context_menu, QtWidgets.QMenu)
sep_mock.assert_called_once_with()
@patch('beeref.actions.mixin.menu_structure',
[{'menu': 'Foo', 'items': [{'menu': 'Bar', 'items': ['foo']}]}])
@patch('beeref.actions.mixin.actions',
ActionList([Action(
id='foo',
text='&Foo',
callback='on_foo',
)]))
def test_build_menu_and_actions_with_submenu(qapp):
widget = FooWidget()
with patch('PyQt6.QtWidgets.QMenu.addAction') as add_mock:
with patch('PyQt6.QtWidgets.QMenu.addMenu') as addmenu_mock:
addmenu_mock.return_value = QtWidgets.QMenu()
widget.build_menu_and_actions()
assert isinstance(widget.context_menu, QtWidgets.QMenu)
addmenu_mock.assert_called_with('Bar')
from beeref.actions.mixin import actions
add_mock.assert_called_once_with(actions['foo'].qaction)
@patch('beeref.actions.mixin.menu_structure',
[{'menu': 'Foo', 'items': ['foo']}])
@patch('beeref.actions.mixin.actions',
ActionList([
Action(
id='foo',
text='&Foo',
callback='on_foo',
group='g1',
),
Action(
id='bar',
text='&Bar',
callback='on_foo',
group='g2',
),
]))
def test_actiongroup_set_enabled(qapp):
widget = FooWidget()
widget.build_menu_and_actions()
widget.actiongroup_set_enabled('g1', True)
from beeref.actions.mixin import actions
assert actions['foo'].qaction.isEnabled() is True
assert actions['bar'].qaction.isEnabled() is False
@patch('beeref.actions.mixin.menu_structure',
[{'menu': 'Foo', 'items': ['foo']}])
@patch('beeref.actions.mixin.actions',
ActionList([Action(
id='foo',
text='&Foo',
callback='on_foo',
group='active_when_selection',
)]))
def test_build_menu_and_actions_disables_actiongroups(qapp):
widget = FooWidget()
widget.scene.has_selection.return_value = False
widget.build_menu_and_actions()
qaction = widget.actions()[0]
assert qaction.isEnabled() is False
@patch('PyQt6.QtGui.QAction.triggered')
@patch('beeref.config.KeyboardSettings.get_list')
@patch('beeref.actions.mixin.menu_structure',
[{'menu': 'Foo', 'items': '_build_recent_files'}])
@patch('beeref.actions.mixin.actions', ActionList([]))
def test_create_recent_files_more_than_10_files(
kb_mock, triggered_mock, qapp):
kb_mock.side_effect = lambda group, key, default: default
widget = FooWidget()
widget.settings.get_recent_files.return_value = [
os.path.abspath(f'{i}.bee') for i in range(15)]
widget.build_menu_and_actions()
triggered_mock.connect.assert_called()
assert len(widget.actions()) == 10
from beeref.actions.mixin import actions
qaction1 = widget.actions()[0]
assert qaction1.text() == '0.bee'
assert qaction1.shortcut() == 'Ctrl+1'
assert qaction1.isEnabled() is True
assert actions['recent_files_0'].qaction == qaction1
qaction10 = widget.actions()[9]
assert qaction10.text() == '9.bee'
assert qaction10.shortcut() == 'Ctrl+0'
assert qaction10.isEnabled() is True
assert actions['recent_files_9'].qaction == qaction10
assert kb_mock.call_count == 10
kb_mock.assert_has_calls(
[call('Actions', 'recent_files_0', ['Ctrl+1']),
call('Actions', 'recent_files_9', ['Ctrl+0'])],
any_order=True)
@patch('PyQt6.QtGui.QAction.triggered')
@patch('beeref.actions.mixin.menu_structure',
[{'menu': 'Foo', 'items': '_build_recent_files'}])
@patch('beeref.actions.mixin.actions', ActionList([]))
@patch('beeref.config.KeyboardSettings.get_list')
def test_create_recent_files_fewer_files_than_10_files(
kb_mock, triggered_mock, qapp):
kb_mock.side_effect = lambda group, key, default: default
widget = FooWidget()
widget.settings.get_recent_files.return_value = [
os.path.abspath(f'{i}.bee') for i in range(5)]
widget.build_menu_and_actions()
triggered_mock.connect.assert_called()
assert len(widget.actions()) == 5
from beeref.actions.mixin import actions
qaction1 = widget.actions()[0]
assert qaction1.text() == '0.bee'
assert qaction1.shortcut() == 'Ctrl+1'
assert qaction1.isEnabled() is True
assert actions['recent_files_0'].qaction == qaction1
qaction5 = widget.actions()[4]
assert qaction5.text() == '4.bee'
assert qaction5.shortcut() == 'Ctrl+5'
assert qaction5.isEnabled() is True
assert actions['recent_files_4'].qaction == qaction5
assert actions['recent_files_5'].qaction is None
assert kb_mock.call_count == 5
kb_mock.assert_has_calls(
[call('Actions', 'recent_files_0', ['Ctrl+1']),
call('Actions', 'recent_files_4', ['Ctrl+5'])],
any_order=True)
@patch('beeref.actions.mixin.menu_structure',
[{'menu': 'Foo', 'items': '_build_recent_files'}])
@patch('beeref.actions.mixin.actions', ActionList([]))
@patch('beeref.config.KeyboardSettings.get_list')
def test_create_recent_files_when_no_files(kb_mock, qapp):
kb_mock.side_effect = lambda group, key, default: default
widget = FooWidget()
widget.settings.get_recent_files.return_value = []
widget.build_menu_and_actions()
assert len(widget.actions()) == 0
kb_mock.assert_not_called()
@patch('PyQt6.QtGui.QAction.triggered')
@patch('beeref.actions.mixin.menu_structure',
[{'menu': 'Foo', 'items': '_build_recent_files'}])
@patch('beeref.actions.mixin.actions', ActionList([]))
def test_update_recent_files(triggered_mock, qapp):
widget = FooWidget()
widget.settings.get_recent_files.return_value = [
os.path.abspath('foo.bee')]
widget.build_menu_and_actions()
triggered_mock.connect.reset_mock()
assert len(widget.actions()) == 1
qaction1 = widget.actions()[0]
assert qaction1.text() == 'foo.bee'
widget.settings.get_recent_files.return_value = [
os.path.abspath('bar.bee')]
widget.update_menu_and_actions()
triggered_mock.connect.assert_called()
assert len(widget.actions()) == 1
qaction1 = widget.actions()[0]
assert qaction1.text() == 'bar.bee'
def test_create_menubar(qapp):
widget = FooWidget()
widget.toplevel_menus = [QtWidgets.QMenu('Foo')]
menubar = widget.create_menubar()
assert isinstance(menubar, QtWidgets.QMenuBar)
assert len(menubar.actions()) == 1