using System;
using System.IO;
using System.Linq;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Media;
using System.Windows.Media.Effects;
using Microsoft.Data.Sqlite;
using Xunit;
using Xunit.Sdk;
using ytLive.Controls;
using ytLive.Models;
using ytLive.Services;
using ytLive.ViewModels;
namespace ytLive.Tests;
/// The hole that let two regressions reach the creator on 2026-10-30: the Socials drawer's
/// appearance controls were tested against the RENDERER, while the preview is its own XAML tree —
/// so a control could paint nothing, or hide the bar entirely, with every test green.
///
/// This drives the real drawer on a real MainWindow and reads the real preview elements:
/// the bar stays visible when the plate is off, the text colour reaches the glyphs, the plate
/// carries its colour and opacity, the shadow switches on, and Cancel restores the pre-edit
/// look.
[Collection("RealApp")]
public class SocialsPreviewTests
{
private readonly RealAppHost _app;
public SocialsPreviewTests(RealAppHost app) => _app = app;
[Fact]
public void The_preview_bar_follows_the_drawer_controls() => _app.Run(Run);
private static void Run()
{
var tempDb = Path.Combine(Path.GetTempPath(), $"ytLlive-socialspreview-{Guid.NewGuid():N}.db");
MainViewModel.LayoutPathOverride = tempDb;
MainWindow? window = null;
try
{
var seeded = new SocialsConfig { BarEnabled = true };
seeded.Entries.Add(new SocialEntry
{
Service = SocialService.YouTube, Handle = "creator",
ProfileUrl = "https://youtube.com/@creator",
});
using (var store = new LayoutStore(tempDb))
store.Save(Array.Empty(), null, seeded);
SqliteConnection.ClearAllPools();
window = new MainWindow();
var vm = (MainViewModel)window.DataContext!;
window.Show();
window.UpdateLayout();
var preview = (PreviewPane)window.FindName("PreviewPane")!;
var bar = (FrameworkElement)preview.FindName("SocialBarElement")!;
var plate = FindChild(bar)!;
var label = FindChild(bar)!;
Assert.Equal(Visibility.Visible, bar.Visibility);
// 1. Drawer edits land on the preview live — no Save needed.
vm.OpenSocialDialogCommand.Execute(null);
var drawer = vm.SocialsDrawer!;
drawer.BarTextColor = "#FF0000";
window.UpdateLayout();
Assert.Equal(Colors.Red, ((SolidColorBrush)label.Foreground).Color);
// 2. Plate off (the default) = transparent plate, NOT a hidden bar.
Assert.Equal(0, ((SolidColorBrush)plate.Background).Color.A);
Assert.Equal(Visibility.Visible, bar.Visibility);
drawer.BarBackgroundEnabled = true;
drawer.BarBackgroundColor = "#0000FF";
drawer.BarBackgroundOpacity = 0.5;
window.UpdateLayout();
var bg = ((SolidColorBrush)plate.Background).Color;
Assert.Equal(Colors.Blue, Color.FromRgb(bg.R, bg.G, bg.B));
Assert.InRange(bg.A, 120, 135); // 50% of 255
Assert.Equal(new Thickness(18, 6, 18, 6), plate.Padding);
// 3. The shadow is a real effect on the glyph, and off by default.
Assert.Null(label.Effect);
drawer.BarTextShadow = true;
window.UpdateLayout();
Assert.IsType(label.Effect);
// 4. Cancel restores the pre-edit look (white text, no plate).
drawer.CancelCommand.Execute(null);
window.UpdateLayout();
Assert.Equal(Colors.White, ((SolidColorBrush)label.Foreground).Color);
Assert.Equal(0, ((SolidColorBrush)plate.Background).Color.A);
}
catch (Exception ex)
{
throw new XunitException(ex.ToString()); // the host otherwise hides the real stack
}
finally
{
window?.Close();
MainViewModel.LayoutPathOverride = null;
SqliteConnection.ClearAllPools();
try { File.Delete(tempDb); } catch { /* best-effort */ }
}
}
private static T? FindChild(DependencyObject root) where T : DependencyObject
{
for (var i = 0; i < VisualTreeHelper.GetChildrenCount(root); i++)
{
var child = VisualTreeHelper.GetChild(root, i);
if (child is T match) return match;
var deeper = FindChild(child);
if (deeper != null) return deeper;
}
return null;
}
}