-
Notifications
You must be signed in to change notification settings - Fork 0
Validate documentation snapshots with mdast JSON #43
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Large diffs are not rendered by default.
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -17,6 +17,15 @@ The main `.github/workflows/ci.yml` workflow deliberately does not run | |||||||||||||||||||||||||||||||||
| `make test WITH_ACT=1`; the separate Act workflow runs those slower | ||||||||||||||||||||||||||||||||||
| container-backed checks in parallel. | ||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||
| Documentation snapshot tests live in `tests/documentation_snapshots.rs`. They | ||||||||||||||||||||||||||||||||||
| parse selected Markdown files into normalized mdast JSON before comparing | ||||||||||||||||||||||||||||||||||
| `insta` snapshots, so line wrapping and table alignment changes do not churn | ||||||||||||||||||||||||||||||||||
| semantic snapshots. Markdown formatting is still checked by `make markdownlint` | ||||||||||||||||||||||||||||||||||
| and formatted by `make fmt`. When a documentation meaning or structure change | ||||||||||||||||||||||||||||||||||
| is intentional, update the affected `tests/snapshots/*.snap` files with | ||||||||||||||||||||||||||||||||||
| `INSTA_UPDATE=always cargo test --test documentation_snapshots`, then review | ||||||||||||||||||||||||||||||||||
| the JSON diff before committing it. | ||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||
|
Comment on lines
+20
to
+28
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win Replace The generated Makefile exposes Proposed fix- Markdown formatting is still checked by `make markdownlint` and formatted by `make fmt`.
+ Markdown formatting is still checked by `make markdownlint` and validated by `make check-fmt`.📝 Committable suggestion
Suggested change
🤖 Prompt for AI Agents |
||||||||||||||||||||||||||||||||||
| ## Tooling | ||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||
| Development builds use Cranelift for debug code generation. On Linux targets, | ||||||||||||||||||||||||||||||||||
|
|
@@ -27,6 +36,10 @@ LLVM-compatible linker behaviour. | |||||||||||||||||||||||||||||||||
| Install `clang`, `lld`, `mold`, `python3`, and `cargo-audit` before running the | ||||||||||||||||||||||||||||||||||
| full generated workflow locally on Linux. | ||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||
| The documentation snapshot tests use the `markdown`, `serde_json`, and `insta` | ||||||||||||||||||||||||||||||||||
| dev-dependencies declared in `Cargo.toml`; no Node or npm Markdown snapshot | ||||||||||||||||||||||||||||||||||
| pipeline is required. | ||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||
| ### Security audit ignores | ||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||
| Security audit jobs may set `CARGO_AUDIT_IGNORES` for narrowly scoped | ||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,106 @@ | ||
| //! Semantic documentation snapshot tests. | ||
|
|
||
| use serde_json::Value; | ||
|
|
||
| fn parse_markdown_semantics(markdown: &str) -> Result<Value, Box<dyn std::error::Error>> { | ||
| let tree = markdown::to_mdast(markdown, &markdown::ParseOptions::gfm()) | ||
| .map_err(|message| std::io::Error::other(message.to_string()))?; | ||
| let mut value = serde_json::to_value(tree)?; | ||
| normalise_node(&mut value); | ||
| Ok(value) | ||
| } | ||
|
|
||
| fn normalise_node(value: &mut Value) { | ||
| match value { | ||
| Value::Object(object) => { | ||
| let node_type = object | ||
| .get("type") | ||
| .and_then(Value::as_str) | ||
| .map(str::to_owned); | ||
| object.remove("position"); | ||
| object.remove("line"); | ||
| object.remove("column"); | ||
| object.remove("offset"); | ||
|
|
||
| if node_type.as_deref() == Some("text") | ||
| && let Some(Value::String(text)) = object.get_mut("value") | ||
| { | ||
| *text = normalise_soft_text(text); | ||
| } | ||
|
|
||
| for child in object.values_mut() { | ||
| normalise_node(child); | ||
| } | ||
| } | ||
| Value::Array(children) => { | ||
| for child in children { | ||
| normalise_node(child); | ||
| } | ||
| } | ||
| _ => {} | ||
| } | ||
| } | ||
|
|
||
| fn normalise_soft_text(value: &str) -> String { | ||
| value.split_whitespace().collect::<Vec<_>>().join(" ") | ||
| } | ||
|
|
||
| fn parse_markdown_semantics_or_panic(markdown: &str) -> Value { | ||
| match parse_markdown_semantics(markdown) { | ||
| Ok(value) => value, | ||
| Err(error) => panic!("failed to parse Markdown semantics: {error}"), | ||
| } | ||
| } | ||
|
|
||
| #[test] | ||
| fn markdown_semantics_ignore_formatting_noise() { | ||
| let compact = "\ | ||
| # Documentation | ||
|
|
||
| The generated project links to [docs](docs/contents.md) and explains | ||
| the workflow in one paragraph. | ||
|
|
||
| | Path | Purpose | | ||
| | - | - | | ||
| | `docs/contents.md` | Index | | ||
|
|
||
| - [x] Keep snapshots semantic | ||
| "; | ||
| let reflowed = "\ | ||
| # Documentation | ||
|
|
||
| The generated project links to [docs](docs/contents.md) and explains the | ||
| workflow in one paragraph. | ||
|
|
||
| | Path | Purpose | | ||
| | -------------------- | ------- | | ||
| | `docs/contents.md` | Index | | ||
|
|
||
| - [x] Keep snapshots semantic | ||
| "; | ||
|
|
||
| assert_eq!( | ||
| parse_markdown_semantics_or_panic(compact), | ||
| parse_markdown_semantics_or_panic(reflowed) | ||
| ); | ||
| } | ||
|
|
||
| #[test] | ||
| fn generated_documentation_matches_semantic_snapshots() { | ||
| let repository_layout = include_str!("../docs/repository-layout.md"); | ||
| let repository_layout_snapshot = if repository_layout.contains(".github/workflows/release.yml") | ||
| { | ||
| "docs_repository_layout_app" | ||
| } else { | ||
| "docs_repository_layout_lib" | ||
| }; | ||
|
|
||
| insta::assert_json_snapshot!( | ||
| "docs_contents", | ||
| parse_markdown_semantics_or_panic(include_str!("../docs/contents.md")) | ||
| ); | ||
| insta::assert_json_snapshot!( | ||
| repository_layout_snapshot, | ||
| parse_markdown_semantics_or_panic(repository_layout) | ||
| ); | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win
Use en-GB spellings here.
Replace
normalized/normalizationwithnormalised/normalisationin both updated sections.Triage:
[type:spelling]As per path instructions, use en-GB-oxendict spelling in
docs/**.♻️ Proposed fix
Also applies to: 98-99
🤖 Prompt for AI Agents
Source: Path instructions