Spaces:
Build error
Build error
| """Tests for frontend.ui.widgets widget creation and UI state helpers.""" | |
| import sys | |
| import pytest | |
| from unittest.mock import patch, MagicMock | |
| def _mock_gradio(): | |
| """Mock gradio module before any widget functions are called.""" | |
| mock_gr = MagicMock() | |
| mock_gr.Blocks = MagicMock() | |
| mock_gr.Checkbox = MagicMock() | |
| mock_gr.Button = MagicMock() | |
| mock_gr.Dropdown = MagicMock() | |
| with patch.dict('sys.modules', {'gradio': mock_gr}): | |
| yield mock_gr | |
| def test_create_toggle_label_with_emoji(_mock_gradio): | |
| """Toggle label includes the provided emoji prefix.""" | |
| from frontend.ui.widgets import create_toggle | |
| checkbox = create_toggle("πΌοΈ Images", value=True, elem_id="toggle-images") | |
| _mock_gradio.Checkbox.assert_called() | |
| call_kwargs = _mock_gradio.Checkbox.call_args[1] | |
| assert "Images" in str(call_kwargs.get("label", "")) | |
| def test_create_toggle_default_value(_mock_gradio): | |
| """Toggle respects the default value parameter.""" | |
| from frontend.ui.widgets import create_toggle | |
| checkbox_false = create_toggle("π Audio", value=False, elem_id="toggle-audio") | |
| call_kwargs = _mock_gradio.Checkbox.call_args[1] | |
| assert call_kwargs.get("value") is False | |
| def test_create_toggle_elem_id_generation(_mock_gradio): | |
| """elem_id follows the pattern toggle-<label-without-emoji>.""" | |
| from frontend.ui.widgets import create_toggle | |
| create_toggle("πΌοΈ Images", value=True, elem_id="toggle-images") | |
| call_kwargs = _mock_gradio.Checkbox.call_args[1] | |
| assert call_kwargs.get("elem_id") == "toggle-images" | |
| def test_create_voice_dropdown_all_choices(_mock_gradio): | |
| """All 6 voice choices present in dropdown.""" | |
| from frontend.ui.widgets import create_voice_dropdown | |
| dropdown = create_voice_dropdown() | |
| call_kwargs = _mock_gradio.Dropdown.call_args[1] | |
| choices = call_kwargs.get("choices", []) | |
| assert len(choices) == 6 | |
| def test_create_voice_dropdown_default_value(_mock_gradio): | |
| """Default value is 'female, young adult' (instruct string).""" | |
| from frontend.ui.widgets import create_voice_dropdown | |
| create_voice_dropdown() | |
| call_kwargs = _mock_gradio.Dropdown.call_args[1] | |
| default = call_kwargs.get("value") | |
| assert default == "female, young adult" | |
| def test_create_voice_dropdown_elem_id(_mock_gradio): | |
| """Voice dropdown elem_id is 'voice-dropdown'.""" | |
| from frontend.ui.widgets import create_voice_dropdown | |
| create_voice_dropdown() | |
| call_kwargs = _mock_gradio.Dropdown.call_args[1] | |
| assert call_kwargs.get("elem_id") == "voice-dropdown" | |
| def test_voice_map_all_six_entries(): | |
| """_VOICE_MAP has exactly 6 entries mapping display labels to instruct strings.""" | |
| from frontend.ui.widgets import _VOICE_MAP | |
| assert len(_VOICE_MAP) == 6 | |
| def test_voice_map_instruct_strings_format(): | |
| """All _VOICE_MAP values are comma-separated gender, age format.""" | |
| from frontend.ui.widgets import _VOICE_MAP | |
| for label, instruct in _VOICE_MAP.items(): | |
| parts = instruct.split(", ") | |
| assert len(parts) == 2 | |
| assert parts[0] in ("female", "male") | |
| assert parts[1] in ("young adult", "middle-aged", "senior", "teenager") | |
| def test_enable_phase2_returns_tuple(_mock_gradio): | |
| """_enable_phase2() returns tuple of (Checkbox, Checkbox, Button, Dropdown, Button, Button, File, File, CSS).""" | |
| from frontend.ui.widgets import _enable_phase2 | |
| result = _enable_phase2() | |
| assert isinstance(result, tuple) | |
| assert len(result) == 9 | |
| def test_reset_to_idle_returns_tuple(_mock_gradio): | |
| """_reset_to_idle() returns tuple with interactive=False, disabled CSS string, export buttons, and file components.""" | |
| from frontend.ui.widgets import _reset_to_idle | |
| result = _reset_to_idle() | |
| assert isinstance(result, tuple) | |
| assert len(result) == 10 | |
| # Element at index 5 should be a CSS string (non-empty) | |
| assert isinstance(result[5], str) | |
| assert len(result[5]) > 0 | |
| # Elements at indices 6, 7 should be the export buttons | |
| assert isinstance(result[6], MagicMock) # export_csv_btn | |
| assert isinstance(result[7], MagicMock) # export_apkg_btn | |
| # Elements at indices 8, 9 should be the file components | |
| assert isinstance(result[8], MagicMock) # export_file | |
| assert isinstance(result[9], MagicMock) # export_apkg_file | |
| def test_reset_to_idle_disabled_css_content(_mock_gradio): | |
| """_reset_to_idle() CSS targets the right elem_ids.""" | |
| from frontend.ui.widgets import _reset_to_idle | |
| result = _reset_to_idle() | |
| # CSS is at index 5 (after generate_text_btn, images_toggle, audio_toggle, generate_cards_btn, voice_dropdown) | |
| css = result[5] | |
| assert "europalex-btn-disabled" in css or "toggle-images" in css | |
| assert "#voice-dropdown" in css | |
| def test_enable_language_dropdown_on_audio_true(_mock_gradio): | |
| """Audio toggle ON β removes disabled CSS, enables dropdown.""" | |
| from frontend.ui.widgets import _enable_language_dropdown_on_audio | |
| result = _enable_language_dropdown_on_audio(True) | |
| assert isinstance(result, tuple) | |
| # Should return (dropdown_update, "") β empty CSS means enabled | |
| assert result[1] == "" | |
| def test_enable_language_dropdown_on_audio_false(_mock_gradio): | |
| """Audio toggle OFF β applies disabled CSS to voice dropdown.""" | |
| from frontend.ui.widgets import _enable_language_dropdown_on_audio | |
| result = _enable_language_dropdown_on_audio(False) | |
| assert isinstance(result, tuple) | |
| # Should return (dropdown_update, css_string) β non-empty CSS means disabled | |
| assert isinstance(result[1], str) | |
| assert len(result[1]) > 0 | |
| assert "#voice-dropdown" in result[1] | |
| def test_restore_generate_cards_button_returns_tuple(_mock_gradio): | |
| """_restore_generate_cards_button() returns tuple of (Button, Button, Button).""" | |
| from frontend.ui.widgets import _restore_generate_cards_button | |
| result = _restore_generate_cards_button() | |
| assert isinstance(result, tuple) | |
| assert len(result) == 3 | |
| def test_restore_generate_cards_button_makes_button_visible_interactive(_mock_gradio): | |
| """Generate Cards button becomes visible and interactive.""" | |
| from frontend.ui.widgets import _restore_generate_cards_button | |
| _restore_generate_cards_button() | |
| # gr.Button was called three times: generate_cards_btn, export_csv_btn, export_apkg_btn | |
| calls = _mock_gradio.Button.call_args_list | |
| assert len(calls) == 3 | |
| first_call_kwargs = calls[0].kwargs if calls[0].kwargs else calls[0][1] | |
| assert first_call_kwargs.get("visible") is True | |
| assert first_call_kwargs.get("interactive") is True | |
| def test_restore_generate_cards_button_export_stays_disabled(_mock_gradio): | |
| """Export buttons stay visible but disabled.""" | |
| from frontend.ui.widgets import _restore_generate_cards_button | |
| _restore_generate_cards_button() | |
| calls = _mock_gradio.Button.call_args_list | |
| assert len(calls) == 3 | |
| # Second call: export_csv_btn (disabled) | |
| second_call_kwargs = calls[1].kwargs if calls[1].kwargs else calls[1][1] | |
| assert second_call_kwargs.get("visible") is True | |
| assert second_call_kwargs.get("interactive") is False | |
| # Third call: export_apkg_btn (disabled) | |
| third_call_kwargs = calls[2].kwargs if calls[2].kwargs else calls[2][1] | |
| assert third_call_kwargs.get("visible") is True | |
| assert third_call_kwargs.get("interactive") is False | |
| def test_restore_generate_cards_button_only_returns_tuple(_mock_gradio): | |
| """_restore_generate_cards_button_only() returns tuple of 4 (Button, Button, Button, Button).""" | |
| from frontend.ui.widgets import _restore_generate_cards_button_only | |
| result = _restore_generate_cards_button_only() | |
| assert isinstance(result, tuple) | |
| assert len(result) == 4 | |
| def test_restore_generate_cards_button_only_hides_generate_text(_mock_gradio): | |
| """Generate Text button stays hidden β only appears on scenario/CEFR/batch reset.""" | |
| from frontend.ui.widgets import _restore_generate_cards_button_only | |
| _restore_generate_cards_button_only() | |
| calls = _mock_gradio.Button.call_args_list | |
| assert len(calls) == 4 | |
| first_call_kwargs = calls[0].kwargs if calls[0].kwargs else calls[0][1] | |
| assert first_call_kwargs.get("visible") is False | |
| def test_restore_generate_cards_button_only_restores_generate_cards(_mock_gradio): | |
| """Generate Cards button becomes visible and interactive.""" | |
| from frontend.ui.widgets import _restore_generate_cards_button_only | |
| _restore_generate_cards_button_only() | |
| calls = _mock_gradio.Button.call_args_list | |
| assert len(calls) == 4 | |
| second_call_kwargs = calls[1].kwargs if calls[1].kwargs else calls[1][1] | |
| assert second_call_kwargs.get("visible") is True | |
| assert second_call_kwargs.get("interactive") is True | |