Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
17 commits
Select commit Hold shift + click to select a range
62867c6
Wire Nullean.Snapshots into every test project
Mpdreamz Nov 5, 2026
a9fd423
Compare rendered HTML with Nullean.Snapshots.Html
Mpdreamz Nov 5, 2026
1ecc88d
Navigation tests: snapshot navigation trees instead of walking them
Mpdreamz Nov 5, 2026
838c8de
Navigation rendering tests: snapshot the rendered component
Mpdreamz Nov 5, 2026
120234a
Configuration tests: snapshot diagnostics and deserialised tables of …
Mpdreamz Nov 5, 2026
0729cef
Use the shared NavigationDump in Navigation.Tests
Mpdreamz Nov 5, 2026
e3ff266
Scrub paths the same way on every operating system
Mpdreamz Nov 5, 2026
12c95f5
Scrub random folder names under the temp directory
Mpdreamz Nov 5, 2026
fc688d1
Carry the rebased branch as a merge of its updated base
Mpdreamz Nov 5, 2026
815c9af
Carry the rebased branch as a merge of its updated base
Mpdreamz Nov 5, 2026
021c3c0
Make diagnostics snapshots and snapshot paths the same on every opera…
Mpdreamz Nov 5, 2026
fe5a8bf
Merge branch 'test/snapshots-infra' into test/snapshots-html-normalisers
Mpdreamz Nov 5, 2026
c7137f5
Merge branch 'test/snapshots-html-normalisers' into test/snapshots-na…
Mpdreamz Nov 5, 2026
2b0c0d9
Match the repository root in the form DiagnosticsSnapshot writes it
Mpdreamz Nov 5, 2026
63a4b4a
Merge branch 'test/snapshots-infra' into test/snapshots-html-normalisers
Mpdreamz Nov 5, 2026
9d7a0b5
Merge branch 'test/snapshots-html-normalisers' into test/snapshots-na…
Mpdreamz Nov 5, 2026
52a63cc
Merge remote-tracking branch 'origin/main' into test/snapshots-nav-co…
Mpdreamz Nov 5, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
error {repo}/docs/_docset.yml 'repository: no-slash' for API 'elasticsearch' must be in 'org/repo' form, e.g. 'elastic/elasticsearch-specification'.
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
error {repo}/docs/_docset.yml 'repository: trailing-slash/' for API 'elasticsearch' must be in 'org/repo' form, e.g. 'elastic/elasticsearch-specification'.
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
error {repo}/docs/_docset.yml 'repository: /leading-slash' for API 'elasticsearch' must be in 'org/repo' form, e.g. 'elastic/elasticsearch-specification'.
Original file line number Diff line number Diff line change
Expand Up @@ -433,7 +433,13 @@ public void EmitsError_WhenSpecMissing()

var (config, collector) = CreateConfiguration(docSetFile);

collector.Errors.Should().Be(1);
collector
.Captured
.ToSnapshotText()
.Should()
.MatchInlineSnapshot(
"error {repo}/docs/_docset.yml API 'elasticsearch' is missing required 'spec:'. Its basename is required to resolve the remote version index, even when the file is not present locally."
);
config.ApiConfigurations.Should().BeNull();
}

Expand All @@ -450,7 +456,13 @@ public void EmitsError_WhenSpecEscapesDocumentationSourceDirectory()

var (config, collector) = CreateConfiguration(docSetFile);

collector.Errors.Should().Be(1);
collector
.Captured
.ToSnapshotText()
.Should()
.MatchInlineSnapshot(
"error {repo}/docs/_docset.yml 'spec: ../../outside.json' for API 'elasticsearch' escapes the documentation source directory."
);
config.ApiConfigurations.Should().BeNull();
}

Expand All @@ -467,7 +479,13 @@ public void EmitsError_WhenProductMissing()

var (config, collector) = CreateConfiguration(docSetFile);

collector.Errors.Should().Be(1);
collector
.Captured
.ToSnapshotText()
.Should()
.MatchInlineSnapshot(
"error {repo}/docs/_docset.yml API 'elasticsearch' is missing required 'product:'. It must match a product id defined in products.yml."
);
config.ApiConfigurations.Should().BeNull();
}

Expand All @@ -487,7 +505,13 @@ public void EmitsError_WhenProductUnknown()

var (config, collector) = CreateConfiguration(docSetFile);

collector.Errors.Should().Be(1);
collector
.Captured
.ToSnapshotText()
.Should()
.MatchInlineSnapshot(
"error {repo}/docs/_docset.yml Unknown 'product: not-a-product' for API 'elasticsearch'. It must be a product id defined in products.yml."
);
config.ApiConfigurations.Should().BeNull();
}

Expand Down Expand Up @@ -591,7 +615,13 @@ public void EmitsError_WhenCatalogCategoryUnknown()

var (config, collector) = CreateConfiguration(docSetFile);

collector.Errors.Should().Be(1);
collector
.Captured
.ToSnapshotText()
.Should()
.MatchInlineSnapshot(
"error {repo}/docs/_docset.yml Unknown catalog category 'eck' for API 'elasticsearch'. Valid categories: ece, ess, self, serverless (or ech for ess)."
);
config.ApiConfigurations!["elasticsearch"].CatalogCategories.Should().Equal("self");
}

Expand Down Expand Up @@ -657,8 +687,8 @@ public void EmitsError_WhenRepositoryNotInOrgSlashRepoForm(string repository)

var (config, collector) = CreateConfiguration(docSetFile);

collector.Errors.Should().Be(1);
config.ApiConfigurations.Should().BeNull();
// the output differs per case, so it is a file snapshot (keyed by the arguments) rather than a shared inline literal
collector.Captured.ToSnapshotText().Should().MatchSnapshot();

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Severity: P3 (Low)

EmitsError_WhenRepositoryNotInOrgSlashRepoForm no longer asserts config.ApiConfigurations.Should().BeNull(), and config is now unused. If a regression kept the API configuration despite the invalid repository: value, this test would still pass. Every sibling EmitsError_* test kept its config assertion next to the new snapshot, so this one is weaker than before. Add the assertion back after MatchSnapshot().

Generated by Libra. Specialist: libra-complexity. React with 👍 or 👎 to give feedback on this comment.

}

[Test]
Expand All @@ -677,7 +707,13 @@ public void EmitsError_WhenMultipleEntries()

var (config, collector) = CreateConfiguration(docSetFile);

collector.Errors.Should().Be(1);
collector
.Captured
.ToSnapshotText()
.Should()
.MatchInlineSnapshot(
"error {repo}/docs/_docset.yml API configuration for 'elasticsearch' must have exactly one entry, found 2."
);
config.ApiConfigurations.Should().BeNull();
}

Expand Down Expand Up @@ -705,7 +741,13 @@ public void EmitsError_WhenChildFileMissing()

var (config, collector) = CreateConfiguration(docSetFile);

collector.Errors.Should().Be(1);
collector
.Captured
.ToSnapshotText()
.Should()
.MatchInlineSnapshot(
"error {repo}/docs/_docset.yml Child page 'missing.md' for API 'elasticsearch' does not exist under 'api/elasticsearch/'."
);
config.ApiConfigurations!["elasticsearch"].Children.Should().BeEmpty();
}

Expand Down Expand Up @@ -733,7 +775,13 @@ public void EmitsError_WhenChildPathEscapesApiKeyDirectory()

var (config, collector) = CreateConfiguration(docSetFile);

collector.Errors.Should().Be(1);
collector
.Captured
.ToSnapshotText()
.Should()
.MatchInlineSnapshot(
"error {repo}/docs/_docset.yml Child page '../../outside.md' for API 'elasticsearch' escapes 'api/elasticsearch/'."
);
config.ApiConfigurations!["elasticsearch"].Children.Should().BeEmpty();
}

Expand Down Expand Up @@ -761,7 +809,13 @@ public void EmitsError_WhenChildFileUsesSupplementalName()

var (config, collector) = CreateConfiguration(docSetFile, extraMarkdownFiles: ["op-search.md"]);

collector.Errors.Should().Be(1);
collector
.Captured
.ToSnapshotText()
.Should()
.MatchInlineSnapshot(
"error {repo}/docs/_docset.yml Child page 'op-search.md' for API 'elasticsearch' uses a supplemental file name (op-*.md / tag-*.md). Those files are auto-discovered and cannot be listed under children:."
);
config.ApiConfigurations!["elasticsearch"].Children.Should().BeEmpty();
}

Expand Down Expand Up @@ -853,14 +907,14 @@ public void ApiContentDirectory_IsSetToApiKeyFolder()

private static readonly string[] DefaultProductIds = ["elasticsearch", "kibana"];

private static (ConfigurationFile Config, DiagnosticsCollector Collector) CreateConfiguration(
private static (ConfigurationFile Config, CapturingDiagnosticsCollector Collector) CreateConfiguration(
DocumentationSetFile docSet,
string[]? extraProducts = null,
bool withLocalSpecFile = true,
string[]? extraMarkdownFiles = null
)
{
var collector = new DiagnosticsCollector([]);
var collector = new CapturingDiagnosticsCollector();
var root = Paths.WorkingDirectoryRoot.FullName;
var configFilePath = Path.Join(root, "docs", "_docset.yml");
var files = new Dictionary<string, MockFileData>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -237,8 +237,11 @@ public async Task LoadAndResolve_PageClaimedByTwoDefaults_EmitsError()

recorder
.Diagnostics
.ToSnapshotText()
.Should()
.Contain(d => d.Severity == Severity.Error && d.Message.Contains("observability") && d.Message.Contains("security"));
.MatchInlineSnapshot(
"error /docs/section-b/toc.yml 'shared/page.md' is registered with default CTA 'observability' and 'security'. Each page can only have one default CTA."
);
}

[Test]
Expand All @@ -260,7 +263,12 @@ public async Task Constructor_UnknownTocDefaultCta_EmitsError()

var (_, diagnostics) = await CreateConfigurationWithDiagnostics(docSet);

diagnostics.Should().ContainSingle(d => d.Severity == Severity.Error).Which.Message.Should().Contain("does-not-exist");
diagnostics
.ToSnapshotText()
.Should()
.MatchInlineSnapshot(
"error /docs/docset.yml 'default_cta: does-not-exist' on page 'solutions/observability/apps/apm.md' does not match any 'cta' template in docset.yml."
);
}

[Test]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ public void SetVariable_ResolvesToEnvironmentValue_AndExposesDefaultAsFallback()
[Test]
public void DisallowedVariable_IsLeftLiteral_AndWarns()
{
var collector = new DiagnosticsCollector([]);
var collector = new CapturingDiagnosticsCollector();
var config = CreateConfiguration(
"${AWS_SECRET_ACCESS_KEY:-fallback}",
new MockEnvironment { ["AWS_SECRET_ACCESS_KEY"] = "super-secret" },
Expand All @@ -51,7 +51,13 @@ public void DisallowedVariable_IsLeftLiteral_AndWarns()

config.StorybookRegistry.Should().Be("${AWS_SECRET_ACCESS_KEY:-fallback}");
config.StorybookRegistry.Should().NotContain("super-secret");
collector.Warnings.Should().Be(1, "a disallowed interpolation variable must emit exactly one warning");
collector
.Captured
.ToSnapshotText()
.Should()
.MatchInlineSnapshot(
"warning {repo}/docs/_docset.yml 'storybook.registry' references environment variable 'AWS_SECRET_ACCESS_KEY' which is not allow-listed for interpolation and is left literal. Allowed: KIBANA_STORYBOOK_REGISTRY."
);
}

private static ConfigurationFile CreateConfiguration(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -55,10 +55,18 @@ public void ConsecutiveSiblingPrivateEntries_BothRemoved()
var result = provider.CreateNavigationFile(config);
var output = fileSystem.File.ReadAllText(result.FullName);

output.Should().NotContain("private-a://");
output.Should().NotContain("private-b://");
output.Should().Contain("public-repo://section-a");
output.Should().Contain("public-repo-two://section-d");
output.Should().MatchInlineSnapshot(
"""
toc:
- toc: docs-content://getting-started
path_prefix: getting-started
children:
- toc: public-repo://section-a
path_prefix: section-a
- toc: public-repo-two://section-d
path_prefix: section-d
"""
);
}

[Test]
Expand Down Expand Up @@ -92,10 +100,21 @@ public void DeeplyNestedConsecutivePrivateEntries_BothRemoved()
var result = provider.CreateNavigationFile(config);
var output = fileSystem.File.ReadAllText(result.FullName);

output.Should().NotContain("private-a://");
output.Should().NotContain("private-b://");
output.Should().Contain("public-repo://leaf-a");
output.Should().Contain("public-repo-two://leaf-d");
output.Should().MatchInlineSnapshot(
"""
toc:
- toc: docs-content://top
path_prefix: top
children:
- toc: docs-content://mid
path_prefix: mid
children:
- toc: public-repo://leaf-a
path_prefix: leaf-a
- toc: public-repo-two://leaf-d
path_prefix: leaf-d
"""
);
}

[Test]
Expand Down Expand Up @@ -125,9 +144,18 @@ public void PrivateRepoWithChildren_EntryRemovedChildrenReindented()
var result = provider.CreateNavigationFile(config);
var output = fileSystem.File.ReadAllText(result.FullName);

output.Should().NotContain("private-a://");
output.Should().Contain("public-child://nested");
output.Should().Contain("public-repo://after");
output.Should().MatchInlineSnapshot(
"""
toc:
- toc: docs-content://top
path_prefix: top
children:
- toc: public-child://nested
path_prefix: nested
- toc: public-repo://after
path_prefix: after
"""
);
}

[Test]
Expand Down Expand Up @@ -156,9 +184,16 @@ public void PublicEntriesBetweenPrivateEntries_Preserved()
var result = provider.CreateNavigationFile(config);
var output = fileSystem.File.ReadAllText(result.FullName);

output.Should().NotContain("private-a://");
output.Should().NotContain("private-b://");
output.Should().Contain("public-repo://middle");
output.Should().MatchInlineSnapshot(
"""
toc:
- toc: docs-content://top
path_prefix: top
children:
- toc: public-repo://middle
path_prefix: middle
"""
);
}

[Test]
Expand Down Expand Up @@ -187,11 +222,17 @@ public void IslandProperty_OnPublicEntry_SurvivesPrivateRepoStripping()
var output = fileSystem.File.ReadAllText(result.FullName);

// The public entry and its island: true property are preserved
output.Should().Contain("public-repo://reference");
output.Should().Contain("island: true", "island: true on a public entry must survive private-repo stripping");
output.Should().Contain("public-child://nested");
// The private entry is removed
output.Should().NotContain("private-a://");
output.Should().MatchInlineSnapshot(
"""
toc:
- toc: public-repo://reference
path_prefix: reference
island: true
children:
- toc: public-child://nested
path_prefix: reference/nested
"""
);
}

[Test]
Expand Down Expand Up @@ -219,11 +260,15 @@ public void IslandProperty_OnPrivateEntry_IsRemovedWithEntry()
var output = fileSystem.File.ReadAllText(result.FullName);

// The private entry and its island: true property are removed
output.Should().NotContain("private-a://");
output.Should().NotContain("island: true", "island: true on a private entry is removed with the entry");
// Public entries are preserved
output.Should().Contain("public-repo://reference");
output.Should().Contain("public-after://other");
output.Should().MatchInlineSnapshot(
"""
toc:
- toc: public-repo://reference
path_prefix: reference
- toc: public-after://other
path_prefix: other
"""
);
}

[Test]
Expand Down
Loading
Loading