Skip to content

Commit

Permalink
test: Add tests for the demo site to check components are rendered co…
Browse files Browse the repository at this point in the history
…rrectly
  • Loading branch information
StuartMacKay committed Jan 12, 2025
1 parent fd540bf commit 0b8c210
Show file tree
Hide file tree
Showing 210 changed files with 620 additions and 10 deletions.
38 changes: 28 additions & 10 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ requires-python = ">=3.10"
dependencies = [
"django>=5.0",
"django-crispy-forms>=2.0,<2.4",
"django-environ>=0.11.2",
]
classifiers = [
"Development Status :: 5 - Production/Stable",
Expand Down Expand Up @@ -50,6 +51,11 @@ dev-dependencies = [
"tox-uv>=1.1.0",
"tox==4.12.1",
"django-environ>=0.11.2",
"pytest-django>=4.9.0",
"playwright>=1.49.1",
"pytest-playwright>=0.6.2",
"pillow>=11.1.0",
"pixelmatch>=0.3.0",
]

[tool.bumpversion]
Expand Down Expand Up @@ -102,11 +108,12 @@ multi_line_output = 3
include_trailing_comma = true

[tool.pytest.ini_options]
DJANGO_SETTINGS_MODULE = "demo.settings"
pythonpath = [
"."
]
testpaths = [
"tests"
"tests/unit"
]

[tool.tox]
Expand All @@ -116,17 +123,11 @@ requires =
tox>=4.12.1
tox-uv>=1.1.1
envlist =
python3.12-django50-crispy20
python3.12-django50-crispy21
python3.12-django50-crispy22
python3.12-django50-crispy23
python3.12-django51-crispy20
python3.12-django51-crispy21
python3.12-django51-crispy22
python3.12-django51-crispy23
django{50,51}-crispy{20,21,22,23}
django51-crispy23-gds{500,520,540,560,580}
[testenv]
commands = pytest {posargs}
commands = pytest {posargs} tests/unit
deps =
pytest
django50: Django>=5.0,<5.1
Expand All @@ -137,4 +138,21 @@ deps =
crispy23: django-crispy-forms>=2.3,<2.4
setenv =
PYTHONPATH = src
[testenv:gds{500,520,540,560,580}]
commands = pytest {posargs} tests/demo
deps =
pytest
pytest-django
pytest-playwright
pillow
pixelmatch
django-environ
setenv =
PYTHONPATH = src
gds500: CRISPY_GDS_FRONTEND_VERSION=5.0.0
gds520: CRISPY_GDS_FRONTEND_VERSION=5.2.0
gds540: CRISPY_GDS_FRONTEND_VERSION=5.4.0
gds560: CRISPY_GDS_FRONTEND_VERSION=5.6.0
gds580: CRISPY_GDS_FRONTEND_VERSION=5.8.0
"""
93 changes: 93 additions & 0 deletions tests/demo/conftest.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
"""
Modified from: https://github.com/symon-storozhenko/pytest-playwright-visual
which nicely crafted and is very easy to use to:
1. change the path where snapshots are written to include the GOV.UK GDS
Frontend version. That way images are compared against the version that
was used to generate them so slight changes in styling between GDS versions
do not trigger test failures. This also allows the snapshots for each
GDS version to be stored in the same directory, making comparisons easy.
2. flatten the directory tree used for saving snapshots so it is easier to
navigate when reviewing snapshots, particularly when using an image viewer
to step through the screenshots for each gds version for a given test.
"""

import os
import shutil
import sys
from io import BytesIO
from pathlib import Path
from typing import Any, Callable

import pytest
from PIL import Image
from pixelmatch.contrib.PIL import pixelmatch

os.environ.setdefault("DJANGO_ALLOW_ASYNC_UNSAFE", "true")


def pytest_addoption(parser: Any) -> None:
group = parser.getgroup("assert-snapshot", "Image Snapshot")
group.addoption(
"--update-snapshots",
action="store_true",
default=False,
help="Update snapshots.",
)


@pytest.fixture
def assert_snapshot(
pytestconfig: Any, request: Any, browser_name: str, settings
) -> Callable:
gds_version = "gds-%s" % settings.CRISPY_GDS_FRONTEND_VERSION
test_name = f"{str(Path(request.node.name))}[{str(sys.platform)}]-{gds_version}"

def compare(
img: bytes, *, threshold: float = 0.1, name=f"{test_name}.png", fail_fast=False
) -> None:
update_snapshot = pytestconfig.getoption("--update-snapshots")
test_file_name = str(os.path.basename(Path(request.node.fspath))).strip(".py")

filepath = (
Path(request.node.fspath).parent.resolve() / "snapshots" / test_file_name
)
filepath.mkdir(parents=True, exist_ok=True)
file = filepath / name

# Create a dir where all snapshot test failures will go
results_dir_name = (
Path(request.node.fspath).parent.resolve() / "snapshot_tests_failures"
)
test_results_dir = results_dir_name / test_file_name

# Remove a single test's past run dir with actual, diff and expected images
if test_results_dir.exists():
shutil.rmtree(test_results_dir)
if update_snapshot:
file.write_bytes(img)
pytest.fail("--> Snapshots updated. Please review images")
if not file.exists():
file.write_bytes(img)
# pytest.fail(
pytest.fail("--> New snapshot(s) created. Please review images")

img_a = Image.open(BytesIO(img))
img_b = Image.open(file)
img_diff = Image.new("RGBA", img_a.size)
mismatch = pixelmatch(
img_a, img_b, img_diff, threshold=threshold, fail_fast=fail_fast
)

if mismatch == 0:
return
else:
# Create new test_results folder
test_results_dir.mkdir(parents=True, exist_ok=True)
img_diff.save(f"{test_results_dir}/Diff_{name}")
img_a.save(f"{test_results_dir}/Actual_{name}")
img_b.save(f"{test_results_dir}/Expected_{name}")
pytest.fail("--> Snapshots DO NOT match!")

return compare
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
38 changes: 38 additions & 0 deletions tests/demo/test_accordion.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
from playwright.sync_api import Page


def test_accordion__layout(live_server, assert_snapshot, page: Page):
page.goto(f"{live_server.url}/components/accordion/")
assert_snapshot(page.screenshot(full_page=True))


def test_accordion__show_all(live_server, assert_snapshot, page: Page):
page.goto(f"{live_server.url}/components/accordion/")
page.locator("button[class=govuk-accordion__show-all]").click()
assert_snapshot(page.screenshot(full_page=True))


def test_accordion__hide_all(live_server, assert_snapshot, page: Page):
page.goto(f"{live_server.url}/components/accordion/")
page.locator("button[class=govuk-accordion__show-all]").click()
page.locator("button[class=govuk-accordion__show-all]").click()
assert_snapshot(page.screenshot(full_page=True))


def test_accordion__show_section(live_server, assert_snapshot, page: Page):
page.goto(f"{live_server.url}/components/accordion/")
page.locator("button[class=govuk-accordion__section-button]").locator(
"nth=0"
).click()
assert_snapshot(page.screenshot(full_page=True))


def test_accordion__hide_section(live_server, assert_snapshot, page: Page):
page.goto(f"{live_server.url}/components/accordion/")
page.locator("button[class=govuk-accordion__section-button]").locator(
"nth=0"
).click()
page.locator("button[class=govuk-accordion__section-button]").locator(
"nth=0"
).click()
assert_snapshot(page.screenshot(full_page=True))
17 changes: 17 additions & 0 deletions tests/demo/test_buttons.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
from playwright.sync_api import Page


def test_buttons__layout(live_server, assert_snapshot, page: Page):
page.goto(f"{live_server.url}/components/buttons/")
assert_snapshot(page.screenshot(full_page=True))


def test_buttons__click_add_button(live_server, assert_snapshot, page: Page):
page.goto(f"{live_server.url}/components/buttons/")
page.locator("button[id=id_add]").click()
assert_snapshot(page.screenshot(full_page=True))


def test_buttons__disabled_button(live_server, page: Page):
page.goto(f"{live_server.url}/components/buttons/")
assert page.locator("button[id=id_win]").is_disabled()
19 changes: 19 additions & 0 deletions tests/demo/test_checkboxes.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
from playwright.sync_api import Page


def test_checkboxes__layout(live_server, assert_snapshot, page: Page):
page.goto(f"{live_server.url}/components/checkboxes/")
assert_snapshot(page.screenshot(full_page=True))


def test_checkboxes__select_option_and_submit(live_server, assert_snapshot, page: Page):
page.goto(f"{live_server.url}/components/checkboxes/")
page.locator("input[id=id_method_1]").click()
page.locator("button[id=id_submit]").click()
assert_snapshot(page.screenshot(full_page=True))


def test_checkboxes__leave_blank_and_submit(live_server, assert_snapshot, page: Page):
page.goto(f"{live_server.url}/components/checkboxes/")
page.locator("button[id=id_submit]").click()
assert_snapshot(page.screenshot(full_page=True))
21 changes: 21 additions & 0 deletions tests/demo/test_date_input.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
from playwright.sync_api import Page


def test_date_input__layout(live_server, assert_snapshot, page: Page):
page.goto(f"{live_server.url}/components/date-input/")
assert_snapshot(page.screenshot(full_page=True))


def test_date_input__enter_date_and_submit(live_server, assert_snapshot, page: Page):
page.goto(f"{live_server.url}/components/date-input/")
page.locator("input[id=id_date_0]").fill("25")
page.locator("input[id=id_date_1]").fill("12")
page.locator("input[id=id_date_2]").fill("2024")
page.locator("button[id=id_submit]").click()
assert_snapshot(page.screenshot(full_page=True))


def test_date_input__leave_blank_and_submit(live_server, assert_snapshot, page: Page):
page.goto(f"{live_server.url}/components/date-input/")
page.locator("button[id=id_submit]").click()
assert_snapshot(page.screenshot(full_page=True))
19 changes: 19 additions & 0 deletions tests/demo/test_details.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
from playwright.sync_api import Page


def test_details__layout(live_server, assert_snapshot, page: Page):
page.goto(f"{live_server.url}/components/details/")
assert_snapshot(page.screenshot(full_page=True))


def test_details__show_summary(live_server, assert_snapshot, page: Page):
page.goto(f"{live_server.url}/components/details/")
page.locator("summary[class=govuk-details__summary]").click()
assert_snapshot(page.screenshot(full_page=True))


def test_details__hide_summary(live_server, assert_snapshot, page: Page):
page.goto(f"{live_server.url}/components/details/")
page.locator("summary[class=govuk-details__summary]").click()
page.locator("summary[class=govuk-details__summary]").click()
assert_snapshot(page.screenshot(full_page=True))
6 changes: 6 additions & 0 deletions tests/demo/test_fieldset.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
from playwright.sync_api import Page


def test_fieldset_component__layout(live_server, assert_snapshot, page: Page):
page.goto(f"{live_server.url}/components/fieldset/")
assert_snapshot(page.screenshot(full_page=True))
6 changes: 6 additions & 0 deletions tests/demo/test_file_upload.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
from playwright.sync_api import Page


def test_file_upload_component__layout(live_server, assert_snapshot, page: Page):
page.goto(f"{live_server.url}/components/file-upload/")
assert_snapshot(page.screenshot(full_page=True))
6 changes: 6 additions & 0 deletions tests/demo/test_inset.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
from playwright.sync_api import Page


def test_inset_component__layout(live_server, assert_snapshot, page: Page):
page.goto(f"{live_server.url}/components/inset/")
assert_snapshot(page.screenshot(full_page=True))
6 changes: 6 additions & 0 deletions tests/demo/test_panels.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
from playwright.sync_api import Page


def test_panel_component__layout(live_server, assert_snapshot, page: Page):
page.goto(f"{live_server.url}/components/panel/")
assert_snapshot(page.screenshot(full_page=True))
20 changes: 20 additions & 0 deletions tests/demo/test_radios.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
from playwright.sync_api import Page


def test_radios__layout(live_server, assert_snapshot, page: Page):
page.goto(f"{live_server.url}/components/radios/")
assert_snapshot(page.screenshot(full_page=True))


def test_radios__click_and_submit(live_server, assert_snapshot, page: Page):
page.goto(f"{live_server.url}/components/radios/")
page.locator("input[id=id_name_1]").click()
page.locator("input[id=id_method_2]").click()
page.locator("button[id=id_submit]").click()
assert_snapshot(page.screenshot(full_page=True))


def test_radios__leave_blank_and_submit(live_server, assert_snapshot, page: Page):
page.goto(f"{live_server.url}/components/radios/")
page.locator("button[id=id_submit]").click()
assert_snapshot(page.screenshot(full_page=True))
25 changes: 25 additions & 0 deletions tests/demo/test_radios_conditional.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
from playwright.sync_api import Page


def test_radios_conditional__layout(live_server, assert_snapshot, page: Page):
page.goto(f"{live_server.url}/components/conditional_radios/")
assert_snapshot(page.screenshot(full_page=True))


def test_radios_conditional__fill_hidden_and_submit(
live_server, assert_snapshot, page: Page
):
page.goto(f"{live_server.url}/components/conditional_radios/")
page.locator("input[id=id_method_1]").click()
page.locator("input[id=id_email_address]").fill("[email protected]")
page.locator("button[id=id_submit]").click()
assert_snapshot(page.screenshot(full_page=True))


def test_radios_conditional__show_hidden_and_submit(
live_server, assert_snapshot, page: Page
):
page.goto(f"{live_server.url}/components/conditional_radios/")
page.locator("input[id=id_method_1]").click()
page.locator("button[id=id_submit]").click()
assert_snapshot(page.screenshot(full_page=True))
13 changes: 13 additions & 0 deletions tests/demo/test_select.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
from playwright.sync_api import Page


def test_select__layout(live_server, assert_snapshot, page: Page):
page.goto(f"{live_server.url}/components/select/")
assert_snapshot(page.screenshot(full_page=True))


def test_select__select_item_and_submit(live_server, assert_snapshot, page: Page):
page.goto(f"{live_server.url}/components/select/")
page.locator("select[id=id_sort_by]").select_option("updated")
page.locator("button[id=id_submit]").click()
assert_snapshot(page.screenshot(full_page=True))
12 changes: 12 additions & 0 deletions tests/demo/test_tabs.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
from playwright.sync_api import Page


def test_tabs__layout(live_server, assert_snapshot, page: Page):
page.goto(f"{live_server.url}/components/tabs/")
assert_snapshot(page.screenshot(full_page=True))


def test_tabs__click_tab(live_server, assert_snapshot, page: Page):
page.goto(f"{live_server.url}/components/tabs/")
page.locator("a[id=tab_past-week]").click()
assert_snapshot(page.screenshot(full_page=True))
6 changes: 6 additions & 0 deletions tests/demo/test_tag.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
from playwright.sync_api import Page


def test_tag_component__layout(live_server, assert_snapshot, page: Page):
page.goto(f"{live_server.url}/components/tag/")
assert_snapshot(page.screenshot(full_page=True))
Loading

0 comments on commit 0b8c210

Please sign in to comment.