using System; using System.IO; using System.Linq; using System.Windows; using System.Windows.Controls; using System.Windows.Controls.Primitives; using System.Windows.Media; using Microsoft.Data.Sqlite; using Xunit; using ytLive.Helpers; using ytLive.ViewModels; namespace ytLive.Tests; [Collection("RealApp")] public sealed class LogsPanelTests { private readonly RealAppHost _app; public LogsPanelTests(RealAppHost app) => _app = app; [Fact] public void NonEditPanel_DefaultsToLiveChat_AndShowsCurrentLaunchLogsNewestFirst() { var directory = Path.Combine(Path.GetTempPath(), $"ytlive-logs-panel-{Guid.NewGuid():N}"); Directory.CreateDirectory(directory); var databasePath = Path.Combine(directory, "layout.db"); var logPath = Path.Combine(directory, "startup.log"); File.WriteAllText(logPath, "previous-launch-sentinel" + Environment.NewLine); AppLog.PathOverride = logPath; MainViewModel.LayoutPathOverride = databasePath; try { _app.Run(() => { var window = new MainWindow(); try { window.Show(); window.UpdateLayout(); var vm = (MainViewModel)window.DataContext; vm.EditingActive = false; window.UpdateLayout(); var panel = (UserControl)window.FindName("LeftPanel")!; var chatTab = (ToggleButton)panel.FindName("LiveChatTabButton")!; var logsTab = (ToggleButton)panel.FindName("LogsTabButton")!; var logList = (ListBox)panel.FindName("LogListBox")!; Assert.True(vm.IsLiveChatTabSelected); Assert.True(chatTab.IsChecked); Assert.False(logsTab.IsChecked); Assert.False(logList.IsVisible); vm.IsLogsTabSelected = true; window.UpdateLayout(); Assert.True(logsTab.IsChecked); Assert.False(chatTab.IsChecked); Assert.True(logList.IsVisible); AppLog.Write("LogsPanel older marker"); AppLog.Write("LogsPanel newer marker"); AppLog.WriteWarning("LogsPanel warning marker"); AppLog.WriteSuccess("LogsPanel success marker"); AppLog.Write("LogsPanel class 2 marker " + new string('2', 240), AppLogMessageType.Error, AppLogErrorClass.Class2); AppLog.Write("LogsPanel class 1 marker " + new string('1', 240), AppLogMessageType.Error, AppLogErrorClass.Class1); vm.RefreshCurrentLaunchLogs(); window.UpdateLayout(); var entries = vm.CurrentLaunchLogEntries.Select(entry => entry.Text).ToArray(); var newerIndex = Array.FindIndex(entries, text => text.Contains("LogsPanel newer marker", StringComparison.Ordinal)); var olderIndex = Array.FindIndex(entries, text => text.Contains("LogsPanel older marker", StringComparison.Ordinal)); Assert.True(newerIndex >= 0 && olderIndex >= 0, "new log writes did not reach the Logs tab"); Assert.True(newerIndex < olderIndex, "the Logs tab must put the newest entry first"); Assert.DoesNotContain(entries, text => text.Contains("previous-launch-sentinel", StringComparison.Ordinal)); Assert.Same(vm.CurrentLaunchLogEntries, logList.ItemsSource); var class1 = vm.PinnedLogEntries.Single(entry => entry.Text.Contains("LogsPanel class 1 marker", StringComparison.Ordinal)); var class2 = vm.PinnedLogEntries.Single(entry => entry.Text.Contains("LogsPanel class 2 marker", StringComparison.Ordinal)); Assert.True(Array.IndexOf(vm.PinnedLogEntries.ToArray(), class1) < Array.IndexOf(vm.PinnedLogEntries.ToArray(), class2)); Assert.Equal("CLASS 1", class1.ErrorClassLabel); Assert.Equal("CLASS 2", class2.ErrorClassLabel); Assert.Equal("CLASS 1", class1.TypeLabel); var pinnedPanel = (ItemsControl)panel.FindName("PinnedLogPanel")!; Assert.Equal(System.Windows.Visibility.Visible, pinnedPanel.Visibility); var class1Text = Find(pinnedPanel, element => element is TextBlock text && text.Text.Contains("LogsPanel class 1 marker", StringComparison.Ordinal)) as TextBlock; var class2Text = Find(pinnedPanel, element => element is TextBlock text && text.Text.Contains("LogsPanel class 2 marker", StringComparison.Ordinal)) as TextBlock; Assert.NotNull(class1Text); Assert.NotNull(class2Text); Assert.Equal(TextWrapping.Wrap, class1Text!.TextWrapping); Assert.Equal(BrushColor("Brush.Error"), Assert.IsType(class1Text.Foreground).Color); Assert.Equal(BrushColor("Brush.Attention"), Assert.IsType(class2Text!.Foreground).Color); var warningText = Find(logList, element => element is TextBlock text && text.Text.Contains("LogsPanel warning marker", StringComparison.Ordinal)) as TextBlock; var successText = Find(logList, element => element is TextBlock text && text.Text.Contains("LogsPanel success marker", StringComparison.Ordinal)) as TextBlock; var infoText = Find(logList, element => element is TextBlock text && text.Text.Contains("LogsPanel newer marker", StringComparison.Ordinal)) as TextBlock; Assert.NotNull(warningText); Assert.NotNull(successText); Assert.NotNull(infoText); Assert.Equal(BrushColor("Brush.Secondary"), Assert.IsType(warningText!.Foreground).Color); Assert.Equal(BrushColor("Brush.Positive"), Assert.IsType(successText!.Foreground).Color); Assert.Equal(BrushColor("Brush.OnSurfaceVariant"), Assert.IsType(infoText!.Foreground).Color); var class1Ack = Find(pinnedPanel, element => element is Button button && button.DataContext is AppLogEntry entry && entry.Sequence == class1.Sequence) as Button; Assert.NotNull(class1Ack?.Command); Assert.Same(class1, class1Ack!.CommandParameter); class1Ack!.Command!.Execute(class1Ack.CommandParameter); Assert.DoesNotContain(class1, vm.PinnedLogEntries); Assert.Contains(class1, vm.CurrentLaunchLogEntries); } finally { window.Close(); } }); } finally { MainViewModel.LayoutPathOverride = null; AppLog.PathOverride = null; SqliteConnection.ClearAllPools(); try { Directory.Delete(directory, recursive: true); } catch { } } } private static System.Windows.Media.Color BrushColor(string key) => Assert.IsType(Application.Current!.FindResource(key)).Color; private static DependencyObject? Find(DependencyObject parent, Func predicate) { if (predicate(parent)) return parent; for (var i = 0; i < VisualTreeHelper.GetChildrenCount(parent); i++) { var child = VisualTreeHelper.GetChild(parent, i); if (predicate(child)) return child; var found = Find(child, predicate); if (found != null) return found; } return null; } }