diff --git a/.github/workflows/ci_cd.yml b/.github/workflows/ci_cd.yml index bb7d1988fd1..acde8531979 100644 --- a/.github/workflows/ci_cd.yml +++ b/.github/workflows/ci_cd.yml @@ -162,6 +162,7 @@ jobs: pytest-extra-args: ${{ env.PYTEST_ARGUMENTS }} python-version: ${{ env.MAIN_PYTHON_VERSION }} optional-dependencies-name: unit-tests + requires-xvfb: true - name: Upload coverage to Codecov uses: codecov/codecov-action@18283e04ce6e62d37312384ff67231eb8fd56d24 # v5.4.3 @@ -684,6 +685,11 @@ jobs: pip uninstall --yes vtk pip install --extra-index-url https://wheels.vtk.org vtk-osmesa + - name: "Install X Virtual Frame Buffer" + run: | + sudo apt-get update + sudo apt-get install -y xvfb + - name: Run tests marked with 'extensions' uses: nick-fields/retry@ce71cc2ab81d554ebbe88c79ab5975992d79ba08 # v3.0.2 with: @@ -693,7 +699,7 @@ jobs: command: | export LD_LIBRARY_PATH=${{ env.ANSYSEM_ROOT251 }}/common/mono/Linux64/lib64:$LD_LIBRARY_PATH source .venv/bin/activate - pytest ${{ env.PYTEST_ARGUMENTS }} --timeout=600 -m extensions + xvfb-run pytest ${{ env.PYTEST_ARGUMENTS }} --timeout=600 -m extensions - uses: codecov/codecov-action@18283e04ce6e62d37312384ff67231eb8fd56d24 # v5.4.3 with: diff --git a/doc/changelog.d/6227.miscellaneous.md b/doc/changelog.d/6227.miscellaneous.md new file mode 100644 index 00000000000..b76c4515c1a --- /dev/null +++ b/doc/changelog.d/6227.miscellaneous.md @@ -0,0 +1 @@ +Import nastran extension and tests \ No newline at end of file diff --git a/src/ansys/aedt/core/extensions/misc.py b/src/ansys/aedt/core/extensions/misc.py index 7fdfa07dd35..9a55a635611 100644 --- a/src/ansys/aedt/core/extensions/misc.py +++ b/src/ansys/aedt/core/extensions/misc.py @@ -26,6 +26,7 @@ import argparse import os +from pathlib import Path import sys from ansys.aedt.core.internal.aedt_versions import aedt_versions @@ -63,6 +64,57 @@ def is_student(): return student_version +def create_default_ui(title, withdraw=False): + import tkinter + from tkinter import ttk + from tkinter.messagebox import showerror + + import PIL.Image + import PIL.ImageTk + + import ansys.aedt.core.extensions + from ansys.aedt.core.extensions.misc import ExtensionTheme + + def report_callback_exception(self, exc, val, tb): + showerror("Error", message=str(val)) + + def report_callback_exception_withdraw(self, exc, val, tb): + raise val + + if withdraw: + tkinter.Tk.report_callback_exception = report_callback_exception_withdraw + else: + tkinter.Tk.report_callback_exception = report_callback_exception + + root = tkinter.Tk() + + if withdraw: + root.withdraw() + root.title(title) + + if not withdraw: + # Load the logo for the main window + icon_path = Path(ansys.aedt.core.extensions.__path__[0]) / "images" / "large" / "logo.png" + im = PIL.Image.open(icon_path) + photo = PIL.ImageTk.PhotoImage(im) + + # Set the icon for the main window + root.iconphoto(True, photo) + + # Configure style for ttk buttons + style = ttk.Style() + theme = ExtensionTheme() + + # Apply light theme initially + theme.apply_light_theme(style) + root.theme = "light" + + # Set background color of the window (optional) + root.configure(bg=theme.light["widget_bg"]) + + return root, theme, style + + def get_arguments(args=None, description=""): # pragma: no cover """Get extension arguments.""" output_args = {"is_batch": False, "is_test": False} diff --git a/src/ansys/aedt/core/extensions/project/import_nastran.py b/src/ansys/aedt/core/extensions/project/import_nastran.py index 05d2bade65b..fc61ea24614 100644 --- a/src/ansys/aedt/core/extensions/project/import_nastran.py +++ b/src/ansys/aedt/core/extensions/project/import_nastran.py @@ -22,7 +22,11 @@ # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE # SOFTWARE. +from dataclasses import asdict +from dataclasses import dataclass +import os from pathlib import Path +import tkinter import ansys.aedt.core from ansys.aedt.core import get_pyaedt_app @@ -34,55 +38,37 @@ from ansys.aedt.core.extensions.misc import is_student from ansys.aedt.core.visualization.advanced.misc import nastran_to_stl -port = get_port() -version = get_aedt_version() -aedt_process_id = get_process_id() -is_student = is_student() -# Extension batch arguments -extension_arguments = {"decimate": 0.0, "lightweight": False, "planar": True, "file_path": ""} -extension_description = "Import Nastran or STL file" +@dataclass +class ExtensionData: + decimate: float = 0.0 + lightweight: bool = False + planar: bool = True + file_path: str = "" -def frontend(): # pragma: no cover - import tkinter - from tkinter import filedialog - from tkinter import ttk - - import PIL.Image - import PIL.ImageTk - - from ansys.aedt.core.extensions.misc import ExtensionTheme - - master = tkinter.Tk() - master.title("Import Nastran or STL file") +PORT = get_port() +VERSION = get_aedt_version() +AEDT_PROCESS_ID = get_process_id() +IS_STUDENT = is_student() +EXTENSION_TITLE = "Import Nastran or STL file" +EXTENSION_DEFAULT_ARGUMENTS = {"decimate": 0.0, "lightweight": False, "planar": True, "file_path": ""} - # Detect if user closes the UI - master.flag = False +result = None - # Load the logo for the main window - icon_path = Path(ansys.aedt.core.extensions.__path__[0]) / "images" / "large" / "logo.png" - im = PIL.Image.open(icon_path) - photo = PIL.ImageTk.PhotoImage(im) - # Set the icon for the main window - master.iconphoto(True, photo) - - # Configure style for ttk buttons - style = ttk.Style() - theme = ExtensionTheme() - - # Apply light theme initially - theme.apply_light_theme(style) - master.theme = "light" +def create_ui(withdraw=False): + from tkinter import filedialog + from tkinter import ttk - # Set background color of the window (optional) - master.configure(bg=theme.light["widget_bg"]) + from ansys.aedt.core.extensions.misc import create_default_ui - label2 = ttk.Label(master, text="Browse file:", style="PyAEDT.TLabel") + root, theme, style = create_default_ui(EXTENSION_TITLE, withdraw=withdraw) + label2 = ttk.Label(root, text="Browse file:", style="PyAEDT.TLabel") label2.grid(row=0, column=0, pady=10) - text = tkinter.Text(master, width=40, height=1) + + text = tkinter.Text(root, width=40, height=1, name="file_path_text") text.configure(bg=theme.light["pane_bg"], foreground=theme.light["text"], font=theme.default_font) text.grid(row=0, column=1, pady=10, padx=5) @@ -94,105 +80,97 @@ def browseFiles(): ) text.insert(tkinter.END, filename) - b1 = ttk.Button(master, text="...", width=10, command=browseFiles, style="PyAEDT.TButton") + b1 = ttk.Button(root, text="...", width=10, command=browseFiles, style="PyAEDT.TButton", name="browse_button") b1.grid(row=0, column=2, pady=10) - label = ttk.Label(master, text="Decimation factor (0-0.9). It may affect results:", style="PyAEDT.TLabel") + label = ttk.Label(root, text="Decimation factor (0-0.9). It may affect results:", style="PyAEDT.TLabel") label.grid(row=1, column=0, pady=10) - check = tkinter.Text(master, width=20, height=1) + check = tkinter.Text(root, width=20, height=1, name="decimation_text") check.configure(bg=theme.light["pane_bg"], foreground=theme.light["text"], font=theme.default_font) check.insert(tkinter.END, "0.0") check.grid(row=1, column=1, pady=10, padx=5) - label = ttk.Label(master, text="Import as lightweight (only HFSS):", style="PyAEDT.TLabel") + label = ttk.Label(root, text="Import as lightweight (only HFSS):", style="PyAEDT.TLabel") label.grid(row=2, column=0, pady=10) - light = tkinter.IntVar() - check2 = ttk.Checkbutton(master, variable=light, style="PyAEDT.TCheckbutton") + light = tkinter.IntVar(root, name="var_lightweight") + check2 = ttk.Checkbutton(root, variable=light, style="PyAEDT.TCheckbutton", name="check_lightweight") check2.grid(row=2, column=1, pady=10, padx=5) - label = ttk.Label(master, text="Enable planar merge:", style="PyAEDT.TLabel") + label = ttk.Label(root, text="Enable planar merge:", style="PyAEDT.TLabel") label.grid(row=3, column=0, pady=10) - planar = tkinter.IntVar(value=1) - check3 = ttk.Checkbutton(master, variable=planar, style="PyAEDT.TCheckbutton") + planar = tkinter.IntVar(root, value=1) + check3 = ttk.Checkbutton(root, variable=planar, style="PyAEDT.TCheckbutton", name="check_planar_merge") check3.grid(row=3, column=1, pady=10, padx=5) def toggle_theme(): - if master.theme == "light": + if root.theme == "light": set_dark_theme() - master.theme = "dark" + root.theme = "dark" else: set_light_theme() - master.theme = "light" + root.theme = "light" def set_light_theme(): - master.configure(bg=theme.light["widget_bg"]) + root.configure(bg=theme.light["widget_bg"]) text.configure(bg=theme.light["pane_bg"], foreground=theme.light["text"], font=theme.default_font) check.configure(bg=theme.light["pane_bg"], foreground=theme.light["text"], font=theme.default_font) theme.apply_light_theme(style) change_theme_button.config(text="\u263d") # Sun icon for light theme def set_dark_theme(): - master.configure(bg=theme.dark["widget_bg"]) + root.configure(bg=theme.dark["widget_bg"]) text.configure(bg=theme.dark["pane_bg"], foreground=theme.dark["text"], font=theme.default_font) check.configure(bg=theme.dark["pane_bg"], foreground=theme.dark["text"], font=theme.default_font) theme.apply_dark_theme(style) change_theme_button.config(text="\u2600") # Moon icon for dark theme # Create a frame for the toggle button to position it correctly - button_frame = ttk.Frame(master, style="PyAEDT.TFrame", relief=tkinter.SUNKEN, borderwidth=2) + button_frame = ttk.Frame( + root, style="PyAEDT.TFrame", relief=tkinter.SUNKEN, borderwidth=2, name="theme_button_frame" + ) button_frame.grid(row=5, column=2, pady=10, padx=10) # Add the toggle theme button inside the frame change_theme_button = ttk.Button( - button_frame, width=20, text="\u263d", command=toggle_theme, style="PyAEDT.TButton" + button_frame, width=20, text="\u263d", command=toggle_theme, style="PyAEDT.TButton", name="theme_toggle_button" ) change_theme_button.grid(row=0, column=0, padx=0) def callback(): - master.flag = True - master.decimate_ui = float(check.get("1.0", tkinter.END).strip()) - master.lightweight_ui = True if light.get() == 1 else False - master.planar_ui = True if planar.get() == 1 else False - master.file_path_ui = text.get("1.0", tkinter.END).strip() - master.destroy() + global result + result = ExtensionData( + decimate=float(check.get("1.0", tkinter.END).strip()), + lightweight=True if light.get() == 1 else False, + planar=True if planar.get() == 1 else False, + file_path=text.get("1.0", tkinter.END).strip(), + ) + root.destroy() def preview(): - master.decimate_ui = float(check.get("1.0", tkinter.END).strip()) - master.lightweight_ui = True if light.get() == 1 else False - master.planar_ui = True if planar.get() == 1 else False - master.file_path_ui = text.get("1.0", tkinter.END).strip() + decimate_ui = float(check.get("1.0", tkinter.END).strip()) + file_path_ui = text.get("1.0", tkinter.END).strip() + if not file_path_ui: + raise ValueError("Incorrect file path. Please select a valid file.") + + if not Path(file_path_ui).is_file(): + raise FileNotFoundError(f"File ({file_path_ui}) not found") - if master.file_path_ui.endswith(".nas"): - nastran_to_stl(input_file=master.file_path_ui, decimation=master.decimate_ui, preview=True) + if file_path_ui.endswith(".nas"): + nastran_to_stl(file_path_ui, decimation=decimate_ui, preview=True) else: from ansys.aedt.core.visualization.advanced.misc import simplify_stl - simplify_stl(master.file_path_ui, decimation=master.decimate_ui, preview=True) + simplify_stl(file_path_ui, decimation=decimate_ui, preview=True) - b2 = ttk.Button(master, text="Preview", width=40, command=preview, style="PyAEDT.TButton") + b2 = ttk.Button(root, text="Preview", width=40, command=preview, style="PyAEDT.TButton", name="preview_button") b2.grid(row=5, column=0, pady=10, padx=10) - b3 = ttk.Button(master, text="Ok", width=40, command=callback, style="PyAEDT.TButton") + b3 = ttk.Button(root, text="Ok", width=40, command=callback, style="PyAEDT.TButton", name="ok_button") b3.grid(row=5, column=1, pady=10, padx=10) - tkinter.mainloop() - - decimate_ui = getattr(master, "decimate_ui", extension_arguments["decimate"]) - lightweight_ui = getattr(master, "lightweight_ui", extension_arguments["lightweight"]) - planar_ui = getattr(master, "planar_ui", extension_arguments["planar"]) - file_path_ui = getattr(master, "file_path_ui", extension_arguments["file_path"]) - - output_dict = {} - if master.flag: - output_dict = { - "decimate": decimate_ui, - "lightweight": lightweight_ui, - "planar": planar_ui, - "file_path": file_path_ui, - } - return output_dict + return root def main(extension_args): @@ -204,10 +182,10 @@ def main(extension_args): if file_path.is_file(): app = ansys.aedt.core.Desktop( new_desktop=False, - version=version, - port=port, - aedt_process_id=aedt_process_id, - student_version=is_student, + version=VERSION, + port=PORT, + aedt_process_id=AEDT_PROCESS_ID, + student_version=IS_STUDENT, ) active_project = app.active_project() @@ -233,28 +211,29 @@ def main(extension_args): else: app = ansys.aedt.core.Desktop( new_desktop=False, - version=version, - port=port, - aedt_process_id=aedt_process_id, - student_version=is_student, + version=VERSION, + port=PORT, + aedt_process_id=AEDT_PROCESS_ID, + student_version=IS_STUDENT, ) app.logger.debug("Wrong file selected. Select a .nas or .stl file") - if not extension_args["is_test"]: # pragma: no cover + if "PYTEST_CURRENT_TEST" not in os.environ: app.release_desktop(False, False) return True if __name__ == "__main__": # pragma: no cover - args = get_arguments(extension_arguments, extension_description) + args = get_arguments(EXTENSION_DEFAULT_ARGUMENTS, EXTENSION_TITLE) # Open UI if not args["is_batch"]: # pragma: no cover - output = frontend() - if output: - for output_name, output_value in output.items(): - if output_name in extension_arguments: - args[output_name] = output_value + root = create_ui() + + tkinter.mainloop() + + if result: + args.update(asdict(result)) main(args) else: main(args) diff --git a/src/ansys/aedt/core/visualization/advanced/misc.py b/src/ansys/aedt/core/visualization/advanced/misc.py index 568abe87e62..9d535f8564a 100644 --- a/src/ansys/aedt/core/visualization/advanced/misc.py +++ b/src/ansys/aedt/core/visualization/advanced/misc.py @@ -25,6 +25,7 @@ import csv import logging import os +from pathlib import Path import re from ansys.aedt.core.generic.constants import CSS4_COLORS @@ -281,6 +282,10 @@ def parse_rdat_file(file_path): @pyaedt_function_handler() def _parse_nastran(file_path): """Nastran file parser.""" + + if not Path(file_path).exists(): + raise FileNotFoundError(f"File ({file_path}) not found") + logger = logging.getLogger("Global") nas_to_dict = {"Points": [], "PointsId": {}, "Assemblies": {}} includes = [] @@ -627,6 +632,9 @@ def nastran_to_stl(input_file, output_folder=None, decimation=0, enable_planar_m """Convert Nastran file into stl.""" import pyvista as pv + if not Path(input_file).exists(): + raise FileNotFoundError(f"File ({input_file}) not found") + logger = logging.getLogger("Global") nas_to_dict = _parse_nastran(input_file) @@ -738,6 +746,9 @@ def simplify_stl(input_file, output_file=None, decimation=0.5, preview=False): """ import pyvista as pv + if not Path(input_file).exists(): + raise FileNotFoundError(f"File ({input_file}) not found") + mesh = pv.read(input_file) if not output_file: output_file = os.path.splitext(input_file)[0] + "_output.stl" diff --git a/tests/system/extensions/test_45_extensions.py b/tests/system/extensions/test_45_extensions.py index 54dea42a6f2..3b4fa7693f9 100644 --- a/tests/system/extensions/test_45_extensions.py +++ b/tests/system/extensions/test_45_extensions.py @@ -145,35 +145,6 @@ def test_04_project_report(self, add_app): assert os.path.isfile(os.path.join(aedtapp.working_directory, "AEDT_Results.pdf")) aedtapp.close_project(aedtapp.project_name) - def test_05_project_import_nastran(self, add_app, local_scratch): - aedtapp = add_app(application=ansys.aedt.core.Hfss, project_name="workflow_nastran") - - from ansys.aedt.core.extensions.project.import_nastran import main - - # Non-existing file - file_path = os.path.join(local_scratch.path, "test_cad_invented.nas") - - assert main({"is_test": True, "file_path": file_path, "lightweight": True, "decimate": 0.0, "planar": True}) - - assert len(aedtapp.modeler.object_list) == 0 - - file_path = shutil.copy( - os.path.join(local_path, "example_models", "T20", "test_cad.nas"), - os.path.join(local_scratch.path, "test_cad.nas"), - ) - shutil.copy( - os.path.join(local_path, "example_models", "T20", "assembly1.key"), - os.path.join(local_scratch.path, "assembly1.key"), - ) - shutil.copy( - os.path.join(local_path, "example_models", "T20", "assembly2.key"), - os.path.join(local_scratch.path, "assembly2.key"), - ) - assert main({"is_test": True, "file_path": file_path, "lightweight": True, "decimate": 0.0, "planar": True}) - - assert len(aedtapp.modeler.object_list) == 4 - aedtapp.close_project(aedtapp.project_name) - def test_06_project_import_stl(self, add_app, local_scratch): aedtapp = add_app(application=ansys.aedt.core.Hfss, project_name="workflow_stl") diff --git a/tests/system/extensions/test_import_nastran.py b/tests/system/extensions/test_import_nastran.py new file mode 100644 index 00000000000..0d6832b20a5 --- /dev/null +++ b/tests/system/extensions/test_import_nastran.py @@ -0,0 +1,62 @@ +# -*- coding: utf-8 -*- +# +# Copyright (C) 2021 - 2025 ANSYS, Inc. and/or its affiliates. +# SPDX-License-Identifier: MIT +# +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in all +# copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +# SOFTWARE. + +from dataclasses import asdict +from pathlib import Path +import shutil +from unittest.mock import patch + +import ansys.aedt.core +from ansys.aedt.core.extensions.project.import_nastran import create_ui +from tests import TESTS_GENERAL_PATH + + +@patch("tkinter.filedialog.askopenfilename") +def test_import_nastran_success(mock_askopenfilename, add_app, local_scratch): + """Test that the extension works correctly when a valid file is selected.""" + from ansys.aedt.core.extensions.project.import_nastran import main + + NAS_PATH = Path(TESTS_GENERAL_PATH, "example_models", "T20", "test_cad.nas") + ASSEMBLY_1_PATH = Path(TESTS_GENERAL_PATH, "example_models", "T20", "assembly1.key") + ASSEMBLY_2_PATH = Path(TESTS_GENERAL_PATH, "example_models", "T20", "assembly2.key") + COPY_NAS_PATH = Path(local_scratch.path, "test_cad.nas") + COPY_ASSEMBLY_1_PATH = Path(local_scratch.path, "assembly1.key") + COPY_ASSEMBLY_2_PATH = Path(local_scratch.path, "assembly2.key") + + mock_askopenfilename.return_value = COPY_NAS_PATH + shutil.copy(NAS_PATH, COPY_NAS_PATH) + shutil.copy(ASSEMBLY_1_PATH, COPY_ASSEMBLY_1_PATH) + shutil.copy(ASSEMBLY_2_PATH, COPY_ASSEMBLY_2_PATH) + + root = create_ui(withdraw=True) + root.nametowidget("browse_button").invoke() + root.nametowidget("check_lightweight").invoke() + root.nametowidget("ok_button").invoke() + + from ansys.aedt.core.extensions.project.import_nastran import result + + aedtapp = add_app(application=ansys.aedt.core.Hfss, project_name="workflow_nastran") + assert main(asdict(result)) + assert len(aedtapp.modeler.object_list) == 4 + aedtapp.close_project(aedtapp.project_name) diff --git a/tests/unit/extensions/test_import_nastran.py b/tests/unit/extensions/test_import_nastran.py new file mode 100644 index 00000000000..3948b44f3bd --- /dev/null +++ b/tests/unit/extensions/test_import_nastran.py @@ -0,0 +1,117 @@ +# -*- coding: utf-8 -*- +# +# Copyright (C) 2021 - 2025 ANSYS, Inc. and/or its affiliates. +# SPDX-License-Identifier: MIT +# +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in all +# copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +# SOFTWARE. + +from dataclasses import asdict +from unittest.mock import patch + +import pytest + +from ansys.aedt.core.extensions.project.import_nastran import EXTENSION_DEFAULT_ARGUMENTS +from ansys.aedt.core.extensions.project.import_nastran import ExtensionData +from ansys.aedt.core.extensions.project.import_nastran import create_ui + +MOCK_NAS_PATH = "/mock/path/file1.nas" +MOCK_STL_PATH = "/mock/path/file2.stl" + + +def test_import_nastran_switch_to_dark_theme(): + """Test theme toggle button when switching to dark theme.""" + root = create_ui(withdraw=True) + assert root.theme == "light" + + toggle_theme = root.nametowidget("theme_button_frame.theme_toggle_button") + toggle_theme.invoke() + + assert root.theme == "dark" + + root.destroy() + + +def test_import_nastran_switch_to_light_theme(): + """Test theme toggle button when switching to light theme.""" + root = create_ui(withdraw=True) + root.theme = "dark" + + toggle_theme = root.nametowidget("theme_button_frame.theme_toggle_button") + toggle_theme.invoke() + + assert root.theme == "light" + + root.destroy() + + +def test_import_nastran_default_values(): + """Test that the default values of the UI are set correctly.""" + root = create_ui(withdraw=True) + root.nametowidget("ok_button").invoke() + + from ansys.aedt.core.extensions.project.import_nastran import result + + assert EXTENSION_DEFAULT_ARGUMENTS == asdict(result) + + +@patch("tkinter.filedialog.askopenfilename") +def test_import_nastran_modified_values(mock_askopenfilename): + """Test that the modifief values of the UI are returned correctly.""" + EXPECTED_RESULT = ExtensionData(0.5, True, False, MOCK_NAS_PATH) + mock_askopenfilename.return_value = MOCK_NAS_PATH + + root = create_ui(withdraw=True) + + root.nametowidget("browse_button").invoke() + root.nametowidget("check_lightweight").invoke() + root.nametowidget("check_planar_merge").invoke() + root.nametowidget("decimation_text").delete("1.0", "end") + root.nametowidget("decimation_text").insert("1.0", "0.5") + root.nametowidget("ok_button").invoke() + + from ansys.aedt.core.extensions.project.import_nastran import result + + assert EXPECTED_RESULT == result + + +@pytest.mark.parametrize("mock_path", [MOCK_NAS_PATH, MOCK_STL_PATH]) +@patch("tkinter.filedialog.askopenfilename") +def test_import_nastran_preview_on_non_existing_file(mock_askopenfilename, mock_path): + """Test that the preview button raises an exception when non existing file is selected.""" + mock_askopenfilename.return_value = mock_path + root = create_ui(withdraw=True) + root.nametowidget("browse_button").invoke() + + content = root.nametowidget("file_path_text").get("1.0", "end").strip() + assert content == mock_path + + preview_button = root.nametowidget("preview_button") + with pytest.raises(Exception): + preview_button.invoke() + + root.destroy() + + +def test_import_nastran_with_ui(): + """Test that the default values of the UI are set correctly.""" + + root = create_ui(withdraw=False) + root.after(100, root.destroy) + root.update()