using System.Net.Http;
using Avalonia;
using Avalonia.Controls;
using Avalonia.Headless.XUnit;
using Avalonia.VisualTree;
using CommunityToolkit.Mvvm.Input;
using PostIt.ViewModels;
using PostIt.Views;
using Yavsc.Api.Client;
namespace PostIt.Tests;
///
/// Headless UI coverage for the "Mes fichiers" page
/// (). The user reported that once a file
/// is listed, "l'item est inerte au click, et ses boutons restent
/// grisés, désactivés" — neither the row nor its action buttons
/// (Télécharger / Supprimer) respond to a click.
///
/// These tests mount a real under
/// the Avalonia headless platform with a stub
/// that lists one file, then walk the
/// realised visual tree to find the row's action buttons and
/// assert the two things that would silently break and reproduce
/// the "grayed / inert" symptom:
///
/// The Command binding
/// ($parent[vm:MyFilesViewModel].DownloadFileCommand)
/// resolved to a non-null command — a null command leaves the
/// button disabled (grayed) and click-silent.
/// The command is armed
/// (CanExecute true) for the listed file — an
/// AsyncRelayCommand with
/// AllowConcurrentExecutions=false (the
///
/// default) disables itself while running; a button stuck
/// "running" is exactly the grayed-and-inert report.
/// Executing the command actually invokes the
/// file download / delete on the API client — the row
/// "activates" on click instead of being inert.
///
/// The OS save picker is absent under headless, so
/// FileSaveHelpers.SaveAsync throws or cancels and the
/// download command's catch sets an error status — but the
/// API download is awaited before the picker, so the call
/// is recorded regardless. We do not await the command's
/// ExecutionTask to completion, to avoid blocking on the
/// (platform-dependent) picker.
///
public class MyFilesPageHeadlessTests
{
private static (MyFilesPage page, MyFilesViewModel vm, StubFsApi api) CreatePage()
{
var api = new StubFsApi();
var fsClient = new UserFilesApiClient(api, "https://blogs.example/api/v1/");
var vm = new MyFilesViewModel(fsClient);
var page = new MyFilesPage { DataContext = vm };
return (page, vm, api);
}
///
/// Mount the page the way the app does — pushed onto a
/// shown in a
/// (a realizes its content template via
/// the navigation host, cf. RdvPageHeadlessTests) — after
/// listing a single file by awaiting
/// . Populating
/// before the first layout
/// pass lets the ItemsControl realise its item templates
/// under headless.
///
private static async Task<(MyFilesPage page, MyFilesViewModel vm, StubFsApi api)> MountAsync()
{
var (page, vm, api) = CreatePage();
await vm.InitializeAsync();
var nav = new NavigationPage();
nav.PushAsync(page).GetAwaiter().GetResult();
var window = new Window { Content = nav };
window.Show();
// Let bindings / the ItemsControl item templates apply.
await Task.Delay(50);
return (page, vm, api);
}
///
/// Find the first action button in a file row whose content
/// matches . The file rows live in an
/// ItemsControl DataTemplate (no x:Name), so we
/// walk the visual tree.
///
private static Button? FindRowButton(MyFilesPage page, string content)
=> page.GetVisualDescendants()
.OfType