Repository: melissawm/sphinx-tags (language: python)

Issue:
Add tests
For now, the documentation itself serves as tests for the current features, but this doesn't test (for example) the use of symlinked directories. We should have a proper test suite for the extension.


Discussion / hints:
I think there are two options for this:
1. Unit test: mock everything (Sphinx app and config objects, etc.), and test individual classes and functions
2. Integration test: Use a full build environment with some test source files and expected HTML output

Option 1 would probably be more straightforward to write (without much knowledge of Sphinx internals), but option 2 would be more thorough. I'm not sure how best to set up a build environment for testing, but I'm curious, so I'm willing to look into it.

Sphinx has a `sphinx.testing` subpackage that doesn't appear to be documented, but looks potentially useful: https://github.com/sphinx-doc/sphinx/tree/master/sphinx/testing

Fix patch:
diff --git a/.circleci/config.yml b/.circleci/config.yml
index e353af0..8d6552d 100644
--- a/.circleci/config.yml
+++ b/.circleci/config.yml
@@ -35,8 +35,14 @@ jobs:
             sudo apt-get update && sudo apt-get install pandoc
             python -m venv venv
             . venv/bin/activate
-            python -m pip install ".[sphinx]"
+            python -m pip install ".[dev,sphinx]"
             cd docs; make html
+      - run:
+          name: Run tests
+          # TODO: This coverage report could be submitted to codecov
+          command: |
+            . venv/bin/activate
+            python -m pytest --cov=src --cov-report=term --cov-report=xml
       - store_artifacts:
           path: docs/_build/html/
 
diff --git a/docs/dev/index.rst b/docs/dev/index.rst
index 823e33e..58a3e4e 100644
--- a/docs/dev/index.rst
+++ b/docs/dev/index.rst
@@ -25,7 +25,7 @@ Pull request guidelines
 
    ::
 
-     python -m pip install -e ".[sphinx]"
+     python -m pip install -e ".[dev,sphinx]"
 
    To build the documentation locally, use
 
@@ -52,7 +52,13 @@ Pull request guidelines
    - Re-add any changed files to your staging area and run pre-commit again to
      make sure any fixes are applied to your code.
 
-4. **Commit your changes and send your pull request as usual.**
+4. **Test Coverage**
+
+    For any new or changed behavior, please add unit test coverage. See notes in
+    `conftest.py <https://github.com/melissawm/sphinx-tags/tree/main/test/conftest.py>`__
+    for details on testing setup.
+
+5. **Commit your changes and send your pull request as usual.**
 
 Releases
 --------
diff --git a/docs/examples/examples.md b/docs/examples/examples.md
index ddd22c7..d9afecb 100644
--- a/docs/examples/examples.md
+++ b/docs/examples/examples.md
@@ -2,8 +2,5 @@
 
 Example of using a markdown file, with MyST.
 
-Because we don't (yet?) have a testing suite, the documentation will serve as
-tests for now.
-
-```{tags} md examples, tag documentation, {{🧪test tag; please ignore}}
+```{tags} md examples, tag documentation
 ```
diff --git a/pyproject.toml b/pyproject.toml
index 043362f..39b746a 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -17,7 +17,6 @@ keywords = [
 
 dependencies = [
     "sphinx>4",
-    "pre-commit",
 ]
 
 [project.optional-dependencies]
@@ -27,6 +26,12 @@ sphinx = [
     "nbsphinx",
     "myst-parser"
 ]
+dev = [
+    "bs4",
+    "pytest>=6.0",
+    "pytest-cov",
+    "pre-commit"
+]
 
 [project.urls]
 Home = "https://github.com/melissawm/sphinx-tags"


Test patch:
diff --git a/test/__init__.py b/test/__init__.py
new file mode 100644
index 0000000..e69de29
diff --git a/test/conftest.py b/test/conftest.py
new file mode 100644
index 0000000..0812e35
--- /dev/null
+++ b/test/conftest.py
@@ -0,0 +1,60 @@
+"""Notes on testing setup:
+* A Sphinx test app is provided via `app` fixture from `sphinx.testing.fixtures`.
+    * This first needs to be enabled as a plugin
+* The `sources` dir contains source files and config to use for tests
+    * Set via the `rootdir` fixture
+* A subdirectory to use for a specific test can be set by a pytest marker:
+    * `@pytest.mark.sphinx("text", testroot="...")
+    * This subdirectory must contain a conf.py and source files
+* The `outputs` dir contains expected output files
+"""
+import shutil
+from os import symlink
+from pathlib import Path
+from unittest.mock import patch
+
+import pytest
+import sphinx.testing
+import sphinx.testing.path
+
+collect_ignore = ["sources", "outputs"]
+pytest_plugins = "sphinx.testing.fixtures"
+
+OUTPUT_ROOT_DIR = Path(__file__).parent.absolute() / "outputs"
+SOURCE_ROOT_DIR = Path(__file__).parent.absolute() / "sources"
+
+
+@pytest.fixture(scope="session")
+def rootdir():
+    """This fixture overrides the root directory used by SphinxTestApp. It also patches
+    sphinx.testing's custom path object to copy symlinks as symlinks; otherwise symlink paths are
+    resolved and copied as files.
+    """
+
+    def copytree(src, dest):
+        shutil.copytree(src, dest, symlinks=True)
+
+    with patch.object(sphinx.testing.path.path, "copytree", copytree):
+        yield sphinx.testing.path.path(SOURCE_ROOT_DIR)
+
+
+@pytest.fixture(scope="session", autouse=True)
+def create_symlinks():
+    """Create symlinks for source files and subdirectories, and remove them after tests finish"""
+    symlink_dest_dir = SOURCE_ROOT_DIR / "test-symlink"
+
+    for file in SOURCE_ROOT_DIR.glob("test-rst/*.rst"):
+        symlink(file, symlink_dest_dir / file.name)
+    for dir in SOURCE_ROOT_DIR.glob("test-rst/subdir*"):
+        symlink(dir, symlink_dest_dir / dir.name, target_is_directory=True)
+
+    yield
+
+    for file in symlink_dest_dir.glob("*.rst"):
+        file.unlink()
+    for dir in symlink_dest_dir.glob("subdir*"):
+        try:
+            dir.unlink()
+        # Unlink will fail on Windows
+        except OSError:
+            dir.rmdir()
diff --git a/test/outputs/badges/_tags/prefix-tag-3.html b/test/outputs/badges/_tags/prefix-tag-3.html
new file mode 100644
index 0000000..97c0cc5
--- /dev/null
+++ b/test/outputs/badges/_tags/prefix-tag-3.html
@@ -0,0 +1,130 @@
+
+<!DOCTYPE html>
+
+<html lang="en">
+  <head>
+    <meta charset="utf-8" />
+    <meta name="viewport" content="width=device-width, initial-scale=1.0" /><meta name="generator" content="Docutils 0.19: https://docutils.sourceforge.io/" />
+
+    <title>My tags: prefix:tag-3 &#8212; Python  documentation</title>
+    <link rel="stylesheet" type="text/css" href="../_static/pygments.css" />
+    <link rel="stylesheet" type="text/css" href="../_static/alabaster.css" />
+    <link rel="stylesheet" type="text/css" href="../_static/design-style.4045f2051d55cab465a707391d5b2007.min.css" />
+    <script data-url_root="../" id="documentation_options" src="../_static/documentation_options.js"></script>
+    <script src="../_static/jquery.js"></script>
+    <script src="../_static/underscore.js"></script>
+    <script src="../_static/_sphinx_javascript_frameworks_compat.js"></script>
+    <script src="../_static/doctools.js"></script>
+    <script src="../_static/sphinx_highlight.js"></script>
+    <script src="../_static/design-tabs.js"></script>
+    <link rel="index" title="Index" href="../genindex.html" />
+    <link rel="search" title="Search" href="../search.html" />
+    <link rel="next" title="My tags: tag-1" href="tag-1.html" />
+    <link rel="prev" title="My tags: [⚫tag 4]" href="tag-4.html" />
+
+  <link rel="stylesheet" href="../_static/custom.css" type="text/css" />
+
+
+  <meta name="viewport" content="width=device-width, initial-scale=0.9, maximum-scale=0.9" />
+
+  </head><body>
+
+
+    <div class="document">
+      <div class="documentwrapper">
+        <div class="bodywrapper">
+
+
+          <div class="body" role="main">
+
+  <section id="my-tags-prefix-tag-3">
+<h1>My tags: prefix:tag-3<a class="headerlink" href="#my-tags-prefix-tag-3" title="Permalink to this heading">¶</a></h1>
+<div class="toctree-wrapper compound">
+<p class="caption" role="heading"><span class="caption-text">With this tag</span></p>
+<ul>
+<li class="toctree-l1"><a class="reference internal" href="../page_1.html">Page 1</a></li>
+</ul>
+</div>
+</section>
+
+
+          </div>
+
+        </div>
+      </div>
+      <div class="sphinxsidebar" role="navigation" aria-label="main navigation">
+        <div class="sphinxsidebarwrapper">
+<h1 class="logo"><a href="../index.html">Python</a></h1>
+
+
+
+
+
+
+
+
+<h3>Navigation</h3>
+<ul class="current">
+<li class="toctree-l1"><a class="reference internal" href="../page_1.html">Page 1</a></li>
+<li class="toctree-l1 current"><a class="reference internal" href="tagsindex.html">Tags overview</a><ul class="current">
+<li class="toctree-l2"><a class="reference internal" href="tag-4.html">[⚫tag 4] (1)</a></li>
+<li class="toctree-l2 current"><a class="current reference internal" href="#">prefix:tag-3 (1)</a><ul>
+<li class="toctree-l3"><a class="reference internal" href="../page_1.html">Page 1</a></li>
+</ul>
+</li>
+<li class="toctree-l2"><a class="reference internal" href="tag-1.html">tag-1 (1)</a></li>
+<li class="toctree-l2"><a class="reference internal" href="tag-2.html">tag-2 (1)</a></li>
+</ul>
+</li>
+</ul>
+
+<div class="relations">
+<h3>Related Topics</h3>
+<ul>
+  <li><a href="../index.html">Documentation overview</a><ul>
+  <li><a href="tagsindex.html">Tags overview</a><ul>
+      <li>Previous: <a href="tag-4.html" title="previous chapter">My tags: [⚫tag 4]</a></li>
+      <li>Next: <a href="tag-1.html" title="next chapter">My tags: tag-1</a></li>
+  </ul></li>
+  </ul></li>
+</ul>
+</div>
+<div id="searchbox" style="display: none" role="search">
+  <h3 id="searchlabel">Quick search</h3>
+    <div class="searchformwrapper">
+    <form class="search" action="../search.html" method="get">
+      <input type="text" name="q" aria-labelledby="searchlabel" autocomplete="off" autocorrect="off" autocapitalize="off" spellcheck="false"/>
+      <input type="submit" value="Go" />
+    </form>
+    </div>
+</div>
+<script>document.getElementById('searchbox').style.display = "block"</script>
+
+
+
+
+
+
+
+
+        </div>
+      </div>
+      <div class="clearer"></div>
+    </div>
+    <div class="footer">
+      &copy;.
+
+      |
+      Powered by <a href="http://sphinx-doc.org/">Sphinx 5.3.0</a>
+      &amp; <a href="https://github.com/bitprophet/alabaster">Alabaster 0.7.12</a>
+
+      |
+      <a href="../_sources/_tags/prefix-tag-3.md.txt"
+          rel="nofollow">Page source</a>
+    </div>
+
+
+
+
+  </body>
+</html>
diff --git a/test/outputs/badges/_tags/tag-1.html b/test/outputs/badges/_tags/tag-1.html
new file mode 100644
index 0000000..5dad6c3
--- /dev/null
+++ b/test/outputs/badges/_tags/tag-1.html
@@ -0,0 +1,130 @@
+
+<!DOCTYPE html>
+
+<html lang="en">
+  <head>
+    <meta charset="utf-8" />
+    <meta name="viewport" content="width=device-width, initial-scale=1.0" /><meta name="generator" content="Docutils 0.19: https://docutils.sourceforge.io/" />
+
+    <title>My tags: tag-1 &#8212; Python  documentation</title>
+    <link rel="stylesheet" type="text/css" href="../_static/pygments.css" />
+    <link rel="stylesheet" type="text/css" href="../_static/alabaster.css" />
+    <link rel="stylesheet" type="text/css" href="../_static/design-style.4045f2051d55cab465a707391d5b2007.min.css" />
+    <script data-url_root="../" id="documentation_options" src="../_static/documentation_options.js"></script>
+    <script src="../_static/jquery.js"></script>
+    <script src="../_static/underscore.js"></script>
+    <script src="../_static/_sphinx_javascript_frameworks_compat.js"></script>
+    <script src="../_static/doctools.js"></script>
+    <script src="../_static/sphinx_highlight.js"></script>
+    <script src="../_static/design-tabs.js"></script>
+    <link rel="index" title="Index" href="../genindex.html" />
+    <link rel="search" title="Search" href="../search.html" />
+    <link rel="next" title="My tags: tag-2" href="tag-2.html" />
+    <link rel="prev" title="My tags: prefix:tag-3" href="prefix-tag-3.html" />
+
+  <link rel="stylesheet" href="../_static/custom.css" type="text/css" />
+
+
+  <meta name="viewport" content="width=device-width, initial-scale=0.9, maximum-scale=0.9" />
+
+  </head><body>
+
+
+    <div class="document">
+      <div class="documentwrapper">
+        <div class="bodywrapper">
+
+
+          <div class="body" role="main">
+
+  <section id="my-tags-tag-1">
+<h1>My tags: tag-1<a class="headerlink" href="#my-tags-tag-1" title="Permalink to this heading">¶</a></h1>
+<div class="toctree-wrapper compound">
+<p class="caption" role="heading"><span class="caption-text">With this tag</span></p>
+<ul>
+<li class="toctree-l1"><a class="reference internal" href="../page_1.html">Page 1</a></li>
+</ul>
+</div>
+</section>
+
+
+          </div>
+
+        </div>
+      </div>
+      <div class="sphinxsidebar" role="navigation" aria-label="main navigation">
+        <div class="sphinxsidebarwrapper">
+<h1 class="logo"><a href="../index.html">Python</a></h1>
+
+
+
+
+
+
+
+
+<h3>Navigation</h3>
+<ul class="current">
+<li class="toctree-l1"><a class="reference internal" href="../page_1.html">Page 1</a></li>
+<li class="toctree-l1 current"><a class="reference internal" href="tagsindex.html">Tags overview</a><ul class="current">
+<li class="toctree-l2"><a class="reference internal" href="tag-4.html">[⚫tag 4] (1)</a></li>
+<li class="toctree-l2"><a class="reference internal" href="prefix-tag-3.html">prefix:tag-3 (1)</a></li>
+<li class="toctree-l2 current"><a class="current reference internal" href="#">tag-1 (1)</a><ul>
+<li class="toctree-l3"><a class="reference internal" href="../page_1.html">Page 1</a></li>
+</ul>
+</li>
+<li class="toctree-l2"><a class="reference internal" href="tag-2.html">tag-2 (1)</a></li>
+</ul>
+</li>
+</ul>
+
+<div class="relations">
+<h3>Related Topics</h3>
+<ul>
+  <li><a href="../index.html">Documentation overview</a><ul>
+  <li><a href="tagsindex.html">Tags overview</a><ul>
+      <li>Previous: <a href="prefix-tag-3.html" title="previous chapter">My tags: prefix:tag-3</a></li>
+      <li>Next: <a href="tag-2.html" title="next chapter">My tags: tag-2</a></li>
+  </ul></li>
+  </ul></li>
+</ul>
+</div>
+<div id="searchbox" style="display: none" role="search">
+  <h3 id="searchlabel">Quick search</h3>
+    <div class="searchformwrapper">
+    <form class="search" action="../search.html" method="get">
+      <input type="text" name="q" aria-labelledby="searchlabel" autocomplete="off" autocorrect="off" autocapitalize="off" spellcheck="false"/>
+      <input type="submit" value="Go" />
+    </form>
+    </div>
+</div>
+<script>document.getElementById('searchbox').style.display = "block"</script>
+
+
+
+
+
+
+
+
+        </div>
+      </div>
+      <div class="clearer"></div>
+    </div>
+    <div class="footer">
+      &copy;.
+
+      |
+      Powered by <a href="http://sphinx-doc.org/">Sphinx 5.3.0</a>
+      &amp; <a href="https://github.com/bitprophet/alabaster">Alabaster 0.7.12</a>
+
+      |
+      <a href="../_sources/_tags/tag-1.md.txt"
+          rel="nofollow">Page source</a>
+    </div>
+
+
+
+
+  </body>
+</html>
diff --git a/test/outputs/badges/_tags/tag-2.html b/test/outputs/badges/_tags/tag-2.html
new file mode 100644
index 0000000..5ae82d6
--- /dev/null
+++ b/test/outputs/badges/_tags/tag-2.html
@@ -0,0 +1,128 @@
+
+<!DOCTYPE html>
+
+<html lang="en">
+  <head>
+    <meta charset="utf-8" />
+    <meta name="viewport" content="width=device-width, initial-scale=1.0" /><meta name="generator" content="Docutils 0.19: https://docutils.sourceforge.io/" />
+
+    <title>My tags: tag-2 &#8212; Python  documentation</title>
+    <link rel="stylesheet" type="text/css" href="../_static/pygments.css" />
+    <link rel="stylesheet" type="text/css" href="../_static/alabaster.css" />
+    <link rel="stylesheet" type="text/css" href="../_static/design-style.4045f2051d55cab465a707391d5b2007.min.css" />
+    <script data-url_root="../" id="documentation_options" src="../_static/documentation_options.js"></script>
+    <script src="../_static/jquery.js"></script>
+    <script src="../_static/underscore.js"></script>
+    <script src="../_static/_sphinx_javascript_frameworks_compat.js"></script>
+    <script src="../_static/doctools.js"></script>
+    <script src="../_static/sphinx_highlight.js"></script>
+    <script src="../_static/design-tabs.js"></script>
+    <link rel="index" title="Index" href="../genindex.html" />
+    <link rel="search" title="Search" href="../search.html" />
+    <link rel="prev" title="My tags: tag-1" href="tag-1.html" />
+
+  <link rel="stylesheet" href="../_static/custom.css" type="text/css" />
+
+
+  <meta name="viewport" content="width=device-width, initial-scale=0.9, maximum-scale=0.9" />
+
+  </head><body>
+
+
+    <div class="document">
+      <div class="documentwrapper">
+        <div class="bodywrapper">
+
+
+          <div class="body" role="main">
+
+  <section id="my-tags-tag-2">
+<h1>My tags: tag-2<a class="headerlink" href="#my-tags-tag-2" title="Permalink to this heading">¶</a></h1>
+<div class="toctree-wrapper compound">
+<p class="caption" role="heading"><span class="caption-text">With this tag</span></p>
+<ul>
+<li class="toctree-l1"><a class="reference internal" href="../page_1.html">Page 1</a></li>
+</ul>
+</div>
+</section>
+
+
+          </div>
+
+        </div>
+      </div>
+      <div class="sphinxsidebar" role="navigation" aria-label="main navigation">
+        <div class="sphinxsidebarwrapper">
+<h1 class="logo"><a href="../index.html">Python</a></h1>
+
+
+
+
+
+
+
+
+<h3>Navigation</h3>
+<ul class="current">
+<li class="toctree-l1"><a class="reference internal" href="../page_1.html">Page 1</a></li>
+<li class="toctree-l1 current"><a class="reference internal" href="tagsindex.html">Tags overview</a><ul class="current">
+<li class="toctree-l2"><a class="reference internal" href="tag-4.html">[⚫tag 4] (1)</a></li>
+<li class="toctree-l2"><a class="reference internal" href="prefix-tag-3.html">prefix:tag-3 (1)</a></li>
+<li class="toctree-l2"><a class="reference internal" href="tag-1.html">tag-1 (1)</a></li>
+<li class="toctree-l2 current"><a class="current reference internal" href="#">tag-2 (1)</a><ul>
+<li class="toctree-l3"><a class="reference internal" href="../page_1.html">Page 1</a></li>
+</ul>
+</li>
+</ul>
+</li>
+</ul>
+
+<div class="relations">
+<h3>Related Topics</h3>
+<ul>
+  <li><a href="../index.html">Documentation overview</a><ul>
+  <li><a href="tagsindex.html">Tags overview</a><ul>
+      <li>Previous: <a href="tag-1.html" title="previous chapter">My tags: tag-1</a></li>
+  </ul></li>
+  </ul></li>
+</ul>
+</div>
+<div id="searchbox" style="display: none" role="search">
+  <h3 id="searchlabel">Quick search</h3>
+    <div class="searchformwrapper">
+    <form class="search" action="../search.html" method="get">
+      <input type="text" name="q" aria-labelledby="searchlabel" autocomplete="off" autocorrect="off" autocapitalize="off" spellcheck="false"/>
+      <input type="submit" value="Go" />
+    </form>
+    </div>
+</div>
+<script>document.getElementById('searchbox').style.display = "block"</script>
+
+
+
+
+
+
+
+
+        </div>
+      </div>
+      <div class="clearer"></div>
+    </div>
+    <div class="footer">
+      &copy;.
+
+      |
+      Powered by <a href="http://sphinx-doc.org/">Sphinx 5.3.0</a>
+      &amp; <a href="https://github.com/bitprophet/alabaster">Alabaster 0.7.12</a>
+
+      |
+      <a href="../_sources/_tags/tag-2.md.txt"
+          rel="nofollow">Page source</a>
+    </div>
+
+
+
+
+  </body>
+</html>
diff --git a/test/outputs/badges/_tags/tag-3.html b/test/outputs/badges/_tags/tag-3.html
new file mode 100644
index 0000000..aa95662
--- /dev/null
+++ b/test/outputs/badges/_tags/tag-3.html
@@ -0,0 +1,129 @@
+
+<!DOCTYPE html>
+
+<html lang="en">
+  <head>
+    <meta charset="utf-8" />
+    <meta name="viewport" content="width=device-width, initial-scale=1.0" /><meta name="generator" content="Docutils 0.19: https://docutils.sourceforge.io/" />
+
+    <title>My tags: [🔵tag 3🔴] &#8212; Python  documentation</title>
+    <link rel="stylesheet" type="text/css" href="../_static/pygments.css" />
+    <link rel="stylesheet" type="text/css" href="../_static/alabaster.css" />
+    <link rel="stylesheet" type="text/css" href="../_static/design-style.4045f2051d55cab465a707391d5b2007.min.css" />
+    <script data-url_root="../" id="documentation_options" src="../_static/documentation_options.js"></script>
+    <script src="../_static/jquery.js"></script>
+    <script src="../_static/underscore.js"></script>
+    <script src="../_static/_sphinx_javascript_frameworks_compat.js"></script>
+    <script src="../_static/doctools.js"></script>
+    <script src="../_static/sphinx_highlight.js"></script>
+    <script src="../_static/design-tabs.js"></script>
+    <link rel="index" title="Index" href="../genindex.html" />
+    <link rel="search" title="Search" href="../search.html" />
+    <link rel="next" title="My tags: tag 2" href="tag-2.html" />
+    <link rel="prev" title="Tags overview" href="tagsindex.html" />
+
+  <link rel="stylesheet" href="../_static/custom.css" type="text/css" />
+
+
+  <meta name="viewport" content="width=device-width, initial-scale=0.9, maximum-scale=0.9" />
+
+  </head><body>
+
+
+    <div class="document">
+      <div class="documentwrapper">
+        <div class="bodywrapper">
+
+
+          <div class="body" role="main">
+
+  <section id="my-tags-tag-3">
+<h1>My tags: [🔵tag 3🔴]<a class="headerlink" href="#my-tags-tag-3" title="Permalink to this heading">¶</a></h1>
+<div class="toctree-wrapper compound">
+<p class="caption" role="heading"><span class="caption-text">With this tag</span></p>
+<ul>
+<li class="toctree-l1"><a class="reference internal" href="../page_1.html">Page 1</a></li>
+</ul>
+</div>
+</section>
+
+
+          </div>
+
+        </div>
+      </div>
+      <div class="sphinxsidebar" role="navigation" aria-label="main navigation">
+        <div class="sphinxsidebarwrapper">
+<h1 class="logo"><a href="../index.html">Python</a></h1>
+
+
+
+
+
+
+
+
+<h3>Navigation</h3>
+<ul class="current">
+<li class="toctree-l1"><a class="reference internal" href="../page_1.html">Page 1</a></li>
+<li class="toctree-l1 current"><a class="reference internal" href="tagsindex.html">Tags overview</a><ul class="current">
+<li class="toctree-l2 current"><a class="current reference internal" href="#">[🔵tag 3🔴] (1)</a><ul>
+<li class="toctree-l3"><a class="reference internal" href="../page_1.html">Page 1</a></li>
+</ul>
+</li>
+<li class="toctree-l2"><a class="reference internal" href="tag-2.html">tag  2 (1)</a></li>
+<li class="toctree-l2"><a class="reference internal" href="tag_1.html">tag_1 (1)</a></li>
+</ul>
+</li>
+</ul>
+
+<div class="relations">
+<h3>Related Topics</h3>
+<ul>
+  <li><a href="../index.html">Documentation overview</a><ul>
+  <li><a href="tagsindex.html">Tags overview</a><ul>
+      <li>Previous: <a href="tagsindex.html" title="previous chapter">Tags overview</a></li>
+      <li>Next: <a href="tag-2.html" title="next chapter">My tags: tag  2</a></li>
+  </ul></li>
+  </ul></li>
+</ul>
+</div>
+<div id="searchbox" style="display: none" role="search">
+  <h3 id="searchlabel">Quick search</h3>
+    <div class="searchformwrapper">
+    <form class="search" action="../search.html" method="get">
+      <input type="text" name="q" aria-labelledby="searchlabel" autocomplete="off" autocorrect="off" autocapitalize="off" spellcheck="false"/>
+      <input type="submit" value="Go" />
+    </form>
+    </div>
+</div>
+<script>document.getElementById('searchbox').style.display = "block"</script>
+
+
+
+
+
+
+
+
+        </div>
+      </div>
+      <div class="clearer"></div>
+    </div>
+    <div class="footer">
+      &copy;.
+
+      |
+      Powered by <a href="http://sphinx-doc.org/">Sphinx 5.3.0</a>
+      &amp; <a href="https://github.com/bitprophet/alabaster">Alabaster 0.7.12</a>
+
+      |
+      <a href="../_sources/_tags/tag-3.md.txt"
+          rel="nofollow">Page source</a>
+    </div>
+
+
+
+
+  </body>
+</html>
diff --git a/test/outputs/badges/_tags/tag-4.html b/test/outputs/badges/_tags/tag-4.html
new file mode 100644
index 0000000..efb948b
--- /dev/null
+++ b/test/outputs/badges/_tags/tag-4.html
@@ -0,0 +1,130 @@
+
+<!DOCTYPE html>
+
+<html lang="en">
+  <head>
+    <meta charset="utf-8" />
+    <meta name="viewport" content="width=device-width, initial-scale=1.0" /><meta name="generator" content="Docutils 0.19: https://docutils.sourceforge.io/" />
+
+    <title>My tags: [⚫tag 4] &#8212; Python  documentation</title>
+    <link rel="stylesheet" type="text/css" href="../_static/pygments.css" />
+    <link rel="stylesheet" type="text/css" href="../_static/alabaster.css" />
+    <link rel="stylesheet" type="text/css" href="../_static/design-style.4045f2051d55cab465a707391d5b2007.min.css" />
+    <script data-url_root="../" id="documentation_options" src="../_static/documentation_options.js"></script>
+    <script src="../_static/jquery.js"></script>
+    <script src="../_static/underscore.js"></script>
+    <script src="../_static/_sphinx_javascript_frameworks_compat.js"></script>
+    <script src="../_static/doctools.js"></script>
+    <script src="../_static/sphinx_highlight.js"></script>
+    <script src="../_static/design-tabs.js"></script>
+    <link rel="index" title="Index" href="../genindex.html" />
+    <link rel="search" title="Search" href="../search.html" />
+    <link rel="next" title="My tags: prefix:tag-3" href="prefix-tag-3.html" />
+    <link rel="prev" title="Tags overview" href="tagsindex.html" />
+
+  <link rel="stylesheet" href="../_static/custom.css" type="text/css" />
+
+
+  <meta name="viewport" content="width=device-width, initial-scale=0.9, maximum-scale=0.9" />
+
+  </head><body>
+
+
+    <div class="document">
+      <div class="documentwrapper">
+        <div class="bodywrapper">
+
+
+          <div class="body" role="main">
+
+  <section id="my-tags-tag-4">
+<h1>My tags: [⚫tag 4]<a class="headerlink" href="#my-tags-tag-4" title="Permalink to this heading">¶</a></h1>
+<div class="toctree-wrapper compound">
+<p class="caption" role="heading"><span class="caption-text">With this tag</span></p>
+<ul>
+<li class="toctree-l1"><a class="reference internal" href="../page_1.html">Page 1</a></li>
+</ul>
+</div>
+</section>
+
+
+          </div>
+
+        </div>
+      </div>
+      <div class="sphinxsidebar" role="navigation" aria-label="main navigation">
+        <div class="sphinxsidebarwrapper">
+<h1 class="logo"><a href="../index.html">Python</a></h1>
+
+
+
+
+
+
+
+
+<h3>Navigation</h3>
+<ul class="current">
+<li class="toctree-l1"><a class="reference internal" href="../page_1.html">Page 1</a></li>
+<li class="toctree-l1 current"><a class="reference internal" href="tagsindex.html">Tags overview</a><ul class="current">
+<li class="toctree-l2 current"><a class="current reference internal" href="#">[⚫tag 4] (1)</a><ul>
+<li class="toctree-l3"><a class="reference internal" href="../page_1.html">Page 1</a></li>
+</ul>
+</li>
+<li class="toctree-l2"><a class="reference internal" href="prefix-tag-3.html">prefix:tag-3 (1)</a></li>
+<li class="toctree-l2"><a class="reference internal" href="tag-1.html">tag-1 (1)</a></li>
+<li class="toctree-l2"><a class="reference internal" href="tag-2.html">tag-2 (1)</a></li>
+</ul>
+</li>
+</ul>
+
+<div class="relations">
+<h3>Related Topics</h3>
+<ul>
+  <li><a href="../index.html">Documentation overview</a><ul>
+  <li><a href="tagsindex.html">Tags overview</a><ul>
+      <li>Previous: <a href="tagsindex.html" title="previous chapter">Tags overview</a></li>
+      <li>Next: <a href="prefix-tag-3.html" title="next chapter">My tags: prefix:tag-3</a></li>
+  </ul></li>
+  </ul></li>
+</ul>
+</div>
+<div id="searchbox" style="display: none" role="search">
+  <h3 id="searchlabel">Quick search</h3>
+    <div class="searchformwrapper">
+    <form class="search" action="../search.html" method="get">
+      <input type="text" name="q" aria-labelledby="searchlabel" autocomplete="off" autocorrect="off" autocapitalize="off" spellcheck="false"/>
+      <input type="submit" value="Go" />
+    </form>
+    </div>
+</div>
+<script>document.getElementById('searchbox').style.display = "block"</script>
+
+
+
+
+
+
+
+
+        </div>
+      </div>
+      <div class="clearer"></div>
+    </div>
+    <div class="footer">
+      &copy;.
+
+      |
+      Powered by <a href="http://sphinx-doc.org/">Sphinx 5.3.0</a>
+      &amp; <a href="https://github.com/bitprophet/alabaster">Alabaster 0.7.12</a>
+
+      |
+      <a href="../_sources/_tags/tag-4.md.txt"
+          rel="nofollow">Page source</a>
+    </div>
+
+
+
+
+  </body>
+</html>
diff --git a/test/outputs/badges/_tags/tag_1.html b/test/outputs/badges/_tags/tag_1.html
new file mode 100644
index 0000000..e6fc798
--- /dev/null
+++ b/test/outputs/badges/_tags/tag_1.html
@@ -0,0 +1,127 @@
+
+<!DOCTYPE html>
+
+<html lang="en">
+  <head>
+    <meta charset="utf-8" />
+    <meta name="viewport" content="width=device-width, initial-scale=1.0" /><meta name="generator" content="Docutils 0.19: https://docutils.sourceforge.io/" />
+
+    <title>My tags: tag_1 &#8212; Python  documentation</title>
+    <link rel="stylesheet" type="text/css" href="../_static/pygments.css" />
+    <link rel="stylesheet" type="text/css" href="../_static/alabaster.css" />
+    <link rel="stylesheet" type="text/css" href="../_static/design-style.4045f2051d55cab465a707391d5b2007.min.css" />
+    <script data-url_root="../" id="documentation_options" src="../_static/documentation_options.js"></script>
+    <script src="../_static/jquery.js"></script>
+    <script src="../_static/underscore.js"></script>
+    <script src="../_static/_sphinx_javascript_frameworks_compat.js"></script>
+    <script src="../_static/doctools.js"></script>
+    <script src="../_static/sphinx_highlight.js"></script>
+    <script src="../_static/design-tabs.js"></script>
+    <link rel="index" title="Index" href="../genindex.html" />
+    <link rel="search" title="Search" href="../search.html" />
+    <link rel="prev" title="My tags: tag 2" href="tag-2.html" />
+
+  <link rel="stylesheet" href="../_static/custom.css" type="text/css" />
+
+
+  <meta name="viewport" content="width=device-width, initial-scale=0.9, maximum-scale=0.9" />
+
+  </head><body>
+
+
+    <div class="document">
+      <div class="documentwrapper">
+        <div class="bodywrapper">
+
+
+          <div class="body" role="main">
+
+  <section id="my-tags-tag-1">
+<h1>My tags: tag_1<a class="headerlink" href="#my-tags-tag-1" title="Permalink to this heading">¶</a></h1>
+<div class="toctree-wrapper compound">
+<p class="caption" role="heading"><span class="caption-text">With this tag</span></p>
+<ul>
+<li class="toctree-l1"><a class="reference internal" href="../page_1.html">Page 1</a></li>
+</ul>
+</div>
+</section>
+
+
+          </div>
+
+        </div>
+      </div>
+      <div class="sphinxsidebar" role="navigation" aria-label="main navigation">
+        <div class="sphinxsidebarwrapper">
+<h1 class="logo"><a href="../index.html">Python</a></h1>
+
+
+
+
+
+
+
+
+<h3>Navigation</h3>
+<ul class="current">
+<li class="toctree-l1"><a class="reference internal" href="../page_1.html">Page 1</a></li>
+<li class="toctree-l1 current"><a class="reference internal" href="tagsindex.html">Tags overview</a><ul class="current">
+<li class="toctree-l2"><a class="reference internal" href="tag-3.html">[🔵tag 3🔴] (1)</a></li>
+<li class="toctree-l2"><a class="reference internal" href="tag-2.html">tag  2 (1)</a></li>
+<li class="toctree-l2 current"><a class="current reference internal" href="#">tag_1 (1)</a><ul>
+<li class="toctree-l3"><a class="reference internal" href="../page_1.html">Page 1</a></li>
+</ul>
+</li>
+</ul>
+</li>
+</ul>
+
+<div class="relations">
+<h3>Related Topics</h3>
+<ul>
+  <li><a href="../index.html">Documentation overview</a><ul>
+  <li><a href="tagsindex.html">Tags overview</a><ul>
+      <li>Previous: <a href="tag-2.html" title="previous chapter">My tags: tag  2</a></li>
+  </ul></li>
+  </ul></li>
+</ul>
+</div>
+<div id="searchbox" style="display: none" role="search">
+  <h3 id="searchlabel">Quick search</h3>
+    <div class="searchformwrapper">
+    <form class="search" action="../search.html" method="get">
+      <input type="text" name="q" aria-labelledby="searchlabel" autocomplete="off" autocorrect="off" autocapitalize="off" spellcheck="false"/>
+      <input type="submit" value="Go" />
+    </form>
+    </div>
+</div>
+<script>document.getElementById('searchbox').style.display = "block"</script>
+
+
+
+
+
+
+
+
+        </div>
+      </div>
+      <div class="clearer"></div>
+    </div>
+    <div class="footer">
+      &copy;.
+
+      |
+      Powered by <a href="http://sphinx-doc.org/">Sphinx 5.3.0</a>
+      &amp; <a href="https://github.com/bitprophet/alabaster">Alabaster 0.7.12</a>
+
+      |
+      <a href="../_sources/_tags/tag_1.md.txt"
+          rel="nofollow">Page source</a>
+    </div>
+
+
+
+
+  </body>
+</html>
diff --git a/test/outputs/badges/_tags/tagsindex.html b/test/outputs/badges/_tags/tagsindex.html
new file mode 100644
index 0000000..b43eb6f
--- /dev/null
+++ b/test/outputs/badges/_tags/tagsindex.html
@@ -0,0 +1,128 @@
+
+<!DOCTYPE html>
+
+<html lang="en">
+  <head>
+    <meta charset="utf-8" />
+    <meta name="viewport" content="width=device-width, initial-scale=1.0" /><meta name="generator" content="Docutils 0.19: https://docutils.sourceforge.io/" />
+
+    <title>Tags overview &#8212; Python  documentation</title>
+    <link rel="stylesheet" type="text/css" href="../_static/pygments.css" />
+    <link rel="stylesheet" type="text/css" href="../_static/alabaster.css" />
+    <link rel="stylesheet" type="text/css" href="../_static/design-style.4045f2051d55cab465a707391d5b2007.min.css" />
+    <script data-url_root="../" id="documentation_options" src="../_static/documentation_options.js"></script>
+    <script src="../_static/jquery.js"></script>
+    <script src="../_static/underscore.js"></script>
+    <script src="../_static/_sphinx_javascript_frameworks_compat.js"></script>
+    <script src="../_static/doctools.js"></script>
+    <script src="../_static/sphinx_highlight.js"></script>
+    <script src="../_static/design-tabs.js"></script>
+    <link rel="index" title="Index" href="../genindex.html" />
+    <link rel="search" title="Search" href="../search.html" />
+    <link rel="next" title="My tags: [⚫tag 4]" href="tag-4.html" />
+    <link rel="prev" title="Page 1" href="../page_1.html" />
+
+  <link rel="stylesheet" href="../_static/custom.css" type="text/css" />
+
+
+  <meta name="viewport" content="width=device-width, initial-scale=0.9, maximum-scale=0.9" />
+
+  </head><body>
+
+
+    <div class="document">
+      <div class="documentwrapper">
+        <div class="bodywrapper">
+
+
+          <div class="body" role="main">
+
+  <section id="tags-overview">
+<span id="tagoverview"></span><h1>Tags overview<a class="headerlink" href="#tags-overview" title="Permalink to this heading">¶</a></h1>
+<div class="toctree-wrapper compound">
+<p class="caption" role="heading"><span class="caption-text">Tags</span></p>
+<ul>
+<li class="toctree-l1"><a class="reference internal" href="tag-4.html">[⚫tag 4] (1)</a></li>
+<li class="toctree-l1"><a class="reference internal" href="prefix-tag-3.html">prefix:tag-3 (1)</a></li>
+<li class="toctree-l1"><a class="reference internal" href="tag-1.html">tag-1 (1)</a></li>
+<li class="toctree-l1"><a class="reference internal" href="tag-2.html">tag-2 (1)</a></li>
+</ul>
+</div>
+</section>
+
+
+          </div>
+
+        </div>
+      </div>
+      <div class="sphinxsidebar" role="navigation" aria-label="main navigation">
+        <div class="sphinxsidebarwrapper">
+<h1 class="logo"><a href="../index.html">Python</a></h1>
+
+
+
+
+
+
+
+
+<h3>Navigation</h3>
+<ul class="current">
+<li class="toctree-l1"><a class="reference internal" href="../page_1.html">Page 1</a></li>
+<li class="toctree-l1 current"><a class="current reference internal" href="#">Tags overview</a><ul>
+<li class="toctree-l2"><a class="reference internal" href="tag-4.html">[⚫tag 4] (1)</a></li>
+<li class="toctree-l2"><a class="reference internal" href="prefix-tag-3.html">prefix:tag-3 (1)</a></li>
+<li class="toctree-l2"><a class="reference internal" href="tag-1.html">tag-1 (1)</a></li>
+<li class="toctree-l2"><a class="reference internal" href="tag-2.html">tag-2 (1)</a></li>
+</ul>
+</li>
+</ul>
+
+<div class="relations">
+<h3>Related Topics</h3>
+<ul>
+  <li><a href="../index.html">Documentation overview</a><ul>
+      <li>Previous: <a href="../page_1.html" title="previous chapter">Page 1</a></li>
+      <li>Next: <a href="tag-4.html" title="next chapter">My tags: [⚫tag 4]</a></li>
+  </ul></li>
+</ul>
+</div>
+<div id="searchbox" style="display: none" role="search">
+  <h3 id="searchlabel">Quick search</h3>
+    <div class="searchformwrapper">
+    <form class="search" action="../search.html" method="get">
+      <input type="text" name="q" aria-labelledby="searchlabel" autocomplete="off" autocorrect="off" autocapitalize="off" spellcheck="false"/>
+      <input type="submit" value="Go" />
+    </form>
+    </div>
+</div>
+<script>document.getElementById('searchbox').style.display = "block"</script>
+
+
+
+
+
+
+
+
+        </div>
+      </div>
+      <div class="clearer"></div>
+    </div>
+    <div class="footer">
+      &copy;.
+
+      |
+      Powered by <a href="http://sphinx-doc.org/">Sphinx 5.3.0</a>
+      &amp; <a href="https://github.com/bitprophet/alabaster">Alabaster 0.7.12</a>
+
+      |
+      <a href="../_sources/_tags/tagsindex.md.txt"
+          rel="nofollow">Page source</a>
+    </div>
+
+
+
+
+  </body>
+</html>
diff --git a/test/outputs/badges/index.html b/test/outputs/badges/index.html
new file mode 100644
index 0000000..c9f69a3
--- /dev/null
+++ b/test/outputs/badges/index.html
@@ -0,0 +1,136 @@
+
+<!DOCTYPE html>
+
+<html lang="en">
+  <head>
+    <meta charset="utf-8" />
+    <meta name="viewport" content="width=device-width, initial-scale=1.0" /><meta name="generator" content="Docutils 0.19: https://docutils.sourceforge.io/" />
+
+    <title>Test Doc &#8212; Python  documentation</title>
+    <link rel="stylesheet" type="text/css" href="_static/pygments.css" />
+    <link rel="stylesheet" type="text/css" href="_static/alabaster.css" />
+    <link rel="stylesheet" type="text/css" href="_static/design-style.4045f2051d55cab465a707391d5b2007.min.css" />
+    <script data-url_root="./" id="documentation_options" src="_static/documentation_options.js"></script>
+    <script src="_static/jquery.js"></script>
+    <script src="_static/underscore.js"></script>
+    <script src="_static/_sphinx_javascript_frameworks_compat.js"></script>
+    <script src="_static/doctools.js"></script>
+    <script src="_static/sphinx_highlight.js"></script>
+    <script src="_static/design-tabs.js"></script>
+    <link rel="index" title="Index" href="genindex.html" />
+    <link rel="search" title="Search" href="search.html" />
+    <link rel="next" title="Page 1" href="page_1.html" />
+
+  <link rel="stylesheet" href="_static/custom.css" type="text/css" />
+
+
+  <meta name="viewport" content="width=device-width, initial-scale=0.9, maximum-scale=0.9" />
+
+  </head><body>
+
+
+    <div class="document">
+      <div class="documentwrapper">
+        <div class="bodywrapper">
+
+
+          <div class="body" role="main">
+
+  <section id="test-doc">
+<h1>Test Doc<a class="headerlink" href="#test-doc" title="Permalink to this heading">¶</a></h1>
+<p>Test document</p>
+<div class="toctree-wrapper compound">
+<ul>
+<li class="toctree-l1"><a class="reference internal" href="page_1.html">Page 1</a></li>
+<li class="toctree-l1"><a class="reference internal" href="_tags/tagsindex.html">Tags overview</a><ul>
+<li class="toctree-l2"><a class="reference internal" href="_tags/tag-4.html">[⚫tag 4] (1)</a><ul>
+<li class="toctree-l3"><a class="reference internal" href="page_1.html">Page 1</a></li>
+</ul>
+</li>
+<li class="toctree-l2"><a class="reference internal" href="_tags/prefix-tag-3.html">prefix:tag-3 (1)</a><ul>
+<li class="toctree-l3"><a class="reference internal" href="page_1.html">Page 1</a></li>
+</ul>
+</li>
+<li class="toctree-l2"><a class="reference internal" href="_tags/tag-1.html">tag-1 (1)</a><ul>
+<li class="toctree-l3"><a class="reference internal" href="page_1.html">Page 1</a></li>
+</ul>
+</li>
+<li class="toctree-l2"><a class="reference internal" href="_tags/tag-2.html">tag-2 (1)</a><ul>
+<li class="toctree-l3"><a class="reference internal" href="page_1.html">Page 1</a></li>
+</ul>
+</li>
+</ul>
+</li>
+</ul>
+</div>
+</section>
+
+
+          </div>
+
+        </div>
+      </div>
+      <div class="sphinxsidebar" role="navigation" aria-label="main navigation">
+        <div class="sphinxsidebarwrapper">
+<h1 class="logo"><a href="#">Python</a></h1>
+
+
+
+
+
+
+
+
+<h3>Navigation</h3>
+<ul>
+<li class="toctree-l1"><a class="reference internal" href="page_1.html">Page 1</a></li>
+<li class="toctree-l1"><a class="reference internal" href="_tags/tagsindex.html">Tags overview</a></li>
+</ul>
+
+<div class="relations">
+<h3>Related Topics</h3>
+<ul>
+  <li><a href="#">Documentation overview</a><ul>
+      <li>Next: <a href="page_1.html" title="next chapter">Page 1</a></li>
+  </ul></li>
+</ul>
+</div>
+<div id="searchbox" style="display: none" role="search">
+  <h3 id="searchlabel">Quick search</h3>
+    <div class="searchformwrapper">
+    <form class="search" action="search.html" method="get">
+      <input type="text" name="q" aria-labelledby="searchlabel" autocomplete="off" autocorrect="off" autocapitalize="off" spellcheck="false"/>
+      <input type="submit" value="Go" />
+    </form>
+    </div>
+</div>
+<script>document.getElementById('searchbox').style.display = "block"</script>
+
+
+
+
+
+
+
+
+        </div>
+      </div>
+      <div class="clearer"></div>
+    </div>
+    <div class="footer">
+      &copy;.
+
+      |
+      Powered by <a href="http://sphinx-doc.org/">Sphinx 5.3.0</a>
+      &amp; <a href="https://github.com/bitprophet/alabaster">Alabaster 0.7.12</a>
+
+      |
+      <a href="_sources/index.md.txt"
+          rel="nofollow">Page source</a>
+    </div>
+
+
+
+
+  </body>
+</html>
diff --git a/test/outputs/badges/page_1.html b/test/outputs/badges/page_1.html
new file mode 100644
index 0000000..347f5ec
--- /dev/null
+++ b/test/outputs/badges/page_1.html
@@ -0,0 +1,114 @@
+
+<!DOCTYPE html>
+
+<html lang="en">
+  <head>
+    <meta charset="utf-8" />
+    <meta name="viewport" content="width=device-width, initial-scale=1.0" /><meta name="generator" content="Docutils 0.19: https://docutils.sourceforge.io/" />
+
+    <title>Page 1 &#8212; Python  documentation</title>
+    <link rel="stylesheet" type="text/css" href="_static/pygments.css" />
+    <link rel="stylesheet" type="text/css" href="_static/alabaster.css" />
+    <link rel="stylesheet" type="text/css" href="_static/design-style.4045f2051d55cab465a707391d5b2007.min.css" />
+    <script data-url_root="./" id="documentation_options" src="_static/documentation_options.js"></script>
+    <script src="_static/jquery.js"></script>
+    <script src="_static/underscore.js"></script>
+    <script src="_static/_sphinx_javascript_frameworks_compat.js"></script>
+    <script src="_static/doctools.js"></script>
+    <script src="_static/sphinx_highlight.js"></script>
+    <script src="_static/design-tabs.js"></script>
+    <link rel="index" title="Index" href="genindex.html" />
+    <link rel="search" title="Search" href="search.html" />
+    <link rel="next" title="Tags overview" href="_tags/tagsindex.html" />
+    <link rel="prev" title="Test Doc" href="index.html" />
+
+  <link rel="stylesheet" href="_static/custom.css" type="text/css" />
+
+
+  <meta name="viewport" content="width=device-width, initial-scale=0.9, maximum-scale=0.9" />
+
+  </head><body>
+
+
+    <div class="document">
+      <div class="documentwrapper">
+        <div class="bodywrapper">
+
+
+          <div class="body" role="main">
+
+  <section id="page-1">
+<h1>Page 1<a class="headerlink" href="#page-1" title="Permalink to this heading">¶</a></h1>
+<p class="tags"><span>Tags: </span><a class="sd-sphinx-override sd-badge sd-bg-primary sd-bg-text-primary reference internal" href="_tags/tag-1.html"><span class="doc">tag-1</span></a><span> </span><a class="sd-sphinx-override sd-badge sd-bg-secondary sd-bg-text-secondary reference internal" href="_tags/tag-2.html"><span class="doc">tag-2</span></a><span> </span><a class="sd-sphinx-override sd-badge sd-bg-info sd-bg-text-info reference internal" href="_tags/prefix-tag-3.html"><span class="doc">prefix:tag-3</span></a><span> </span><a class="sd-sphinx-override sd-badge sd-bg-dark sd-bg-text-dark reference internal" href="_tags/tag-4.html"><span class="doc">[⚫tag 4]</span></a></p>
+</section>
+
+
+          </div>
+
+        </div>
+      </div>
+      <div class="sphinxsidebar" role="navigation" aria-label="main navigation">
+        <div class="sphinxsidebarwrapper">
+<h1 class="logo"><a href="index.html">Python</a></h1>
+
+
+
+
+
+
+
+
+<h3>Navigation</h3>
+<ul class="current">
+<li class="toctree-l1 current"><a class="current reference internal" href="#">Page 1</a></li>
+<li class="toctree-l1"><a class="reference internal" href="_tags/tagsindex.html">Tags overview</a></li>
+</ul>
+
+<div class="relations">
+<h3>Related Topics</h3>
+<ul>
+  <li><a href="index.html">Documentation overview</a><ul>
+      <li>Previous: <a href="index.html" title="previous chapter">Test Doc</a></li>
+      <li>Next: <a href="_tags/tagsindex.html" title="next chapter">Tags overview</a></li>
+  </ul></li>
+</ul>
+</div>
+<div id="searchbox" style="display: none" role="search">
+  <h3 id="searchlabel">Quick search</h3>
+    <div class="searchformwrapper">
+    <form class="search" action="search.html" method="get">
+      <input type="text" name="q" aria-labelledby="searchlabel" autocomplete="off" autocorrect="off" autocapitalize="off" spellcheck="false"/>
+      <input type="submit" value="Go" />
+    </form>
+    </div>
+</div>
+<script>document.getElementById('searchbox').style.display = "block"</script>
+
+
+
+
+
+
+
+
+        </div>
+      </div>
+      <div class="clearer"></div>
+    </div>
+    <div class="footer">
+      &copy;.
+
+      |
+      Powered by <a href="http://sphinx-doc.org/">Sphinx 5.3.0</a>
+      &amp; <a href="https://github.com/bitprophet/alabaster">Alabaster 0.7.12</a>
+
+      |
+      <a href="_sources/page_1.md.txt"
+          rel="nofollow">Page source</a>
+    </div>
+
+
+
+
+  </body>
+</html>
diff --git a/test/outputs/general/_tags/tag-3.txt b/test/outputs/general/_tags/tag-3.txt
new file mode 100644
index 0000000..684210b
--- /dev/null
+++ b/test/outputs/general/_tags/tag-3.txt
@@ -0,0 +1,10 @@
+My tags: tag 3
+**************
+
+
+With this tag
+^^^^^^^^^^^^^
+
+* Page 1
+
+* Page 3
diff --git a/test/outputs/general/_tags/tag-4.txt b/test/outputs/general/_tags/tag-4.txt
new file mode 100644
index 0000000..a44fdac
--- /dev/null
+++ b/test/outputs/general/_tags/tag-4.txt
@@ -0,0 +1,8 @@
+My tags: [{(tag   4)}]
+**********************
+
+
+With this tag
+^^^^^^^^^^^^^
+
+* Page 1
diff --git a/test/outputs/general/_tags/tag2.txt b/test/outputs/general/_tags/tag2.txt
new file mode 100644
index 0000000..d179774
--- /dev/null
+++ b/test/outputs/general/_tags/tag2.txt
@@ -0,0 +1,8 @@
+My tags: tag2
+*************
+
+
+With this tag
+^^^^^^^^^^^^^
+
+* Page 1
diff --git a/test/outputs/general/_tags/tag_1.txt b/test/outputs/general/_tags/tag_1.txt
new file mode 100644
index 0000000..924fb5a
--- /dev/null
+++ b/test/outputs/general/_tags/tag_1.txt
@@ -0,0 +1,10 @@
+My tags: tag_1
+**************
+
+
+With this tag
+^^^^^^^^^^^^^
+
+* Page 1
+
+* Page 2
diff --git a/test/outputs/general/_tags/tag_5.txt b/test/outputs/general/_tags/tag_5.txt
new file mode 100644
index 0000000..bbc29f9
--- /dev/null
+++ b/test/outputs/general/_tags/tag_5.txt
@@ -0,0 +1,8 @@
+My tags: tag_5
+**************
+
+
+With this tag
+^^^^^^^^^^^^^
+
+* Page 2
diff --git a/test/outputs/general/_tags/tagsindex.txt b/test/outputs/general/_tags/tagsindex.txt
new file mode 100644
index 0000000..968e8bb
--- /dev/null
+++ b/test/outputs/general/_tags/tagsindex.txt
@@ -0,0 +1,18 @@
+Tags overview
+*************
+
+
+Tags
+^^^^
+
+* [{(tag   4)}] (1)
+
+* tag 3 (2)
+
+* tag2 (1)
+
+* tag_1 (2)
+
+* tag_5 (1)
+
+* {{🧪test tag; please ignore🧪}} (1)
diff --git a/test/outputs/general/_tags/test-tag-please-ignore.txt b/test/outputs/general/_tags/test-tag-please-ignore.txt
new file mode 100644
index 0000000..4b65f26
--- /dev/null
+++ b/test/outputs/general/_tags/test-tag-please-ignore.txt
@@ -0,0 +1,8 @@
+My tags: {{🧪test tag; please ignore🧪}}
+****************************************
+
+
+With this tag
+^^^^^^^^^^^^^
+
+* Page 2
diff --git a/test/outputs/general/index.txt b/test/outputs/general/index.txt
new file mode 100644
index 0000000..90b0f1d
--- /dev/null
+++ b/test/outputs/general/index.txt
@@ -0,0 +1,42 @@
+Test Doc
+********
+
+Test document
+
+* Page 1
+
+* Page 2
+
+* Subdir
+
+  * Page 3
+
+* Tags overview
+
+  * [{(tag   4)}] (1)
+
+    * Page 1
+
+  * tag 3 (2)
+
+    * Page 1
+
+    * Page 3
+
+  * tag2 (1)
+
+    * Page 1
+
+  * tag_1 (2)
+
+    * Page 1
+
+    * Page 2
+
+  * tag_5 (1)
+
+    * Page 2
+
+  * {{🧪test tag; please ignore🧪}} (1)
+
+    * Page 2
diff --git a/test/outputs/general/page_1.txt b/test/outputs/general/page_1.txt
new file mode 100644
index 0000000..268110e
--- /dev/null
+++ b/test/outputs/general/page_1.txt
@@ -0,0 +1,4 @@
+Page 1
+******
+
+Tags: tag_1, tag2, tag 3, [{(tag 4)}]
diff --git a/test/outputs/general/page_2.txt b/test/outputs/general/page_2.txt
new file mode 100644
index 0000000..05f5aef
--- /dev/null
+++ b/test/outputs/general/page_2.txt
@@ -0,0 +1,4 @@
+Page 2
+******
+
+Tags: tag_1, tag_5, {{🧪test tag; please ignore🧪}}
diff --git a/test/outputs/general/subdir/index.txt b/test/outputs/general/subdir/index.txt
new file mode 100644
index 0000000..9bf5629
--- /dev/null
+++ b/test/outputs/general/subdir/index.txt
@@ -0,0 +1,4 @@
+Subdir
+******
+
+* Page 3
diff --git a/test/outputs/general/subdir/page_3.txt b/test/outputs/general/subdir/page_3.txt
new file mode 100644
index 0000000..c60123a
--- /dev/null
+++ b/test/outputs/general/subdir/page_3.txt
@@ -0,0 +1,4 @@
+Page 3
+******
+
+Tags: tag 3
diff --git a/test/sources/test-badges/conf.py b/test/sources/test-badges/conf.py
new file mode 100644
index 0000000..664af23
--- /dev/null
+++ b/test/sources/test-badges/conf.py
@@ -0,0 +1,10 @@
+extensions = ["sphinx_design", "sphinx_tags", "myst_parser"]
+tags_create_tags = True
+tags_extension = ["md"]
+tags_create_badges = True
+tags_badge_colors = {
+    "tag-1": "primary",
+    "prefix:*": "info",
+    "*⚫*": "dark",
+    "*": "secondary",
+}
diff --git a/test/sources/test-badges/index.md b/test/sources/test-badges/index.md
new file mode 100644
index 0000000..ba09c17
--- /dev/null
+++ b/test/sources/test-badges/index.md
@@ -0,0 +1,8 @@
+# Test Doc
+Test document
+
+
+```{toctree}
+page_1
+_tags/tagsindex
+```
diff --git a/test/sources/test-badges/page_1.md b/test/sources/test-badges/page_1.md
new file mode 100644
index 0000000..ff6d3db
--- /dev/null
+++ b/test/sources/test-badges/page_1.md
@@ -0,0 +1,3 @@
+# Page 1
+```{tags} tag-1, tag-2, prefix:tag-3, [⚫tag 4]
+```
diff --git a/test/sources/test-ipynb/conf.py b/test/sources/test-ipynb/conf.py
new file mode 100644
index 0000000..d9eb896
--- /dev/null
+++ b/test/sources/test-ipynb/conf.py
@@ -0,0 +1,4 @@
+extensions = ["sphinx_tags", "nbsphinx"]
+tags_create_tags = True
+tags_extension = ["rst", "ipynb"]
+exclude_patterns = ["_build"]
diff --git a/test/sources/test-ipynb/index.rst b/test/sources/test-ipynb/index.rst
new file mode 100644
index 0000000..e9895f1
--- /dev/null
+++ b/test/sources/test-ipynb/index.rst
@@ -0,0 +1,9 @@
+Test Doc
+========
+Test document
+
+.. toctree::
+    page_1
+    page_2
+    subdir/index
+    _tags/tagsindex
diff --git a/test/sources/test-ipynb/page_1.ipynb b/test/sources/test-ipynb/page_1.ipynb
new file mode 100644
index 0000000..bc28734
--- /dev/null
+++ b/test/sources/test-ipynb/page_1.ipynb
@@ -0,0 +1,43 @@
+{
+ "cells": [
+  {
+   "attachments": {},
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "# Page 1"
+   ]
+  },
+  {
+   "cell_type": "raw",
+   "metadata": {
+    "raw_mimetype": "text/restructuredtext",
+    "tags": []
+   },
+   "source": [
+    ".. tags:: tag_1, tag2, tag 3, [{(tag   4)}]"
+   ]
+  }
+ ],
+ "metadata": {
+  "kernelspec": {
+   "display_name": "Python 3 (ipykernel)",
+   "language": "python",
+   "name": "python3"
+  },
+  "language_info": {
+   "codemirror_mode": {
+    "name": "ipython",
+    "version": 3
+   },
+   "file_extension": ".py",
+   "mimetype": "text/x-python",
+   "name": "python",
+   "nbconvert_exporter": "python",
+   "pygments_lexer": "ipython3",
+   "version": "3.11.0"
+  }
+ },
+ "nbformat": 4,
+ "nbformat_minor": 4
+}
diff --git a/test/sources/test-ipynb/page_2.ipynb b/test/sources/test-ipynb/page_2.ipynb
new file mode 100644
index 0000000..b06e3fe
--- /dev/null
+++ b/test/sources/test-ipynb/page_2.ipynb
@@ -0,0 +1,43 @@
+{
+ "cells": [
+  {
+   "attachments": {},
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "# Page 2"
+   ]
+  },
+  {
+   "cell_type": "raw",
+   "metadata": {
+    "raw_mimetype": "text/restructuredtext",
+    "tags": []
+   },
+   "source": [
+    ".. tags:: tag_1, tag_5, {{🧪test tag; please ignore🧪}}"
+   ]
+  }
+ ],
+ "metadata": {
+  "kernelspec": {
+   "display_name": "Python 3 (ipykernel)",
+   "language": "python",
+   "name": "python3"
+  },
+  "language_info": {
+   "codemirror_mode": {
+    "name": "ipython",
+    "version": 3
+   },
+   "file_extension": ".py",
+   "mimetype": "text/x-python",
+   "name": "python",
+   "nbconvert_exporter": "python",
+   "pygments_lexer": "ipython3",
+   "version": "3.11.0"
+  }
+ },
+ "nbformat": 4,
+ "nbformat_minor": 4
+}
diff --git a/test/sources/test-ipynb/subdir/index.rst b/test/sources/test-ipynb/subdir/index.rst
new file mode 100644
index 0000000..07bb1c8
--- /dev/null
+++ b/test/sources/test-ipynb/subdir/index.rst
@@ -0,0 +1,5 @@
+Subdir
+======
+
+.. toctree::
+    page_3
diff --git a/test/sources/test-ipynb/subdir/page_3.ipynb b/test/sources/test-ipynb/subdir/page_3.ipynb
new file mode 100644
index 0000000..34c9022
--- /dev/null
+++ b/test/sources/test-ipynb/subdir/page_3.ipynb
@@ -0,0 +1,43 @@
+{
+ "cells": [
+  {
+   "attachments": {},
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "# Page 3"
+   ]
+  },
+  {
+   "cell_type": "raw",
+   "metadata": {
+    "raw_mimetype": "text/restructuredtext",
+    "tags": []
+   },
+   "source": [
+    ".. tags:: tag 3"
+   ]
+  }
+ ],
+ "metadata": {
+  "kernelspec": {
+   "display_name": "Python 3 (ipykernel)",
+   "language": "python",
+   "name": "python3"
+  },
+  "language_info": {
+   "codemirror_mode": {
+    "name": "ipython",
+    "version": 3
+   },
+   "file_extension": ".py",
+   "mimetype": "text/x-python",
+   "name": "python",
+   "nbconvert_exporter": "python",
+   "pygments_lexer": "ipython3",
+   "version": "3.11.0"
+  }
+ },
+ "nbformat": 4,
+ "nbformat_minor": 4
+}
diff --git a/test/sources/test-myst/conf.py b/test/sources/test-myst/conf.py
new file mode 100644
index 0000000..a9216cf
--- /dev/null
+++ b/test/sources/test-myst/conf.py
@@ -0,0 +1,3 @@
+extensions = ["sphinx_tags", "myst_parser"]
+tags_create_tags = True
+tags_extension = ["md"]
diff --git a/test/sources/test-myst/index.md b/test/sources/test-myst/index.md
new file mode 100644
index 0000000..ff497b0
--- /dev/null
+++ b/test/sources/test-myst/index.md
@@ -0,0 +1,10 @@
+# Test Doc
+Test document
+
+
+```{toctree}
+page_1
+page_2
+subdir/index
+_tags/tagsindex
+```
diff --git a/test/sources/test-myst/page_1.md b/test/sources/test-myst/page_1.md
new file mode 100644
index 0000000..98b4fac
--- /dev/null
+++ b/test/sources/test-myst/page_1.md
@@ -0,0 +1,3 @@
+# Page 1
+```{tags} tag_1, tag2, tag 3, [{(tag   4)}]
+```
diff --git a/test/sources/test-myst/page_2.md b/test/sources/test-myst/page_2.md
new file mode 100644
index 0000000..61b0c39
--- /dev/null
+++ b/test/sources/test-myst/page_2.md
@@ -0,0 +1,3 @@
+# Page 2
+```{tags} tag_1, tag_5, {{🧪test tag; please ignore🧪}}
+```
diff --git a/test/sources/test-myst/subdir/index.md b/test/sources/test-myst/subdir/index.md
new file mode 100644
index 0000000..6d538e5
--- /dev/null
+++ b/test/sources/test-myst/subdir/index.md
@@ -0,0 +1,5 @@
+# Subdir
+
+```{toctree}
+page_3
+```
diff --git a/test/sources/test-myst/subdir/page_3.md b/test/sources/test-myst/subdir/page_3.md
new file mode 100644
index 0000000..564e74f
--- /dev/null
+++ b/test/sources/test-myst/subdir/page_3.md
@@ -0,0 +1,3 @@
+# Page 3
+```{tags} tag 3
+```
diff --git a/test/sources/test-rst/conf.py b/test/sources/test-rst/conf.py
new file mode 100644
index 0000000..9945dde
--- /dev/null
+++ b/test/sources/test-rst/conf.py
@@ -0,0 +1,4 @@
+extensions = ["sphinx_tags"]
+tags_create_tags = True
+tags_extension = ["rst"]
+exclude_patterns = ["excluded"]
diff --git a/test/sources/test-rst/excluded/page_4.rst b/test/sources/test-rst/excluded/page_4.rst
new file mode 100644
index 0000000..e41cdf8
--- /dev/null
+++ b/test/sources/test-rst/excluded/page_4.rst
@@ -0,0 +1,3 @@
+Page 4
+======
+.. tags:: tag_1, tag_5,
diff --git a/test/sources/test-rst/index.rst b/test/sources/test-rst/index.rst
new file mode 100644
index 0000000..e9895f1
--- /dev/null
+++ b/test/sources/test-rst/index.rst
@@ -0,0 +1,9 @@
+Test Doc
+========
+Test document
+
+.. toctree::
+    page_1
+    page_2
+    subdir/index
+    _tags/tagsindex
diff --git a/test/sources/test-rst/page_1.rst b/test/sources/test-rst/page_1.rst
new file mode 100644
index 0000000..4b3b4c2
--- /dev/null
+++ b/test/sources/test-rst/page_1.rst
@@ -0,0 +1,3 @@
+Page 1
+======
+.. tags:: tag_1, tag2, tag 3, [{(tag   4)}]
diff --git a/test/sources/test-rst/page_2.rst b/test/sources/test-rst/page_2.rst
new file mode 100644
index 0000000..12567f4
--- /dev/null
+++ b/test/sources/test-rst/page_2.rst
@@ -0,0 +1,3 @@
+Page 2
+======
+.. tags:: tag_1, tag_5, {{🧪test tag; please ignore🧪}}
diff --git a/test/sources/test-rst/subdir/index.rst b/test/sources/test-rst/subdir/index.rst
new file mode 100644
index 0000000..07bb1c8
--- /dev/null
+++ b/test/sources/test-rst/subdir/index.rst
@@ -0,0 +1,5 @@
+Subdir
+======
+
+.. toctree::
+    page_3
diff --git a/test/sources/test-rst/subdir/page_3.rst b/test/sources/test-rst/subdir/page_3.rst
new file mode 100644
index 0000000..2311a7a
--- /dev/null
+++ b/test/sources/test-rst/subdir/page_3.rst
@@ -0,0 +1,3 @@
+Page 3
+======
+.. tags:: tag 3
diff --git a/test/sources/test-symlink/conf.py b/test/sources/test-symlink/conf.py
new file mode 100644
index 0000000..959608c
--- /dev/null
+++ b/test/sources/test-symlink/conf.py
@@ -0,0 +1,3 @@
+extensions = ["sphinx_tags"]
+tags_create_tags = True
+tags_extension = ["rst"]
diff --git a/test/test_badges.py b/test/test_badges.py
new file mode 100644
index 0000000..850e3e8
--- /dev/null
+++ b/test/test_badges.py
@@ -0,0 +1,37 @@
+"""General tests for tag index and tag pages"""
+from io import StringIO
+from pathlib import Path
+from test.conftest import OUTPUT_ROOT_DIR
+
+import pytest
+from bs4 import BeautifulSoup
+from sphinx.testing.util import SphinxTestApp
+
+OUTPUT_DIR = OUTPUT_ROOT_DIR / "badges"
+EXPECTED_CLASSES = {
+    "tag-1": "sd-bg-primary",
+    "tag-2": "sd-bg-secondary",
+    "prefix-tag-3": "sd-bg-info",
+    "tag-4": "sd-bg-dark",
+}
+
+
+@pytest.mark.sphinx("html", testroot="badges")
+def test_badges(app: SphinxTestApp, status: StringIO, warning: StringIO):
+    """Parse output HTML for a page with badges, find badge links, and check for CSS classes for
+    expected badge colors
+    """
+    app.build()
+    assert "build succeeded" in status.getvalue()
+    assert not warning.getvalue().strip()
+
+    build_dir = Path(app.srcdir) / "_build" / "html"
+    page_1 = (build_dir / "page_1.html").read_text()
+    soup = BeautifulSoup(page_1, "html.parser")
+    # print(soup.prettify())
+
+    badge_links = soup.find_all("a", class_="sd-badge")
+    classes_by_tag = {Path(a.get("href")).stem: a.get("class") for a in badge_links}
+
+    for tag, cls in EXPECTED_CLASSES.items():
+        assert cls in classes_by_tag[tag]
diff --git a/test/test_general_tags.py b/test/test_general_tags.py
new file mode 100644
index 0000000..ff0e3e5
--- /dev/null
+++ b/test/test_general_tags.py
@@ -0,0 +1,73 @@
+"""General tests for tag index and tag pages"""
+from io import StringIO
+from pathlib import Path
+from test.conftest import OUTPUT_ROOT_DIR
+
+import pytest
+from sphinx.testing.util import SphinxTestApp
+
+OUTPUT_DIR = OUTPUT_ROOT_DIR / "general"
+
+
+def run_all_formats():
+    """Return a decorator that runs a test in all supported markup formats"""
+    return pytest.mark.parametrize(
+        (),
+        [
+            pytest.param(marks=pytest.mark.sphinx("text", testroot="myst")),
+            pytest.param(marks=pytest.mark.sphinx("text", testroot="rst")),
+            pytest.param(marks=pytest.mark.sphinx("text", testroot="symlink")),
+            pytest.param(marks=pytest.mark.sphinx("text", testroot="ipynb")),
+        ],
+    )
+
+
+@run_all_formats()
+def test_build(app: SphinxTestApp, status: StringIO, warning: StringIO):
+    app.build()
+    assert "build succeeded" in status.getvalue()
+
+    # Build with notebooks and text output results in spurious errors "File Not Found: _tags/*.html"
+    # For all other formats, ensure no warnings are raised
+    if not app.srcdir.endswith("ipynb"):
+        assert not warning.getvalue().strip()
+
+
+@run_all_formats()
+def test_index(app: SphinxTestApp):
+    app.build()
+    build_dir = Path(app.srcdir) / "_build" / "text"
+
+    # Check tags index page
+    contents = (build_dir / "_tags" / "tagsindex.txt").read_text()
+    expected_contents = (OUTPUT_DIR / "_tags" / "tagsindex.txt").read_text()
+    assert contents == expected_contents
+
+    # Check full toctree created by index
+    contents = (build_dir / "index.txt").read_text()
+    expected_contents = (OUTPUT_DIR / "index.txt").read_text()
+    assert contents == expected_contents
+
+
+@run_all_formats()
+def test_tag_pages(app: SphinxTestApp):
+    app.build()
+    build_dir = Path(app.srcdir) / "_build" / "text"
+
+    # Check all expected tag pages
+    for tag in ["tag_1", "tag2", "tag-3", "tag-4", "tag_5", "test-tag-please-ignore"]:
+        contents = (build_dir / "_tags" / f"{tag}.txt").read_text()
+        expected_contents = (OUTPUT_DIR / "_tags" / f"{tag}.txt").read_text()
+        assert contents == expected_contents
+
+
+@run_all_formats()
+def test_tagged_pages(app: SphinxTestApp):
+    app.build()
+    build_dir = Path(app.srcdir) / "_build" / "text"
+
+    # Check all expected tag pages
+    for page in [Path("page_1.txt"), Path("page_2.txt"), Path("subdir") / "page_3.txt"]:
+        contents = (build_dir / page).read_text()
+        expected_contents = (OUTPUT_DIR / page).read_text()
+        assert contents == expected_contents