Scene strip now renders in canonical F-key order regardless of persisted SortOrder. The strip loads Scenes ORDER BY SortOrder while each tab's F-key label is name-derived, so a session that persisted Chat before BRB (found on the creator's DB: SortOrder 2/3) showed tabs 'Chat F4' then 'BRB F3'. MainViewModel.LoadLayout now calls the new ReorderScenesToCanonical() - the five canonical scenes reorder to Starting, Live, BRB, Chat, Ending (F1-F5, stable among renamed non-canonical scenes), and the next auto-save re-stamps SortOrder so the heal persists. One integration test (SceneOrderTests) seeds the swapped-order DB and asserts loaded order, F1-F5 labels, and that a save persists the corrected ordering. 423 pass, 0 build warnings. (Unit: scene order)
This commit is contained in:
@@ -0,0 +1,89 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using Microsoft.Data.Sqlite;
|
||||
using Xunit;
|
||||
using ytLive.ViewModels;
|
||||
|
||||
namespace ytLive.Tests;
|
||||
|
||||
/// <summary>
|
||||
/// Scene-strip ordering vs F-keys (creator's report 2026-10-10). The five
|
||||
/// canonical scenes are loaded <c>ORDER BY SortOrder</c>, and a session can
|
||||
/// persist Chat before BRB, so the thumbnail strip renders in the wrong order
|
||||
/// while each tab's F-key label is name-derived — "the ordering of the 5
|
||||
/// screens and their corresponding F-key is out of sequence". Fix: the VM
|
||||
/// reorders Scenes to the canonical F1-F5 order (Starting, Live, BRB, Chat,
|
||||
/// Ending) at load; the next save re-stamps SortOrder to the corrected order.
|
||||
/// </summary>
|
||||
[Collection("RealApp")]
|
||||
public sealed class SceneOrderTests
|
||||
{
|
||||
private readonly RealAppHost _app;
|
||||
|
||||
public SceneOrderTests(RealAppHost app) => _app = app;
|
||||
|
||||
[Fact]
|
||||
public void Scenes_Load_Into_Canonical_FKey_Order_Whatever_The_SortOrder_Rows()
|
||||
{
|
||||
_app.Run(() =>
|
||||
{
|
||||
using var layout = new TempSwappedOrderLayout();
|
||||
|
||||
var vm = new MainViewModel();
|
||||
|
||||
var names = vm.Scenes.Select(s => s.Name).ToArray();
|
||||
Assert.Equal(new[] { "Starting", "Live", "BRB", "Chat", "Ending" }, names);
|
||||
|
||||
var keys = vm.Scenes.Select(s => s.HotkeyLabel).ToArray();
|
||||
Assert.Equal(new[] { "F1", "F2", "F3", "F4", "F5" }, keys);
|
||||
|
||||
// The heal persists: a save re-stamps SortOrder, so a fresh load (the
|
||||
// thumbnail strip's rendering source) keeps the canonical order.
|
||||
vm.SaveLayoutNow();
|
||||
using var reloaded = new Services.LayoutStore(layout.DbPath);
|
||||
Assert.Equal(new[] { "Starting", "Live", "BRB", "Chat", "Ending" },
|
||||
reloaded.Load().Select(s => s.Name).ToArray());
|
||||
});
|
||||
}
|
||||
|
||||
/// <summary>Creates a DB whose five scenes are persisted with Chat BEFORE
|
||||
/// BRB — the exact stale state found on the creator's box.</summary>
|
||||
private sealed class TempSwappedOrderLayout : IDisposable
|
||||
{
|
||||
public string DbPath { get; }
|
||||
private readonly string _path;
|
||||
|
||||
public TempSwappedOrderLayout()
|
||||
{
|
||||
_path = Path.Combine(Path.GetTempPath(), $"ytLive-sceneorder-{Guid.NewGuid():N}.db");
|
||||
DbPath = _path;
|
||||
using (var schema = new Services.LayoutStore(_path)) { }
|
||||
SqliteConnection.ClearAllPools();
|
||||
MainViewModel.LayoutPathOverride = _path;
|
||||
|
||||
using var connection = new SqliteConnection($"Data Source={_path}");
|
||||
connection.Open();
|
||||
using (var cmd = connection.CreateCommand())
|
||||
{
|
||||
cmd.CommandText = """
|
||||
INSERT INTO Scene (Id, Name, IsHidden, IsChatScene, HasBackground, SortOrder)
|
||||
VALUES
|
||||
('scene-starting', 'Starting', 0, 0, 1, 0),
|
||||
('scene-live', 'Live', 0, 0, 1, 1),
|
||||
('scene-chat', 'Chat', 0, 1, 1, 2),
|
||||
('scene-brb', 'BRB', 0, 0, 1, 3),
|
||||
('scene-ending', 'Ending', 0, 0, 1, 4);
|
||||
""";
|
||||
cmd.ExecuteNonQuery();
|
||||
}
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
MainViewModel.LayoutPathOverride = null;
|
||||
SqliteConnection.ClearAllPools();
|
||||
try { File.Delete(_path); } catch { /* best-effort cleanup */ }
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user