Creator ruling 2026-11-13: "the app does three things. builds. records. live-streams." So the bar holds three buttons that never disappear; the active one is lit and runs its own flow in place, and the other two dim but stay clickable, so there is no state where the creator cannot see where they are or how to step back. Build [Build Scenes] -> editing on, nothing to start YouTube [Live][Private] -> [Start] -> [Stop] (Private = the private-visibility test stream) Record [Start Recording] -> [● REC hh:mm:ss][Stop Recording] Arming and starting are separate presses on purpose (creator): Start is the last chance to tell someone "check you are ready". Collapsing them would save one click and lose that prompt. This replaces the two-world model (BarWorld) and its output stage outright, including the chips added earlier today. The old pills remain the underlying state (OnAirPillOn/RecordPillOn) so the start, stop and step-down paths are untouched; the activity only decides what the bar shows. Closing the app no longer waits 6 s for a session that cannot stop: it waits ~2 s, then clears the state and closes anyway. A window that refuses to close is worse than a stale flag — and that refusal is what hung the suite in ZoomBoxInteractionTests, which sets IsRecording and closes a real window. Test: TopBarActivityTests pins all three flows, the arm-then-start split, and that stepping back to Build forgets the YouTube choice instead of carrying it. TopBarWorldTests is retired with the model it tested.
137 lines
7.8 KiB
C#
137 lines
7.8 KiB
C#
using System.Linq;
|
|
using Xunit;
|
|
|
|
namespace ytLive.Tests;
|
|
|
|
/// <summary>The creator's six corrections (2026-10-30) are visual rulings, so the only honest guard
|
|
/// is one that pins the ruling to the markup: if a later edit quietly reverts one of them, this
|
|
/// fails and names it.
|
|
///
|
|
/// <para>1. Stream Info + YPP rails are YouTube surfaces (YouTube red). 2. EDIT shows a real
|
|
/// off/on difference. 3. The active scene outline is orange again. 4. Scene labels wear the same
|
|
/// pill as the hotkey chips. 5. REC / ON-AIR and the running stop button read red. 6. The
|
|
/// properties list no longer caps its height, so no scrollbar appears while space remains.</para></summary>
|
|
public class UiShellTests
|
|
{
|
|
[Fact]
|
|
public void The_creators_six_ui_corrections_are_in_the_markup()
|
|
{
|
|
var preview = SourceAssert.Text("Controls", "PreviewPane.xaml");
|
|
var topBar = SourceAssert.Text("Controls", "TopBar.xaml");
|
|
var strip = SourceAssert.Text("Controls", "SceneThumbnailStrip.xaml");
|
|
var leftPanel = SourceAssert.Text("Controls", "LeftPanel.xaml");
|
|
var theme = SourceAssert.Text("Themes", "Controls.xaml");
|
|
var tokens = SourceAssert.Text("Themes", "Tokens.xaml");
|
|
|
|
// 1. YouTube-branded rails.
|
|
Assert.Contains("Brush.Youtube", tokens);
|
|
Assert.Equal(2, Count(preview, "Background=\"{StaticResource Brush.Youtube}\""));
|
|
|
|
// 2. The world segment replaced the EDIT toggle (creator ruling 2026-11-12): both halves are
|
|
// present, each lit by its own world flag through the shared style.
|
|
Assert.Contains("Text=\"Build Scenes\"", topBar);
|
|
Assert.Contains("Text=\"YouTube\"", topBar);
|
|
Assert.Contains("Text=\"Record\"", topBar);
|
|
Assert.Contains("Tag=\"{Binding IsBuildActivity}\"", topBar);
|
|
Assert.Contains("Tag=\"{Binding IsYouTubeActivity}\"", topBar);
|
|
Assert.DoesNotContain("x:Name=\"EditBody\"", topBar);
|
|
|
|
// 2b. The segment is Build-world only: choosing Record / Go Live REPLACES it with the output
|
|
// line (creator ruling 2026-11-12), and a chevron is the way back.
|
|
Assert.Contains("Data=\"M3,4h8v6H3V4zM13,4h8v6h-8V4zM3,12h8v8H3V12zM13,12h8v8h-8V12z\"", topBar);
|
|
// Segmented switcher (creator mock 2026-11-12): pill track, two equal halves, coral active icon.
|
|
Assert.Contains("Brush.SegmentTrack", topBar);
|
|
Assert.Contains("Style=\"{StaticResource SegmentHalf}\"", topBar);
|
|
Assert.Contains("Style=\"{StaticResource SegmentIcon}\"", topBar);
|
|
|
|
|
|
|
|
Assert.DoesNotContain("ToolTip=\"Back to Build Scenes\"", topBar); // Esc replaced the chevron
|
|
|
|
// 2c. The output stage: two labelled switches and the exclusivity promise beside them.
|
|
|
|
Assert.Contains("Text=\"YouTube\"", topBar);
|
|
Assert.Contains("Command=\"{Binding SelectYouTubeActivityCommand}\"", topBar);
|
|
Assert.Contains("Command=\"{Binding SelectRecordActivityCommand}\"", topBar);
|
|
|
|
// 2d. The bar carries NO action button: the output line is the bar's whole middle
|
|
// (creator ruling 2026-11-12 — the action is placed elsewhere, on instruction).
|
|
Assert.DoesNotContain("Content=\"Start Recording\"", topBar);
|
|
Assert.DoesNotContain("Content=\"Private Test Stream\"", topBar);
|
|
Assert.DoesNotContain("Content=\"Review & Go Live\"", topBar);
|
|
|
|
// 3. Active scene = orange; live stays red.
|
|
Assert.Contains("IsStaged}\" Value=\"True\">\n <Setter Property=\"BorderBrush\" Value=\"{StaticResource Brush.Attention}\"/>", strip);
|
|
|
|
// 4. Scene name pill, same shape as the hotkey chip.
|
|
Assert.Contains("Padding=\"8,2\" CornerRadius=\"10\" Background=\"#000000\"", strip);
|
|
Assert.Contains("Text=\"{Binding Name, Mode=OneWay}\"", strip);
|
|
|
|
// 5. Armed session controls are red, and the running stop button is red — not a grey literal.
|
|
Assert.Contains("x:Key=\"RecordPillToggle\"", theme);
|
|
// One rule for session controls: white ink in every state, the fill carries the state.
|
|
// The missing base foreground is what painted the REC/ON-AIR labels black (2026-10-30).
|
|
var pill = theme[theme.IndexOf("x:Key=\"RecordPillToggle\"", System.StringComparison.Ordinal)..];
|
|
pill = pill[..pill.IndexOf("</Style>", System.StringComparison.Ordinal)];
|
|
Assert.Contains("<Setter Property=\"Foreground\" Value=\"{StaticResource Brush.OnAccent}\"/>", pill);
|
|
Assert.Contains("Brush.Primary", theme);
|
|
Assert.Contains("Style=\"{StaticResource OutputChip}\"", topBar);
|
|
Assert.Contains("Background=\"{StaticResource Brush.Primary}\" Margin=\"14,0,0,0\"", topBar);
|
|
Assert.DoesNotContain("Background=\"#333\"", topBar);
|
|
|
|
// 6. No artificial height cap on the properties list: the list fills the row, so a
|
|
// scrollbar appears only when the content genuinely exceeds the space.
|
|
var listLine = leftPanel.Split('\n').First(line => line.Contains("ElementsScrollViewer"));
|
|
Assert.DoesNotContain("MaxHeight", listLine);
|
|
}
|
|
|
|
[Fact]
|
|
public void The_wordmark_ty_stays_youtube_red_and_ctrl_opens_the_context_menu()
|
|
{
|
|
var topBar = SourceAssert.Text("Controls", "TopBar.xaml");
|
|
// Adjacent, with nothing between: whitespace inside a TextBlock between inline Runs
|
|
// collapses to a space and prints "LlamaCas ty" (this exact bug shipped once).
|
|
Assert.Contains("<Run Text=\"LlamaCas\" Foreground=\"{StaticResource Brush.OnSurface}\"/><Run Text=\"ty\" Foreground=\"{StaticResource Brush.Youtube}\"/>", topBar);
|
|
|
|
// Plain right-click is the zoom box; the context menu moved to Ctrl+right-click so a
|
|
// creator can change capture sources mid-stream.
|
|
var preview = SourceAssert.Text("Controls", "PreviewPane.xaml.cs");
|
|
Assert.Contains("ModifierKeys.Control", preview);
|
|
Assert.Contains("return;", preview);
|
|
}
|
|
|
|
[Fact]
|
|
public void Settings_gear_is_orange_on_the_preview_rail_and_EDIT_precedes_the_session_cluster()
|
|
{
|
|
var topBar = SourceAssert.Text("Controls", "TopBar.xaml");
|
|
var preview = SourceAssert.Text("Controls", "PreviewPane.xaml");
|
|
|
|
// The settings gear moved from the top bar to the first orange preview-rail tab.
|
|
var settingsTooltip = preview.IndexOf("ToolTip=\"Settings\"", System.StringComparison.Ordinal);
|
|
Assert.True(settingsTooltip >= 0, "the Settings gear tab is missing from the preview rail");
|
|
var gearStart = preview.LastIndexOf("<Border ", settingsTooltip, System.StringComparison.Ordinal);
|
|
var gearEnd = preview.IndexOf("</Border>", settingsTooltip, System.StringComparison.Ordinal);
|
|
Assert.True(gearStart >= 0 && gearEnd > settingsTooltip, "the Settings tab has no bordered gear surface");
|
|
var gear = preview[gearStart..gearEnd];
|
|
Assert.Contains("Background=\"{StaticResource Brush.Attention}\"", gear);
|
|
Assert.Contains("MouseLeftButtonUp=\"SettingsTab_Click\"", gear);
|
|
Assert.DoesNotContain("ToolTip=\"Settings\"", topBar);
|
|
|
|
// The world segment leads the centre cluster; the brand column carries no edit control.
|
|
var centre = topBar.IndexOf("<StackPanel Grid.Column=\"1\"", System.StringComparison.Ordinal);
|
|
var segment = topBar.IndexOf("Text=\"Build Scenes\"", System.StringComparison.Ordinal);
|
|
Assert.True(centre > 0 && segment > centre,
|
|
"the three-activity segment must lead the centre cluster");
|
|
Assert.DoesNotContain("x:Name=\"EditBody\"", topBar);
|
|
}
|
|
|
|
private static int Count(string haystack, string needle)
|
|
{
|
|
var hits = 0;
|
|
for (var i = haystack.IndexOf(needle, System.StringComparison.Ordinal); i >= 0;
|
|
i = haystack.IndexOf(needle, i + needle.Length, System.StringComparison.Ordinal))
|
|
hits++;
|
|
return hits;
|
|
}
|
|
}
|