+
+ Markdown
+
+
Edit on GitHub
diff --git a/tests/Integration/tests/markdown-rendering/basics/expected/index.md b/tests/Integration/tests/markdown-rendering/basics/expected/index.md
new file mode 100644
index 000000000..3a1a1179c
--- /dev/null
+++ b/tests/Integration/tests/markdown-rendering/basics/expected/index.md
@@ -0,0 +1,38 @@
+---
+title: "Markdown rendering"
+permalink: "https://docs.typo3.org/permalink/mdtest:markdown-rendering"
+source: "index.rst"
+start: true
+modified: "2023-01-01T12:00:00+00:00"
+---
+
+# Markdown rendering
+
+A paragraph with **strong**, *emphasis* and `literal` text, plus a
+[link to the TYPO3 website](https://typo3.org).
+
+## A section
+
+- first bullet
+- second bullet
+
+```php
+$greeting = 'hello';
+```
+
+> [!NOTE]
+> An admonition renders as a GFM alert.
+
+There are the following subpages:
+
+- [Visible child](https://docs.typo3.org/permalink/mdtest:visible-child)
+- [A nested page](https://docs.typo3.org/permalink/mdtest:a-nested-page)
+
+The navigation below is built but not shown:
+
+A directive without a Markdown template leaves a marker naming it, and its
+content is still rendered, so a gap is visible rather than silent:
+
+
+
+The content of an unhandled directive is kept.
diff --git a/tests/Integration/tests/markdown-rendering/basics/expected/logs/warning.log b/tests/Integration/tests/markdown-rendering/basics/expected/logs/warning.log
new file mode 100644
index 000000000..d3796eca9
--- /dev/null
+++ b/tests/Integration/tests/markdown-rendering/basics/expected/logs/warning.log
@@ -0,0 +1 @@
+No template found for rendering directive "not-a-real-directive".
diff --git a/tests/Integration/tests/markdown-rendering/basics/expected/sub/nested.md b/tests/Integration/tests/markdown-rendering/basics/expected/sub/nested.md
new file mode 100644
index 000000000..2f69b057c
--- /dev/null
+++ b/tests/Integration/tests/markdown-rendering/basics/expected/sub/nested.md
@@ -0,0 +1,11 @@
+---
+title: "A nested page"
+permalink: "https://docs.typo3.org/permalink/mdtest:a-nested-page"
+source: "sub/nested.rst"
+modified: "2023-01-01T12:00:00+00:00"
+---
+
+# A nested page
+
+A page in a subdirectory, so that the source path in the front matter has
+to carry the directory too, the way a changelog entry does.
diff --git a/tests/Integration/tests/markdown-rendering/basics/expected/visible-child.md b/tests/Integration/tests/markdown-rendering/basics/expected/visible-child.md
new file mode 100644
index 000000000..e914bbca0
--- /dev/null
+++ b/tests/Integration/tests/markdown-rendering/basics/expected/visible-child.md
@@ -0,0 +1,10 @@
+---
+title: "Visible child"
+permalink: "https://docs.typo3.org/permalink/mdtest:visible-child"
+source: "visible-child.rst"
+modified: "2023-01-01T12:00:00+00:00"
+---
+
+# Visible child
+
+This page is listed in the toctree above.
diff --git a/tests/Integration/tests/markdown-rendering/basics/input/guides.xml b/tests/Integration/tests/markdown-rendering/basics/input/guides.xml
new file mode 100644
index 000000000..64fc073bb
--- /dev/null
+++ b/tests/Integration/tests/markdown-rendering/basics/input/guides.xml
@@ -0,0 +1,11 @@
+
+
+
+ html
+
diff --git a/tests/Integration/tests/markdown-rendering/basics/input/hidden-child.rst b/tests/Integration/tests/markdown-rendering/basics/input/hidden-child.rst
new file mode 100644
index 000000000..fc1568db7
--- /dev/null
+++ b/tests/Integration/tests/markdown-rendering/basics/input/hidden-child.rst
@@ -0,0 +1,5 @@
+============
+Hidden child
+============
+
+This page is reachable but not listed.
diff --git a/tests/Integration/tests/markdown-rendering/basics/input/index.rst b/tests/Integration/tests/markdown-rendering/basics/input/index.rst
new file mode 100644
index 000000000..a56604583
--- /dev/null
+++ b/tests/Integration/tests/markdown-rendering/basics/input/index.rst
@@ -0,0 +1,41 @@
+==================
+Markdown rendering
+==================
+
+A paragraph with **strong**, *emphasis* and ``literal`` text, plus a
+`link to the TYPO3 website
`__.
+
+A section
+=========
+
+* first bullet
+* second bullet
+
+.. code-block:: php
+
+ $greeting = 'hello';
+
+.. note::
+
+ An admonition renders as a GFM alert.
+
+There are the following subpages:
+
+.. toctree::
+
+ visible-child
+ sub/nested
+
+The navigation below is built but not shown:
+
+.. toctree::
+ :hidden:
+
+ hidden-child
+
+A directive without a Markdown template leaves a marker naming it, and its
+content is still rendered, so a gap is visible rather than silent:
+
+.. not-a-real-directive::
+
+ The content of an unhandled directive is kept.
diff --git a/tests/Integration/tests/markdown-rendering/basics/input/sub/nested.rst b/tests/Integration/tests/markdown-rendering/basics/input/sub/nested.rst
new file mode 100644
index 000000000..81f45d5ec
--- /dev/null
+++ b/tests/Integration/tests/markdown-rendering/basics/input/sub/nested.rst
@@ -0,0 +1,6 @@
+==============
+A nested page
+==============
+
+A page in a subdirectory, so that the source path in the front matter has
+to carry the directory too, the way a changelog entry does.
diff --git a/tests/Integration/tests/markdown-rendering/basics/input/visible-child.rst b/tests/Integration/tests/markdown-rendering/basics/input/visible-child.rst
new file mode 100644
index 000000000..3a74e0338
--- /dev/null
+++ b/tests/Integration/tests/markdown-rendering/basics/input/visible-child.rst
@@ -0,0 +1,5 @@
+=============
+Visible child
+=============
+
+This page is listed in the toctree above.
diff --git a/tests/Integration/tests/markdown-rendering/confval/expected/index.md b/tests/Integration/tests/markdown-rendering/confval/expected/index.md
new file mode 100644
index 000000000..b46a68481
--- /dev/null
+++ b/tests/Integration/tests/markdown-rendering/confval/expected/index.md
@@ -0,0 +1,66 @@
+---
+title: "Configuration values in md"
+permalink: "https://docs.typo3.org/permalink/mdconfval:configuration-values-in-md"
+source: "index.rst"
+start: true
+modified: "2023-01-01T12:00:00+00:00"
+---
+
+# Configuration values in md
+
+A confval is written as a list item so that the definition has an end:
+everything indented under it belongs to it, and the next one starts where
+the indentation does.
+
+## Nested values
+
+- **mail**
+
+ - *Type:* array
+
+ Settings for outgoing mail.
+
+ - **mail.transport**
+
+ - *Type:* string
+ - *Required:* true
+ - *Default:* sendmail
+
+ How messages are handed over.
+
+ - **mail.transport.timeout**
+
+ - *Type:* int
+ - *Default:* 30
+
+ Seconds before the transport gives up.
+
+- **cache**
+
+ - *Type:* array
+
+ Settings for the cache.
+
+## Without properties
+
+- **bare**
+
+ A value with no type, no default and not required lists no properties
+ at all, rather than saying "N/A" three times.
+
+## Description with its own blocks
+
+- **complex**
+
+ - *Type:* string
+
+ The description can hold more than a paragraph:
+
+ - a list item
+ - another one
+
+ ```php
+ $config['complex'] = 'value';
+ ```
+
+ And a closing paragraph, still inside the definition.
diff --git a/tests/Integration/tests/markdown-rendering/confval/input/guides.xml b/tests/Integration/tests/markdown-rendering/confval/input/guides.xml
new file mode 100644
index 000000000..28b381197
--- /dev/null
+++ b/tests/Integration/tests/markdown-rendering/confval/input/guides.xml
@@ -0,0 +1,11 @@
+
+
+
+ md
+
diff --git a/tests/Integration/tests/markdown-rendering/confval/input/index.rst b/tests/Integration/tests/markdown-rendering/confval/input/index.rst
new file mode 100644
index 000000000..d75dbe7ac
--- /dev/null
+++ b/tests/Integration/tests/markdown-rendering/confval/input/index.rst
@@ -0,0 +1,70 @@
+==========================
+Configuration values in md
+==========================
+
+A confval is written as a list item so that the definition has an end:
+everything indented under it belongs to it, and the next one starts where
+the indentation does.
+
+Nested values
+=============
+
+.. confval-menu::
+ :name: mail
+ :display: tree
+ :type:
+ :default:
+
+ .. confval:: mail
+ :name: mail
+ :type: array
+
+ Settings for outgoing mail.
+
+ .. confval:: mail.transport
+ :name: mail-transport
+ :type: string
+ :default: sendmail
+ :required: true
+
+ How messages are handed over.
+
+ .. confval:: mail.transport.timeout
+ :name: mail-transport-timeout
+ :type: int
+ :default: 30
+
+ Seconds before the transport gives up.
+
+ .. confval:: cache
+ :name: cache
+ :type: array
+
+ Settings for the cache.
+
+Without properties
+==================
+
+.. confval:: bare
+ :name: bare
+
+ A value with no type, no default and not required lists no properties
+ at all, rather than saying "N/A" three times.
+
+Description with its own blocks
+===============================
+
+.. confval:: complex
+ :name: complex
+ :type: string
+
+ The description can hold more than a paragraph:
+
+ - a list item
+ - another one
+
+ .. code-block:: php
+
+ $config['complex'] = 'value';
+
+ And a closing paragraph, still inside the definition.
diff --git a/tests/Integration/tests/markdown-rendering/console-command/expected/index.md b/tests/Integration/tests/markdown-rendering/console-command/expected/index.md
new file mode 100644
index 000000000..8c70955f0
--- /dev/null
+++ b/tests/Integration/tests/markdown-rendering/console-command/expected/index.md
@@ -0,0 +1,79 @@
+---
+title: "Console commands"
+permalink: "https://docs.typo3.org/permalink/mdconsole:console-commands"
+source: "index.rst"
+start: true
+modified: "2023-01-01T12:00:00+00:00"
+---
+
+# Console commands
+
+The overview lists every command and renders each one below the table. A
+hidden command is shown because "show-hidden" is set.
+
+| Command | Description | Hidden |
+| --- | --- | --- |
+| **global** | | |
+| [`_complete`](https://docs.typo3.org/permalink/mdconsole:console-command-complete) | Internal command to provide shell completion suggestions | True |
+| **cache** | | |
+| [`cache:flush`](https://docs.typo3.org/permalink/mdconsole:console-command-cache-flush) | Flush TYPO3 caches. | |
+| **language** | | |
+| [`language:update`](https://docs.typo3.org/permalink/mdconsole:console-command-language-update) | Update the language files of all activated extensions | |
+
+- **cache:flush**
+
+ Flush TYPO3 caches.
+
+ *Usage*
+
+ ```bash
+ cache:flush [-g|--group [GROUP]]
+ ```
+
+ *Options*
+
+ - **--group / -g**
+
+ - *Value:* Optional
+ - *Default value:* "all"
+
+ The cache group to flush (system, pages, di or all)
+
+ *Help*
+
+ Flushes the caches of the given group.
+
+- **language:update**
+
+ Update the language files of all activated extensions
+
+ *Usage*
+
+ ```bash
+ language:update [--skip-extension SKIP-EXTENSION] [--] [...]
+ ```
+
+ *Arguments*
+
+ - **locales**
+
+ Provide iso codes separated by space to update only selected language packs.
+
+ *Options*
+
+ - **--skip-extension**
+
+ - *Value:* Required (multiple)
+ - *Default value:* \[\]
+
+ Skip extension, for example one that has no language packs.
+
+- **\_complete**
+
+ Internal command to provide shell completion suggestions
+
+ *Usage*
+
+ ```bash
+ _complete
+ ```
diff --git a/tests/Integration/tests/markdown-rendering/console-command/expected/single.md b/tests/Integration/tests/markdown-rendering/console-command/expected/single.md
new file mode 100644
index 000000000..2a05b49c7
--- /dev/null
+++ b/tests/Integration/tests/markdown-rendering/console-command/expected/single.md
@@ -0,0 +1,34 @@
+---
+title: "A single command"
+permalink: "https://docs.typo3.org/permalink/mdconsole:a-single-command"
+source: "single.rst"
+modified: "2023-01-01T12:00:00+00:00"
+---
+
+# A single command
+
+A command declared on its own, with the script that invokes it in front of its
+name. It repeats a command from the overview, so it is not indexed again.
+
+- **vendor/bin/typo3 cache:flush**
+
+ Flush TYPO3 caches.
+
+ *Usage*
+
+ ```bash
+ vendor/bin/typo3 cache:flush [-g|--group [GROUP]]
+ ```
+
+ *Options*
+
+ - **--group / -g**
+
+ - *Value:* Optional
+ - *Default value:* "all"
+
+ The cache group to flush (system, pages, di or all)
+
+ *Help*
+
+ Flushes the caches of the given group.
diff --git a/tests/Integration/tests/markdown-rendering/console-command/input/commands.json b/tests/Integration/tests/markdown-rendering/console-command/input/commands.json
new file mode 100644
index 000000000..ea82be8a7
--- /dev/null
+++ b/tests/Integration/tests/markdown-rendering/console-command/input/commands.json
@@ -0,0 +1,95 @@
+{
+ "application": {
+ "name": "TYPO3 CMS",
+ "version": "14.0.0-dev"
+ },
+ "commands": [
+ {
+ "name": "cache:flush",
+ "description": "Flush TYPO3 caches.",
+ "usage": [
+ "cache:flush [-g|--group [GROUP]]"
+ ],
+ "help": "Flushes the caches of the given group.",
+ "definition": {
+ "arguments": [],
+ "options": {
+ "group": {
+ "name": "--group",
+ "shortcut": "-g",
+ "accept_value": true,
+ "is_value_required": false,
+ "is_multiple": false,
+ "description": "The cache group to flush (system, pages, di or all)",
+ "default": "all"
+ }
+ }
+ },
+ "hidden": false
+ },
+ {
+ "name": "language:update",
+ "description": "Update the language files of all activated extensions",
+ "usage": [
+ "language:update [--skip-extension SKIP-EXTENSION] [--] [...]"
+ ],
+ "help": "",
+ "definition": {
+ "arguments": {
+ "locales": {
+ "name": "locales",
+ "is_required": false,
+ "is_array": true,
+ "description": "Provide iso codes separated by space to update only selected language packs.",
+ "default": []
+ }
+ },
+ "options": {
+ "skip-extension": {
+ "name": "--skip-extension",
+ "shortcut": "",
+ "accept_value": true,
+ "is_value_required": true,
+ "is_multiple": true,
+ "description": "Skip extension, for example one that has no language packs.",
+ "default": []
+ }
+ }
+ },
+ "hidden": false
+ },
+ {
+ "name": "_complete",
+ "description": "Internal command to provide shell completion suggestions",
+ "usage": [
+ "_complete"
+ ],
+ "help": "",
+ "definition": {
+ "arguments": [],
+ "options": {}
+ },
+ "hidden": true
+ }
+ ],
+ "namespaces": [
+ {
+ "id": "_global",
+ "commands": [
+ "_complete"
+ ]
+ },
+ {
+ "id": "cache",
+ "commands": [
+ "cache:flush"
+ ]
+ },
+ {
+ "id": "language",
+ "commands": [
+ "language:update"
+ ]
+ }
+ ]
+}
diff --git a/tests/Integration/tests/markdown-rendering/console-command/input/guides.xml b/tests/Integration/tests/markdown-rendering/console-command/input/guides.xml
new file mode 100644
index 000000000..172083c74
--- /dev/null
+++ b/tests/Integration/tests/markdown-rendering/console-command/input/guides.xml
@@ -0,0 +1,11 @@
+
+
+
+ md
+
diff --git a/tests/Integration/tests/markdown-rendering/console-command/input/index.rst b/tests/Integration/tests/markdown-rendering/console-command/input/index.rst
new file mode 100644
index 000000000..e8704c84e
--- /dev/null
+++ b/tests/Integration/tests/markdown-rendering/console-command/input/index.rst
@@ -0,0 +1,15 @@
+==================
+Console commands
+==================
+
+The overview lists every command and renders each one below the table. A
+hidden command is shown because "show-hidden" is set.
+
+.. console:command-list::
+ :json: commands.json
+ :show-hidden:
+
+.. toctree::
+ :hidden:
+
+ single
diff --git a/tests/Integration/tests/markdown-rendering/console-command/input/single.rst b/tests/Integration/tests/markdown-rendering/console-command/input/single.rst
new file mode 100644
index 000000000..3a3de6337
--- /dev/null
+++ b/tests/Integration/tests/markdown-rendering/console-command/input/single.rst
@@ -0,0 +1,11 @@
+=================
+A single command
+=================
+
+A command declared on its own, with the script that invokes it in front of its
+name. It repeats a command from the overview, so it is not indexed again.
+
+.. console:command:: cache:flush
+ :json: commands.json
+ :script: vendor/bin/typo3
+ :noindex:
diff --git a/tests/Integration/tests/markdown-rendering/directives/expected/index.md b/tests/Integration/tests/markdown-rendering/directives/expected/index.md
new file mode 100644
index 000000000..dec651651
--- /dev/null
+++ b/tests/Integration/tests/markdown-rendering/directives/expected/index.md
@@ -0,0 +1,68 @@
+---
+title: "Directives"
+permalink: "https://docs.typo3.org/permalink/mddirectives:directives"
+source: "index.rst"
+start: true
+modified: "2023-01-01T12:00:00+00:00"
+---
+
+# Directives
+
+A horizontal list keeps its items but loses the columns, which Markdown
+cannot lay out sideways.
+
+- A list of
+- short items
+- that would be
+- side by side
+
+A video cannot be embedded, so it becomes a link to itself.
+
+[Watch this video on YouTube](https://www.youtube.com/watch?v=UdIYDZgBrQU)
+
+A glossary is its entries; the A-Z navigation around them does not survive.
+
+- **CMS**
+
+ Content management system
+
+- **magic number**
+
+ A magic number is a magic number.
+
+A diagram and a formula are source text that HTML turns into a picture.
+Markdown shows the source, tagged for the renderers that know it.
+
+```plantuml
+Alice -> Bob: Hello
+Bob --> Alice: Hi
+```
+
+```math
+a^2 + b^2 = c^2
+```
+
+A code block keeps its caption, which says where the snippet belongs.
+
+**EXT:my_extension/ext_localconf.php**
+
+```php
+$GLOBALS['TYPO3_CONF_VARS']['SYS']['caching'] = [];
+```
+
+The "option" directive is how the manuals write a configuration value; it is
+shaped like a confval.
+
+- **errorFluidTemplate**
+
+ The path to the Fluid template file.
+
+A TYPO3 file definition carries both of its paths.
+
+- **ext_localconf.php**
+
+ - *Scope:* extension
+ - *Path (Composer):* packages/my_extension/ext_localconf.php
+ - *Path (Classic):* typo3conf/ext/my_extension/ext_localconf.php
+
+ Included on every request.
diff --git a/tests/Integration/tests/markdown-rendering/directives/input/guides.xml b/tests/Integration/tests/markdown-rendering/directives/input/guides.xml
new file mode 100644
index 000000000..015801a90
--- /dev/null
+++ b/tests/Integration/tests/markdown-rendering/directives/input/guides.xml
@@ -0,0 +1,11 @@
+
+
+
+ md
+
diff --git a/tests/Integration/tests/markdown-rendering/directives/input/index.rst b/tests/Integration/tests/markdown-rendering/directives/input/index.rst
new file mode 100644
index 000000000..257d2122a
--- /dev/null
+++ b/tests/Integration/tests/markdown-rendering/directives/input/index.rst
@@ -0,0 +1,64 @@
+==========
+Directives
+==========
+
+A horizontal list keeps its items but loses the columns, which Markdown
+cannot lay out sideways.
+
+.. hlist::
+ :columns: 3
+
+ * A list of
+ * short items
+ * that would be
+ * side by side
+
+A video cannot be embedded, so it becomes a link to itself.
+
+.. youtube:: UdIYDZgBrQU
+
+A glossary is its entries; the A-Z navigation around them does not survive.
+
+.. glossary::
+
+ CMS
+ Content management system
+
+ magic number
+ A magic number is a magic number.
+
+A diagram and a formula are source text that HTML turns into a picture.
+Markdown shows the source, tagged for the renderers that know it.
+
+.. uml::
+
+ Alice -> Bob: Hello
+ Bob --> Alice: Hi
+
+.. math::
+
+ a^2 + b^2 = c^2
+
+A code block keeps its caption, which says where the snippet belongs.
+
+.. code-block:: php
+ :caption: EXT:my_extension/ext_localconf.php
+
+ $GLOBALS['TYPO3_CONF_VARS']['SYS']['caching'] = [];
+
+The "option" directive is how the manuals write a configuration value; it is
+shaped like a confval.
+
+.. option:: errorFluidTemplate
+
+ The path to the Fluid template file.
+
+A TYPO3 file definition carries both of its paths.
+
+.. typo3:file:: ext_localconf.php
+ :scope: extension
+ :composer-path: my_extension/
+ :classic-path: typo3conf/ext/my_extension/
+
+ Included on every request.
+
diff --git a/tests/Integration/tests/markdown-rendering/escaping/expected/index.md b/tests/Integration/tests/markdown-rendering/escaping/expected/index.md
new file mode 100644
index 000000000..f29a061c9
--- /dev/null
+++ b/tests/Integration/tests/markdown-rendering/escaping/expected/index.md
@@ -0,0 +1,50 @@
+---
+title: "Markdown escaping of text"
+permalink: "https://docs.typo3.org/permalink/mdescape:markdown-escaping-of-text"
+source: "index.rst"
+start: true
+modified: "2023-01-01T12:00:00+00:00"
+---
+
+# Markdown escaping of text
+
+Source prose is not Markdown, so characters that would start a Markdown
+construct have to be escaped. Characters that cannot start one are left
+alone, otherwise the output fills up with backslashes.
+
+## Emphasis characters
+
+An underscore inside a word is not emphasis in CommonMark and stays as it
+is: snake_case_name. One at a word boundary can open emphasis and is
+escaped: \_\_dunder\_\_ names.
+
+A matched pair of asterisks is emphasis already in the source and stays
+emphasis: 2*3*4 renders as a product with an emphasised 3. An unmatched one
+is text and is escaped so it cannot open emphasis here: 5 \* 3 equals 15.
+
+## Links and code
+
+Brackets would open a link: see \[1\] and array\[0\](x).
+
+A backtick would open a code span: use \` to quote.
+
+An angle bracket only matters before a letter: 3 < 4 stays, but \bold\
+is escaped.
+
+## Block markers at the start of a line
+
+\# not a heading\
+\- not a list item\
+1\. not an ordered item\
+\> not a quote\
+-1 is not a list either
+
+## Backslashes
+
+A doubled backslash in the source is one literal backslash, and Markdown
+needs it doubled again: C:\\temp\\file.
+
+## Inside code spans
+
+Nothing is escaped inside a code span, because backticks already take the
+content literally: `$a[0] . "_" . $b`.
diff --git a/tests/Integration/tests/markdown-rendering/escaping/input/guides.xml b/tests/Integration/tests/markdown-rendering/escaping/input/guides.xml
new file mode 100644
index 000000000..b6de6a10f
--- /dev/null
+++ b/tests/Integration/tests/markdown-rendering/escaping/input/guides.xml
@@ -0,0 +1,11 @@
+
+
+
+ md
+
diff --git a/tests/Integration/tests/markdown-rendering/escaping/input/index.rst b/tests/Integration/tests/markdown-rendering/escaping/input/index.rst
new file mode 100644
index 000000000..e0dfe0efa
--- /dev/null
+++ b/tests/Integration/tests/markdown-rendering/escaping/input/index.rst
@@ -0,0 +1,49 @@
+=========================
+Markdown escaping of text
+=========================
+
+Source prose is not Markdown, so characters that would start a Markdown
+construct have to be escaped. Characters that cannot start one are left
+alone, otherwise the output fills up with backslashes.
+
+Emphasis characters
+===================
+
+An underscore inside a word is not emphasis in CommonMark and stays as it
+is: snake_case_name. One at a word boundary can open emphasis and is
+escaped: __dunder__ names.
+
+A matched pair of asterisks is emphasis already in the source and stays
+emphasis: 2*3*4 renders as a product with an emphasised 3. An unmatched one
+is text and is escaped so it cannot open emphasis here: 5 * 3 equals 15.
+
+Links and code
+==============
+
+Brackets would open a link: see [1] and array[0](x).
+
+A backtick would open a code span: use ` to quote.
+
+An angle bracket only matters before a letter: 3 < 4 stays, but bold
+is escaped.
+
+Block markers at the start of a line
+====================================
+
+| # not a heading
+| - not a list item
+| 1. not an ordered item
+| > not a quote
+| -1 is not a list either
+
+Backslashes
+===========
+
+A doubled backslash in the source is one literal backslash, and Markdown
+needs it doubled again: C:\\temp\\file.
+
+Inside code spans
+=================
+
+Nothing is escaped inside a code span, because backticks already take the
+content literally: :php:`$a[0] . "_" . $b`.
diff --git a/tests/Integration/tests/markdown-rendering/permalink-unversioned/expected/index.md b/tests/Integration/tests/markdown-rendering/permalink-unversioned/expected/index.md
new file mode 100644
index 000000000..18909d864
--- /dev/null
+++ b/tests/Integration/tests/markdown-rendering/permalink-unversioned/expected/index.md
@@ -0,0 +1,16 @@
+---
+title: "Unversioned manual"
+version: "13.4"
+permalink: "https://docs.typo3.org/permalink/h2document:unversioned-manual"
+source: "index.rst"
+start: true
+modified: "2023-01-01T12:00:00+00:00"
+---
+
+# Unversioned manual
+
+A manual that exists only once carries no version in its permalinks.
+
+## The target
+
+Linked from [above](https://docs.typo3.org/permalink/h2document:the-target).
diff --git a/tests/Integration/tests/markdown-rendering/permalink-unversioned/input/guides.xml b/tests/Integration/tests/markdown-rendering/permalink-unversioned/input/guides.xml
new file mode 100644
index 000000000..606918a90
--- /dev/null
+++ b/tests/Integration/tests/markdown-rendering/permalink-unversioned/input/guides.xml
@@ -0,0 +1,12 @@
+
+
+
+
+ md
+
diff --git a/tests/Integration/tests/markdown-rendering/permalink-unversioned/input/index.rst b/tests/Integration/tests/markdown-rendering/permalink-unversioned/input/index.rst
new file mode 100644
index 000000000..9e67aa178
--- /dev/null
+++ b/tests/Integration/tests/markdown-rendering/permalink-unversioned/input/index.rst
@@ -0,0 +1,12 @@
+===================
+Unversioned manual
+===================
+
+A manual that exists only once carries no version in its permalinks.
+
+.. _the-target:
+
+The target
+==========
+
+Linked from :ref:`above `.
diff --git a/tests/Integration/tests/markdown-rendering/permalink-version/expected/index.md b/tests/Integration/tests/markdown-rendering/permalink-version/expected/index.md
new file mode 100644
index 000000000..66984a314
--- /dev/null
+++ b/tests/Integration/tests/markdown-rendering/permalink-version/expected/index.md
@@ -0,0 +1,17 @@
+---
+title: "Versioned manual"
+version: "13.4"
+permalink: "https://docs.typo3.org/permalink/mdversion:versioned-manual@13.4"
+source: "index.rst"
+start: true
+modified: "2023-01-01T12:00:00+00:00"
+---
+
+# Versioned manual
+
+A manual that exists once per release pins its permalinks to that release,
+because a permalink without a version resolves to the latest stable one.
+
+## The target
+
+Linked from [above](https://docs.typo3.org/permalink/mdversion:the-target@13.4).
diff --git a/tests/Integration/tests/markdown-rendering/permalink-version/input/guides.xml b/tests/Integration/tests/markdown-rendering/permalink-version/input/guides.xml
new file mode 100644
index 000000000..1f045a569
--- /dev/null
+++ b/tests/Integration/tests/markdown-rendering/permalink-version/input/guides.xml
@@ -0,0 +1,12 @@
+
+
+
+
+ md
+
diff --git a/tests/Integration/tests/markdown-rendering/permalink-version/input/index.rst b/tests/Integration/tests/markdown-rendering/permalink-version/input/index.rst
new file mode 100644
index 000000000..47fc293a7
--- /dev/null
+++ b/tests/Integration/tests/markdown-rendering/permalink-version/input/index.rst
@@ -0,0 +1,13 @@
+=================
+Versioned manual
+=================
+
+A manual that exists once per release pins its permalinks to that release,
+because a permalink without a version resolves to the latest stable one.
+
+.. _the-target:
+
+The target
+==========
+
+Linked from :ref:`above `.
diff --git a/tests/Integration/tests/markdown-rendering/php-domain/expected/index.md b/tests/Integration/tests/markdown-rendering/php-domain/expected/index.md
new file mode 100644
index 000000000..ea6fc8d99
--- /dev/null
+++ b/tests/Integration/tests/markdown-rendering/php-domain/expected/index.md
@@ -0,0 +1,53 @@
+---
+title: "PHP domain"
+permalink: "https://docs.typo3.org/permalink/mdphpdomain:php-domain"
+source: "index.rst"
+start: true
+modified: "2023-01-01T12:00:00+00:00"
+---
+
+# PHP domain
+
+- **class Renderer**
+
+ - *Fully qualified name:* `\LibraryName\Renderer`
+
+ Turns a document into something else.
+
+ - **const FORMAT = 'md'**
+
+ The format this renderer writes.
+
+ - **target**
+
+ Where the output goes.
+
+ - **render(Document $document, string $format = 'md')**
+
+ Renders one document.
+
+ - *param Document $document:* The document to render.
+
+ *Returns:* The rendered document.
+
+ - **static create()**
+
+ Builds a renderer.
+
+- **interface RendererInterface**
+
+ - *Fully qualified name:* `\LibraryName\RendererInterface`
+
+ What every renderer can do.
+
+- **trait RendersMarkdown**
+
+ - *Fully qualified name:* `\LibraryName\RendersMarkdown`
+
+ Shared by the Markdown renderers.
+
+- **exception RenderFailed**
+
+ - *Fully qualified name:* `\LibraryName\RenderFailed`
+
+ Thrown when a document cannot be rendered.
diff --git a/tests/Integration/tests/markdown-rendering/php-domain/input/guides.xml b/tests/Integration/tests/markdown-rendering/php-domain/input/guides.xml
new file mode 100644
index 000000000..c01654431
--- /dev/null
+++ b/tests/Integration/tests/markdown-rendering/php-domain/input/guides.xml
@@ -0,0 +1,11 @@
+
+
+
+ md
+
diff --git a/tests/Integration/tests/markdown-rendering/php-domain/input/index.rst b/tests/Integration/tests/markdown-rendering/php-domain/input/index.rst
new file mode 100644
index 000000000..622b32bb0
--- /dev/null
+++ b/tests/Integration/tests/markdown-rendering/php-domain/input/index.rst
@@ -0,0 +1,40 @@
+==========
+PHP domain
+==========
+
+.. php:namespace:: LibraryName
+
+.. php:class:: Renderer
+
+ Turns a document into something else.
+
+ .. php:const:: FORMAT = 'md'
+
+ The format this renderer writes.
+
+ .. php:attr:: target
+
+ Where the output goes.
+
+ .. php:method:: render(Document $document, string $format = 'md')
+
+ Renders one document.
+
+ :param Document $document: The document to render.
+ :returns: The rendered document.
+
+ .. php:staticmethod:: create()
+
+ Builds a renderer.
+
+.. php:interface:: RendererInterface
+
+ What every renderer can do.
+
+.. php:trait:: RendersMarkdown
+
+ Shared by the Markdown renderers.
+
+.. php:exception:: RenderFailed
+
+ Thrown when a document cannot be rendered.
diff --git a/tests/Integration/tests/markdown-rendering/text-roles/expected/index.md b/tests/Integration/tests/markdown-rendering/text-roles/expected/index.md
new file mode 100644
index 000000000..b7078981b
--- /dev/null
+++ b/tests/Integration/tests/markdown-rendering/text-roles/expected/index.md
@@ -0,0 +1,17 @@
+---
+title: "Text roles"
+permalink: "https://docs.typo3.org/permalink/mdtextroles:text-roles"
+source: "index.rst"
+start: true
+modified: "2023-01-01T12:00:00+00:00"
+---
+
+# Text roles
+
+A file is code, and so is a path: `/etc/passwd` next to
+`Configuration/TCA`.
+
+The file role is a link node, so a renderer registered for its base class
+can swallow it. `$a = 1` is not, and never could.
+
+An ordinary link still behaves like one: [phpDocumentor](https://phpdoc.org).
diff --git a/tests/Integration/tests/markdown-rendering/text-roles/input/guides.xml b/tests/Integration/tests/markdown-rendering/text-roles/input/guides.xml
new file mode 100644
index 000000000..bdb148db5
--- /dev/null
+++ b/tests/Integration/tests/markdown-rendering/text-roles/input/guides.xml
@@ -0,0 +1,11 @@
+
+
+
+ md
+
diff --git a/tests/Integration/tests/markdown-rendering/text-roles/input/index.rst b/tests/Integration/tests/markdown-rendering/text-roles/input/index.rst
new file mode 100644
index 000000000..cac5bbc01
--- /dev/null
+++ b/tests/Integration/tests/markdown-rendering/text-roles/input/index.rst
@@ -0,0 +1,11 @@
+==========
+Text roles
+==========
+
+A file is code, and so is a path: :file:`/etc/passwd` next to
+:path:`Configuration/TCA`.
+
+The file role is a link node, so a renderer registered for its base class
+can swallow it. :code:`$a = 1` is not, and never could.
+
+An ordinary link still behaves like one: `phpDocumentor `__.
diff --git a/tests/Integration/tests/markdown-rendering/viewhelper/expected/index.md b/tests/Integration/tests/markdown-rendering/viewhelper/expected/index.md
new file mode 100644
index 000000000..410c30c3c
--- /dev/null
+++ b/tests/Integration/tests/markdown-rendering/viewhelper/expected/index.md
@@ -0,0 +1,66 @@
+---
+title: "ViewHelpers"
+permalink: "https://docs.typo3.org/permalink/mdviewhelper:viewhelpers"
+source: "index.rst"
+start: true
+modified: "2023-01-01T12:00:00+00:00"
+---
+
+# ViewHelpers
+
+A ViewHelper with arguments, one of them required and one with a default.
+
+Splits a string, and shows what an argument list looks like.
+
+**Arguments**
+
+The following arguments are available for the demo ViewHelper:
+
+- **limit**
+
+ - *Type:* int
+ - *Default:* 9223372036854775807
+
+ How many items to return at most.
+
+- **value**
+
+ - *Type:* string
+ - *Required:* true
+
+ The string to split
+
+A ViewHelper that takes arbitrary arguments on top of the ones listed.
+
+Passes anything it is given on to the tag it creates.
+
+**Arguments**
+
+> [!NOTE]
+> **Allows arbitrary arguments**
+>
+> This ViewHelper allows you to pass arbitrary arguments not defined below directly to the HTML tag created. This includes custom `data-` arguments.
+
+The following arguments are available for the arbitrary ViewHelper:
+
+- **data**
+
+ - *Type:* array
+
+ Additional data-\* attributes.
+
+Doc tags become alerts.
+
+> [!WARNING]
+> **Deprecated**
+>
+> since v13, will be removed in v14
+
+> [!WARNING]
+> **Internal**
+>
+> This ViewHelper is marked as internal. It is subject to be changed without notice. Use at your own risk.
+
+Scope: backend
+
+Kept for one more major version.
diff --git a/tests/Integration/tests/markdown-rendering/viewhelper/input/guides.xml b/tests/Integration/tests/markdown-rendering/viewhelper/input/guides.xml
new file mode 100644
index 000000000..28ce16931
--- /dev/null
+++ b/tests/Integration/tests/markdown-rendering/viewhelper/input/guides.xml
@@ -0,0 +1,11 @@
+
+
+
+ md
+
diff --git a/tests/Integration/tests/markdown-rendering/viewhelper/input/index.rst b/tests/Integration/tests/markdown-rendering/viewhelper/input/index.rst
new file mode 100644
index 000000000..327ee0aad
--- /dev/null
+++ b/tests/Integration/tests/markdown-rendering/viewhelper/input/index.rst
@@ -0,0 +1,18 @@
+============
+ViewHelpers
+============
+
+A ViewHelper with arguments, one of them required and one with a default.
+
+.. typo3:viewhelper:: demo
+ :source: resources/viewhelpers.json
+
+A ViewHelper that takes arbitrary arguments on top of the ones listed.
+
+.. typo3:viewhelper:: arbitrary
+ :source: resources/viewhelpers.json
+
+Doc tags become alerts.
+
+.. typo3:viewhelper:: retired
+ :source: resources/viewhelpers.json
diff --git a/tests/Integration/tests/markdown-rendering/viewhelper/input/resources/viewhelpers.json b/tests/Integration/tests/markdown-rendering/viewhelper/input/resources/viewhelpers.json
new file mode 100644
index 000000000..09d242be6
--- /dev/null
+++ b/tests/Integration/tests/markdown-rendering/viewhelper/input/resources/viewhelpers.json
@@ -0,0 +1,78 @@
+{
+ "namespace": "http://typo3.org/ns/TYPO3/CMS/Fluid/ViewHelpers",
+ "viewHelpers": {
+ "demo": {
+ "className": "TYPO3Fluid\\Fluid\\ViewHelpers\\DemoViewHelper",
+ "namespace": "TYPO3Fluid\\Fluid\\ViewHelpers",
+ "name": "DemoViewHelper",
+ "tagName": "demo",
+ "documentation": "Splits a string, and shows what an argument list looks like.",
+ "xmlNamespace": "http://typo3.org/ns/TYPO3Fluid/Fluid/ViewHelpers",
+ "docTags": [],
+ "argumentDefinitions": {
+ "value": {
+ "name": "value",
+ "type": "string",
+ "description": "The string to split",
+ "required": true,
+ "defaultValue": null,
+ "escape": null
+ },
+ "limit": {
+ "name": "limit",
+ "type": "int",
+ "description": "How many items to return at most.",
+ "required": false,
+ "defaultValue": 9223372036854775807,
+ "escape": null
+ }
+ },
+ "allowsArbitraryArguments": false,
+ "nameWithoutSuffix": "Demo",
+ "namespaceWithoutSuffix": "TYPO3Fluid\\Fluid",
+ "uri": "Global/Demo"
+ },
+ "arbitrary": {
+ "className": "TYPO3Fluid\\Fluid\\ViewHelpers\\ArbitraryViewHelper",
+ "namespace": "TYPO3Fluid\\Fluid\\ViewHelpers",
+ "name": "ArbitraryViewHelper",
+ "tagName": "arbitrary",
+ "documentation": "Passes anything it is given on to the tag it creates.",
+ "xmlNamespace": "http://typo3.org/ns/TYPO3Fluid/Fluid/ViewHelpers",
+ "docTags": [],
+ "argumentDefinitions": {
+ "data": {
+ "name": "data",
+ "type": "array",
+ "description": "Additional data-* attributes.",
+ "required": false,
+ "defaultValue": null,
+ "escape": null
+ }
+ },
+ "allowsArbitraryArguments": true,
+ "nameWithoutSuffix": "Arbitrary",
+ "namespaceWithoutSuffix": "TYPO3Fluid\\Fluid",
+ "uri": "Global/Arbitrary"
+ },
+ "retired": {
+ "className": "TYPO3Fluid\\Fluid\\ViewHelpers\\RetiredViewHelper",
+ "namespace": "TYPO3Fluid\\Fluid\\ViewHelpers",
+ "name": "RetiredViewHelper",
+ "tagName": "retired",
+ "documentation": "Kept for one more major version.",
+ "xmlNamespace": "http://typo3.org/ns/TYPO3Fluid/Fluid/ViewHelpers",
+ "docTags": {
+ "@deprecated": "since v13, will be removed in v14",
+ "@internal": "",
+ "@see": "Something that is dropped on purpose",
+ "Scope": "backend"
+ },
+ "argumentDefinitions": {},
+ "allowsArbitraryArguments": false,
+ "nameWithoutSuffix": "Retired",
+ "namespaceWithoutSuffix": "TYPO3Fluid\\Fluid",
+ "uri": "Global/Retired"
+ }
+ }
+}