Files
MBN_STOCK_WEBVIEW/tests/MBN_STOCK_WEBVIEW.LegacyWeb.Tests/LegacyWorkspaceLayoutContractTests.cs

144 lines
6.4 KiB
C#

using System.Text.RegularExpressions;
namespace MBN_STOCK_WEBVIEW.LegacyWeb.Tests;
public sealed class LegacyWorkspaceLayoutContractTests
{
private static readonly string RepositoryRoot = FindRepositoryRoot();
private static readonly string WebRoot = Path.Combine(
RepositoryRoot,
"src",
"MBN_STOCK_WEBVIEW.LegacyParityApp",
"Web");
private static readonly string Markup = File.ReadAllText(Path.Combine(WebRoot, "index.html"));
private static readonly string Styles = File.ReadAllText(Path.Combine(WebRoot, "styles.css"));
private static readonly string App = File.ReadAllText(Path.Combine(WebRoot, "app.js"));
private static readonly string Navigation = File.ReadAllText(
Path.Combine(WebRoot, "workspace-navigation.js"));
[Fact]
public void ShellUsesSwitchableWorkspaceBesidePersistentSchedule()
{
var workspaceStart = Markup.IndexOf("id=\"workspace-region\"", StringComparison.Ordinal);
var stockStart = Markup.IndexOf("id=\"stock-workspace\"", StringComparison.Ordinal);
var catalogStart = Markup.IndexOf("id=\"catalog-workspace\"", StringComparison.Ordinal);
var scheduleStart = Markup.IndexOf("class=\"playlist-panel\"", StringComparison.Ordinal);
Assert.True(workspaceStart >= 0);
Assert.True(stockStart > workspaceStart);
Assert.True(catalogStart > stockStart);
Assert.True(scheduleStart > catalogStart);
Assert.Matches(
@"<\/section>\s*<\/div>\s*<\/section>\s*<\/section>\s*<section class=""playlist-panel""",
Markup);
Assert.Matches("id=\"catalog-workspace\"[^>]*hidden inert", Markup);
Assert.Contains("<small>항상 표시</small><h2>송출 스케줄</h2>", Markup,
StringComparison.Ordinal);
}
[Fact]
public void WorkAndScheduleUseResponsiveTwoToOneColumns()
{
Assert.Contains(
"grid-template-columns: minmax(900px, 2fr) minmax(600px, 1fr);",
Styles,
StringComparison.Ordinal);
Assert.Matches(
new Regex(
@"\.playlist-toolbar\s*\{[^}]*flex-wrap:\s*wrap",
RegexOptions.CultureInvariant),
Styles);
Assert.Matches(
new Regex(
@"\.playlist-row\s*\{[^}]*min-width:\s*600px",
RegexOptions.CultureInvariant),
Styles);
}
[Fact]
public void ModernOperatorThemePreservesAccessibleAndSemanticStates()
{
Assert.Contains(
"Modern operator theme: visual-only overrides; geometry and native contracts stay intact",
Styles,
StringComparison.Ordinal);
Assert.Contains("--ui-canvas:", Styles, StringComparison.Ordinal);
Assert.Contains("--ui-accent:", Styles, StringComparison.Ordinal);
Assert.Contains("--ui-success:", Styles, StringComparison.Ordinal);
Assert.Contains("--ui-danger:", Styles, StringComparison.Ordinal);
Assert.Contains(
"font-family: \"Segoe UI Variable\", \"Segoe UI\", \"Noto Sans KR\"",
Styles,
StringComparison.Ordinal);
Assert.Matches(
new Regex(
@"button:focus-visible,[\s\S]*?outline:\s*2px solid var\(--ui-accent\)",
RegexOptions.CultureInvariant),
Styles);
Assert.Contains(".playlist-data-row.on-air.selected", Styles, StringComparison.Ordinal);
Assert.Contains(".playout-actions .take-in:not(:disabled)", Styles, StringComparison.Ordinal);
Assert.Contains(".playout-actions .next:not(:disabled)", Styles, StringComparison.Ordinal);
Assert.Contains(".playout-actions .take-out:not(:disabled)", Styles, StringComparison.Ordinal);
Assert.Contains(
".playlist-data-row[data-active=\"true\"]:not(.on-air)",
Styles,
StringComparison.Ordinal);
Assert.Contains(
".playlist-data-row[aria-selected=\"true\"]:not(.on-air)",
Styles,
StringComparison.Ordinal);
Assert.Contains("@media (forced-colors: active)", Styles, StringComparison.Ordinal);
Assert.Contains("scrollbar-gutter: stable", Styles, StringComparison.Ordinal);
Assert.Contains("@media (prefers-reduced-motion: reduce)", Styles, StringComparison.Ordinal);
Assert.Contains("aria-keyshortcuts=\"F8\"", Markup, StringComparison.Ordinal);
Assert.Contains("aria-keyshortcuts=\"Escape\"", Markup, StringComparison.Ordinal);
Assert.Contains(
".connection-state.dry-run:not(.outcome-unknown)",
Styles,
StringComparison.Ordinal);
Assert.Contains("accessibleStates.push(\"현재 PGM\")", App, StringComparison.Ordinal);
Assert.Contains("accessibleStates.push(\"송출 후보\")", App, StringComparison.Ordinal);
Assert.Contains("accessibleStates.push(\"삭제 선택됨\")", App, StringComparison.Ordinal);
Assert.Matches(
new Regex(
@"id=""stock-search-button""[\s\S]*?<svg[^>]*viewBox=""0 0 24 24""",
RegexOptions.CultureInvariant),
Markup);
Assert.Matches(
new Regex(
@"class=""playlist-heading-icon""[\s\S]*?<svg[^>]*viewBox=""0 0 24 24""",
RegexOptions.CultureInvariant),
Markup);
}
[Fact]
public void NavigationChangesPresentationWithoutNativeOrStoredState()
{
Assert.Contains("entry.view.hidden = !isCurrent", Navigation, StringComparison.Ordinal);
Assert.Contains(
"entry.view.toggleAttribute(\"inert\", !isCurrent)",
Navigation,
StringComparison.Ordinal);
Assert.Contains("nav-collapsed", Navigation, StringComparison.Ordinal);
Assert.DoesNotContain("postMessage", Navigation, StringComparison.Ordinal);
Assert.DoesNotContain("localStorage", Navigation, StringComparison.Ordinal);
Assert.DoesNotContain("sessionStorage", Navigation, StringComparison.Ordinal);
}
private static string FindRepositoryRoot()
{
var directory = new DirectoryInfo(AppContext.BaseDirectory);
while (directory is not null)
{
if (File.Exists(Path.Combine(directory.FullName, "MBN_STOCK_WEBVIEW.sln")))
{
return directory.FullName;
}
directory = directory.Parent;
}
throw new DirectoryNotFoundException("Repository root could not be located.");
}
}