68945d931f
Two non-blocking suggestions from the PR #124 code review: 1. Symmetric coverage for the Discord PoolSlotCapacity no-limit button. The Capacity step already had a customId-shape assertion for the '♾ Без лимита' button; PoolSlotCapacity only had a label-presence assertion. If ChoiceButtonCustomId's wire format ever diverged between the two steps, only Capacity would catch it. Convert the single Fact to a Theory with two InlineData rows so a regression in either step produces a targeted test failure. 2. Brittle hard-coded version literal in NavMenu_ShouldExposeCurrent ProjectVersion. The test had to be hand-edited on every version bump (we hit this in PR #124 — bumping 3.9.2 -> 3.9.3 broke CI). Read the version from Directory.Build.props via XDocument instead so the test only fails when the rendered NavMenu actually disagrees with the canonical version, not when someone forgot to update a literal. Tolerant of whitespace, comments and attribute order; the <Version> element is a plain string body in the MSBuild schema. No production code changes; production version is bumped in a follow-up commit. Closes #125
99 lines
4.1 KiB
C#
99 lines
4.1 KiB
C#
namespace GmRelay.Bot.Tests.Web;
|
|
|
|
public sealed class CampaignTemplatesNavigationTests
|
|
{
|
|
[Fact]
|
|
public async Task NavMenu_ShouldExposeTemplatesTab()
|
|
{
|
|
var navMenu = await File.ReadAllTextAsync(FindRepositoryFile("src/GmRelay.Web/Components/Layout/NavMenu.razor"));
|
|
|
|
Assert.Contains("href=\"templates\"", navMenu, StringComparison.Ordinal);
|
|
Assert.Contains("Шаблоны", navMenu, StringComparison.Ordinal);
|
|
}
|
|
|
|
[Fact]
|
|
public async Task NavMenu_ShouldExposeCurrentProjectVersion()
|
|
{
|
|
// Read the version from Directory.Build.props (the canonical source of
|
|
// truth) so the test doesn't need to be hand-edited on every version
|
|
// bump. Asserting the rendered NavMenu matches the canonical version
|
|
// catches real bugs (e.g. someone bumps Directory.Build.props but
|
|
// forgets to update NavMenu.razor) without false alarms from a stale
|
|
// hard-coded literal.
|
|
var propsPath = FindRepositoryFile("Directory.Build.props");
|
|
var version = ReadVersionFromProps(propsPath);
|
|
var navMenu = await File.ReadAllTextAsync(FindRepositoryFile("src/GmRelay.Web/Components/Layout/NavMenu.razor"));
|
|
Assert.Contains($"v{version}", navMenu, StringComparison.Ordinal);
|
|
}
|
|
|
|
[Fact]
|
|
public async Task NavMenuStyles_ShouldStyleNavLinkAnchorsAsStackedRows()
|
|
{
|
|
var navCss = await File.ReadAllTextAsync(FindRepositoryFile("src/GmRelay.Web/Components/Layout/NavMenu.razor.css"));
|
|
|
|
Assert.Contains("::deep .nav-item", navCss, StringComparison.Ordinal);
|
|
Assert.Matches(
|
|
@"\.nav-section\s*\{[^}]*display:\s*flex;[^}]*flex-direction:\s*column;[^}]*gap:\s*0\.25rem;",
|
|
navCss);
|
|
Assert.Matches(
|
|
@"::deep\s+\.nav-item\s*\{[^}]*display:\s*flex;[^}]*width:\s*100%;",
|
|
navCss);
|
|
}
|
|
|
|
[Fact]
|
|
public async Task GroupDetails_ShouldApplyTemplatesWithoutManagingThem()
|
|
{
|
|
var groupDetails = await File.ReadAllTextAsync(FindRepositoryFile("src/GmRelay.Web/Components/Pages/GroupDetails.razor"));
|
|
|
|
Assert.Contains("CreateBatchFromTemplate", groupDetails, StringComparison.Ordinal);
|
|
Assert.DoesNotContain("OnValidSubmit=\"CreateCampaignTemplate\"", groupDetails, StringComparison.Ordinal);
|
|
Assert.DoesNotContain("DeleteCampaignTemplate", groupDetails, StringComparison.Ordinal);
|
|
}
|
|
|
|
[Fact]
|
|
public async Task CampaignTemplatesPage_ShouldOwnTemplateManagement()
|
|
{
|
|
var templatesPage = await File.ReadAllTextAsync(FindRepositoryFile("src/GmRelay.Web/Components/Pages/CampaignTemplates.razor"));
|
|
|
|
Assert.Contains("@page \"/templates\"", templatesPage, StringComparison.Ordinal);
|
|
Assert.Contains("OnValidSubmit=\"CreateCampaignTemplate\"", templatesPage, StringComparison.Ordinal);
|
|
Assert.Contains("DeleteCampaignTemplate", templatesPage, StringComparison.Ordinal);
|
|
}
|
|
|
|
private static string FindRepositoryFile(string relativePath)
|
|
{
|
|
var directory = new DirectoryInfo(AppContext.BaseDirectory);
|
|
while (directory is not null)
|
|
{
|
|
var candidate = Path.Combine(directory.FullName, relativePath);
|
|
if (File.Exists(candidate))
|
|
{
|
|
return candidate;
|
|
}
|
|
|
|
directory = directory.Parent;
|
|
}
|
|
|
|
throw new FileNotFoundException($"Could not locate repository file '{relativePath}'.");
|
|
}
|
|
|
|
/// <summary>
|
|
/// Parse the <c><Version>...</Version></c> element from
|
|
/// <c>Directory.Build.props</c>. Tolerant of whitespace, comments and
|
|
/// attribute shuffling — the MSBuild schema for <c>Version</c> is just
|
|
/// a plain element with a string body.
|
|
/// </summary>
|
|
private static string ReadVersionFromProps(string propsPath)
|
|
{
|
|
var doc = System.Xml.Linq.XDocument.Load(propsPath);
|
|
var versionElement = doc.Descendants()
|
|
.FirstOrDefault(e => e.Name.LocalName == "Version");
|
|
Assert.NotNull(versionElement);
|
|
var version = versionElement!.Value.Trim();
|
|
Assert.False(
|
|
string.IsNullOrEmpty(version),
|
|
$"<Version> in {propsPath} is empty");
|
|
return version;
|
|
}
|
|
}
|