mirror of
https://github.com/NVIDIA-NeMo/DataDesigner
synced 2026-05-24 09:48:29 +00:00
* feat: add --save-report option to preview command * feat: add save_path option to display_sample_record Allow saving rendered sample records as HTML or SVG files via an optional save_path parameter on both the standalone function and the WithRecordSamplerMixin method. * feat: replace --save-report with --save-results on preview command Replace the single-file --save-report option with --save-results, which saves all preview artifacts (dataset parquet, analysis report HTML, and per-record sample HTMLs) into a timestamped directory under the artifact path. Add error handling around the save block, improve timestamp precision to microseconds, and expand test coverage for the new behavior. * feat: add sample records pager with theme toggle, postMessage bridge, and UI polish * feat: add dataset metadata subtitle to pager and clean up toolbar layout * fix: address review findings for preview save-results feature - Split try/except in generation_controller so report display errors don't produce misleading "failed to save" messages when not saving - Add browser HTML path to save success output for discoverability - Remove 5 unused CSS variables from pager theme constants - Add "N of M" record counter to pager toolbar - Add theme/display_width assertions to all preview_command tests - Add dedicated test for custom theme and display_width passthrough - Add tests for record counter and CSS variable cleanup * fix: address code review findings and simplify pager - Fix critical bug: analysis report now displays to console even when --save-results is active (was silently dropped via pass statement) - Fix latent UnboundLocalError in display_sample_record when index is out of bounds (num_records computed before try block) - Eliminate duplicated dark CSS between constant and theme listener script - Simplify sample_records_pager: remove dual-theme system, postMessage bridge, and responsive media queries; restore GitHub link; reorder toolbar to put prev/next buttons on the far left - Narrow except Exception to except OSError in save-results path - Use case-insensitive extension check and lambda-based re.sub - Collapse redundant preview command delegation tests into parametrize - Add missing type annotations and remove tautological assertions * style: move record counter to far right of pager toolbar * refactor: remove dead theme-listener script and inline CSS constant _THEME_LISTENER_SCRIPT and _SAMPLE_RECORD_DARK_CSS_INLINE became orphaned after the pager simplification removed the postMessage bridge. This removes both constants, drops the injection line, switches the idempotency guard to the viewport meta tag, and cleans up related test assertions. * fix: move Path import out of TYPE_CHECKING block in test_visualization * fix: rename _logger to logger to match codebase convention * fix: remove unnecessary cast in preview command theme parameter * refactor: extract DEFAULT_DISPLAY_WIDTH constant and make apply_html_post_processing public * Update packages/data-designer-config/tests/config/utils/test_visualization.py --------- Co-authored-by: greptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com>
134 lines
4.3 KiB
Python
134 lines
4.3 KiB
Python
# SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
|
|
# SPDX-License-Identifier: Apache-2.0
|
|
|
|
from __future__ import annotations
|
|
|
|
from unittest.mock import MagicMock, patch
|
|
|
|
import pytest
|
|
|
|
from data_designer.cli.commands.preview import preview_command
|
|
from data_designer.cli.ui import wait_for_navigation_key
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# preview_command delegation tests
|
|
# ---------------------------------------------------------------------------
|
|
|
|
|
|
@pytest.mark.parametrize(
|
|
"kwargs",
|
|
[
|
|
pytest.param(
|
|
{
|
|
"config_source": "config.yaml",
|
|
"num_records": 5,
|
|
"non_interactive": True,
|
|
"save_results": False,
|
|
"artifact_path": None,
|
|
"theme": "dark",
|
|
"display_width": 110,
|
|
},
|
|
id="defaults",
|
|
),
|
|
pytest.param(
|
|
{
|
|
"config_source": "config.yaml",
|
|
"num_records": 10,
|
|
"non_interactive": False,
|
|
"save_results": False,
|
|
"artifact_path": None,
|
|
"theme": "dark",
|
|
"display_width": 110,
|
|
},
|
|
id="non_interactive_false",
|
|
),
|
|
pytest.param(
|
|
{
|
|
"config_source": "my_config.py",
|
|
"num_records": 20,
|
|
"non_interactive": True,
|
|
"save_results": False,
|
|
"artifact_path": None,
|
|
"theme": "dark",
|
|
"display_width": 110,
|
|
},
|
|
id="custom_num_records",
|
|
),
|
|
pytest.param(
|
|
{
|
|
"config_source": "config.yaml",
|
|
"num_records": 5,
|
|
"non_interactive": True,
|
|
"save_results": True,
|
|
"artifact_path": "/custom/output",
|
|
"theme": "dark",
|
|
"display_width": 110,
|
|
},
|
|
id="save_results_and_artifact_path",
|
|
),
|
|
pytest.param(
|
|
{
|
|
"config_source": "config.yaml",
|
|
"num_records": 5,
|
|
"non_interactive": True,
|
|
"save_results": True,
|
|
"artifact_path": None,
|
|
"theme": "light",
|
|
"display_width": 80,
|
|
},
|
|
id="custom_theme_and_display_width",
|
|
),
|
|
],
|
|
)
|
|
@patch("data_designer.cli.commands.preview.GenerationController")
|
|
def test_preview_command_delegates_to_controller(mock_ctrl_cls: MagicMock, kwargs: dict[str, object]) -> None:
|
|
"""Test preview_command delegates all arguments to GenerationController.run_preview."""
|
|
mock_ctrl = MagicMock()
|
|
mock_ctrl_cls.return_value = mock_ctrl
|
|
|
|
preview_command(**kwargs)
|
|
|
|
mock_ctrl_cls.assert_called_once()
|
|
mock_ctrl.run_preview.assert_called_once_with(**kwargs)
|
|
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# wait_for_navigation_key unit tests (UI-layer, unchanged by refactor)
|
|
# ---------------------------------------------------------------------------
|
|
|
|
|
|
@patch("data_designer.cli.ui.Application")
|
|
def test_wait_for_navigation_key_creates_app_and_runs(mock_app_cls: MagicMock) -> None:
|
|
"""Test wait_for_navigation_key creates an Application and calls run()."""
|
|
mock_app_instance = MagicMock()
|
|
mock_app_cls.return_value = mock_app_instance
|
|
|
|
result = wait_for_navigation_key()
|
|
|
|
mock_app_cls.assert_called_once()
|
|
mock_app_instance.run.assert_called_once()
|
|
assert result == "q"
|
|
|
|
|
|
@patch("data_designer.cli.ui.Application")
|
|
def test_wait_for_navigation_key_handles_keyboard_interrupt(mock_app_cls: MagicMock) -> None:
|
|
"""Test wait_for_navigation_key returns 'q' on KeyboardInterrupt."""
|
|
mock_app_instance = MagicMock()
|
|
mock_app_cls.return_value = mock_app_instance
|
|
mock_app_instance.run.side_effect = KeyboardInterrupt
|
|
|
|
result = wait_for_navigation_key()
|
|
|
|
assert result == "q"
|
|
|
|
|
|
@patch("data_designer.cli.ui.Application")
|
|
def test_wait_for_navigation_key_handles_eof_error(mock_app_cls: MagicMock) -> None:
|
|
"""Test wait_for_navigation_key returns 'q' on EOFError."""
|
|
mock_app_instance = MagicMock()
|
|
mock_app_cls.return_value = mock_app_instance
|
|
mock_app_instance.run.side_effect = EOFError
|
|
|
|
result = wait_for_navigation_key()
|
|
|
|
assert result == "q"
|