Add the MBN-branded startup intro, semantic sidebar icons, and refined card-based operator visuals. Strengthen packaged UI and input smoke coverage for the updated experience.
239 lines
9.2 KiB
C#
239 lines
9.2 KiB
C#
namespace MBN_STOCK_WEBVIEW.LegacyWeb.Tests;
|
|
|
|
public sealed class LegacySidebarIconContractTests
|
|
{
|
|
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 Navigation = File.ReadAllText(
|
|
Path.Combine(WebRoot, "workspace-navigation.js"));
|
|
private static readonly string Styles = File.ReadAllText(
|
|
Path.Combine(WebRoot, "styles.css"));
|
|
private static readonly string PackageInputSmoke = File.ReadAllText(
|
|
Path.Combine(RepositoryRoot, "scripts", "Test-LegacyPackageInput.mjs"));
|
|
private static readonly string UiOnlySmoke = File.ReadAllText(
|
|
Path.Combine(RepositoryRoot, "scripts", "Test-LegacyPackageUiOnlySmoke.mjs"));
|
|
|
|
[Fact]
|
|
public void EverySidebarDestinationUsesOneClosedSemanticSvgIcon()
|
|
{
|
|
var mappings = new (string Anchor, string Icon)[]
|
|
{
|
|
("id=\"workspace-stock-tab\"", "stock-cut"),
|
|
("data-tab-id=\"overseas\"", "overseas"),
|
|
("data-tab-id=\"exchange\"", "exchange"),
|
|
("data-tab-id=\"index\"", "index"),
|
|
("data-tab-id=\"kospiIndustry\"", "kospi"),
|
|
("data-tab-id=\"kosdaqIndustry\"", "kosdaq"),
|
|
("data-tab-id=\"comparison\"", "comparison"),
|
|
("data-tab-id=\"theme\"", "theme"),
|
|
("data-tab-id=\"overseasStocks\"", "overseas-stocks"),
|
|
("data-tab-id=\"expert\"", "expert"),
|
|
("data-tab-id=\"tradingHalt\"", "trading-halt"),
|
|
("id=\"workspace-settings-tab\"", "settings")
|
|
};
|
|
|
|
foreach (var (anchor, icon) in mappings)
|
|
{
|
|
var button = Slice(Markup, anchor, "</button>");
|
|
Assert.Contains($"data-icon=\"{icon}\"", button, StringComparison.Ordinal);
|
|
Assert.Contains(
|
|
$"href=\"#workspace-icon-{icon}\"",
|
|
button,
|
|
StringComparison.Ordinal);
|
|
Assert.DoesNotContain("<img", button, StringComparison.OrdinalIgnoreCase);
|
|
}
|
|
|
|
var sprite = Slice(
|
|
Markup,
|
|
"<svg class=\"workspace-icon-sprite\"",
|
|
"</svg>");
|
|
Assert.DoesNotContain("<text", sprite, StringComparison.OrdinalIgnoreCase);
|
|
Assert.DoesNotContain("http://", sprite, StringComparison.OrdinalIgnoreCase);
|
|
Assert.DoesNotContain("https://", sprite, StringComparison.OrdinalIgnoreCase);
|
|
}
|
|
|
|
[Fact]
|
|
public void KospiAndKosdaqUseDistinctFontIndependentMonograms()
|
|
{
|
|
var kospi = Slice(
|
|
Markup,
|
|
"<symbol id=\"workspace-icon-kospi\"",
|
|
"</symbol>");
|
|
var kosdaq = Slice(
|
|
Markup,
|
|
"<symbol id=\"workspace-icon-kosdaq\"",
|
|
"</symbol>");
|
|
|
|
Assert.Contains(
|
|
"M4.5 18V6h4a4 4 0 0 1 0 8h-4",
|
|
kospi,
|
|
StringComparison.Ordinal);
|
|
Assert.Contains(
|
|
"M15 6h4M17 6v12M15 18h4",
|
|
kospi,
|
|
StringComparison.Ordinal);
|
|
Assert.Contains(
|
|
"M2.3 18V6h1.6c2.8 0 4.3 2.2 4.3 6",
|
|
kosdaq,
|
|
StringComparison.Ordinal);
|
|
Assert.Contains(
|
|
"M9 18l2.5-12H13l2.5 12M10 14h4.5",
|
|
kosdaq,
|
|
StringComparison.Ordinal);
|
|
Assert.Contains(
|
|
"<ellipse cx=\"19.2\" cy=\"12\" rx=\"3.1\" ry=\"6\" stroke-width=\"1.65\">",
|
|
kosdaq,
|
|
StringComparison.Ordinal);
|
|
Assert.Contains("m19.5 14.5 3.2 4", kosdaq, StringComparison.Ordinal);
|
|
Assert.DoesNotContain("<text", kospi + kosdaq, StringComparison.OrdinalIgnoreCase);
|
|
Assert.NotEqual(kospi, kosdaq);
|
|
}
|
|
|
|
[Fact]
|
|
public void HeadingClonesTheSelectedIconAndThemeStylingStaysVectorNative()
|
|
{
|
|
Assert.Contains(
|
|
"const titleIcon = document.querySelector(\".workspace-heading-icon\")",
|
|
Navigation,
|
|
StringComparison.Ordinal);
|
|
var sync = Slice(
|
|
Navigation,
|
|
"function syncHeadingIcon(button)",
|
|
"function refreshCurrentMenu()");
|
|
Assert.Contains("source.cloneNode(true)", sync, StringComparison.Ordinal);
|
|
Assert.Contains("titleIcon.replaceChildren(next)", sync, StringComparison.Ordinal);
|
|
|
|
var workspace = Slice(
|
|
Navigation,
|
|
"function setWorkspace(name)",
|
|
"function applyStartPreference(mode)");
|
|
AssertInOrder(
|
|
workspace,
|
|
"activeWorkspace = name;",
|
|
"refreshCurrentMenu();",
|
|
"syncHeadingIcon(selectedButton);",
|
|
"title.textContent");
|
|
|
|
Assert.Contains(
|
|
".workspace-icon-sprite",
|
|
Styles,
|
|
StringComparison.Ordinal);
|
|
Assert.Contains(
|
|
".workspace-nav-icon { shape-rendering: geometricPrecision; }",
|
|
Styles,
|
|
StringComparison.Ordinal);
|
|
Assert.Contains(
|
|
".workspace-nav-toggle svg, .workspace-nav-item svg",
|
|
Styles,
|
|
StringComparison.Ordinal);
|
|
Assert.Contains("stroke: currentColor;", Styles, StringComparison.Ordinal);
|
|
}
|
|
|
|
[Fact]
|
|
public void PackageSmokesProveIconsArePaintedMappedAndCentered()
|
|
{
|
|
var mappings = new (string TabId, string Icon)[]
|
|
{
|
|
("overseas", "overseas"),
|
|
("exchange", "exchange"),
|
|
("index", "index"),
|
|
("kospiIndustry", "kospi"),
|
|
("kosdaqIndustry", "kosdaq"),
|
|
("comparison", "comparison"),
|
|
("theme", "theme"),
|
|
("overseasStocks", "overseas-stocks"),
|
|
("expert", "expert"),
|
|
("tradingHalt", "trading-halt")
|
|
};
|
|
|
|
foreach (var harness in new[] { PackageInputSmoke, UiOnlySmoke })
|
|
{
|
|
foreach (var (tabId, icon) in mappings)
|
|
{
|
|
Assert.Contains(
|
|
$"{tabId}: \"{icon}\"",
|
|
harness,
|
|
StringComparison.Ordinal);
|
|
}
|
|
|
|
Assert.Contains("svg?.getBBox?.()", harness, StringComparison.Ordinal);
|
|
Assert.Contains("reference:", harness, StringComparison.Ordinal);
|
|
Assert.Contains("Number.parseFloat(style.opacity) > 0",
|
|
harness,
|
|
StringComparison.Ordinal);
|
|
Assert.Contains("style.stroke !== \"none\" || style.fill !== \"none\"",
|
|
harness,
|
|
StringComparison.Ordinal);
|
|
Assert.Contains("rendered: Boolean(", harness, StringComparison.Ordinal);
|
|
Assert.Contains("expectedHeadingIcon", harness, StringComparison.Ordinal);
|
|
}
|
|
|
|
Assert.Contains("navigationItems:", PackageInputSmoke, StringComparison.Ordinal);
|
|
Assert.Contains("iconCenterOffset:", PackageInputSmoke, StringComparison.Ordinal);
|
|
Assert.Contains("iconCenterOffsetY:", PackageInputSmoke, StringComparison.Ordinal);
|
|
Assert.Contains("item.iconCenterOffset > 1.5", PackageInputSmoke, StringComparison.Ordinal);
|
|
Assert.Contains("item.iconCenterOffsetY > 1.5",
|
|
PackageInputSmoke,
|
|
StringComparison.Ordinal);
|
|
Assert.Contains("collapsedIconCenterOffsets:", PackageInputSmoke, StringComparison.Ordinal);
|
|
Assert.Contains("tab.icon.key === baseline.marketTabs[index].icon.key",
|
|
PackageInputSmoke,
|
|
StringComparison.Ordinal);
|
|
|
|
Assert.Contains("layout.stockTabIcon.rendered === true",
|
|
UiOnlySmoke,
|
|
StringComparison.Ordinal);
|
|
Assert.Contains("layout.settingsTabIcon.rendered === true",
|
|
UiOnlySmoke,
|
|
StringComparison.Ordinal);
|
|
Assert.Contains("tab.icon.rendered === true",
|
|
UiOnlySmoke,
|
|
StringComparison.Ordinal);
|
|
}
|
|
|
|
private static string Slice(string value, string start, string end)
|
|
{
|
|
var startIndex = value.IndexOf(start, StringComparison.Ordinal);
|
|
Assert.True(startIndex >= 0, $"Required marker was not found: {start}");
|
|
var endIndex = value.IndexOf(
|
|
end,
|
|
startIndex + start.Length,
|
|
StringComparison.Ordinal);
|
|
Assert.True(endIndex > startIndex, $"Required marker was not found: {end}");
|
|
return value[startIndex..(endIndex + end.Length)];
|
|
}
|
|
|
|
private static void AssertInOrder(string value, params string[] needles)
|
|
{
|
|
var previous = -1;
|
|
foreach (var needle in needles)
|
|
{
|
|
var current = value.IndexOf(needle, previous + 1, StringComparison.Ordinal);
|
|
Assert.True(current > previous, $"Required ordered marker was not found: {needle}");
|
|
previous = current;
|
|
}
|
|
}
|
|
|
|
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 was not found.");
|
|
}
|
|
}
|