casact / FASLR

Free Actuarial System for Loss Reserving
https://faslr.com
GNU General Public License v3.0
33 stars 11 forks source link

Unable to invoke context menu using pytest-qt QtBot #96

Closed genedan closed 1 year ago

genedan commented 1 year ago

Right-clicking a cell in a table should invoke a context menu:

image

I'm having trouble activating this using QtBot from the pytest-qt package for unit testing. This seems to be a known issue stemming from an unresolved bug from 2016, see the following discussions:

https://github.com/pytest-dev/pytest-qt/issues/208 https://github.com/pytest-dev/pytest-qt/issues/269 https://bugreports.qt.io/browse/QTBUG-52552

One way to get around this is to invoke the context menu programmatically, but I'm having some trouble since the menu won't close after doing so. I would think one way to do this is to create a separate thread that executes the ESC button after a split second or so. My attempts at doing this are here:

https://github.com/casact/FASLR/blob/f4f8e23a4d75a402ee8c4e5391b98b0afc960703/faslr/tests/test_triangle_model.py#L206

genedan commented 1 year ago

I was able to activate the menu and then simulate the keystrokes by writing a handler and using the pynput package. No separate thread is needed. The following handler creates a reference to the active menu and then we hit the escape key by using pynput:

def test_context_menu(
        triangle_model: TriangleModel,
        qtbot: QtBot
) -> None:
    """
    Test the invocation and closing of the triangle view context menu. 

    :param triangle_model: A TriangleModel object.
    :param qtbot: The QtBot fixture.
    :return: None 
    """

    def handle_menu() -> None:
        """
        Simulates closing the menu. 
        :return: None
        """

        keyboard = Controller()

        # Refers to the active context menu.
        menu = QApplication.activePopupWidget()
        qtbot.addWidget(menu)

        # Simulate the escape key to exit the menu. 
        keyboard.press(Key.esc)
        keyboard.release(Key.esc)

    triangle_view = TriangleView()
    qtbot.addWidget(triangle_view)
    triangle_view.setModel(triangle_model)
    triangle_view.show()
    qtbot.wait_for_window_shown(triangle_view)

    QTimer.singleShot(
        1000,
        handle_menu
    )

    # Use an arbitrary position on the widget, any will do just to invoke the context menu.
    position = QPoint(
        0,
        0
    )

    triangle_view.customContextMenuRequested.emit(position) # noqa

I'll leave the previous attempt commented out. Perhaps we can switch back to it if the bug is ever fixed. pynput has been used on several tests now so I'll be adding it as a dependency.