Files
chapter-organizer/Tests/Printing/MarkdownTableStencil_Tests.cs
T
poprhythmandCursor 1337d9833d feat: stitch page-printer tables into one roster and add chapter team-count tokens
When New page per record is off, a single-table template shares one header across records. Event and team pages can print regional/state team counts and nationals eligibility.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-09-02 21:37:49 -04:00

130 lines
4.2 KiB
C#

using Core.Printing;
namespace Tests.Printing;
[TestFixture]
public class MarkdownTableStencil_Tests
{
private const string Roster = """
| Name | Grade |
| --- | --- |
| {{LastNameFirstName}} | {{Grade}} |
""";
[Test]
public void TryParse_SingleTable_SplitsPrefixHeaderBodySuffix()
{
var template = """
# Rankings
| Name | 1st |
| --- | --- |
| {{LastNameFirstName}} | {{Rank1}} |
{{Legend}}
""";
Assert.That(MarkdownTableStencil.TryParse(template, out var stencil), Is.True);
Assert.That(stencil!.Prefix, Does.Contain("# Rankings"));
Assert.That(stencil.Header, Does.Contain("| Name | 1st |"));
Assert.That(stencil.Header, Does.Contain("| --- | --- |"));
Assert.That(stencil.Body.Trim(), Is.EqualTo("| {{LastNameFirstName}} | {{Rank1}} |"));
Assert.That(stencil.Suffix, Does.Contain("{{Legend}}"));
}
[Test]
public void TryParse_NoTable_Fails()
{
Assert.That(MarkdownTableStencil.TryParse("# Interview\n\n{{FirstName}}", out _), Is.False);
}
[Test]
public void TryParse_TwoTables_Fails()
{
var template = """
| Grade | Time |
| --- | --- |
| {{Grade}} | {{Interview Time}} |
| 1st | 2nd |
| --- | --- |
| {{Rank1}} | {{Rank2}} |
""";
Assert.That(MarkdownTableStencil.TryParse(template, out _), Is.False);
}
[Test]
public void TryParse_HeaderOnly_Fails()
{
Assert.That(MarkdownTableStencil.TryParse("| Name |\n| --- |\n", out _), Is.False);
}
[Test]
public void Stitch_TwoStudents_HeaderOnceAndTwoBodyRows()
{
Assert.That(MarkdownTableStencil.TryParse(Roster, out var stencil), Is.True);
var merged = stencil!.Stitch(
string.Empty,
[
NoteTemplateMerger.Merge(stencil.Body, Map(("LastNameFirstName", "Cole, Aria"), ("Grade", "6"))),
NoteTemplateMerger.Merge(stencil.Body, Map(("LastNameFirstName", "Dean, Lucas"), ("Grade", "7")))
],
string.Empty);
Assert.That(CountOccurrences(merged, "| Name | Grade |"), Is.EqualTo(1));
Assert.That(CountOccurrences(merged, "| --- | --- |"), Is.EqualTo(1));
Assert.That(merged, Does.Contain("| Cole, Aria | 6 |"));
Assert.That(merged, Does.Contain("| Dean, Lucas | 7 |"));
}
[Test]
public void Stitch_AttributesRow_RepeatsPerRecord()
{
var template = """
| 1st | 2nd |
| --- | --- |
| {{Rank1}} | {{Rank2}} |
| {{Rank1.Attributes}} | {{Rank2.Attributes}} |
""";
Assert.That(MarkdownTableStencil.TryParse(template, out var stencil), Is.True);
var merged = stencil!.Stitch(
string.Empty,
[
NoteTemplateMerger.Merge(stencil.Body, Map(("Rank1", "Coding"), ("Rank2", "Flight"), ("Rank1.Attributes", "I"), ("Rank2.Attributes", "T"))),
NoteTemplateMerger.Merge(stencil.Body, Map(("Rank1", "Drone"), ("Rank2", "Robotics"), ("Rank1.Attributes", "R"), ("Rank2.Attributes", "O")))
],
string.Empty);
Assert.That(CountOccurrences(merged, "| Coding | Flight |"), Is.EqualTo(1));
Assert.That(CountOccurrences(merged, "| I | T |"), Is.EqualTo(1));
Assert.That(CountOccurrences(merged, "| Drone | Robotics |"), Is.EqualTo(1));
Assert.That(CountOccurrences(merged, "| R | O |"), Is.EqualTo(1));
Assert.That(CountOccurrences(merged, "| --- | --- |"), Is.EqualTo(1));
}
private static Dictionary<string, string> Map(params (string Key, string Value)[] pairs)
{
var tokens = PrintTokenMap.Create();
foreach (var (key, value) in pairs)
tokens[key] = value;
return tokens;
}
private static int CountOccurrences(string haystack, string needle)
{
var count = 0;
var index = 0;
while ((index = haystack.IndexOf(needle, index, StringComparison.Ordinal)) >= 0)
{
count++;
index += needle.Length;
}
return count;
}
}