Implements the creator's mock (2026-11-12) for the world segment: pill track (#1E222B, radius 12, padding 4) holding two equal halves; the active half sits on a lighter pill (#2E3440, radius 8) with #E2E8F0 bold text and a coral (#F26D6D) icon; the inactive half is #8892A4 on a one-alpha black face so the whole area hit-tests; hover tints #252A34 on the inactive half only, so the active one never looks clickable-but-dim. Labels are "Build scenes" / "Record / go live". Two deliberate deviations from the pasted recipe, both to honour this repo: - State is the existing BarWorld binding plus a command, not a code-behind SetActiveMode — one source of truth, and the change still raises the same property-changed notification a mode event would. - Styles live in Themes/Controls.xaml (SegmentHalf, SegmentIcon) with colours as tokens in Tokens.xaml, rather than inline properties on the elements: AGENTS requires shared styles, and one DataTrigger on each element's own Tag serves both halves. DesignTokensTests caught the one raw literal (#01000000) and it is now Brush.SegmentHitArea, which is the rule working as intended. 680 pass, 0 warnings. Scope justification: Assets/starting-background.jpg is outside this unit — the creator's replacement art, still awaiting their visual check.
138 lines
8.1 KiB
C#
138 lines
8.1 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=\"Record / go live\"", topBar);
|
|
Assert.Contains("Tag=\"{Binding IsBuildWorld}\"", topBar);
|
|
Assert.Contains("Tag=\"{Binding IsOutputWorld}\"", 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.Contains("Visibility=\"{Binding IsBuildWorld, Converter={StaticResource BoolToVis}}\"", topBar);
|
|
Assert.Contains("Text=\"Output:\"", topBar);
|
|
Assert.Contains("Text=\"press <esc> to return\"", 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=\"Output:\"", topBar);
|
|
Assert.Contains("Text=\"YouTube\"", topBar);
|
|
Assert.Contains("Text=\"Local MP4\"", topBar);
|
|
Assert.Contains("Text=\"One output at a time\"", topBar);
|
|
Assert.Contains("IsChecked=\"{Binding OnAirPillOn}\"", topBar);
|
|
Assert.Contains("IsChecked=\"{Binding RecordPillOn}\"", 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 RecordPillToggle}\"", 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 [ Build Scenes ][ Record / Go Live ] 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;
|
|
}
|
|
}
|