using System;
using System.IO;
using Xunit;
using ytLive.ViewModels;
namespace ytLive.Tests;
///
/// The top bar's one decision: [ Build Scenes ][ Record / Go Live ]. Build Scenes ships
/// highlighted, and it is edit mode — the old EDIT toggle is gone, so "can I edit?" and "which
/// world am I in?" cannot disagree.
///
[Collection("MainViewModelLayoutPath")]
public class TopBarWorldTests
{
[Fact]
public void The_output_stage_belongs_to_the_output_world_and_holds_one_output_at_a_time()
{
var db = Path.Combine(Path.GetTempPath(), $"llamacasty-outputstage-{Guid.NewGuid():N}.db");
MainViewModel.LayoutPathOverride = db;
try
{
var vm = new MainViewModel();
// Nothing to arm while building scenes.
Assert.False(vm.ShowOutputStage);
vm.EnterOutputWorldCommand.Execute(null);
Assert.True(vm.IsOutputWorld);
Assert.True(vm.IsOffline);
Assert.False(vm.IsRecording);
Assert.True(vm.ShowOutputStage);
// Entering the output world starts with NOTHING chosen (creator, build 729); the pair is
// one axis, so arming either drops the other.
Assert.False(vm.RecordPillOn);
Assert.False(vm.OnAirPillOn);
vm.OnAirPillOn = true;
Assert.True(vm.OnAirPillOn);
Assert.False(vm.RecordPillOn);
vm.RecordPillOn = true;
Assert.False(vm.OnAirPillOn);
vm.RecordPillOn = false; // back to "nothing chosen" for the state checks below
// Nothing chosen yet: both output chips, no action.
Assert.True(vm.ShowOutputChoice);
Assert.False(vm.ShowYouTubeSelected);
Assert.False(vm.ShowLocalMp4Selected);
Assert.True(vm.YouTubeChipVisible);
Assert.True(vm.LocalMp4ChipVisible);
// YouTube chosen: its chip stays (red), two actions appear, MP4's chip goes away.
vm.SelectYouTubeCommand.Execute(null);
Assert.True(vm.ShowYouTubeSelected);
Assert.False(vm.ShowOutputChoice);
Assert.True(vm.YouTubeChipVisible);
Assert.False(vm.LocalMp4ChipVisible);
// Local MP4 chosen: it takes the left slot, one action, and Start/End swap on recording.
vm.SelectLocalMp4Command.Execute(null);
Assert.True(vm.ShowLocalMp4Selected);
Assert.False(vm.YouTubeChipVisible);
Assert.True(vm.LocalMp4ChipVisible);
Assert.True(vm.ShowStartRecordingAction);
Assert.False(vm.ShowEndRecordingAction);
Assert.Equal("Local MP4", vm.RecordChipText);
// Recording: the pill carries REC + the clock, the stage stays up, and the live reality
// line does not (its dot/elapsed/stop would be a second set of the same facts).
vm.IsRecording = true;
Assert.True(vm.ShowOutputStage);
Assert.False(vm.RunningVisible);
Assert.False(vm.ShowStartRecordingAction);
Assert.True(vm.ShowEndRecordingAction);
Assert.StartsWith("REC: ", vm.RecordChipText);
vm.IsRecording = false;
Assert.Equal("Local MP4", vm.RecordChipText);
// Neither action exists while building scenes.
vm.EnterBuildWorldCommand.Execute(null);
Assert.False(vm.ShowStartRecordingAction);
Assert.False(vm.ShowEndRecordingAction);
Assert.False(vm.YouTubeChipVisible);
Assert.False(vm.LocalMp4ChipVisible);
}
finally
{
MainViewModel.LayoutPathOverride = null;
try { File.Delete(db); } catch (IOException) { }
}
}
/// Closing mid-session must end the session first (2026-11-12). The clear case: with
/// nothing running, closing is immediate — no hold, no wait.
[Fact]
public async Task Closing_with_nothing_running_does_not_hold_the_window()
{
var db = Path.Combine(Path.GetTempPath(), $"llamacasty-shutdown-{Guid.NewGuid():N}.db");
MainViewModel.LayoutPathOverride = db;
try
{
var vm = new MainViewModel();
var started = DateTime.UtcNow;
Assert.True(await vm.StopBeforeShutdownAsync());
Assert.True(DateTime.UtcNow - started < TimeSpan.FromSeconds(1));
}
finally
{
MainViewModel.LayoutPathOverride = null;
try { File.Delete(db); } catch (IOException) { }
}
}
///
/// The symptom the creator hit: pressing Start left the idle chip on screen with a dead command, so
/// the recording could not be stopped. Reading the property directly was never the test — the UI must
/// be TOLD the stage changed.
///
[Fact]
public void Starting_or_stopping_a_recording_announces_the_stage_change()
{
var db = Path.Combine(Path.GetTempPath(), $"llamacasty-recannounce-{Guid.NewGuid():N}.db");
MainViewModel.LayoutPathOverride = db;
try
{
var vm = new MainViewModel();
vm.EnterOutputWorldCommand.Execute(null);
vm.SelectLocalMp4Command.Execute(null);
var raised = new System.Collections.Generic.List();
vm.PropertyChanged += (_, e) => raised.Add(e.PropertyName ?? string.Empty);
vm.IsRecording = true;
Assert.Contains(nameof(MainViewModel.ShowEndRecordingAction), raised);
Assert.Contains(nameof(MainViewModel.ShowStartRecordingAction), raised);
Assert.Contains(nameof(MainViewModel.RecordChipText), raised);
raised.Clear();
vm.IsRecording = false;
Assert.Contains(nameof(MainViewModel.ShowStartRecordingAction), raised);
Assert.Contains(nameof(MainViewModel.RecordChipText), raised);
}
finally
{
MainViewModel.LayoutPathOverride = null;
try { File.Delete(db); } catch (IOException) { }
}
}
[Fact]
public void Build_scenes_is_the_default_world_and_is_the_only_place_editing_lives()
{
var db = Path.Combine(Path.GetTempPath(), $"llamacasty-world-{Guid.NewGuid():N}.db");
MainViewModel.LayoutPathOverride = db;
try
{
var vm = new MainViewModel();
// Launch state: Build Scenes highlighted, editing on.
Assert.Equal(BarWorld.Build, vm.BarWorld);
Assert.True(vm.IsBuildWorld);
Assert.False(vm.IsOutputWorld);
Assert.True(vm.IsEditMode);
// Record / Go Live leaves edit mode — the output surface has nothing to drag.
vm.EnterOutputWorldCommand.Execute(null);
Assert.Equal(BarWorld.Output, vm.BarWorld);
Assert.True(vm.IsOutputWorld);
Assert.False(vm.IsBuildWorld);
Assert.False(vm.IsEditMode);
// Clicking the highlighted half is not a no-op: it re-enters that world, which is how a
// creator takes editing back during a session.
vm.EnterBuildWorldCommand.Execute(null);
Assert.True(vm.IsBuildWorld);
Assert.True(vm.IsEditMode);
// …and re-entering the world already in force still re-asserts its edit state.
vm.EditingActive = false;
vm.EnterBuildWorldCommand.Execute(null);
Assert.True(vm.IsEditMode);
}
finally
{
MainViewModel.LayoutPathOverride = null;
try { File.Delete(db); } catch (IOException) { }
}
}
}