mkdocs2confluence 0.9.2__tar.gz → 0.9.4__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (87) hide show
  1. {mkdocs2confluence-0.9.2 → mkdocs2confluence-0.9.4}/PKG-INFO +6 -2
  2. {mkdocs2confluence-0.9.2 → mkdocs2confluence-0.9.4}/README.md +5 -1
  3. {mkdocs2confluence-0.9.2 → mkdocs2confluence-0.9.4}/pyproject.toml +1 -1
  4. {mkdocs2confluence-0.9.2 → mkdocs2confluence-0.9.4}/src/mkdocs2confluence.egg-info/PKG-INFO +6 -2
  5. {mkdocs2confluence-0.9.2 → mkdocs2confluence-0.9.4}/src/mkdocs2confluence.egg-info/SOURCES.txt +1 -0
  6. {mkdocs2confluence-0.9.2 → mkdocs2confluence-0.9.4}/src/mkdocs_to_confluence/emitter/xhtml.py +12 -0
  7. {mkdocs2confluence-0.9.2 → mkdocs2confluence-0.9.4}/src/mkdocs_to_confluence/ir/nodes.py +9 -0
  8. {mkdocs2confluence-0.9.2 → mkdocs2confluence-0.9.4}/src/mkdocs_to_confluence/publisher/pipeline.py +5 -2
  9. mkdocs2confluence-0.9.4/tests/test_children_macro.py +107 -0
  10. {mkdocs2confluence-0.9.2 → mkdocs2confluence-0.9.4}/LICENSE +0 -0
  11. {mkdocs2confluence-0.9.2 → mkdocs2confluence-0.9.4}/setup.cfg +0 -0
  12. {mkdocs2confluence-0.9.2 → mkdocs2confluence-0.9.4}/src/mkdocs2confluence.egg-info/dependency_links.txt +0 -0
  13. {mkdocs2confluence-0.9.2 → mkdocs2confluence-0.9.4}/src/mkdocs2confluence.egg-info/entry_points.txt +0 -0
  14. {mkdocs2confluence-0.9.2 → mkdocs2confluence-0.9.4}/src/mkdocs2confluence.egg-info/requires.txt +0 -0
  15. {mkdocs2confluence-0.9.2 → mkdocs2confluence-0.9.4}/src/mkdocs2confluence.egg-info/top_level.txt +0 -0
  16. {mkdocs2confluence-0.9.2 → mkdocs2confluence-0.9.4}/src/mkdocs_to_confluence/__init__.py +0 -0
  17. {mkdocs2confluence-0.9.2 → mkdocs2confluence-0.9.4}/src/mkdocs_to_confluence/cli.py +0 -0
  18. {mkdocs2confluence-0.9.2 → mkdocs2confluence-0.9.4}/src/mkdocs_to_confluence/emitter/__init__.py +0 -0
  19. {mkdocs2confluence-0.9.2 → mkdocs2confluence-0.9.4}/src/mkdocs_to_confluence/ir/__init__.py +0 -0
  20. {mkdocs2confluence-0.9.2 → mkdocs2confluence-0.9.4}/src/mkdocs_to_confluence/ir/document.py +0 -0
  21. {mkdocs2confluence-0.9.2 → mkdocs2confluence-0.9.4}/src/mkdocs_to_confluence/ir/treeutil.py +0 -0
  22. {mkdocs2confluence-0.9.2 → mkdocs2confluence-0.9.4}/src/mkdocs_to_confluence/loader/__init__.py +0 -0
  23. {mkdocs2confluence-0.9.2 → mkdocs2confluence-0.9.4}/src/mkdocs_to_confluence/loader/config.py +0 -0
  24. {mkdocs2confluence-0.9.2 → mkdocs2confluence-0.9.4}/src/mkdocs_to_confluence/loader/extra_css.py +0 -0
  25. {mkdocs2confluence-0.9.2 → mkdocs2confluence-0.9.4}/src/mkdocs_to_confluence/loader/nav.py +0 -0
  26. {mkdocs2confluence-0.9.2 → mkdocs2confluence-0.9.4}/src/mkdocs_to_confluence/loader/page.py +0 -0
  27. {mkdocs2confluence-0.9.2 → mkdocs2confluence-0.9.4}/src/mkdocs_to_confluence/parser/__init__.py +0 -0
  28. {mkdocs2confluence-0.9.2 → mkdocs2confluence-0.9.4}/src/mkdocs_to_confluence/parser/markdown.py +0 -0
  29. {mkdocs2confluence-0.9.2 → mkdocs2confluence-0.9.4}/src/mkdocs_to_confluence/pdf/__init__.py +0 -0
  30. {mkdocs2confluence-0.9.2 → mkdocs2confluence-0.9.4}/src/mkdocs_to_confluence/pdf/generator.py +0 -0
  31. {mkdocs2confluence-0.9.2 → mkdocs2confluence-0.9.4}/src/mkdocs_to_confluence/pdf/render.py +0 -0
  32. {mkdocs2confluence-0.9.2 → mkdocs2confluence-0.9.4}/src/mkdocs_to_confluence/preprocess/__init__.py +0 -0
  33. {mkdocs2confluence-0.9.2 → mkdocs2confluence-0.9.4}/src/mkdocs_to_confluence/preprocess/abbrevs.py +0 -0
  34. {mkdocs2confluence-0.9.2 → mkdocs2confluence-0.9.4}/src/mkdocs_to_confluence/preprocess/fence.py +0 -0
  35. {mkdocs2confluence-0.9.2 → mkdocs2confluence-0.9.4}/src/mkdocs_to_confluence/preprocess/frontmatter.py +0 -0
  36. {mkdocs2confluence-0.9.2 → mkdocs2confluence-0.9.4}/src/mkdocs_to_confluence/preprocess/icons.py +0 -0
  37. {mkdocs2confluence-0.9.2 → mkdocs2confluence-0.9.4}/src/mkdocs_to_confluence/preprocess/includes.py +0 -0
  38. {mkdocs2confluence-0.9.2 → mkdocs2confluence-0.9.4}/src/mkdocs_to_confluence/preprocess/linkdefs.py +0 -0
  39. {mkdocs2confluence-0.9.2 → mkdocs2confluence-0.9.4}/src/mkdocs_to_confluence/preview/__init__.py +0 -0
  40. {mkdocs2confluence-0.9.2 → mkdocs2confluence-0.9.4}/src/mkdocs_to_confluence/preview/render.py +0 -0
  41. {mkdocs2confluence-0.9.2 → mkdocs2confluence-0.9.4}/src/mkdocs_to_confluence/preview/server.py +0 -0
  42. {mkdocs2confluence-0.9.2 → mkdocs2confluence-0.9.4}/src/mkdocs_to_confluence/publisher/__init__.py +0 -0
  43. {mkdocs2confluence-0.9.2 → mkdocs2confluence-0.9.4}/src/mkdocs_to_confluence/publisher/client.py +0 -0
  44. {mkdocs2confluence-0.9.2 → mkdocs2confluence-0.9.4}/src/mkdocs_to_confluence/sync/__init__.py +0 -0
  45. {mkdocs2confluence-0.9.2 → mkdocs2confluence-0.9.4}/src/mkdocs_to_confluence/sync/anchoring.py +0 -0
  46. {mkdocs2confluence-0.9.2 → mkdocs2confluence-0.9.4}/src/mkdocs_to_confluence/sync/command.py +0 -0
  47. {mkdocs2confluence-0.9.2 → mkdocs2confluence-0.9.4}/src/mkdocs_to_confluence/sync/comments.py +0 -0
  48. {mkdocs2confluence-0.9.2 → mkdocs2confluence-0.9.4}/src/mkdocs_to_confluence/sync/github.py +0 -0
  49. {mkdocs2confluence-0.9.2 → mkdocs2confluence-0.9.4}/src/mkdocs_to_confluence/sync/platform.py +0 -0
  50. {mkdocs2confluence-0.9.2 → mkdocs2confluence-0.9.4}/src/mkdocs_to_confluence/sync/state.py +0 -0
  51. {mkdocs2confluence-0.9.2 → mkdocs2confluence-0.9.4}/src/mkdocs_to_confluence/transforms/__init__.py +0 -0
  52. {mkdocs2confluence-0.9.2 → mkdocs2confluence-0.9.4}/src/mkdocs_to_confluence/transforms/abbrevs.py +0 -0
  53. {mkdocs2confluence-0.9.2 → mkdocs2confluence-0.9.4}/src/mkdocs_to_confluence/transforms/assets.py +0 -0
  54. {mkdocs2confluence-0.9.2 → mkdocs2confluence-0.9.4}/src/mkdocs_to_confluence/transforms/editlink.py +0 -0
  55. {mkdocs2confluence-0.9.2 → mkdocs2confluence-0.9.4}/src/mkdocs_to_confluence/transforms/footer.py +0 -0
  56. {mkdocs2confluence-0.9.2 → mkdocs2confluence-0.9.4}/src/mkdocs_to_confluence/transforms/images.py +0 -0
  57. {mkdocs2confluence-0.9.2 → mkdocs2confluence-0.9.4}/src/mkdocs_to_confluence/transforms/internallinks.py +0 -0
  58. {mkdocs2confluence-0.9.2 → mkdocs2confluence-0.9.4}/src/mkdocs_to_confluence/transforms/mermaid.py +0 -0
  59. {mkdocs2confluence-0.9.2 → mkdocs2confluence-0.9.4}/tests/test_abbrevs.py +0 -0
  60. {mkdocs2confluence-0.9.2 → mkdocs2confluence-0.9.4}/tests/test_cli.py +0 -0
  61. {mkdocs2confluence-0.9.2 → mkdocs2confluence-0.9.4}/tests/test_editlink.py +0 -0
  62. {mkdocs2confluence-0.9.2 → mkdocs2confluence-0.9.4}/tests/test_emitter.py +0 -0
  63. {mkdocs2confluence-0.9.2 → mkdocs2confluence-0.9.4}/tests/test_extra_css.py +0 -0
  64. {mkdocs2confluence-0.9.2 → mkdocs2confluence-0.9.4}/tests/test_footer.py +0 -0
  65. {mkdocs2confluence-0.9.2 → mkdocs2confluence-0.9.4}/tests/test_frontmatter.py +0 -0
  66. {mkdocs2confluence-0.9.2 → mkdocs2confluence-0.9.4}/tests/test_icons.py +0 -0
  67. {mkdocs2confluence-0.9.2 → mkdocs2confluence-0.9.4}/tests/test_images.py +0 -0
  68. {mkdocs2confluence-0.9.2 → mkdocs2confluence-0.9.4}/tests/test_internallinks.py +0 -0
  69. {mkdocs2confluence-0.9.2 → mkdocs2confluence-0.9.4}/tests/test_ir.py +0 -0
  70. {mkdocs2confluence-0.9.2 → mkdocs2confluence-0.9.4}/tests/test_linkdefs.py +0 -0
  71. {mkdocs2confluence-0.9.2 → mkdocs2confluence-0.9.4}/tests/test_loader.py +0 -0
  72. {mkdocs2confluence-0.9.2 → mkdocs2confluence-0.9.4}/tests/test_mermaid.py +0 -0
  73. {mkdocs2confluence-0.9.2 → mkdocs2confluence-0.9.4}/tests/test_page_loader.py +0 -0
  74. {mkdocs2confluence-0.9.2 → mkdocs2confluence-0.9.4}/tests/test_parser.py +0 -0
  75. {mkdocs2confluence-0.9.2 → mkdocs2confluence-0.9.4}/tests/test_pdf.py +0 -0
  76. {mkdocs2confluence-0.9.2 → mkdocs2confluence-0.9.4}/tests/test_preprocess.py +0 -0
  77. {mkdocs2confluence-0.9.2 → mkdocs2confluence-0.9.4}/tests/test_preview.py +0 -0
  78. {mkdocs2confluence-0.9.2 → mkdocs2confluence-0.9.4}/tests/test_publish_client.py +0 -0
  79. {mkdocs2confluence-0.9.2 → mkdocs2confluence-0.9.4}/tests/test_publish_config.py +0 -0
  80. {mkdocs2confluence-0.9.2 → mkdocs2confluence-0.9.4}/tests/test_publish_pipeline.py +0 -0
  81. {mkdocs2confluence-0.9.2 → mkdocs2confluence-0.9.4}/tests/test_server.py +0 -0
  82. {mkdocs2confluence-0.9.2 → mkdocs2confluence-0.9.4}/tests/test_sync_anchoring.py +0 -0
  83. {mkdocs2confluence-0.9.2 → mkdocs2confluence-0.9.4}/tests/test_sync_command.py +0 -0
  84. {mkdocs2confluence-0.9.2 → mkdocs2confluence-0.9.4}/tests/test_sync_comments.py +0 -0
  85. {mkdocs2confluence-0.9.2 → mkdocs2confluence-0.9.4}/tests/test_sync_github.py +0 -0
  86. {mkdocs2confluence-0.9.2 → mkdocs2confluence-0.9.4}/tests/test_sync_state.py +0 -0
  87. {mkdocs2confluence-0.9.2 → mkdocs2confluence-0.9.4}/tests/test_treeutil.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: mkdocs2confluence
3
- Version: 0.9.2
3
+ Version: 0.9.4
4
4
  Summary: Publish MkDocs Material pages to Confluence Cloud — admonitions, Mermaid diagrams, tabs, page properties and more
5
5
  Author: Anders Hybertz
6
6
  License: GPL-3.0-or-later
@@ -431,7 +431,11 @@ When `repo_url` + `edit_uri` are set in `mkdocs.yml`, a **Page source** footer p
431
431
 
432
432
  - **Edit this page** — links to the source file in your VCS (GitHub/GitLab/etc.)
433
433
  - **View history** — links to the file's commit history (derived automatically for GitHub and GitLab URLs)
434
- - **Last commit** — short commit SHA, message, author, and relative date from `git log` at publish time (omitted if git is unavailable or the file is untracked)
434
+ - **Last commit** — short commit SHA (linked to that commit), message, author, and relative date from `git log` at publish time (omitted if git is unavailable or the file is untracked)
435
+
436
+ ### Section index child pages
437
+
438
+ When a MkDocs navigation section has an `index.md`, the published Confluence page automatically includes the native **Children Display macro** below the page content. This renders a live, auto-maintained list of all direct child pages — no manual curation needed. The macro is injected by default on every section index page.
435
439
 
436
440
  ### Abbreviation expansion
437
441
 
@@ -391,7 +391,11 @@ When `repo_url` + `edit_uri` are set in `mkdocs.yml`, a **Page source** footer p
391
391
 
392
392
  - **Edit this page** — links to the source file in your VCS (GitHub/GitLab/etc.)
393
393
  - **View history** — links to the file's commit history (derived automatically for GitHub and GitLab URLs)
394
- - **Last commit** — short commit SHA, message, author, and relative date from `git log` at publish time (omitted if git is unavailable or the file is untracked)
394
+ - **Last commit** — short commit SHA (linked to that commit), message, author, and relative date from `git log` at publish time (omitted if git is unavailable or the file is untracked)
395
+
396
+ ### Section index child pages
397
+
398
+ When a MkDocs navigation section has an `index.md`, the published Confluence page automatically includes the native **Children Display macro** below the page content. This renders a live, auto-maintained list of all direct child pages — no manual curation needed. The macro is injected by default on every section index page.
395
399
 
396
400
  ### Abbreviation expansion
397
401
 
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "mkdocs2confluence"
3
- version = "0.9.2"
3
+ version = "0.9.4"
4
4
  description = "Publish MkDocs Material pages to Confluence Cloud — admonitions, Mermaid diagrams, tabs, page properties and more"
5
5
  readme = "README.md"
6
6
  license = { text = "GPL-3.0-or-later" }
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: mkdocs2confluence
3
- Version: 0.9.2
3
+ Version: 0.9.4
4
4
  Summary: Publish MkDocs Material pages to Confluence Cloud — admonitions, Mermaid diagrams, tabs, page properties and more
5
5
  Author: Anders Hybertz
6
6
  License: GPL-3.0-or-later
@@ -431,7 +431,11 @@ When `repo_url` + `edit_uri` are set in `mkdocs.yml`, a **Page source** footer p
431
431
 
432
432
  - **Edit this page** — links to the source file in your VCS (GitHub/GitLab/etc.)
433
433
  - **View history** — links to the file's commit history (derived automatically for GitHub and GitLab URLs)
434
- - **Last commit** — short commit SHA, message, author, and relative date from `git log` at publish time (omitted if git is unavailable or the file is untracked)
434
+ - **Last commit** — short commit SHA (linked to that commit), message, author, and relative date from `git log` at publish time (omitted if git is unavailable or the file is untracked)
435
+
436
+ ### Section index child pages
437
+
438
+ When a MkDocs navigation section has an `index.md`, the published Confluence page automatically includes the native **Children Display macro** below the page content. This renders a live, auto-maintained list of all direct child pages — no manual curation needed. The macro is injected by default on every section index page.
435
439
 
436
440
  ### Abbreviation expansion
437
441
 
@@ -54,6 +54,7 @@ src/mkdocs_to_confluence/transforms/images.py
54
54
  src/mkdocs_to_confluence/transforms/internallinks.py
55
55
  src/mkdocs_to_confluence/transforms/mermaid.py
56
56
  tests/test_abbrevs.py
57
+ tests/test_children_macro.py
57
58
  tests/test_cli.py
58
59
  tests/test_editlink.py
59
60
  tests/test_emitter.py
@@ -27,6 +27,7 @@ from mkdocs_to_confluence.ir.nodes import (
27
27
  BlockQuote,
28
28
  BoldNode,
29
29
  BulletList,
30
+ ChildrenMacro,
30
31
  CodeBlock,
31
32
  CodeInlineNode,
32
33
  ContentTabs,
@@ -198,6 +199,8 @@ def _emit_node(node: IRNode) -> str:
198
199
  return _emit_grid_cards(node)
199
200
  if isinstance(node, SourceFooter):
200
201
  return _emit_source_footer(node)
202
+ if isinstance(node, ChildrenMacro):
203
+ return _emit_children_macro()
201
204
  if isinstance(node, UnsupportedBlock):
202
205
  return _emit_unsupported(node)
203
206
  # Inline nodes at block level (shouldn't normally appear, but be safe)
@@ -262,6 +265,15 @@ def _xml_safe(text: str) -> str:
262
265
  return html.escape(text).encode("ascii", "xmlcharrefreplace").decode()
263
266
 
264
267
 
268
+ def _emit_children_macro() -> str:
269
+ """Emit the Confluence Children Display macro for section index pages."""
270
+ return (
271
+ '<ac:structured-macro ac:name="children">'
272
+ '<ac:parameter ac:name="depth">1</ac:parameter>'
273
+ "</ac:structured-macro>"
274
+ )
275
+
276
+
265
277
  def _emit_source_footer(node: SourceFooter) -> str:
266
278
  """Emit a borderless panel macro with edit/history links and last-commit info."""
267
279
  edit_href = html.escape(node.edit_url)
@@ -428,6 +428,15 @@ class FrontMatter(IRNode):
428
428
  # ── Abbreviation footnotes ────────────────────────────────────────────────────
429
429
 
430
430
 
431
+ @dataclass(frozen=True)
432
+ class ChildrenMacro(IRNode):
433
+ """Confluence Children Display macro listing direct child pages.
434
+
435
+ Appended to section index pages so readers see a live, auto-maintained
436
+ sub-page list. Emitted as ``ac:structured-macro ac:name="children"``.
437
+ """
438
+
439
+
431
440
  @dataclass(frozen=True)
432
441
  class SourceFooter(IRNode):
433
442
  """Footer panel showing source-control links and last-commit info.
@@ -24,7 +24,7 @@ from typing import TYPE_CHECKING, Literal
24
24
  import yaml
25
25
 
26
26
  from mkdocs_to_confluence.emitter.xhtml import emit
27
- from mkdocs_to_confluence.ir.nodes import FrontMatter, SourceFooter
27
+ from mkdocs_to_confluence.ir.nodes import ChildrenMacro, FrontMatter, SourceFooter
28
28
  from mkdocs_to_confluence.loader.config import ConfluenceConfig, MkDocsConfig
29
29
  from mkdocs_to_confluence.loader.nav import NavNode
30
30
  from mkdocs_to_confluence.loader.page import PageLoadError, load_page
@@ -139,6 +139,7 @@ def compile_page(
139
139
  config: MkDocsConfig,
140
140
  link_map: dict[str, str] | None = None,
141
141
  *,
142
+ is_section_index: bool = False,
142
143
  quiet: bool = False,
143
144
  ) -> tuple[str, list[Path], tuple[str, ...], str | None, str | None]:
144
145
  """Run the full compile pipeline for one page.
@@ -168,6 +169,8 @@ def compile_page(
168
169
  preprocessed = expand_link_refs(preprocessed, link_defs)
169
170
  preprocessed = strip_link_defs(preprocessed)
170
171
  ir_nodes = parse(preprocessed)
172
+ if is_section_index:
173
+ ir_nodes = ir_nodes + (ChildrenMacro(),)
171
174
  ir_nodes = apply_abbreviations(ir_nodes, abbrevs, page_text=preprocessed)
172
175
  ir_nodes, attachments = resolve_local_assets(
173
176
  ir_nodes,
@@ -283,7 +286,7 @@ def _plan_nodes(
283
286
  print(f" compiling '{clean_title}' (section index)")
284
287
  try:
285
288
  xhtml, attachments, labels, confluence_status, version_message = compile_page(
286
- index_child, config, link_map, quiet=quiet
289
+ index_child, config, link_map, is_section_index=True, quiet=quiet
287
290
  )
288
291
  existing = client.find_page(space_id, clean_title)
289
292
  xhtml_h = _xhtml_hash(xhtml)
@@ -0,0 +1,107 @@
1
+ """Tests for the ChildrenMacro IR node, emitter, and pipeline integration."""
2
+
3
+ from pathlib import Path
4
+ from unittest.mock import patch
5
+
6
+ from mkdocs_to_confluence.emitter.xhtml import emit
7
+ from mkdocs_to_confluence.ir.nodes import ChildrenMacro
8
+ from mkdocs_to_confluence.publisher.pipeline import compile_page
9
+
10
+ # ── Emitter ───────────────────────────────────────────────────────────────────
11
+
12
+
13
+ def test_children_macro_emits_structured_macro() -> None:
14
+ """ChildrenMacro must emit the Confluence children structured macro."""
15
+ xhtml = emit((ChildrenMacro(),))
16
+ assert 'ac:name="children"' in xhtml
17
+ assert 'ac:parameter ac:name="depth"' in xhtml
18
+ assert ">1<" in xhtml
19
+
20
+
21
+ def test_children_macro_no_extra_params() -> None:
22
+ """Children macro must not emit sort/style params — Confluence defaults are fine."""
23
+ xhtml = emit((ChildrenMacro(),))
24
+ assert "sort" not in xhtml
25
+ assert "style" not in xhtml
26
+
27
+
28
+ # ── Pipeline integration ──────────────────────────────────────────────────────
29
+
30
+
31
+ def test_compile_page_section_index_includes_children_macro(tmp_path: Path) -> None:
32
+ """is_section_index=True must inject ChildrenMacro into emitted XHTML."""
33
+ from mkdocs_to_confluence.loader.config import MkDocsConfig
34
+
35
+ docs = tmp_path / "docs"
36
+ docs.mkdir()
37
+ md = docs / "index.md"
38
+ md.write_text("# Section\n\nIntro text.\n", encoding="utf-8")
39
+
40
+ node = _page_node("Section", md)
41
+ config = MkDocsConfig(site_name="Test", docs_dir=docs, repo_url=None, edit_uri=None, nav=None)
42
+
43
+ xhtml, _, _, _, _ = compile_page(node, config, is_section_index=True)
44
+
45
+ assert 'ac:name="children"' in xhtml
46
+
47
+
48
+ def test_compile_page_non_index_excludes_children_macro(tmp_path: Path) -> None:
49
+ """Regular pages must NOT include the ChildrenMacro."""
50
+ from mkdocs_to_confluence.loader.config import MkDocsConfig
51
+
52
+ docs = tmp_path / "docs"
53
+ docs.mkdir()
54
+ md = docs / "guide.md"
55
+ md.write_text("# Guide\n\nContent.\n", encoding="utf-8")
56
+
57
+ node = _page_node("Guide", md)
58
+ config = MkDocsConfig(site_name="Test", docs_dir=docs, repo_url=None, edit_uri=None, nav=None)
59
+
60
+ xhtml, _, _, _, _ = compile_page(node, config, is_section_index=False)
61
+
62
+ assert 'ac:name="children"' not in xhtml
63
+
64
+
65
+ def test_compile_page_children_macro_before_footer(tmp_path: Path) -> None:
66
+ """ChildrenMacro must appear before the source footer in the XHTML."""
67
+ from mkdocs_to_confluence.loader.config import MkDocsConfig
68
+
69
+ docs = tmp_path / "docs"
70
+ docs.mkdir()
71
+ md = docs / "index.md"
72
+ md.write_text("# Section\n\nIntro.\n", encoding="utf-8")
73
+
74
+ node = _page_node("Section", md)
75
+ config = MkDocsConfig(
76
+ site_name="Test",
77
+ docs_dir=docs,
78
+ repo_url="https://github.com/org/repo",
79
+ edit_uri="edit/main/docs/",
80
+ nav=None,
81
+ )
82
+
83
+ with patch(
84
+ "mkdocs_to_confluence.transforms.footer._last_commit_info", return_value=None
85
+ ):
86
+ xhtml, _, _, _, _ = compile_page(node, config, is_section_index=True)
87
+
88
+ children_pos = xhtml.find('ac:name="children"')
89
+ panel_pos = xhtml.find('ac:name="panel"')
90
+ assert children_pos != -1
91
+ assert panel_pos != -1
92
+ assert children_pos < panel_pos
93
+
94
+
95
+ # ── Helpers ───────────────────────────────────────────────────────────────────
96
+
97
+
98
+ def _page_node(title: str, path: Path) -> object:
99
+ from mkdocs_to_confluence.loader.nav import NavNode
100
+
101
+ return NavNode(
102
+ title=title,
103
+ docs_path=path.name,
104
+ source_path=path,
105
+ level=0,
106
+ children=(),
107
+ )