Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .github/workflows/stage-4-acceptance.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -129,6 +129,7 @@ jobs:
uses: actions/checkout@v5
- name: "Run accessibility test"
run: |
cp .env.example .env
make test-accessibility
- name: "Save result"
run: |
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,19 @@
from playwright.sync_api import expect
from axe_playwright_python.sync_playwright import Axe


def expect_back_link_to_have_url(page, url):
back_link = page.locator(".nhsuk-back-link")
expect(back_link).to_have_count(1)
expect(back_link).to_have_attribute("href", url)


def expect_no_accessibility_violations(page):
axe = Axe()
axe_results = axe.run(page)
violations_msg = (
f"Found the following accessibility violations: \n"
f"{axe_results.generate_snapshot()}"
)
assert axe_results.violations_count == 0, violations_msg

Original file line number Diff line number Diff line change
@@ -1,5 +1,10 @@
from playwright.sync_api import expect

def setup_participant(page, live_server_url):
participant_id = 'abc123'
page.goto(f"{live_server_url}/start")
fill_in_and_submit_participant_id(page, participant_id)

def fill_in_and_submit_participant_id(page, participant_id):
page.fill("input[name='participant_id']", participant_id)
page.click('text=Start now')
Expand Down
194 changes: 194 additions & 0 deletions lung_cancer_screening/core/tests/acceptance/test_accessibility.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,194 @@
import os

from django.contrib.staticfiles.testing import StaticLiveServerTestCase
from django.test import tag
from playwright.sync_api import sync_playwright

from .helpers.assertion_helpers import expect_no_accessibility_violations
from .helpers.user_interaction_helpers import setup_participant

@tag('accessibility')
class TestQuestionnaireAccessibility(StaticLiveServerTestCase):

@classmethod
def setUpClass(cls):
os.environ["DJANGO_ALLOW_ASYNC_UNSAFE"] = "true"
super().setUpClass()
cls.playwright = sync_playwright().start()
cls.browser = cls.playwright.chromium.launch()

@classmethod
def tearDownClass(cls):
super().tearDownClass()
cls.browser.close()
cls.playwright.stop()

def test_start_page_accessibility(self):
page = self.browser.new_page()
page.goto(f"{self.live_server_url}/start")
expect_no_accessibility_violations(page)

def test_start_page_errors_accessibility(self):
page = self.browser.new_page()
page.goto(f"{self.live_server_url}/start")
page.click("text=Start now")
expect_no_accessibility_violations(page)

def test_have_you_ever_smoked_page_accessibility(self):
page = self.browser.new_page()
setup_participant(page, self.live_server_url)
page.goto(f"{self.live_server_url}/have-you-ever-smoked")
expect_no_accessibility_violations(page)

def test_have_you_ever_smoked_page_errors_accessibility(self):
page = self.browser.new_page()
setup_participant(page, self.live_server_url)
page.goto(f"{self.live_server_url}/have-you-ever-smoked")
page.click("text=Continue")
expect_no_accessibility_violations(page)

def test_date_of_birth_page_accessibility(self):
page = self.browser.new_page()
setup_participant(page, self.live_server_url)
page.goto(f"{self.live_server_url}/date-of-birth")
expect_no_accessibility_violations(page)

def test_date_of_birth_page_errors_accessibility(self):
page = self.browser.new_page()
setup_participant(page, self.live_server_url)
page.goto(f"{self.live_server_url}/date-of-birth")
page.click("text=Continue")
expect_no_accessibility_violations(page)

def test_height_page_accessibility(self):
page = self.browser.new_page()
setup_participant(page, self.live_server_url)
page.goto(f"{self.live_server_url}/height")
expect_no_accessibility_violations(page)

def test_height_page_errors_accessibility(self):
page = self.browser.new_page()
setup_participant(page, self.live_server_url)
page.goto(f"{self.live_server_url}/height")
page.click("text=Continue")
expect_no_accessibility_violations(page)

def test_height_imperial_page_accessibility(self):
page = self.browser.new_page()
setup_participant(page, self.live_server_url)
page.goto(f"{self.live_server_url}/height?unit=imperial")
expect_no_accessibility_violations(page)

def test_weight_page_accessibility(self):
page = self.browser.new_page()
setup_participant(page, self.live_server_url)
page.goto(f"{self.live_server_url}/weight")
expect_no_accessibility_violations(page)

def test_weight_page_errors_accessibility(self):
page = self.browser.new_page()
setup_participant(page, self.live_server_url)
page.goto(f"{self.live_server_url}/weight")
page.click("text=Continue")
expect_no_accessibility_violations(page)

def test_weight_imperial_page_accessibility(self):
page = self.browser.new_page()
setup_participant(page, self.live_server_url)
page.goto(f"{self.live_server_url}/weight?unit=imperial")
expect_no_accessibility_violations(page)

def test_weight_imperial_page_errors_accessibility(self):
page = self.browser.new_page()
setup_participant(page, self.live_server_url)
page.goto(f"{self.live_server_url}/weight?unit=imperial")
page.click("text=Continue")
expect_no_accessibility_violations(page)

def test_sex_at_birth_page_accessibility(self):
page = self.browser.new_page()
setup_participant(page, self.live_server_url)
page.goto(f"{self.live_server_url}/sex-at-birth")
expect_no_accessibility_violations(page)

def test_sex_at_birth_page_errors_accessibility(self):
page = self.browser.new_page()
setup_participant(page, self.live_server_url)
page.goto(f"{self.live_server_url}/sex-at-birth")
page.click("text=Continue")
expect_no_accessibility_violations(page)

def test_gender_page_accessibility(self):
page = self.browser.new_page()
setup_participant(page, self.live_server_url)
page.goto(f"{self.live_server_url}/gender")
expect_no_accessibility_violations(page)

def test_gender_page_errors_accessibility(self):
page = self.browser.new_page()
setup_participant(page, self.live_server_url)
page.goto(f"{self.live_server_url}/gender")
page.click("text=Continue")
expect_no_accessibility_violations(page)

def test_ethnicity_page_accessibility(self):
page = self.browser.new_page()
setup_participant(page, self.live_server_url)
page.goto(f"{self.live_server_url}/ethnicity")
expect_no_accessibility_violations(page)

def test_ethnicity_page_errors_accessibility(self):
page = self.browser.new_page()
setup_participant(page, self.live_server_url)
page.goto(f"{self.live_server_url}/ethnicity")
page.click("text=Continue")
expect_no_accessibility_violations(page)

# def test_education_page_accessibility(self):
# page = self.browser.new_page()
# setup_participant(page, self.live_server_url)
# page.goto(f"{self.live_server_url}/education")
# expect_no_accessibility_violations(page)

# def test_respiratory_conditions_page_accessibility(self):
# page = self.browser.new_page()
# setup_participant(page, self.live_server_url)
# page.goto(f"{self.live_server_url}/respiratory-conditions")
# expect_no_accessibility_violations(page)

def test_asbestos_exposure_page_errors_accessibility(self):
page = self.browser.new_page()
setup_participant(page, self.live_server_url)
page.goto(f"{self.live_server_url}/asbestos-exposure")
page.click("text=Continue")
expect_no_accessibility_violations(page)

def test_asbestos_exposure_page_accessibility(self):
page = self.browser.new_page()
setup_participant(page, self.live_server_url)
page.goto(f"{self.live_server_url}/asbestos-exposure")
expect_no_accessibility_violations(page)

# def test_cancer_diagnosis_page_accessibility(self):
# page = self.browser.new_page()
# setup_participant(page, self.live_server_url)
# page.goto(f"{self.live_server_url}/cancer-diagnosis")
# expect_no_accessibility_violations(page)

# def test_family_history_lung_cancer_page_accessibility(self):
# page = self.browser.new_page()
# setup_participant(page, self.live_server_url)
# page.goto(f"{self.live_server_url}/family-history-lung-cancer")
# expect_no_accessibility_violations(page)

def test_responses_page_accessibility(self):
page = self.browser.new_page()
setup_participant(page, self.live_server_url)
page.goto(f"{self.live_server_url}/responses")
expect_no_accessibility_violations(page)

def test_your_results_page_accessibility(self):
page = self.browser.new_page()
setup_participant(page, self.live_server_url)
page.goto(f"{self.live_server_url}/your-results")
expect_no_accessibility_violations(page)
4 changes: 4 additions & 0 deletions lung_cancer_screening/nhsuk_forms/choice_field.py
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@ def __init__(
*args,
hint=None,
label_classes="nhsuk-fieldset__legend--m",
label_is_page_heading=False,
classes=None,
**kwargs,
):
Expand All @@ -65,6 +66,7 @@ def __init__(
self.hint = hint
self.classes = classes
self.label_classes = label_classes
self.label_is_page_heading = label_is_page_heading

super().__init__(*args, **kwargs)

Expand Down Expand Up @@ -97,6 +99,7 @@ def __init__(
*args,
hint=None,
label_classes="nhsuk-fieldset__legend--m",
label_is_page_heading=False,
classes=None,
**kwargs,
):
Expand All @@ -105,5 +108,6 @@ def __init__(
self.hint = hint
self.classes = classes
self.label_classes = label_classes
self.label_is_page_heading = label_is_page_heading

super().__init__(*args, **kwargs)
2 changes: 1 addition & 1 deletion lung_cancer_screening/nhsuk_forms/jinja2/date-input.jinja
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
"legend": {
"text": field.label,
"classes": "nhsuk-fieldset__legend--m",
"isPageHeading": false
"isPageHeading": unbound_field.label_is_page_heading
}
} if field.label,
"errorMessage": error_message,
Expand Down
3 changes: 2 additions & 1 deletion lung_cancer_screening/nhsuk_forms/jinja2/radios.jinja
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,8 @@
"fieldset": {
"legend": {
"text": field.label,
"classes": unbound_field.label_classes
"classes": unbound_field.label_classes,
"isPageHeading": unbound_field.label_is_page_heading
}
} if field.use_fieldset else none,
"errorMessage": error_message,
Expand Down
1 change: 1 addition & 0 deletions lung_cancer_screening/nhsuk_forms/split_date_field.py
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,7 @@ class SplitDateField(forms.MultiValueField):
def __init__(self, *args, **kwargs):
max_value = kwargs.pop("max_value", datetime.date.today())
min_value = kwargs.pop("min_value", datetime.date(1900, 1, 1))
self.label_is_page_heading = kwargs.pop("label_is_page_heading", False)
self.hint = kwargs.pop("hint", None)

day_bounds_error = gettext("Day should be between 1 and 31.")
Expand Down
36 changes: 36 additions & 0 deletions lung_cancer_screening/nhsuk_forms/tests/unit/test_choice_field.py
Original file line number Diff line number Diff line change
Expand Up @@ -38,3 +38,39 @@ def test_renders_nhs_radios(self):
</div>
""",
)

def test_renders_labels_as_headers_when_true(self):
class TestForm(Form):
field = ChoiceField(
label="Abc",
label_classes="app-abc",
label_is_page_heading=True,
choices=(("a", "A"), ("b", "B")),
hint="Pick either one",
)

self.assertHTMLEqual(
TestForm()["field"].as_field_group(),
"""
<div class="nhsuk-form-group">
<fieldset aria-describedby="id_field-hint" class="nhsuk-fieldset">
<legend class="nhsuk-fieldset__legend app-abc">
<h1 class="nhsuk-fieldset__heading">Abc</h1>
</legend>
<div class="nhsuk-hint" id="id_field-hint">
Pick either one
</div>
<div class="nhsuk-radios" data-module="nhsuk-radios">
<div class="nhsuk-radios__item">
<input class="nhsuk-radios__input" id="id_field" name="field" type="radio" value="a">
<label class="nhsuk-label nhsuk-radios__label" for="id_field">A</label>
</div>
<div class="nhsuk-radios__item">
<input class="nhsuk-radios__input" id="id_field-2" name="field" type="radio" value="b">
<label class="nhsuk-label nhsuk-radios__label" for="id_field-2">B</label>
</div>
</div>
</fieldset>
</div>
""",
)
Original file line number Diff line number Diff line change
Expand Up @@ -181,6 +181,54 @@ class TestForm(Form):
""",
)

def test_render_labels_as_headers_when_true(self):
class TestForm(Form):
date = SplitDateField(
max_value=datetime.date(2026, 12, 31),
label_is_page_heading=True,
)

f = TestForm()

self.assertHTMLEqual(
str(f),
"""<div>
<div class="nhsuk-form-group">
<fieldset class="nhsuk-fieldset" role="group">
<legend class="nhsuk-fieldset__legend nhsuk-fieldset__legend--m">
<h1 class="nhsuk-fieldset__heading">Date</h1>
</legend>
<div class="nhsuk-date-input">
<div class="nhsuk-date-input__item">
<div class="nhsuk-form-group">
<label class="nhsuk-label nhsuk-date-input__label" for="id_date">
Day
</label>
<input class="nhsuk-input nhsuk-date-input__input nhsuk-input--width-2" id="id_date" name="date_0" type="text" inputmode="numeric">
</div>
</div>
<div class="nhsuk-date-input__item">
<div class="nhsuk-form-group">
<label class="nhsuk-label nhsuk-date-input__label" for="id_date_1">
Month
</label>
<input class="nhsuk-input nhsuk-date-input__input nhsuk-input--width-2" id="id_date_1" name="date_1" type="text" inputmode="numeric">
</div>
</div>
<div class="nhsuk-date-input__item">
<div class="nhsuk-form-group">
<label class="nhsuk-label nhsuk-date-input__label" for="id_date_2">
Year
</label>
<input class="nhsuk-input nhsuk-date-input__input nhsuk-input--width-4" id="id_date_2" name="date_2" type="text" inputmode="numeric">
</div>
</div>
</div>
</fieldset>
</div></div>
""",
)

def test_form_cleaned_data(self):
class TestForm(Form):
date = SplitDateField(max_value=datetime.date(2026, 12, 31))
Expand Down
Loading
Loading