jupyter-widgets / ipywidgets

Interactive Widgets for the Jupyter Notebook
https://ipywidgets.readthedocs.io
BSD 3-Clause "New" or "Revised" License
3.11k stars 947 forks source link

Automated testing of widgets #2934

Open jankrepl opened 3 years ago

jankrepl commented 3 years ago

First of all, great job on the project!

I wonder if there are some ways how to test widgets in an automated fashion? Something like pytest-qt for QT applications.

To give an example, let's say I have a TextArea widget, a Button widget and an Output widget. The Output one just displays what one writes into the TextArea as soon as one presses the button. Is there a way how to test it programmatically without even running them in jupyter notebooks?

Thanks for your response.

EDIT I stumbled upon a similar issue #1814. However, I wonder whether things have changed over the past 3 years.

aloctavodia commented 1 year ago

Hi @jankrepl, did you find an answer to this?

maartenbreddels commented 8 months ago

Solara has a pytest plugin that makes this easier, it's documented here: https://solara.dev/docs/howto/testing

The advantage of using the solara_test fixture is that it is quite fast since it doesn't require a kernel running in a separate process (we run virtual kernels). The other advantage is that the virtual kernel runs in the same process, so you can inspect if callbacks are called.

Hope that helps, and we should maybe add this to the ipywidgets documentation since we see this request coming up more often.

maartenbreddels commented 2 months ago

An update to this: We split off the package from solara and it is now called pytest-ipywidgets, which allows you to test with or without a browser (playwright).

A basic example is:

import ipywidgets as widgets
import playwright.sync_api
from IPython.display import display

def test_widget_button_solara(solara_test, page_session: playwright.sync_api.Page):
    # The test code runs in the same process as solara-server (which runs in a separate thread)
    # Note: this test uses ipywidgets directly, not solara components.
    button = widgets.Button(description="Click Me!")

    def change_description(obj):
        button.description = "Tested event"

    button.on_click(change_description)
    display(button)
    page_session.locator("text=Click Me!").click()
    page_session.locator("text=Tested event").wait_for()