namespace MBN_STOCK_WEBVIEW.LegacyWeb.Tests; public sealed class LegacyOperatorFeedbackWebContractTests { private static readonly string App = File.ReadAllText(Path.Combine( FindRepositoryRoot(), "src", "MBN_STOCK_WEBVIEW.LegacyParityApp", "Web", "app.js")); [Fact] public void StockSearchFeedbackStaysConciseAndUsesNativeState() { var feedback = Slice( App, "function renderStockSearchMeta(state)", "function pointerPosition(event)"); Assert.Contains("state.isBusy === true", feedback, StringComparison.Ordinal); Assert.Contains("label = \"검색 중\"", feedback, StringComparison.Ordinal); Assert.Contains("String(results.length) + \"건\"", feedback, StringComparison.Ordinal); Assert.Contains("row.index === state.selectedStockIndex", feedback, StringComparison.Ordinal); Assert.Contains("selected.displayName", feedback, StringComparison.Ordinal); Assert.Contains("stockSearchMeta.hidden = label.length === 0", feedback, StringComparison.Ordinal); Assert.Contains("stockResults.dataset.emptyLabel = \"결과 없음\"", feedback, StringComparison.Ordinal); Assert.Contains("delete stockResults.dataset.emptyLabel", feedback, StringComparison.Ordinal); } [Fact] public void EmptyCutFeedbackDistinguishesStockSelectionAndClearsWhenRowsReturn() { var cuts = Slice(App, "function renderCuts(state)", "cutList.addEventListener"); Assert.Contains("state.cutRows.length === 0", cuts, StringComparison.Ordinal); Assert.Contains("Number.isInteger(state.selectedStockIndex)", cuts, StringComparison.Ordinal); Assert.Contains("? \"컷 없음\"", cuts, StringComparison.Ordinal); Assert.Contains(": \"종목을 선택하세요\"", cuts, StringComparison.Ordinal); Assert.Contains("delete cutList.dataset.emptyLabel", cuts, StringComparison.Ordinal); } [Fact] public void GlobalBusyFeedbackDoesNotMislabelEveryOperationAsSearch() { var render = Slice(App, "function render(state)", "Object.keys(operatorFolderControls)"); Assert.Contains("document.body.classList.toggle(\"busy\", isBusy)", render, StringComparison.Ordinal); Assert.Contains("document.body.setAttribute(\"aria-busy\"", render, StringComparison.Ordinal); Assert.Contains("renderStockResults(state);\n renderStockSearchMeta(state);", render, StringComparison.Ordinal); Assert.Contains("isBusy ? \"처리 중\"", render, StringComparison.Ordinal); Assert.DoesNotContain("종목을 검색하고 있습니다.", render, StringComparison.Ordinal); } private static string Slice(string source, string startMarker, string endMarker) { var start = source.IndexOf(startMarker, StringComparison.Ordinal); Assert.True(start >= 0, $"Missing start marker: {startMarker}"); var end = source.IndexOf(endMarker, start, StringComparison.Ordinal); Assert.True(end > start, $"Missing end marker: {endMarker}"); return source[start..end]; } 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."); } }