3a4e17c735
Creator on the live build: 'why do you make the youtube user icon go away?'
The TASK 42 one-surface bar had scoped the account zone to Stream only
('Record world has no YouTube identity at all'), so arming REC dropped the
avatar and the bar height bounced with it ('really fucking annoying').
Fix: ShowSignInButton / ShowAvatarButton are now plain !IsConnected /
IsConnected — app-level identity, present in both worlds, so the right
column never pops in/out and the bar height stays pinned. Test remains a
child of Stream (still stream-armed AND signed-in).
TopBarModeTests updated to the new contract: signed-out Record world shows
Sign In; flipping back to REC keeps the avatar. PillRadioTests untouched
(it only exercises the ON-AIR face). Docs corrected in-place (ai.md state
model, TASK 42 scope). Gate: clean build 0-warnings, 316/316, scope passed.
109 lines
4.3 KiB
C#
109 lines
4.3 KiB
C#
using System;
|
|
using System.IO;
|
|
using Microsoft.Data.Sqlite;
|
|
using Xunit;
|
|
using ytLive.Models;
|
|
using ytLive.Services;
|
|
using ytLive.ViewModels;
|
|
|
|
namespace ytLive.Tests;
|
|
|
|
/// <summary>
|
|
/// Top bar redesign — the bar is ONE surface rendered by the FIRST decision
|
|
/// (Record or Stream). Each world is the whole bar: Test is a child of Stream,
|
|
/// but the account zone (Sign In / avatar) is WORLD-INDEPENDENT (creator
|
|
/// 2026-09-23) so the right column never pops in and out and the bar height
|
|
/// stays pinned. The switch is a two-position gate, so "going back" is one tap
|
|
/// on the other segment. While running, the switch retires and a single reality
|
|
/// line (dot + word + elapsed) + End replace it.
|
|
/// </summary>
|
|
[Collection("RealApp")]
|
|
public sealed class TopBarModeTests
|
|
{
|
|
private readonly RealAppHost _app;
|
|
|
|
public TopBarModeTests(RealAppHost app) => _app = app;
|
|
|
|
[Fact]
|
|
public void TopBar_Renders_Only_The_Armed_World()
|
|
{
|
|
_app.Run(Run);
|
|
}
|
|
|
|
private void Run()
|
|
{
|
|
var tempDb = Path.Combine(Path.GetTempPath(), $"ytLlive-topbar-{Guid.NewGuid():N}.db");
|
|
MainViewModel.LayoutPathOverride = tempDb;
|
|
try
|
|
{
|
|
using (var schema = new LayoutStore(tempDb)) { }
|
|
SqliteConnection.ClearAllPools();
|
|
|
|
var vm = new MainViewModel();
|
|
|
|
// The dev machine may hold a real saved OAuth session (loads synchronously
|
|
// during the ctor), so the sign-in state is forced deterministically.
|
|
vm.IsConnected = false;
|
|
|
|
// Default (nothing armed) = the Record world: Go says "Start Recording",
|
|
// no Test — but the account zone is app-level and stays (signed out →
|
|
// Sign In visible even here; no avatar).
|
|
Assert.True(vm.ShowIdleWorld);
|
|
Assert.True(vm.ShowRecordAction);
|
|
Assert.False(vm.ShowStreamActions);
|
|
Assert.True(vm.ShowSignInButton);
|
|
Assert.False(vm.ShowAvatarButton);
|
|
Assert.False(vm.ShowTestButton);
|
|
Assert.Equal("Start Recording", vm.PrimaryStartButtonLabel);
|
|
|
|
// Arming ON-AIR switches the whole bar to the Stream world.
|
|
vm.OnAirPillOn = true;
|
|
Assert.False(vm.ShowRecordAction);
|
|
Assert.True(vm.ShowStreamActions);
|
|
|
|
// Signed out: the account zone shows Sign In; no avatar; Test is gated
|
|
// behind the sign-in (Test rides on Stream AND a connected account).
|
|
Assert.True(vm.ShowSignInButton);
|
|
Assert.False(vm.ShowAvatarButton);
|
|
Assert.False(vm.ShowTestButton);
|
|
Assert.Equal("Go Live", vm.PrimaryStartButtonLabel);
|
|
|
|
// Connected: avatar + Test join the Stream world; Sign In retires.
|
|
vm.IsConnected = true;
|
|
Assert.False(vm.ShowSignInButton);
|
|
Assert.True(vm.ShowAvatarButton);
|
|
Assert.True(vm.ShowTestButton);
|
|
|
|
// Going back is one tap: flipping REC returns the bar to the Record world —
|
|
// the avatar STAYS (world-independent account zone), only Test retires.
|
|
vm.RecordPillOn = true;
|
|
Assert.False(vm.ShowStreamActions);
|
|
Assert.True(vm.ShowRecordAction);
|
|
Assert.True(vm.ShowAvatarButton);
|
|
Assert.False(vm.ShowTestButton);
|
|
Assert.Equal("Start Recording", vm.PrimaryStartButtonLabel);
|
|
|
|
// Running retires the switch and worlds; the reality line pins the mode.
|
|
vm.RecordPillOn = false;
|
|
vm.OnAirPillOn = true;
|
|
vm.IsRecording = true;
|
|
Assert.False(vm.ShowIdleWorld);
|
|
Assert.True(vm.RunningVisible);
|
|
Assert.Equal("REC", vm.RunningText);
|
|
Assert.Equal("#22c55e", vm.RunningDotBrush);
|
|
vm.IsRecording = false;
|
|
vm.StreamStatus = StreamStatus.Streaming;
|
|
Assert.Equal("LIVE", vm.RunningText);
|
|
Assert.Equal("#e94560", vm.RunningDotBrush);
|
|
vm.IsTestStream = true;
|
|
Assert.Equal("TEST", vm.RunningText);
|
|
Assert.Equal("#9c6f1c", vm.RunningDotBrush);
|
|
}
|
|
finally
|
|
{
|
|
MainViewModel.LayoutPathOverride = null;
|
|
SqliteConnection.ClearAllPools();
|
|
try { File.Delete(tempDb); } catch { /* best-effort cleanup */ }
|
|
}
|
|
}
|
|
} |