Files
cthulhu/tests/test_preferences_grid_base.py

983 lines
33 KiB
Python

# Unit tests for preferences_grid_base.py.
#
# Copyright 2026 Igalia, S.L.
# Author: Joanmarie Diggs <jdiggs@igalia.com>
#
# This library is free software; you can redistribute it and/or
# modify it under the terms of the GNU Lesser General Public
# License as published by the Free Software Foundation; either
# version 2.1 of the License, or (at your option) any later version.
#
# This library is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
# Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public
# License along with this library; if not, write to the
# Free Software Foundation, Inc., Franklin Street, Fifth Floor,
# Boston MA 02110-1301 USA.
# pylint: disable=wrong-import-position
# pylint: disable=protected-access
# pylint: disable=import-outside-toplevel
"""Unit tests for preferences_grid_base.py."""
from __future__ import annotations
from typing import Any
import gi
import pytest
gi.require_version("Gtk", "3.0")
from gi.repository import Gtk
from cthulhu.preferences_grid_base import (
AutoPreferencesGrid,
BooleanPreferenceControl,
CategoryListBoxRow,
CommandListBoxRow,
ControlType,
EnumPreferenceControl,
FloatRangePreferenceControl,
FocusManagedListBox,
IntRangePreferenceControl,
PreferencesGridBase,
RadioButtonWithActions,
SelectionPreferenceControl,
StackedPreferencesHelper,
)
@pytest.mark.unit
class TestPreferenceControlDataclasses:
"""Test preference control dataclass creation and properties."""
def test_boolean_preference_control_creation(self) -> None:
"""Test BooleanPreferenceControl can be created with required fields."""
getter_called: list[bool] = [False]
setter_value: list[bool | None] = [None]
def getter() -> bool:
getter_called[0] = True
return True
def setter(value: bool) -> bool:
setter_value[0] = value
return True
control = BooleanPreferenceControl(
label="Test Label",
getter=getter,
setter=setter,
prefs_key="testKey",
)
assert control.label == "Test Label"
assert control.prefs_key == "testKey"
assert control.getter() is True
assert getter_called[0] is True
control.setter(False)
assert setter_value[0] is False
def test_boolean_preference_control_optional_fields(self) -> None:
"""Test BooleanPreferenceControl optional fields have correct defaults."""
control = BooleanPreferenceControl(label="Test", getter=lambda: True, setter=lambda x: True)
assert control.prefs_key is None
assert control.member_of is None
assert control.determine_sensitivity is None
assert control.apply_immediately is True
def test_int_range_preference_control_creation(self) -> None:
"""Test IntRangePreferenceControl can be created with all fields."""
control = IntRangePreferenceControl(
label="Volume",
minimum=0,
maximum=100,
getter=lambda: 50,
setter=lambda x: True,
prefs_key="volume",
)
assert control.label == "Volume"
assert control.minimum == 0
assert control.maximum == 100
assert control.getter() == 50
def test_float_range_preference_control_creation(self) -> None:
"""Test FloatRangePreferenceControl can be created with all fields."""
control = FloatRangePreferenceControl(
label="Rate",
minimum=0.0,
maximum=1.0,
getter=lambda: 0.5,
setter=lambda x: True,
)
assert control.label == "Rate"
assert control.minimum == 0.0
assert control.maximum == 1.0
assert control.getter() == 0.5
def test_enum_preference_control_creation(self) -> None:
"""Test EnumPreferenceControl can be created with options and values."""
control = EnumPreferenceControl(
label="Style",
options=["Option A", "Option B", "Option C"],
values=[1, 2, 3],
getter=lambda: 2,
setter=lambda x: True,
)
assert control.label == "Style"
assert len(control.options) == 3
assert control.values == [1, 2, 3]
assert control.getter() == 2
def test_selection_preference_control_creation(self) -> None:
"""Test SelectionPreferenceControl can be created with actions callback."""
def get_actions(_value: Any) -> list[tuple[str, str, Any]]:
return [("Edit", "edit-symbolic", lambda: None)]
control = SelectionPreferenceControl(
label="Profile",
options=["Default", "Custom"],
getter=lambda: "Default",
setter=lambda x: True,
get_actions_for_option=get_actions,
)
assert control.label == "Profile"
assert len(control.options) == 2
assert control.get_actions_for_option is not None
actions = control.get_actions_for_option("Default")
assert len(actions) == 1
assert actions[0][0] == "Edit"
@pytest.mark.unit
class TestHelperClasses:
"""Test helper classes in preferences_grid_base."""
def test_category_list_box_row_stores_category(self) -> None:
"""Test CategoryListBoxRow stores and returns category identifier."""
row = CategoryListBoxRow("speech_settings")
assert row.category == "speech_settings"
def test_category_list_box_row_is_gtk_list_box_row(self) -> None:
"""Test CategoryListBoxRow inherits from Gtk.ListBoxRow."""
row = CategoryListBoxRow("test")
assert isinstance(row, Gtk.ListBoxRow)
def test_command_list_box_row_properties(self) -> None:
"""Test CommandListBoxRow property getters and setters."""
row = CommandListBoxRow()
assert row.command is None
assert row.vbox is None
assert row.binding_label is None
mock_command = object()
mock_vbox = Gtk.Box()
mock_label = Gtk.Label()
row.command = mock_command
row.vbox = mock_vbox
row.binding_label = mock_label
assert row.command is mock_command
assert row.vbox is mock_vbox
assert row.binding_label is mock_label
def test_radio_button_with_actions_stores_buttons(self) -> None:
"""Test RadioButtonWithActions stores action buttons list."""
radio = RadioButtonWithActions(label="Test Option")
assert not radio.action_buttons
button1 = Gtk.Button()
button2 = Gtk.Button()
radio.action_buttons = [button1, button2]
assert len(radio.action_buttons) == 2
assert radio.action_buttons[0] is button1
def test_radio_button_with_actions_is_gtk_radio_button(self) -> None:
"""Test RadioButtonWithActions inherits from Gtk.RadioButton."""
radio = RadioButtonWithActions(label="Test")
assert isinstance(radio, Gtk.RadioButton)
def test_stacked_preferences_helper_initial_state(self) -> None:
"""Test StackedPreferencesHelper initializes with None values."""
helper = StackedPreferencesHelper()
assert helper.stack is None
assert helper.categories_listbox is None
assert helper.detail_listbox is None
assert not helper.disable_widgets
assert helper.on_category_activated_callback is None
def test_stacked_preferences_helper_register_widgets(self) -> None:
"""Test StackedPreferencesHelper can register widgets to disable."""
helper = StackedPreferencesHelper()
button1 = Gtk.Button()
button2 = Gtk.Button()
helper.register_disable_widgets(button1, button2)
assert len(helper.disable_widgets) == 2
assert button1 in helper.disable_widgets
def test_stacked_preferences_helper_show_categories(self) -> None:
"""Test StackedPreferencesHelper show_categories enables registered widgets."""
helper = StackedPreferencesHelper()
helper.stack = Gtk.Stack()
cat_label = Gtk.Label(label="Categories")
det_label = Gtk.Label(label="Detail")
helper.stack.add_named(cat_label, "categories")
helper.stack.add_named(det_label, "detail")
# Show the stack to ensure children are realized
helper.stack.show_all() # pylint: disable=no-member
helper.stack.set_visible_child_name("detail")
button = Gtk.Button()
button.set_sensitive(False)
helper.register_disable_widgets(button)
helper.show_categories()
assert button.get_sensitive() is True
# Verify the visible child is the categories child
assert helper.stack.get_visible_child() is cat_label
def test_stacked_preferences_helper_show_detail(self) -> None:
"""Test StackedPreferencesHelper show_detail disables registered widgets."""
helper = StackedPreferencesHelper()
helper.stack = Gtk.Stack()
cat_label = Gtk.Label(label="Categories")
det_label = Gtk.Label(label="Detail")
helper.stack.add_named(cat_label, "categories")
helper.stack.add_named(det_label, "detail")
# Show the stack to ensure children are realized
helper.stack.show_all() # pylint: disable=no-member
helper.stack.set_visible_child_name("categories")
button = Gtk.Button()
button.set_sensitive(True)
helper.register_disable_widgets(button)
helper.show_detail()
assert button.get_sensitive() is False
# Verify the visible child is the detail child
assert helper.stack.get_visible_child() is det_label
@pytest.mark.unit
class TestFocusManagedListBox:
"""Test FocusManagedListBox focus management."""
def test_focus_managed_listbox_initial_state(self) -> None:
"""Test FocusManagedListBox initializes with correct settings."""
listbox = FocusManagedListBox()
assert listbox.get_selection_mode() == Gtk.SelectionMode.NONE
assert listbox.get_can_focus() is False
def test_focus_managed_listbox_add_row_with_widget(self) -> None:
"""Test FocusManagedListBox tracks added rows and widgets."""
listbox = FocusManagedListBox()
row = Gtk.ListBoxRow()
switch = Gtk.Switch()
listbox.add_row_with_widget(row, switch)
assert len(listbox._widgets) == 1
assert len(listbox._rows) == 1
assert listbox._widgets[0] is switch
assert listbox._rows[0] is row
def test_focus_managed_listbox_get_last_row(self) -> None:
"""Test FocusManagedListBox.get_last_row returns last added row."""
listbox = FocusManagedListBox()
assert listbox.get_last_row() is None
row1 = Gtk.ListBoxRow()
row2 = Gtk.ListBoxRow()
listbox.add_row_with_widget(row1, Gtk.Switch())
listbox.add_row_with_widget(row2, Gtk.Switch())
assert listbox.get_last_row() is row2
def test_focus_managed_listbox_multiple_rows(self) -> None:
"""Test FocusManagedListBox handles multiple rows correctly."""
listbox = FocusManagedListBox()
for _i in range(5):
row = Gtk.ListBoxRow()
switch = Gtk.Switch()
listbox.add_row_with_widget(row, switch)
assert len(listbox._widgets) == 5
assert len(listbox._rows) == 5
@pytest.mark.unit
class TestPreferencesGridBase:
"""Test PreferencesGridBase UI helper methods."""
def test_preferences_grid_base_initialization(self) -> None:
"""Test PreferencesGridBase initializes with correct properties."""
grid = PreferencesGridBase("Test Tab")
assert grid._tab_label == "Test Tab"
assert grid._has_unsaved_changes is False
assert grid.get_border_width() == 24 # pylint: disable=no-member
def test_preferences_grid_base_get_label(self) -> None:
"""Test PreferencesGridBase.get_label returns Gtk.Label."""
grid = PreferencesGridBase("My Tab")
label = grid.get_label()
assert isinstance(label, Gtk.Label)
assert label.get_text() == "My Tab"
def test_preferences_grid_base_has_changes(self) -> None:
"""Test PreferencesGridBase.has_changes tracks unsaved changes."""
grid = PreferencesGridBase("Test")
assert grid.has_changes() is False
grid._has_unsaved_changes = True
assert grid.has_changes() is True
def test_create_frame_returns_frame_and_grid(self) -> None:
"""Test _create_frame returns Gtk.Frame and content Gtk.Grid."""
grid = PreferencesGridBase("Test")
frame, content = grid._create_frame("Section Label")
assert isinstance(frame, Gtk.Frame)
assert isinstance(content, Gtk.Grid)
assert frame.get_child() is content
def test_create_label_with_mnemonic(self) -> None:
"""Test _create_label creates label with mnemonic support."""
grid = PreferencesGridBase("Test")
label = grid._create_label("_Test Label")
assert isinstance(label, Gtk.Label)
# Label should use underline for mnemonic
assert label.get_use_underline() is True
def test_create_switch_row(self) -> None:
"""Test _create_switch_row creates row with label and switch."""
grid = PreferencesGridBase("Test")
handler_called = [False]
def handler(_switch, _param):
handler_called[0] = True
row, switch, label = grid._create_switch_row("Enable Feature", handler, state=True)
assert isinstance(row, Gtk.ListBoxRow)
assert isinstance(switch, Gtk.Switch)
assert isinstance(label, Gtk.Label)
assert switch.get_active() is True
def test_create_slider_row(self) -> None:
"""Test _create_slider_row creates row with label and scale."""
grid = PreferencesGridBase("Test")
adjustment = Gtk.Adjustment(
value=50,
lower=0,
upper=100,
step_increment=1,
page_increment=10,
)
row, scale, label = grid._create_slider_row("Volume", adjustment)
assert isinstance(row, Gtk.ListBoxRow)
assert isinstance(scale, Gtk.Scale)
assert isinstance(label, Gtk.Label)
assert scale.get_value() == 50
def test_create_info_listbox(self) -> None:
"""Test _create_info_listbox creates listbox with info message."""
grid = PreferencesGridBase("Test")
listbox = grid._create_info_listbox("This is an informational message.")
assert isinstance(listbox, Gtk.ListBox)
assert listbox.get_selection_mode() == Gtk.SelectionMode.NONE
def test_create_scrolled_window(self) -> None:
"""Test _create_scrolled_window wraps widget in scrolled container."""
grid = PreferencesGridBase("Test")
label = Gtk.Label(label="Content")
scrolled = grid._create_scrolled_window(label)
assert isinstance(scrolled, Gtk.ScrolledWindow)
assert scrolled.get_hexpand() is True
assert scrolled.get_vexpand() is True
@pytest.mark.unit
class TestAutoPreferencesGrid:
"""Test AutoPreferencesGrid automatic UI building."""
def _create_test_grid(self) -> tuple:
"""Create a test grid with various controls for testing."""
values: dict[str, Any] = {
"bool_setting": True,
"int_setting": 5,
"float_setting": 0.5,
"enum_setting": "Option B",
}
def set_bool(x: bool) -> bool:
values["bool_setting"] = x
return True
def set_int(x: int) -> bool:
values["int_setting"] = x
return True
def set_float(x: float) -> bool:
values["float_setting"] = x
return True
def set_enum(x: str) -> bool:
values["enum_setting"] = x
return True
controls: list[ControlType] = [
BooleanPreferenceControl(
label="Enable Feature",
getter=lambda: values["bool_setting"],
setter=set_bool,
prefs_key="boolSetting",
),
IntRangePreferenceControl(
label="Count",
minimum=0,
maximum=10,
getter=lambda: values["int_setting"],
setter=set_int,
prefs_key="intSetting",
),
FloatRangePreferenceControl(
label="Rate",
minimum=0.0,
maximum=1.0,
getter=lambda: values["float_setting"],
setter=set_float,
prefs_key="floatSetting",
),
EnumPreferenceControl(
label="Style",
options=["Option A", "Option B", "Option C"],
getter=lambda: values["enum_setting"],
setter=set_enum,
prefs_key="enumSetting",
),
]
grid = AutoPreferencesGrid("Test Tab", controls)
return grid, values, controls
def test_auto_preferences_grid_builds_widgets(self) -> None:
"""Test AutoPreferencesGrid creates widgets for all controls."""
grid, _values, controls = self._create_test_grid()
assert len(grid._widgets) == len(controls)
assert len(grid._controls) == len(controls)
def test_auto_preferences_grid_boolean_control_creates_switch(self) -> None:
"""Test AutoPreferencesGrid creates Switch for boolean control."""
grid, _values, _controls = self._create_test_grid()
# First control is boolean, should create a Switch
widget = grid.get_widget(0)
assert isinstance(widget, Gtk.Switch)
assert widget.get_active() is True
def test_auto_preferences_grid_int_range_creates_spinbutton(self) -> None:
"""Test AutoPreferencesGrid creates SpinButton for int range control."""
grid, _values, _controls = self._create_test_grid()
widget = grid.get_widget(1)
assert isinstance(widget, Gtk.SpinButton)
assert int(widget.get_value()) == 5
def test_auto_preferences_grid_float_range_creates_scale(self) -> None:
"""Test AutoPreferencesGrid creates Scale for float range control."""
grid, _values, _controls = self._create_test_grid()
widget = grid.get_widget(2)
assert isinstance(widget, Gtk.Scale)
assert widget.get_value() == pytest.approx(0.5, abs=0.01)
def test_auto_preferences_grid_enum_creates_combobox(self) -> None:
"""Test AutoPreferencesGrid creates ComboBoxText for enum control."""
grid, _values, _controls = self._create_test_grid()
widget = grid.get_widget(3)
assert isinstance(widget, Gtk.ComboBoxText)
# Option B is at index 1
assert widget.get_active() == 1
def test_auto_preferences_grid_refresh_updates_widgets(self) -> None:
"""Test AutoPreferencesGrid.refresh updates widget values from getters."""
current_value = [True]
controls = [
BooleanPreferenceControl(
label="Test",
getter=lambda: current_value[0],
setter=lambda x: True,
),
]
grid = AutoPreferencesGrid("Test", controls)
switch = grid.get_widget(0)
assert switch and switch.get_active()
current_value[0] = False
grid.refresh()
assert switch and not switch.get_active()
def test_auto_preferences_grid_save_settings_calls_setters(self) -> None:
"""Test AutoPreferencesGrid.save_settings calls all setters."""
grid, values, _controls = self._create_test_grid()
switch = grid.get_widget(0)
switch.set_active(False)
spin = grid.get_widget(1)
spin.set_value(8)
result = grid.save_settings()
assert values["bool_setting"] is False
assert values["int_setting"] == 8
assert "boolSetting" in result
assert result["boolSetting"] is False
def test_auto_preferences_grid_save_returns_prefs_dict(self) -> None:
"""Test AutoPreferencesGrid.save_settings returns dict with prefs_keys."""
grid, _values, _controls = self._create_test_grid()
result = grid.save_settings()
assert "boolSetting" in result
assert "intSetting" in result
assert "floatSetting" in result
assert "enumSetting" in result
def test_auto_preferences_grid_reload_clears_changes_flag(self) -> None:
"""Test AutoPreferencesGrid.reload clears unsaved changes flag."""
grid, _values, _controls = self._create_test_grid()
grid._has_unsaved_changes = True
grid.reload()
assert grid._has_unsaved_changes is False
def test_auto_preferences_grid_get_widget_returns_none_for_invalid_index(self) -> None:
"""Test AutoPreferencesGrid.get_widget returns None for out-of-range index."""
grid, _values, controls = self._create_test_grid()
assert grid.get_widget(-1) is None
assert grid.get_widget(len(controls)) is None
assert grid.get_widget(100) is None
def test_auto_preferences_grid_get_widget_for_control(self) -> None:
"""Test AutoPreferencesGrid.get_widget_for_control finds correct widget."""
grid, _values, controls = self._create_test_grid()
widget = grid.get_widget_for_control(controls[0])
assert widget is not None
assert isinstance(widget, Gtk.Switch)
# Non-existent control should return None
other_control = BooleanPreferenceControl(
label="Other",
getter=lambda: True,
setter=lambda x: True,
)
assert grid.get_widget_for_control(other_control) is None
def test_auto_preferences_grid_with_grouped_controls(self) -> None:
"""Test AutoPreferencesGrid groups controls by member_of field."""
controls = [
BooleanPreferenceControl(label="Ungrouped", getter=lambda: True, setter=lambda x: True),
BooleanPreferenceControl(
label="Group A Item 1",
getter=lambda: True,
setter=lambda x: True,
member_of="Group A",
),
BooleanPreferenceControl(
label="Group A Item 2",
getter=lambda: False,
setter=lambda x: True,
member_of="Group A",
),
BooleanPreferenceControl(
label="Group B Item",
getter=lambda: True,
setter=lambda x: True,
member_of="Group B",
),
]
grid = AutoPreferencesGrid("Test", controls)
assert "Group A" in grid._group_labels
assert "Group B" in grid._group_labels
assert len(grid._widgets) == 4
def test_auto_preferences_grid_sensitivity_callback(self) -> None:
"""Test AutoPreferencesGrid updates sensitivity based on callback."""
primary_enabled = [True]
def set_primary(x: bool) -> bool:
primary_enabled[0] = x
return True
controls = [
BooleanPreferenceControl(
label="Primary",
getter=lambda: primary_enabled[0],
setter=set_primary,
apply_immediately=True,
),
BooleanPreferenceControl(
label="Dependent",
getter=lambda: True,
setter=lambda x: True,
determine_sensitivity=lambda: primary_enabled[0],
),
]
grid = AutoPreferencesGrid("Test", controls)
primary_switch = grid.get_widget(0)
dependent_switch = grid.get_widget(1)
assert dependent_switch and dependent_switch.get_sensitive()
grid._initializing = False # Allow change handlers to run
assert primary_switch
primary_switch.set_active(False)
assert dependent_switch and not dependent_switch.get_sensitive()
def test_auto_preferences_grid_with_info_message(self) -> None:
"""Test AutoPreferencesGrid displays info message when provided."""
controls = [
BooleanPreferenceControl(label="Test", getter=lambda: True, setter=lambda x: True),
]
grid = AutoPreferencesGrid("Test", controls, info_message="This is helpful information.")
assert grid._info_listbox is not None
@pytest.mark.unit
class TestAutoPreferencesGridSelectionControl:
"""Test AutoPreferencesGrid with SelectionPreferenceControl."""
def test_selection_control_creates_combobox_without_actions(self) -> None:
"""Test SelectionPreferenceControl without actions creates ComboBoxText."""
controls = [
SelectionPreferenceControl(
label="Choose",
options=["A", "B", "C"],
getter=lambda: "B",
setter=lambda x: True,
),
]
grid = AutoPreferencesGrid("Test", controls)
widget = grid.get_widget(0)
assert isinstance(widget, Gtk.ComboBoxText)
assert widget.get_active() == 1
def test_selection_control_creates_radio_buttons_with_actions(self) -> None:
"""Test SelectionPreferenceControl with actions creates RadioButtons."""
def get_actions(_value):
return [("Edit", "edit-symbolic", lambda: None)]
controls = [
SelectionPreferenceControl(
label="Profile",
options=["Default", "Custom"],
getter=lambda: "Default",
setter=lambda x: True,
get_actions_for_option=get_actions,
),
]
grid = AutoPreferencesGrid("Test", controls)
widget = grid.get_widget(0)
assert isinstance(widget, RadioButtonWithActions)
def test_selection_control_with_values_mapping(self) -> None:
"""Test SelectionPreferenceControl maps display options to values."""
current_value = [100]
def set_level(x: int) -> bool:
current_value[0] = x
return True
controls = [
SelectionPreferenceControl(
label="Level",
options=["Low", "Medium", "High"],
values=[50, 100, 150],
getter=lambda: current_value[0],
setter=set_level,
prefs_key="level",
),
]
grid = AutoPreferencesGrid("Test", controls)
widget = grid.get_widget(0)
assert widget and widget.get_active() == 1
widget.set_active(2)
result = grid.save_settings()
assert current_value[0] == 150
assert result["level"] == 150
@pytest.mark.unit
class TestPreferencesGridBaseMultiPageStack:
"""Test PreferencesGridBase multi-page stack functionality."""
def test_create_multi_page_stack_basic(self) -> None:
"""Test _create_multi_page_stack creates stack with categories."""
class TestGrid(PreferencesGridBase):
"""Test grid."""
def __init__(self):
super().__init__("Test")
self._initializing = True
child1 = PreferencesGridBase("Child 1")
child2 = PreferencesGridBase("Child 2")
enable_listbox, stack, categories = self._create_multi_page_stack(
enable_label="Enable",
enable_getter=lambda: True,
enable_setter=lambda x: True,
categories=[
("General", "general", child1),
("Advanced", "advanced", child2),
],
main_title="Test Settings",
)
self.my_enable_listbox = enable_listbox
self.my_stack = stack
self.my_categories = categories
self._initializing = False
grid = TestGrid()
assert grid.my_stack is not None
assert grid.my_categories is not None
assert grid.my_enable_listbox is not None
def test_create_multi_page_stack_without_enable(self) -> None:
"""Test _create_multi_page_stack without enable switch."""
class TestGrid(PreferencesGridBase):
"""Test grid."""
def __init__(self):
super().__init__("Test")
self._initializing = True
child = PreferencesGridBase("Child")
enable_listbox, stack, _categories = self._create_multi_page_stack(
enable_label=None,
enable_getter=None,
enable_setter=None,
categories=[("General", "general", child)],
main_title="Test",
)
self.my_enable_listbox = enable_listbox
self.my_stack = stack
self._initializing = False
grid = TestGrid()
assert grid.my_enable_listbox is None
assert grid.my_stack is not None
@pytest.mark.unit
class TestPreferencesGridBaseStackedPreferences:
"""Test PreferencesGridBase stacked drill-down preferences."""
def test_create_stacked_preferences(self) -> None:
"""Test _create_stacked_preferences creates stack with categories and detail."""
class TestGrid(PreferencesGridBase):
"""Test grid."""
def __init__(self):
super().__init__("Test")
self.activated_row = None
stack, categories, detail = self._create_stacked_preferences(
on_category_activated=self._on_category,
on_detail_row_activated=None,
)
self.my_stack = stack
self.my_categories = categories
self.my_detail = detail
def _on_category(self, row):
self.activated_row = row
grid = TestGrid()
assert grid.my_stack is not None
assert grid.my_categories is not None
assert grid.my_detail is not None
assert isinstance(grid.my_stack, Gtk.Stack)
assert isinstance(grid.my_categories, Gtk.ListBox)
assert isinstance(grid.my_detail, Gtk.ListBox)
def test_add_stack_category_row(self) -> None:
"""Test _add_stack_category_row adds category with correct properties."""
class TestGrid(PreferencesGridBase):
"""Test grid."""
def __init__(self):
super().__init__("Test")
_stack, categories, _detail = self._create_stacked_preferences(
on_category_activated=lambda r: None,
)
self.my_categories = categories
grid = TestGrid()
row = grid._add_stack_category_row(grid.my_categories, "Speech Settings", category="speech")
assert isinstance(row, CategoryListBoxRow)
assert row.category == "speech"
def test_add_stack_category_row_chevron_accessible_role(self) -> None:
"""Test _add_stack_category_row chevron has PUSH_BUTTON accessible role."""
gi.require_version("Atk", "1.0")
from gi.repository import Atk
class TestGrid(PreferencesGridBase):
"""Test grid."""
def __init__(self):
super().__init__("Test")
_stack, categories, _detail = self._create_stacked_preferences(
on_category_activated=lambda r: None,
)
self.my_categories = categories
grid = TestGrid()
row = grid._add_stack_category_row(grid.my_categories, "Test Category", category="test")
# Find the chevron image in the row's hbox
hbox = row.get_child()
chevron = None
for child in hbox.get_children():
if isinstance(child, Gtk.Image):
chevron = child
break
assert chevron is not None, "Chevron image not found in category row"
chevron_accessible = chevron.get_accessible()
assert chevron_accessible is not None
assert chevron_accessible.get_role() == Atk.Role.BUTTON
assert chevron_accessible.get_name() == ""
def test_show_stack_categories_and_detail(self) -> None:
"""Test _show_stack_categories and _show_stack_detail switch views."""
class TestGrid(PreferencesGridBase):
"""Test grid."""
def __init__(self):
super().__init__("Test")
stack, categories, detail = self._create_stacked_preferences(
on_category_activated=lambda r: None,
)
self.my_stack = stack
self.my_categories = categories
self.my_detail = detail
grid = TestGrid()
grid.my_stack.show_all() # pylint: disable=no-member
cat_scrolled = grid.my_stack.get_child_by_name("categories")
det_scrolled = grid.my_stack.get_child_by_name("detail")
grid._show_stack_detail()
assert grid.my_stack.get_visible_child() is det_scrolled
grid._show_stack_categories()
assert grid.my_stack.get_visible_child() is cat_scrolled