From f8eafc6c07ba3fbda061f1b0e86591b3647683d6 Mon Sep 17 00:00:00 2001 From: Alexander Goscinski Date: Thu, 4 Jul 2024 19:19:59 +0200 Subject: [PATCH 1/3] Tests: Adding test for CodeExercise with ExerciseRegistry --- tests/test_code.py | 39 ++++++++++++++++++++++++++++++++++++--- 1 file changed, 36 insertions(+), 3 deletions(-) diff --git a/tests/test_code.py b/tests/test_code.py index e516f0f..68a6139 100644 --- a/tests/test_code.py +++ b/tests/test_code.py @@ -1,4 +1,5 @@ -from typing import Callable, List +import os +from typing import Callable, List, Optional import numpy as np import pytest @@ -8,7 +9,7 @@ from scwidgets.check import Check, CheckRegistry, ChecksResult from scwidgets.code import CodeInput from scwidgets.cue import CueObject -from scwidgets.exercise import CodeExercise +from scwidgets.exercise import CodeExercise, ExerciseRegistry from .test_check import multi_param_check, single_param_check @@ -76,7 +77,7 @@ def test_invalid_code_theme_raises_error(self): def get_code_exercise( checks: List[Check], - code: Callable = None, + code: Optional[Callable] = None, include_checks=True, include_params=True, tunable_params=False, @@ -221,3 +222,35 @@ def test_erroneous_run_code(self, code_ex): match="NameError in code input: name 'bug' is not defined.*", ): code_ex.run_code(**code_ex.parameters) + + @pytest.mark.parametrize( + "function", + [ + None, + TestCodeInput.mock_function_1, + ], + ) + def test_save_registry(self, function): + """ + Verifies that the CodeExercise works with an answer_registry. + """ + + def print_success(code_ex: CodeExercise | None): + code_ex.cue_outputs[0].display_object = "Success" + + cue_output = CueObject("Not initialized") + exercise_registry = ExerciseRegistry() + + code_ex = CodeExercise( + code=function, + parameters={"parameter": fixed(5)}, + exercise_registry=exercise_registry, + exercise_key="test_save_registry_ex", + cue_outputs=[cue_output], + update_func=print_success, + ) + + exercise_registry._student_name_text.value = "test_save_registry-student_name" + exercise_registry.create_new_file() + code_ex._save_button.click() + os.remove("test_save_registry-student_name.json") From 2d79d6d8ee6c080eaf01d259c4e81ad67c3cbdf4 Mon Sep 17 00:00:00 2001 From: Alexander Goscinski Date: Thu, 4 Jul 2024 19:21:17 +0200 Subject: [PATCH 2/3] Core: Fix code is None in CodeExercise with ExerciseRegistry When the code is None in the CodeExercise and a ExerciseRegistry is given, then an error is raised when the save cue box is created, because the cue_widget is None. Since the cue widgets are set later on, we use a dummy widget on initialization with is later replaced. --- .../exercise/_widget_code_exercise.py | 27 ++++++++++++------- 1 file changed, 17 insertions(+), 10 deletions(-) diff --git a/src/scwidgets/exercise/_widget_code_exercise.py b/src/scwidgets/exercise/_widget_code_exercise.py index 17283a0..0d15f36 100644 --- a/src/scwidgets/exercise/_widget_code_exercise.py +++ b/src/scwidgets/exercise/_widget_code_exercise.py @@ -327,21 +327,28 @@ def __init__( self._load_button = None self._save_cue_box = None else: - save_widgets_to_observe = [self._code] - save_traits_to_observe = ["function_body"] + save_widgets_to_observe = [] + save_traits_to_observe = [] + + if self._cue_code is not None: + save_widgets_to_observe.append(self._code) + save_traits_to_observe.append("function_body") + if self._parameter_panel is not None: save_widgets_to_observe.extend(self._parameter_panel.parameters_widget) save_traits_to_observe.extend(self._parameter_panel.parameters_trait) - self._cue_code = SaveCueBox( - save_widgets_to_observe, - save_traits_to_observe, - self._cue_code, - cued=True, - ) + + if self._cue_code is not None: + self._cue_code = SaveCueBox( + save_widgets_to_observe, + save_traits_to_observe, + self._cue_code, + cued=True, + ) self._save_cue_box = self._cue_code self._save_button = SaveResetCueButton( - self._cue_code, + SaveCueBox(Box()), # dummy cue box, because we set cues later on self._on_click_save_action, cued=True, disable_on_successful_action=kwargs.pop( @@ -354,7 +361,7 @@ def __init__( button_tooltip="Loads your code and parameters from the loaded file", ) self._load_button = SaveResetCueButton( - self._cue_code, + SaveCueBox(Box()), # dummy cue box, because we set cues later on self._on_click_load_action, cued=True, disable_on_successful_action=kwargs.pop( From cf8314384632e6594d962e34ea22cdac9bb13960 Mon Sep 17 00:00:00 2001 From: Alexander Goscinski Date: Thu, 4 Jul 2024 20:02:16 +0200 Subject: [PATCH 3/3] Tests: Increase pytest reruns from 2 to 5 --- tox.ini | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tox.ini b/tox.ini index 81a6c72..1ffb510 100644 --- a/tox.ini +++ b/tox.ini @@ -42,7 +42,7 @@ deps = commands = # converts the python files to ipython notebooks jupytext tests/notebooks/*.py --to ipynb - pytest {posargs:-v --reruns 2} --driver Firefox + pytest {posargs:-v --reruns 4} --driver Firefox [testenv:tests-lab-4] description = @@ -75,7 +75,7 @@ deps = commands = # converts the python files to ipython notebooks jupytext tests/notebooks/*.py --to ipynb - pytest {posargs:-v --reruns 2} -m "not matplotlib" --driver Firefox + pytest {posargs:-v --reruns 4} -m "not matplotlib" --driver Firefox [testenv:coverage] # We do coverage in a separate environment that skips the selenium tests but