TASK 7 UI polish batch: scene/source row cleanup, dedup naming, full social handles — scenes are now pure selection rows (edit/trash/visibility icons and inline rename removed with EditSceneCommand/RemoveSceneCommand/ToggleSceneVisibilityCommand + Scene.IsEditing); source rows gained the trio (new EditElementCommand → inline rename via SceneElement.IsEditing, ToggleElementVisibilityCommand → eye flips IsVisible, open/slashed style rebound, hidden rows dim to 45%); duplicate resource names get a no-space incrementing suffix via shared NextSourceName (Image, Image2, Image3…) derived from actual names so deletions never collide (AddSource + AddReusedImage); social bar renders the full validated handle (MaxWidth=200 + TextTrimming removed from SocialBarRenderer and the preview template); side panels stay fixed 220/300; focus-loss capture lag documented in ai.md as a known OS limit (deferred) — the two real-App tests now share RealAppHost, a dedicated STA thread owning the single WPF App, instead of each calling new App(); new SourceNamingTests integration test (Text/Text2/Text3, delete-middle re-add no-collision) — 170 tests passing, 0 warnings
This commit is contained in:
@@ -0,0 +1,65 @@
|
||||
using System;
|
||||
using System.Linq;
|
||||
using Microsoft.Data.Sqlite;
|
||||
using Xunit;
|
||||
using ytLive.Models;
|
||||
using ytLive.ViewModels;
|
||||
|
||||
namespace ytLive.Tests;
|
||||
|
||||
/// <summary>
|
||||
/// Duplicate source names get an incrementing suffix with no space (Image,
|
||||
/// Image2, Image3…) and the next free number is derived from the names actually
|
||||
/// in the scene — deleting a middle resource never collides with a survivor.
|
||||
/// Drives the real VM through the Add Source command (TextOverlay needs no
|
||||
/// dialog), the same real-App + temp-DB pattern as the round-clip test.
|
||||
/// </summary>
|
||||
[Collection("RealApp")]
|
||||
public sealed class SourceNamingTests
|
||||
{
|
||||
private readonly RealAppHost _app;
|
||||
|
||||
public SourceNamingTests(RealAppHost app)
|
||||
{
|
||||
_app = app;
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Duplicate_Sources_Get_Next_Free_Numbered_Name()
|
||||
{
|
||||
_app.Run(Run);
|
||||
}
|
||||
|
||||
private void Run()
|
||||
{
|
||||
var tempDb = System.IO.Path.Combine(System.IO.Path.GetTempPath(), $"ytLlive-srcname-{Guid.NewGuid():N}.db");
|
||||
MainViewModel.LayoutPathOverride = tempDb;
|
||||
var window = new MainWindow();
|
||||
var vm = (MainViewModel)window.DataContext;
|
||||
try
|
||||
{
|
||||
var scene = vm.ActiveScene!;
|
||||
|
||||
vm.AddSourceCommand.Execute(SourceType.TextOverlay);
|
||||
vm.AddSourceCommand.Execute(SourceType.TextOverlay);
|
||||
vm.AddSourceCommand.Execute(SourceType.TextOverlay);
|
||||
|
||||
var names = scene.Elements.OfType<Source>().Select(s => s.Name).ToList();
|
||||
Assert.Equal(new[] { "Text", "Text2", "Text3" }, names);
|
||||
|
||||
var middle = scene.Elements.OfType<Source>().Single(s => s.Name == "Text2");
|
||||
scene.Elements.Remove(middle);
|
||||
vm.AddSourceCommand.Execute(SourceType.TextOverlay);
|
||||
|
||||
var survivors = scene.Elements.OfType<Source>().Select(s => s.Name).ToList();
|
||||
Assert.Equal(new[] { "Text", "Text3", "Text2" }, survivors);
|
||||
}
|
||||
finally
|
||||
{
|
||||
window.Close();
|
||||
MainViewModel.LayoutPathOverride = null;
|
||||
SqliteConnection.ClearAllPools();
|
||||
try { System.IO.File.Delete(tempDb); } catch { /* best-effort cleanup */ }
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user