diff --git a/noxfile.py b/noxfile.py new file mode 100644 index 0000000..70e6593 --- /dev/null +++ b/noxfile.py @@ -0,0 +1,6 @@ +import nox + +@nox.session() +def tests(session): + session.install(".[tests]") + session.run("pytest", '--driver=firefox', *session.posargs) diff --git a/pyproject.toml b/pyproject.toml index 44e6d4c..22fb14f 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -55,3 +55,17 @@ follow_imports = "skip" incremental = true check_untyped_defs = true warn_unused_ignores = true + +[project.optional-dependencies] +tests = [ + 'pytest>=8', + 'selenium', + 'pytest-selenium', +] + +[tool.pytest.ini_options] +minversion = "6.0" +addopts = "-ra -q" +testpaths = [ + "tests", +] diff --git a/tests/conftest.py b/tests/conftest.py new file mode 100644 index 0000000..97f95c6 --- /dev/null +++ b/tests/conftest.py @@ -0,0 +1,33 @@ +import pytest +import subprocess +from selenium import webdriver +from pathlib import Path + + +@pytest.fixture(scope="session") +def firefox_driver(): + _driver = webdriver.Firefox() + yield _driver + _driver.quit() + + +@pytest.fixture(scope="session") +def chrome_driver(): + _driver = webdriver.Chrome() + yield _driver + _driver.quit() + + +@pytest.fixture(autouse=True) +def run_make_commands(request): + # Get the directory of the current test file + test_dir = request.fspath.dirname + cmd = """ + uv venv && \\ + . .venv/bin/activate && \\ + uv pip install -r requirements.txt && \\ + .venv/bin/sphinx-build -M zundler . _build +""" + + for d in ["copy-button", "mermaid", "multi-page"]: + subprocess.run(cmd.strip(), shell=True, check=True, cwd=Path(test_dir) / d) diff --git a/tests/copy-button/conf.py b/tests/copy-button/conf.py new file mode 100644 index 0000000..a159f3b --- /dev/null +++ b/tests/copy-button/conf.py @@ -0,0 +1,12 @@ +from sphinx.application import Sphinx + +project = 'copy-button' +copyright = '2024, Adrian Vollmer' +author = 'Adrian Vollmer' +version = '0.0.1' + +exclude_patterns = ['_build', 'Thumbs.db', '.DS_Store'] + +extensions = [ + "sphinx_copybutton", +] diff --git a/tests/copy-button/index.rst b/tests/copy-button/index.rst new file mode 100644 index 0000000..44b22fa --- /dev/null +++ b/tests/copy-button/index.rst @@ -0,0 +1,3 @@ +.. code:: python + + Copy me diff --git a/tests/copy-button/requirements.txt b/tests/copy-button/requirements.txt new file mode 100644 index 0000000..2276438 --- /dev/null +++ b/tests/copy-button/requirements.txt @@ -0,0 +1,3 @@ +sphinx +sphinx-copybutton +../.. diff --git a/tests/mermaid/conf.py b/tests/mermaid/conf.py new file mode 100644 index 0000000..e0aa7b6 --- /dev/null +++ b/tests/mermaid/conf.py @@ -0,0 +1,12 @@ +from sphinx.application import Sphinx + +project = 'mermaid' +copyright = '2024, Adrian Vollmer' +author = 'Adrian Vollmer' +version = '0.0.1' + +exclude_patterns = ['_build', 'Thumbs.db', '.DS_Store'] + +extensions = [ + 'sphinxcontrib.mermaid' +] diff --git a/tests/mermaid/index.rst b/tests/mermaid/index.rst new file mode 100644 index 0000000..1493dc0 --- /dev/null +++ b/tests/mermaid/index.rst @@ -0,0 +1,17 @@ +.. mermaid:: + + sequenceDiagram + participant Alice + participant Bob + Alice->John: Hello John, how are you? + loop Healthcheck + John->John: Fight against hypochondria + end + Note right of John: Rational thoughts
prevail... + John-->Alice: Great! + John->Bob: How about you? + Bob-->John: Jolly good! + + +Section +======= diff --git a/tests/mermaid/requirements.txt b/tests/mermaid/requirements.txt new file mode 100644 index 0000000..e33c256 --- /dev/null +++ b/tests/mermaid/requirements.txt @@ -0,0 +1,3 @@ +sphinx +sphinxcontrib-mermaid +../.. diff --git a/tests/multi-page/conf.py b/tests/multi-page/conf.py new file mode 100644 index 0000000..0b32a6b --- /dev/null +++ b/tests/multi-page/conf.py @@ -0,0 +1,8 @@ +from sphinx.application import Sphinx + +project = 'multi-page' +copyright = '2024, Adrian Vollmer' +author = 'Adrian Vollmer' +version = '0.0.1' + +exclude_patterns = ['_build', 'Thumbs.db', '.DS_Store'] diff --git a/tests/multi-page/first.rst b/tests/multi-page/first.rst new file mode 100644 index 0000000..becd46a --- /dev/null +++ b/tests/multi-page/first.rst @@ -0,0 +1,6 @@ +.. _first: + +First +===== + +Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet. Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet. Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet. diff --git a/tests/multi-page/index.rst b/tests/multi-page/index.rst new file mode 100644 index 0000000..5355fc7 --- /dev/null +++ b/tests/multi-page/index.rst @@ -0,0 +1,18 @@ +Welcome +======= + +.. toctree:: + :maxdepth: 2 + :caption: Contents: + + first + second + third + + +Chapters +======== + +* :ref:`first` +* :ref:`second` +* :ref:`third` diff --git a/tests/multi-page/requirements.txt b/tests/multi-page/requirements.txt new file mode 100644 index 0000000..9e1dc97 --- /dev/null +++ b/tests/multi-page/requirements.txt @@ -0,0 +1,2 @@ +sphinx +../.. diff --git a/tests/multi-page/second.rst b/tests/multi-page/second.rst new file mode 100644 index 0000000..0aac8ab --- /dev/null +++ b/tests/multi-page/second.rst @@ -0,0 +1,6 @@ +.. _second: + +Second +====== + +Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet. Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet. Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet. diff --git a/tests/multi-page/third.rst b/tests/multi-page/third.rst new file mode 100644 index 0000000..a2c747c --- /dev/null +++ b/tests/multi-page/third.rst @@ -0,0 +1,6 @@ +.. _third: + +Third +===== + +Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet. Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet. Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet. diff --git a/tests/test_zundler.py b/tests/test_zundler.py new file mode 100644 index 0000000..c4b1e6a --- /dev/null +++ b/tests/test_zundler.py @@ -0,0 +1,96 @@ +import time +from pathlib import Path + +from selenium.webdriver.common.by import By +from selenium.webdriver.common.keys import Keys + + +TEST_DIR = Path(__file__).resolve().parent + + +def test_copy_button(selenium): + path = TEST_DIR / "copy-button//_build//zundler//index.html" + selenium.get(path.as_uri()) + + time.sleep(1) + + assert "copy-button documentation" in selenium.title + + selenium.switch_to.frame("main") + # Not sure why there are two buttons, but we need the second one. + # The first cannot be clicked by selenium because it's obscured. + + button = selenium.find_element(By.CSS_SELECTOR, "button.copybtn + button.copybtn") + + assert button.get_attribute("data-tooltip") == "Copy" + assert "success" not in button.get_attribute("class") + + button.click() + + assert button.get_attribute("data-tooltip") == "Copied!" + assert "success" in button.get_attribute("class") + + # Check layout + assert button.location["y"] < 55 + assert button.location["y"] > 45 + + +def test_mermaid(selenium): + path = TEST_DIR / "mermaid//_build//zundler//index.html" + selenium.get(path.as_uri()) + + time.sleep(1) + + assert "mermaid documentation" in selenium.title + + selenium.switch_to.frame("main") + + section = selenium.find_element(By.CSS_SELECTOR, "#section") + svg = selenium.find_element(By.CSS_SELECTOR, "div.mermaid") + + # Check layout + assert svg.size["height"] > 500 + assert section.location["y"] > 500 + + +def test_multi_page(selenium): + path = TEST_DIR / "multi-page//_build//zundler//index.html" + selenium.get(path.as_uri()) + + time.sleep(1) + + assert "multi-page documentation" in selenium.title + + selenium.switch_to.frame("main") + + second_link = selenium.find_element(By.XPATH, "//a[text() = 'Second' and @class = 'reference internal']") + + second_link.click() + + time.sleep(1) + + assert selenium.title.startswith("Second") + + +def test_multi_page_search(selenium): + path = TEST_DIR / "multi-page//_build//zundler//index.html" + selenium.get(path.as_uri()) + + time.sleep(1) + + assert "multi-page documentation" in selenium.title + + selenium.switch_to.frame("main") + + searchbox = selenium.find_element(By.CSS_SELECTOR, "#searchbox input[type='text']") + + searchbox.send_keys("Lorem" + Keys.ENTER) + selenium.switch_to.parent_frame() + time.sleep(2) + selenium.switch_to.frame("main") + + assert selenium.title.startswith("Search") + + span = selenium.find_element(By.CSS_SELECTOR, "span.highlighted") + + assert span.text == "Lorem"