feat: add configurable operator appearance and layout
This commit is contained in:
@@ -13,9 +13,14 @@ public sealed class LegacyOperatorSettingsWebContractTests
|
||||
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 SettingsHost = File.ReadAllText(Path.Combine(
|
||||
RepositoryRoot,
|
||||
"src",
|
||||
"MBN_STOCK_WEBVIEW.LegacyParityApp",
|
||||
"MainWindow.Settings.cs"));
|
||||
|
||||
[Fact]
|
||||
public void SettingsScreenOffersThreeNativeFolderPickersAndRestartFeedback()
|
||||
public void SettingsScreenKeepsThreeNativeFolderPickersWithConciseFeedback()
|
||||
{
|
||||
var expectedKinds = new[] { "design", "resource", "background" };
|
||||
var actualKinds = Regex.Matches(Markup, "data-operator-folder=\"([^\"]+)\"")
|
||||
@@ -33,6 +38,9 @@ public sealed class LegacyOperatorSettingsWebContractTests
|
||||
StringComparison.Ordinal);
|
||||
Assert.Contains($"id=\"operator-{kind}-folder-reset\"", Markup,
|
||||
StringComparison.Ordinal);
|
||||
Assert.Matches(
|
||||
$"id=\"operator-{kind}-folder-source\"[^>]*hidden",
|
||||
Markup);
|
||||
}
|
||||
|
||||
Assert.Matches(
|
||||
@@ -41,10 +49,31 @@ public sealed class LegacyOperatorSettingsWebContractTests
|
||||
Assert.Matches(
|
||||
"id=\"operator-settings-restart\"[^>]*role=\"status\"[^>]*aria-live=\"polite\"",
|
||||
Markup);
|
||||
Assert.Contains("앱을 다시 시작하면 새 폴더가 적용됩니다.", Markup,
|
||||
Assert.Contains("<h3>폴더</h3>", Markup, StringComparison.Ordinal);
|
||||
Assert.Contains("<h3>메뉴</h3>", Markup, StringComparison.Ordinal);
|
||||
Assert.Contains("<h3>상태</h3>", Markup, StringComparison.Ordinal);
|
||||
Assert.Contains("재시작하면 적용됩니다.", Markup,
|
||||
StringComparison.Ordinal);
|
||||
Assert.Contains("id=\"operator-design-folder-name\">앱 기본값", Markup,
|
||||
StringComparison.Ordinal);
|
||||
Assert.Contains("id=\"operator-resource-folder-name\">앱 기본값", Markup,
|
||||
StringComparison.Ordinal);
|
||||
Assert.Contains("id=\"operator-background-folder-name\">자동", Markup,
|
||||
StringComparison.Ordinal);
|
||||
Assert.Contains("id=\"operator-navigation-expanded\"", Markup,
|
||||
StringComparison.Ordinal);
|
||||
Assert.Contains("메뉴 펼치기", Markup, StringComparison.Ordinal);
|
||||
Assert.Contains("settings-status-badge.valid", Styles, StringComparison.Ordinal);
|
||||
Assert.Contains("settings-status-badge.invalid", Styles, StringComparison.Ordinal);
|
||||
|
||||
Assert.DoesNotContain("settings-intro", Markup, StringComparison.Ordinal);
|
||||
Assert.DoesNotContain("이 PC에서 사용할 디자인·설정·배경 폴더", Markup,
|
||||
StringComparison.Ordinal);
|
||||
Assert.DoesNotContain("경로를 직접 입력하지 않고", Markup,
|
||||
StringComparison.Ordinal);
|
||||
Assert.DoesNotContain("송출 장면(.t2s)과 장면별 디자인 자산", Markup,
|
||||
StringComparison.Ordinal);
|
||||
Assert.DoesNotContain("핵심 자산 확인됨", Markup, StringComparison.Ordinal);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
@@ -67,6 +96,63 @@ public sealed class LegacyOperatorSettingsWebContractTests
|
||||
Assert.DoesNotContain("displayName", dispatch, StringComparison.Ordinal);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void AppearanceSettingsExposeThreeConciseClosedChoices()
|
||||
{
|
||||
var appearance = Slice(
|
||||
Markup,
|
||||
"<article class=\"settings-card settings-appearance-card\">",
|
||||
"<article class=\"settings-card\">");
|
||||
|
||||
Assert.Contains("<h3>화면</h3>", appearance, StringComparison.Ordinal);
|
||||
Assert.Contains("id=\"operator-color-theme\"", appearance, StringComparison.Ordinal);
|
||||
Assert.Contains("value=\"system\">자동", appearance, StringComparison.Ordinal);
|
||||
Assert.Contains("value=\"light\">라이트", appearance, StringComparison.Ordinal);
|
||||
Assert.Contains("value=\"dark\">다크", appearance, StringComparison.Ordinal);
|
||||
Assert.Contains("id=\"operator-view-mode\"", appearance, StringComparison.Ordinal);
|
||||
Assert.Contains("value=\"automatic\">자동", appearance, StringComparison.Ordinal);
|
||||
Assert.Contains("value=\"compact\">컴팩트", appearance, StringComparison.Ordinal);
|
||||
Assert.Contains("value=\"cards\">카드", appearance, StringComparison.Ordinal);
|
||||
Assert.Contains("id=\"operator-start-workspace\"", appearance,
|
||||
StringComparison.Ordinal);
|
||||
Assert.Contains("value=\"stockCut\">종목·컷", appearance, StringComparison.Ordinal);
|
||||
Assert.Contains("value=\"lastWorkspace\">마지막 화면", appearance,
|
||||
StringComparison.Ordinal);
|
||||
Assert.DoesNotContain("<p", appearance, StringComparison.OrdinalIgnoreCase);
|
||||
Assert.DoesNotContain("<small", appearance, StringComparison.OrdinalIgnoreCase);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void AppearanceSettingsApplyClosedValuesAndDispatchOneCompleteSnapshot()
|
||||
{
|
||||
var render = Slice(App, "function normalizeOperatorAppearanceValue", "function render(state)");
|
||||
var dispatch = Slice(
|
||||
App,
|
||||
"Object.keys(operatorFolderControls).forEach(function (kind)",
|
||||
"document.addEventListener(\"input\"");
|
||||
|
||||
Assert.Contains("definition.values.includes(value)", render, StringComparison.Ordinal);
|
||||
Assert.Contains("element.dataset.colorTheme = appearance.colorTheme", render,
|
||||
StringComparison.Ordinal);
|
||||
Assert.Contains("element.dataset.viewMode = appearance.viewMode", render,
|
||||
StringComparison.Ordinal);
|
||||
Assert.Contains("element.dataset.startWorkspace = appearance.startWorkspace", render,
|
||||
StringComparison.Ordinal);
|
||||
Assert.Contains("operatorAppearanceControls[key].element.disabled = isBusy", render,
|
||||
StringComparison.Ordinal);
|
||||
Assert.Contains("applyStartPreference(appearance.startWorkspace)", render,
|
||||
StringComparison.Ordinal);
|
||||
Assert.DoesNotContain("isInitialSettingsSnapshot", render, StringComparison.Ordinal);
|
||||
Assert.Contains("send(\"set-operator-appearance\", {", dispatch,
|
||||
StringComparison.Ordinal);
|
||||
Assert.Contains("colorTheme: appearance.colorTheme", dispatch, StringComparison.Ordinal);
|
||||
Assert.Contains("viewMode: appearance.viewMode", dispatch, StringComparison.Ordinal);
|
||||
Assert.Contains("startWorkspace: appearance.startWorkspace", dispatch,
|
||||
StringComparison.Ordinal);
|
||||
Assert.Contains("syncOperatorAppearanceControls(operatorAppearanceSnapshot)", dispatch,
|
||||
StringComparison.Ordinal);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void RepeatedSettingsStateKeepsStaticControlsAndAppliesNavigationByRevision()
|
||||
{
|
||||
@@ -96,7 +182,7 @@ public sealed class LegacyOperatorSettingsWebContractTests
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void SettingsShowsSafeDiagnosticsAndNamesProtectedValues()
|
||||
public void SettingsKeepsSafeDiagnosticsWithoutProtectedValueExplanations()
|
||||
{
|
||||
foreach (var id in new[]
|
||||
{
|
||||
@@ -114,10 +200,26 @@ public sealed class LegacyOperatorSettingsWebContractTests
|
||||
Assert.Contains("id=\"operator-navigation-expanded\"", Markup,
|
||||
StringComparison.Ordinal);
|
||||
Assert.Contains("databasePollingSeconds", App, StringComparison.Ordinal);
|
||||
Assert.Contains("DB 자격 증명", Markup, StringComparison.Ordinal);
|
||||
Assert.Contains("Tornado2 송출 대상", Markup, StringComparison.Ordinal);
|
||||
Assert.Contains("Live/DryRun 권한", Markup, StringComparison.Ordinal);
|
||||
Assert.Contains("장면 허용 목록", Markup, StringComparison.Ordinal);
|
||||
Assert.Contains("LegacyOperatorFolderValidator.TryValidate", SettingsHost,
|
||||
StringComparison.Ordinal);
|
||||
Assert.Contains("CanReadSelectedDatabaseProfile", SettingsHost,
|
||||
StringComparison.Ordinal);
|
||||
Assert.Contains("ResolveDatabaseHealthLabel", SettingsHost,
|
||||
StringComparison.Ordinal);
|
||||
|
||||
Assert.Contains("\"저장됨\"", SettingsHost, StringComparison.Ordinal);
|
||||
Assert.Contains("\"저장됨 · 재시작 필요\"", SettingsHost,
|
||||
StringComparison.Ordinal);
|
||||
Assert.Contains("\"초기화됨 · 재시작 필요\"", SettingsHost,
|
||||
StringComparison.Ordinal);
|
||||
Assert.Contains("\"사용 가능\"", SettingsHost, StringComparison.Ordinal);
|
||||
Assert.DoesNotContain("외부 영상", SettingsHost, StringComparison.Ordinal);
|
||||
|
||||
Assert.DoesNotContain("보호되는 운영 설정", Markup, StringComparison.Ordinal);
|
||||
Assert.DoesNotContain("DB 자격 증명", Markup, StringComparison.Ordinal);
|
||||
Assert.DoesNotContain("Tornado2 송출 대상", Markup, StringComparison.Ordinal);
|
||||
Assert.DoesNotContain("Live/DryRun 권한", Markup, StringComparison.Ordinal);
|
||||
Assert.DoesNotContain("장면 허용 목록", Markup, StringComparison.Ordinal);
|
||||
}
|
||||
|
||||
private static string Slice(string source, string startMarker, string endMarker)
|
||||
|
||||
Reference in New Issue
Block a user