using Bunit; using Marathon.Application.Abstractions; using Marathon.Application.Storage; using Marathon.Application.UseCases; using AppDateRange = Marathon.Application.Storage.DateRange; using Marathon.UI.Components; using Marathon.UI.Tests.Support; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Logging.Abstractions; using Microsoft.Extensions.Options; using MudBlazor; using NSubstitute; namespace Marathon.UI.Tests.Components; public sealed class ExportDialogTests : MarathonTestContext { private readonly IExcelExporter _exporter = Substitute.For(); public ExportDialogTests() { Services.AddSingleton(_exporter); Services.AddSingleton(Options.Create(new StorageOptions { ExportDirectory = Path.Combine(Path.GetTempPath(), "marathon-tests"), })); Services.AddSingleton(new ExportToExcelUseCase( _exporter, Options.Create(new StorageOptions { ExportDirectory = Path.Combine(Path.GetTempPath(), "marathon-tests"), }), NullLogger.Instance)); } [Fact] public async Task Submit_calls_export_use_case() { _exporter.ExportAsync(Arg.Any(), Arg.Any(), Arg.Any(), Arg.Any()) .Returns(Task.FromResult("C:/temp/Marathon_2026-05-04_to_2026-05-06.xlsx")); var host = RenderComponent(); IDialogReference? reference = null; await host.InvokeAsync(async () => { var svc = Services.GetRequiredService(); reference = await svc.ShowAsync("Export"); }); reference.Should().NotBeNull(); // Click the Export button (the second action button — first is Cancel). host.WaitForAssertion(() => host.FindAll(".mud-button").Count.Should().BeGreaterThanOrEqualTo(2)); var actionButtons = host.FindAll(".mud-dialog-actions .mud-button"); var submit = actionButtons.Last(); await host.InvokeAsync(() => submit.Click()); await _exporter.Received(1).ExportAsync( Arg.Any(), Arg.Any(), Arg.Any(), Arg.Any()); } [Fact] public async Task Cancel_does_not_call_export() { var host = RenderComponent(); await host.InvokeAsync(async () => { var svc = Services.GetRequiredService(); await svc.ShowAsync("Export"); }); host.WaitForAssertion(() => host.FindAll(".mud-dialog-actions .mud-button").Count.Should().BeGreaterThanOrEqualTo(2)); var cancelBtn = host.FindAll(".mud-dialog-actions .mud-button").First(); await host.InvokeAsync(() => cancelBtn.Click()); await _exporter.DidNotReceiveWithAnyArgs().ExportAsync(default!, default, default!, default); } /// Renders the MudDialogProvider so dialogs can be hosted in tests. private sealed class DialogHost : Microsoft.AspNetCore.Components.ComponentBase { protected override void BuildRenderTree(Microsoft.AspNetCore.Components.Rendering.RenderTreeBuilder builder) { builder.OpenComponent(0); builder.CloseComponent(); builder.OpenComponent(1); builder.CloseComponent(); builder.OpenComponent(2); builder.CloseComponent(); } } }