diff --git a/.github/workflows/staging-scheduled-deploy.yml b/.github/workflows/staging-scheduled-deploy.yml index 35657a866..4ea67d849 100644 --- a/.github/workflows/staging-scheduled-deploy.yml +++ b/.github/workflows/staging-scheduled-deploy.yml @@ -1,49 +1,49 @@ ---- -name: Scheduled Deploy From Main to Staging -on: - # schedule: - # Invoke every Mon-Sat - # - cron: '0 10 * * 1-6' - workflow_dispatch: null - -jobs: - trivy-scan: - uses: ./.github/workflows/trivy.yml - secrets: inherit - permissions: - contents: read - packages: write - actions: read - security-events: write - - build-container: - needs: - - trivy-scan - uses: ./.github/workflows/build-docker-container.yml - secrets: inherit - permissions: - contents: read - packages: write - with: - docker-name: fac - image-name: web-container - repo-name: gsa-tts/fac - work-dir: ./backend - - testing: - name: Run Django, Lighthouse, a11y and lint - needs: - - build-container - uses: ./.github/workflows/testing-from-ghcr.yml - secrets: inherit - - linting: - uses: ./.github/workflows/linting.yml - secrets: inherit - - create-pr: - needs: - - testing - name: Create Pull Request to Staging - uses: ./.github/workflows/create-pull-request-to-staging.yml - secrets: inherit +--- +name: Scheduled Deploy From Main to Staging +on: + # schedule: + # Invoke every Mon-Sat + # - cron: '0 10 * * 1-6' + workflow_dispatch: null + +jobs: + trivy-scan: + uses: ./.github/workflows/trivy.yml + secrets: inherit + permissions: + contents: read + packages: write + actions: read + security-events: write + + build-container: + needs: + - trivy-scan + uses: ./.github/workflows/build-docker-container.yml + secrets: inherit + permissions: + contents: read + packages: write + with: + docker-name: fac + image-name: web-container + repo-name: gsa-tts/fac + work-dir: ./backend + + testing: + name: Run Django and lint + needs: + - build-container + uses: ./.github/workflows/testing-from-ghcr.yml + secrets: inherit + + linting: + uses: ./.github/workflows/linting.yml + secrets: inherit + + create-pr: + needs: + - testing + name: Create Pull Request to Staging + uses: ./.github/workflows/create-pull-request-to-staging.yml + secrets: inherit diff --git a/.github/workflows/testing-from-build.yml b/.github/workflows/testing-from-build.yml index 7bf1de547..61b6c2e33 100644 --- a/.github/workflows/testing-from-build.yml +++ b/.github/workflows/testing-from-build.yml @@ -78,10 +78,8 @@ jobs: # run: | # touch .env # docker compose -f docker-compose.yml up -d - # - # - name: Run Lighthouse CI and pa11y + # - name: Run A11y tests # working-directory: ./backend # run: | # sudo npm ci - # npm run test:a11y:lighthouse - # npm run test:a11y:pa11y + # npx cypress run --spec "cypress/e2e/accessibility.cy.js" diff --git a/.github/workflows/testing-from-ghcr.yml b/.github/workflows/testing-from-ghcr.yml index 2ba353bd0..98379c5a1 100644 --- a/.github/workflows/testing-from-ghcr.yml +++ b/.github/workflows/testing-from-ghcr.yml @@ -80,9 +80,8 @@ jobs: # run: | # touch .env # docker compose -f docker-compose.yml up -d - # - name: Run Lighthouse CI and pa11y + # - name: Run A11y tests # working-directory: ./backend # run: | # sudo npm ci - # npm run test:a11y:lighthouse - # npm run test:a11y:pa11y + # npx cypress run --spec "cypress/e2e/accessibility.cy.js" diff --git a/.gitignore b/.gitignore index 3bd252bac..0e95d7bbf 100644 --- a/.gitignore +++ b/.gitignore @@ -166,8 +166,6 @@ node_modules/ backend/static/compiled staticfiles/ -# pa11y_tests/screenshots/*.png - # Things that folks might want to have in their local directories # that don't need to be checked in .local/ diff --git a/.pa11yci b/.pa11yci deleted file mode 100644 index 31f8a2050..000000000 --- a/.pa11yci +++ /dev/null @@ -1,21 +0,0 @@ -{ - "urls": [ - "http://localhost:8000/", - "http://localhost:8000/report_submission/eligibility", - "http://localhost:8000/report_submission/auditeeinfo", - "http://localhost:8000/report_submission/accessandsubmission", - "http://localhost:8000/audit" - ], - "defaults": { - "timeout": 60000, - "standard": "WCAG2AA", - "runners": [ - "htmlcs", - "axe" - ], - "hideElements": ".pa11y-ignore, .usa-select, .usa-step-indicator__segment-label, .is-hidden, #upload-worksheet, .usa-file-input__instructions, #my-submissions", - "chromeLaunchConfig": { - "executablePath": "/usr/bin/google-chrome" - } - } -} diff --git a/backend/.stylelintignore b/backend/.stylelintignore index 3c49cb3ff..2470d7044 100644 --- a/backend/.stylelintignore +++ b/backend/.stylelintignore @@ -1,3 +1,2 @@ **/uswds static/compiled/** -.lighthouseci/** diff --git a/backend/audit/fixtures/dissemination.py b/backend/audit/fixtures/dissemination.py new file mode 100644 index 000000000..0aac2b551 --- /dev/null +++ b/backend/audit/fixtures/dissemination.py @@ -0,0 +1,38 @@ +""" +Fixtures for dissemination models. +We want a few example submissions for testing front end accessibility. +""" + +import logging +from model_bakery import baker +from dissemination.models import ( + General, + FederalAward, +) + +logger = logging.getLogger(__name__) + +sac_info_without_findings = { + "auditee_name": "Test SAC - No findings", + "auditee_uei": "GSA_TESTDATA", + "audit_year": "2024", + "report_id": "2024-06-TSTDAT-0000000001", +} + + +def load_dissemination(): + """ + Generate an example SAC with an accompanying award. + """ + logger.info("Creating example SACs for dissemination.") + + report_id_no_findings = sac_info_without_findings.get("report_id", "") + sac_no_findings = General.objects.filter( + report_id=report_id_no_findings, + ).first() + if sac_no_findings is None: + general_no_findings = baker.make(General, **sac_info_without_findings) + baker.make(FederalAward, report_id=general_no_findings) + logger.info("Created SAC example %s", report_id_no_findings) + else: + logger.info("SAC %s already exists, skipping.", report_id_no_findings) diff --git a/backend/audit/fixtures/single_audit_checklist.py b/backend/audit/fixtures/single_audit_checklist.py index 2c5fef00d..abde66455 100644 --- a/backend/audit/fixtures/single_audit_checklist.py +++ b/backend/audit/fixtures/single_audit_checklist.py @@ -119,12 +119,13 @@ def fake_auditee_certification(): return data_step_1, data_step_2 -def _create_sac(user, auditee_name): +def _create_sac(user, auditee_name, submission_status="in_progress"): """Create a single example SAC.""" SingleAuditChecklist = apps.get_model("audit.SingleAuditChecklist") sac = SingleAuditChecklist.objects.create( submitted_by=user, general_information=_fake_general_information(auditee_name), + submission_status=submission_status, ) Access = apps.get_model("audit.Access") @@ -134,6 +135,18 @@ def _create_sac(user, auditee_name): email=user.email, role="editor", ) + Access.objects.create( + sac=sac, + user=user, + email=user.email, + role="certifying_auditor_contact", + ) + Access.objects.create( + sac=sac, + user=user, + email=user.email, + role="certifying_auditee_contact", + ) logger.info("Created single audit checklist %s", sac) return sac @@ -193,9 +206,10 @@ def _post_create_federal_awards(this_sac, this_user): SACS = [ {"auditee_name": "SAC in progress"}, { - "auditee_name": "Federal awards submitted", - "post_create_callable": _post_create_federal_awards, + "auditee_name": "SAC ready for certification", + "submission_status": "ready_for_certification", }, + {"auditee_name": "SAC fully submitted", "submission_status": "disseminated"}, ] @@ -205,12 +219,13 @@ def _load_single_audit_checklists_for_user(user): SingleAuditChecklist = apps.get_model("audit.SingleAuditChecklist") for item_info in SACS: auditee_name = item_info["auditee_name"] + submission_status = item_info.get("submission_status", "in_progress") sac = SingleAuditChecklist.objects.filter( submitted_by=user, general_information__auditee_name=auditee_name ).first() if sac is None: # need to make this object - sac = _create_sac(user, auditee_name) + sac = _create_sac(user, auditee_name, submission_status) def load_single_audit_checklists(): diff --git a/backend/audit/management/commands/load_fixtures.py b/backend/audit/management/commands/load_fixtures.py index dfa0196cb..9341cf878 100644 --- a/backend/audit/management/commands/load_fixtures.py +++ b/backend/audit/management/commands/load_fixtures.py @@ -13,7 +13,7 @@ load_single_audit_checklists, load_single_audit_checklists_for_email_address, ) - +from audit.fixtures.dissemination import load_dissemination from users.fixtures import load_users logger = logging.getLogger(__name__) @@ -33,6 +33,7 @@ def handle(self, *args, **options): if not options.get("email_addresses"): load_users() load_single_audit_checklists() + load_dissemination() logger.info("All fixtures loaded.") else: # We assume each arg is an email address: diff --git a/backend/audit/mixins.py b/backend/audit/mixins.py index 626836611..19387a0c1 100644 --- a/backend/audit/mixins.py +++ b/backend/audit/mixins.py @@ -4,6 +4,7 @@ from django.http.request import HttpRequest from django.http.response import HttpResponse from django.core.exceptions import PermissionDenied +from django.conf import settings from .models import Access, SingleAuditChecklist @@ -50,7 +51,7 @@ def dispatch(self, request: HttpRequest, *args: Any, **kwargs: Any) -> HttpRespo sac = SingleAuditChecklist.objects.get(report_id=kwargs["report_id"]) - if not has_access(sac, request.user): + if not has_access(sac, request.user) and not settings.DISABLE_AUTH: raise PermissionDenied(PERMISSION_DENIED_MESSAGE) except SingleAuditChecklist.DoesNotExist: raise PermissionDenied(PERMISSION_DENIED_MESSAGE) diff --git a/backend/audit/templates/audit/audit-info-form.html b/backend/audit/templates/audit/audit-info-form.html index b57dd2930..b589c4318 100644 --- a/backend/audit/templates/audit/audit-info-form.html +++ b/backend/audit/templates/audit/audit-info-form.html @@ -244,6 +244,7 @@

Federal programs

Federal programs - Cancel + Cancel diff --git a/backend/audit/templates/audit/auditee-certification-step-1.html b/backend/audit/templates/audit/auditee-certification-step-1.html index be3cf95d4..c01de146d 100644 --- a/backend/audit/templates/audit/auditee-certification-step-1.html +++ b/backend/audit/templates/audit/auditee-certification-step-1.html @@ -9,8 +9,8 @@ method="post"> {% csrf_token %}
- Auditee certification -

Check the box next to each item confirm your report meets the requirements.

+

Auditee certification

+

Check the box next to each item confirm your report meets the requirements.

You must check all boxes must be checked in order to certify your single audit.

{% csrf_token %}
- Auditor certification -

Check the box next to each item confirm your report meets the requirements.

+

Auditor certification

+

Check the box next to each item confirm your report meets the requirements.

You must check all boxes in order to certify your single audit.

No errors were found. Proceed to certification {% endif %} - Cancel + Cancel
diff --git a/backend/audit/templates/audit/cross-validation/cross-validation.html b/backend/audit/templates/audit/cross-validation/cross-validation.html index b224be20a..95790bc78 100644 --- a/backend/audit/templates/audit/cross-validation/cross-validation.html +++ b/backend/audit/templates/audit/cross-validation/cross-validation.html @@ -6,7 +6,7 @@
{% csrf_token %}
- Pre-submission validation +

Pre-submission validation

Check your workbooks to confirm you entered your data correctly. This tool also cross-validates the workbooks against each other.

@@ -19,8 +19,7 @@
- Cancel + Cancel
diff --git a/backend/audit/templates/audit/cross-validation/ready-for-certification.html b/backend/audit/templates/audit/cross-validation/ready-for-certification.html index e8affcedf..70911c72a 100644 --- a/backend/audit/templates/audit/cross-validation/ready-for-certification.html +++ b/backend/audit/templates/audit/cross-validation/ready-for-certification.html @@ -6,7 +6,7 @@
{% csrf_token %}
- Lock for certification +

Lock for certification

You are now ready to lock your single audit submission for auditee and auditor certification. Each must review and certify the documents before they can be submitted to the FAC.

@@ -20,8 +20,7 @@ Cancel + href="{% url 'audit:SubmissionProgress' report_id %}">Cancel
diff --git a/backend/audit/templates/audit/manage-submission-change-access.html b/backend/audit/templates/audit/manage-submission-change-access.html index 58035dcc1..61216e7dd 100644 --- a/backend/audit/templates/audit/manage-submission-change-access.html +++ b/backend/audit/templates/audit/manage-submission-change-access.html @@ -51,8 +51,7 @@

Add Audit Editor

Cancel + href="{% url 'audit:ManageSubmission' report_id %}">Cancel diff --git a/backend/audit/templates/audit/manage-submission.html b/backend/audit/templates/audit/manage-submission.html index abb930c47..8d79d5b70 100644 --- a/backend/audit/templates/audit/manage-submission.html +++ b/backend/audit/templates/audit/manage-submission.html @@ -5,7 +5,7 @@
{% csrf_token %}
- Manage user roles +

Manage user roles

{% if auditee_name %}

{{ auditee_name }}

{% endif %}

@@ -67,11 +67,9 @@

(*) Indicates user has not logged in to view this submission.

Add editor + href="{{ add_editor_url }}">Add editor Return to checklist + href="{{ progress_url }}">Return to checklist
{% include "audit-metadata.html" %} diff --git a/backend/audit/templates/audit/my_submissions.html b/backend/audit/templates/audit/my_submissions.html index b4d0130d8..3bbd72dea 100644 --- a/backend/audit/templates/audit/my_submissions.html +++ b/backend/audit/templates/audit/my_submissions.html @@ -12,7 +12,7 @@

Single audit submissions

{% if data.in_progress_audits %}

Audits in progress

- +
@@ -56,7 +56,7 @@

Audits in progress

{% if data.completed_audits %}

Completed audits

-
The audits listed below are in progress, and you are still able to edit them before submission. Select an audit by clicking on its Status to make changes and continue the submission process.
+

The audits listed below are with the FAC for processing and can't be edited.

    diff --git a/backend/audit/templates/audit/submission_checklist/key-info-certification.html b/backend/audit/templates/audit/submission_checklist/key-info-certification.html index 6cef5548c..66c3c30bc 100644 --- a/backend/audit/templates/audit/submission_checklist/key-info-certification.html +++ b/backend/audit/templates/audit/submission_checklist/key-info-certification.html @@ -1,8 +1,8 @@ {% comment %} Key information (certification steps) {% endcomment %} -
    +
  • + aria-label="summary-box-certification">
    Key information @@ -15,4 +15,4 @@
    -
  • + diff --git a/backend/audit/templates/audit/submission_checklist/key-info-pre-validation.html b/backend/audit/templates/audit/submission_checklist/key-info-pre-validation.html index 8a2c980fb..7fd3c3c53 100644 --- a/backend/audit/templates/audit/submission_checklist/key-info-pre-validation.html +++ b/backend/audit/templates/audit/submission_checklist/key-info-pre-validation.html @@ -1,8 +1,8 @@ {% comment %} Key information (certification steps) {% endcomment %} -
    +
  • + aria-label="summary-box-pre-validation">
    Key information @@ -13,4 +13,4 @@
    -
  • \ No newline at end of file + \ No newline at end of file diff --git a/backend/audit/templates/audit/submission_checklist/key-info-preliminary.html b/backend/audit/templates/audit/submission_checklist/key-info-preliminary.html index 4296d3428..de83bafa7 100644 --- a/backend/audit/templates/audit/submission_checklist/key-info-preliminary.html +++ b/backend/audit/templates/audit/submission_checklist/key-info-preliminary.html @@ -1,7 +1,7 @@ -
    +
  • + aria-label="summary-box-preliminary">
    Key information @@ -16,4 +16,4 @@
    -
  • \ No newline at end of file + \ No newline at end of file diff --git a/backend/audit/templates/audit/submission_checklist/preview-button-group.html b/backend/audit/templates/audit/submission_checklist/preview-button-group.html index 0de190121..c1a47c0a3 100644 --- a/backend/audit/templates/audit/submission_checklist/preview-button-group.html +++ b/backend/audit/templates/audit/submission_checklist/preview-button-group.html @@ -7,7 +7,7 @@ 3. The unlock page {% endcomment %} - \ No newline at end of file + \ No newline at end of file diff --git a/backend/audit/templates/audit/submission_checklist/submission-checklist.html b/backend/audit/templates/audit/submission_checklist/submission-checklist.html index 10afa5965..245a8dc5c 100644 --- a/backend/audit/templates/audit/submission_checklist/submission-checklist.html +++ b/backend/audit/templates/audit/submission_checklist/submission-checklist.html @@ -106,10 +106,10 @@ {% comment %} Submit to the FAC for processing {% endcomment %} {% if submission.enabled %} -
    +
  • + aria-label="summary-box-submission">

    @@ -121,12 +121,12 @@

    -
  • + {% else %} -
    +
  • + aria-label="summary-box-submission">

    @@ -138,7 +138,7 @@

    -
  • + {% endif %}
diff --git a/backend/audit/templates/audit/upload-report.html b/backend/audit/templates/audit/upload-report.html index 2dbea8af3..f772c2606 100644 --- a/backend/audit/templates/audit/upload-report.html +++ b/backend/audit/templates/audit/upload-report.html @@ -13,16 +13,16 @@

Upload single audit report package

There were errors when attempting to submit the form. Scroll down for more details. {% endif %} -
    -
    - {% csrf_token %} -
    + + {% csrf_token %} +
    +
    1. -

      Formatting requirements:

      +

      Formatting requirements:

      • Less than 30MB
      • Unlocked with no password requirements
      • @@ -32,13 +32,13 @@

        Formatting requirements:

    2. -

      Remove all Personally Identifiable Information (PII)

      +

      Remove all Personally Identifiable Information (PII)

      PII includes but is not limited to: Social Security Numbers, account numbers, vehicle identification numbers, copies of cancelled checks, student names, dates of birth, personal addresses or personal phone numbers.

    3. -

      Component page number

      +

      Component page number

      Each required component on the checklist must have a numeric page number. Enter the starting PDF page number for each of the components listed below. * indicates a required field.

      @@ -56,7 +56,7 @@

      Component page number

      {% endif %} Component page number
    4. -

      {% if already_submitted %}Re-upload{% else %}Upload{% endif %} your PDF

      +

      {% if already_submitted %}Re-upload{% else %}Upload{% endif %} your PDF

      Select a compliant PDF to upload. *

      @@ -84,7 +84,8 @@

      {% if already_submitted %}Re-upload{% else type="file" /> {{ form.errors.upload_report | striptags }}

    5. -
    +
+ {% if already_submitted %}

A file has already been uploaded for this section. A successful reupload will overwrite your previous submission. @@ -96,10 +97,8 @@

{% if already_submitted %}Re-upload{% else - Cancel - - + Cancel + {% include "audit-metadata.html" %} diff --git a/backend/audit/views/home.py b/backend/audit/views/home.py index ce30f60ef..cf86c565b 100644 --- a/backend/audit/views/home.py +++ b/backend/audit/views/home.py @@ -1,6 +1,7 @@ -from django.views import generic +from django.conf import settings from django.shortcuts import render, redirect from django.urls import reverse +from django.views import generic # class based views for posts @@ -13,9 +14,9 @@ class Home(generic.View): """ def get(self, request, *args, **kwargs): - if request.user.is_authenticated: + if request.user.is_authenticated and not settings.DISABLE_AUTH: url = reverse("audit:MySubmissions") return redirect(url) template_name = "home.html" - extra_context = {} + extra_context = {"DISABLE_AUTH": settings.DISABLE_AUTH} return render(request, template_name, extra_context) diff --git a/backend/census_historical_migration/test_general_information_xforms.py b/backend/census_historical_migration/test_general_information_xforms.py index c2ef1f585..b00a92f14 100644 --- a/backend/census_historical_migration/test_general_information_xforms.py +++ b/backend/census_historical_migration/test_general_information_xforms.py @@ -299,9 +299,7 @@ def test_missing_auditee_contact_name(self): class TestXformReplaceEmptyOrInvalidUEIs(SimpleTestCase): - class MockAuditHeader: - def __init__(self, UEI): self.UEI = UEI diff --git a/backend/cypress/e2e/accessibility.cy.js b/backend/cypress/e2e/accessibility.cy.js new file mode 100644 index 000000000..6c986c623 --- /dev/null +++ b/backend/cypress/e2e/accessibility.cy.js @@ -0,0 +1,151 @@ +import { terminalLog } from '../support/log-functions'; + +const screen_sizes = ['iphone-6', ['iphone-6', 'landscape'], [3840, 2160]]; + +function test_check_a11y(url, pageName, report_id) { + if (report_id) { + it(`Tests the full ${pageName} page for all screen sizes`, () => { + check_a11y(`${url}${report_id}`, 'eligibility'); + }); + } else { + it(`Tests the full ${pageName} page for all screen sizes`, () => { + check_a11y(url); + }); + } +} + +function check_a11y(url) { + cy.visit(url); + cy.injectAxe(); + screen_sizes.forEach((size) => { + if (Cypress._.isArray(size)) { + cy.viewport(size[0], size[1]); + } else { + cy.viewport(size); + } + cy.checkA11y(null, null, terminalLog); + }); +} + +describe('A11y Testing on Home Page', () => { + test_check_a11y('/', 'home'); + + it('Tests the pop out primary nav', () => { + cy.visit('/'); + cy.injectAxe(); + cy.get('.usa-menu-btn').contains('Menu').click(); + cy.checkA11y(null, null, terminalLog); + }); + + // Log in modal fails contrast tests with the darker background, which is expected. + // it('Tests the log in modal', () => { + // cy.get('[aria-controls="login-modal"]').contains("Submit an audit").click(); + // cy.checkA11y(null, null, terminalLog) + // }) +}); + +describe('A11y Testing on search pages', () => { + before(() => { + cy.visit('/dissemination/search/'); + cy.get('label').contains('All years').click(); + cy.get('[id="search-form"]').submit(); + cy.get('tbody > :nth-child(1) > td > a') + .invoke('attr', 'href') + .as('summary_url'); + }); + + it(`Tests the summary page for all screen sizes`, () => { + cy.get('@summary_url').then((val) => { + check_a11y(val); + }); + }); + + test_check_a11y('/dissemination/search/', 'basic search'); + test_check_a11y('/dissemination/search/advanced/', 'advanced search'); +}); + +describe('A11y Testing on pre-submission pages', () => { + test_check_a11y('/audit/', 'my submissions'); + test_check_a11y('/report_submission/eligibility/', 'eligibility'); + test_check_a11y('/report_submission/auditeeinfo/', 'auditee info'); + test_check_a11y( + '/report_submission/accessandsubmission/', + 'submission access' + ); +}); + +describe('A11y tests on an in progress report', () => { + before(() => { + cy.visit('/audit'); + cy.get('tr') + .contains('In Progress') + .parent() + .parent() + .contains('GSAFAC') + .invoke('text') + .as('report_id'); + }); + + it(`Tests submission pages for all screen sizes`, () => { + cy.get('@report_id').then((val) => { + // Submission items: + check_a11y(`/audit/submission-progress/${val}`); + check_a11y(`/report_submission/general-information/${val}`); + check_a11y(`/audit/audit-info/${val}`); + check_a11y(`/audit/upload-report/${val}`); + check_a11y(`/report_submission/federal-awards/${val}`); + // Cross validation: + check_a11y(`/audit/cross-validation/${val}`); + check_a11y(`/audit/ready-for-certification/${val}`); + // Access Management: + check_a11y(`/audit/manage-submission/${val}`); + check_a11y(`/audit/manage-submission/add-editor/${val}`); + check_a11y(`/audit/manage-submission/auditee-certifying-official/${val}`); + check_a11y(`/audit/manage-submission/auditor-certifying-official/${val}`); + }); + }); +}); + +describe('A11y tests on a ready for certification report', () => { + before(() => { + cy.visit('/audit'); + cy.get('tr') + .contains('Ready for Certification') + .parent() + .parent() + .contains('GSAFAC') + .invoke('text') + .as('report_id'); + }); + + it(`Tests submission pages for all screen sizes after locking for certification`, () => { + cy.get('@report_id').then((val) => { + // Check checklist after locking for certification: + check_a11y(`/audit/submission-progress/${val}`); + // Certification and final submission pages: + check_a11y(`/audit/auditor-certification/${val}`); + check_a11y(`/audit/auditee-certification/${val}`); + check_a11y(`/audit/submission/${val}`); + }); + }); +}); + +describe('A11y tests on a fully submitted report', () => { + before(() => { + cy.visit('/audit'); + cy.get('tr') + .contains('Accepted') + .parent() + .parent() + .contains('GSAFAC') + .invoke('text') + .as('report_id'); + }); + + it(`Tests submission checklist for all screen sizes after submission`, () => { + cy.get('@report_id').then((val) => { + // Check the checklist after submission: + check_a11y(`/audit/submission-progress/${val}`); + }); + }); +}); diff --git a/backend/cypress/plugins/index.js b/backend/cypress/plugins/index.js index f39f3dcb3..139f4c51c 100644 --- a/backend/cypress/plugins/index.js +++ b/backend/cypress/plugins/index.js @@ -18,6 +18,17 @@ // eslint-disable-next-line no-unused-vars module.exports = (on, config) => { on("task", { - generateOTP: require("cypress-otp") + generateOTP: require("cypress-otp"), + log(message) { + console.log(message) + + return null + }, + table(message) { + console.table(message) + + return null + } }); + } diff --git a/backend/cypress/support/commands.js b/backend/cypress/support/commands.js index 19aa4caff..439c4e177 100644 --- a/backend/cypress/support/commands.js +++ b/backend/cypress/support/commands.js @@ -23,8 +23,6 @@ // // -- This will overwrite an existing command -- // Cypress.Commands.overwrite('visit', (originalFn, url, options) => { ... }) -//import '@cypress-audit/lighthouse/commands'; -//import '@cypress-audit/pa11y/commands'; import 'cypress-file-upload'; import { testLoginGovLogin } from './login-gov.js'; import { testLogoutGov } from './logout-gov.js'; diff --git a/backend/cypress/support/e2e.js b/backend/cypress/support/e2e.js index 8e86ba548..285ae4e4c 100644 --- a/backend/cypress/support/e2e.js +++ b/backend/cypress/support/e2e.js @@ -14,6 +14,8 @@ // *********************************************************** // Import commands.js using ES2015 syntax: +import './commands' +import 'cypress-axe' // Alternatively you can use CommonJS syntax: -require('./commands'); +// require('./commands'); diff --git a/backend/cypress/support/log-functions.js b/backend/cypress/support/log-functions.js new file mode 100644 index 000000000..7dbd2fe27 --- /dev/null +++ b/backend/cypress/support/log-functions.js @@ -0,0 +1,23 @@ +export function terminalLog(violations) { + cy.task( + 'log', + `${violations.length} accessibility violation${ + violations.length === 1 ? '' : 's' + } ${violations.length === 1 ? 'was' : 'were'} detected` + ); + + console.log(violations); + + // Pluck specific keys for readability. Add helpUrl if messages are not as clear as needed. + const violationData = violations.map( + ({ id, impact, description, nodes, helpUrl }) => ({ + id, + impact, + description, + nodes: nodes.length, + helpUrl: helpUrl, + }) + ); + + cy.task('table', violationData); +} diff --git a/backend/cypress/support/report-pdf.js b/backend/cypress/support/report-pdf.js index ab2a560b7..51de615b8 100644 --- a/backend/cypress/support/report-pdf.js +++ b/backend/cypress/support/report-pdf.js @@ -1,16 +1,17 @@ export function testPdfAuditReport() { - cy.get('#financial_statements').type(1); - cy.get('#financial_statements_opinion').type(1); - cy.get('#schedule_expenditures').type(1); - cy.get('#schedule_expenditures_opinion').type(1); - cy.get('#uniform_guidance_control').type(1); - cy.get('#uniform_guidance_compliance').type(1); - cy.get('#GAS_control').type(1); - cy.get('#GAS_compliance').type(1); - cy.get('#schedule_findings').type(1); - cy.get('#schedule_prior_findings').type(1); - cy.get('#CAP_page').type(1); + cy.get('[id_test="test-financial_statements"]').type(1); + cy.get('[id_test="test-financial_statements_opinion"]').type(1); + cy.get('[id_test="test-schedule_expenditures"]').type(1); + cy.get('[id_test="test-schedule_expenditures_opinion"]').type(1); + cy.get('[id_test="test-uniform_guidance_control"]').type(1); + cy.get('[id_test="test-uniform_guidance_compliance"]').type(1); + cy.get('[id_test="test-GAS_control"]').type(1); + cy.get('[id_test="test-GAS_compliance"]').type(1); + cy.get('[id_test="test-schedule_findings"]').type(1); + cy.get('[id_test="test-schedule_prior_findings"]').type(1); + cy.get('[id_test="test-CAP_page"]').type(1); cy.get('#upload_report').selectFile('cypress/fixtures/basic.pdf'); cy.get('#continue').click(); // Performs upload and return to main page } + diff --git a/backend/dissemination/management/commands/populate_aln_table.py b/backend/dissemination/management/commands/populate_aln_table.py deleted file mode 100644 index e69de29bb..000000000 diff --git a/backend/dissemination/templates/search-alert-info.html b/backend/dissemination/templates/search-alert-info.html index 24fdf3b4b..017daa83a 100644 --- a/backend/dissemination/templates/search-alert-info.html +++ b/backend/dissemination/templates/search-alert-info.html @@ -2,7 +2,7 @@ {% load sprite_helper %}
-

Searching the FAC database

+

Searching the FAC database

{% if advanced_search_flag %}

Learn more about how our advanced search flters work on our resources page. @@ -13,19 +13,19 @@

Searching the FAC database

{% endif %} -

Summary reports

+

Summary reports

For search results of {{ summary_report_download_limit|intcomma }} submissions or less, you can download a combined spreadsheet of all data. If you need to download more than {{ summary_report_download_limit|intcomma }} submissions, try limiting your search parameters to download in batches.

{% if advanced_search_flag %} -

Basic search

+

Basic search

We update advanced search nightly with new submissions. Basic search is always up-to-date. This is best for users wanting to confirm their submissions are complete.

{% else %} -

Advanced search

+

Advanced search

Audit resolution officials may want to use the advanced search for additional filters.

diff --git a/backend/dissemination/templates/search.html b/backend/dissemination/templates/search.html index f8a04b1a3..83490956a 100644 --- a/backend/dissemination/templates/search.html +++ b/backend/dissemination/templates/search.html @@ -81,7 +81,7 @@

Filters

-

Search single audit reports

+

Search single audit reports

{% include "search-alert-info.html"%} {% if results|length > 0 %}
@@ -270,6 +270,7 @@

Please rota

diff --git a/backend/dissemination/templates/summary.html b/backend/dissemination/templates/summary.html index a6d6701ec..faf147a31 100644 --- a/backend/dissemination/templates/summary.html +++ b/backend/dissemination/templates/summary.html @@ -10,7 +10,7 @@ {% comment %} Title & Header {% endcomment %}
- Single audit summary +

Single audit summary

{{ auditee_name }}

@@ -254,7 +254,7 @@

Summary

-

+

Notes to SEFA:  {% if general.is_public or include_private %} {{ data|getkey:"Notes to SEFA"|length }} @@ -267,7 +267,7 @@

Summary

-

+

Findings:  {{ data|getkey:"Audit Findings"|length }} @@ -275,7 +275,7 @@

Summary

{% comment %} Findings text - If none, show the gray box with no link. {% endcomment %}
-

+

Findings text:  {% if general.is_public or include_private %} {{ data|getkey:"Audit Findings Text"|length }} @@ -289,7 +289,7 @@

Summary

-

+

CAP:  {% if general.is_public or include_private %} {{ data|getkey:"Corrective Action Plan"|length }} diff --git a/backend/docker-compose-web.yml b/backend/docker-compose-web.yml index 610b95c2c..b126572e9 100644 --- a/backend/docker-compose-web.yml +++ b/backend/docker-compose-web.yml @@ -5,7 +5,7 @@ services: db: image: "postgres:15" environment: - POSTGRES_HOST_AUTH_METHOD: "trust" + POSTGRES_HOST_AUTH_METHOD: trust volumes: - postgres-data:/var/lib/postgresql/data/ ports: diff --git a/backend/package-lock.json b/backend/package-lock.json index 91be90b70..a8db51359 100644 --- a/backend/package-lock.json +++ b/backend/package-lock.json @@ -21,6 +21,7 @@ "devDependencies": { "@4tw/cypress-drag-drop": "^2.2.5", "cypress": "^13.8.1", + "cypress-axe": "^1.5.0", "cypress-file-upload": "^5.0.8", "cypress-otp": "^1.0.3", "eslint": "8.57", @@ -1306,6 +1307,16 @@ "integrity": "sha512-NmWvPnx0F1SfrQbYwOi7OeaNGokp9XhzNioJ/CSBs8Qa4vxug81mhJEAVZwxXuBmYB5KDRfMq/F3RR0BIU7sWg==", "dev": true }, + "node_modules/axe-core": { + "version": "4.9.1", + "resolved": "https://registry.npmjs.org/axe-core/-/axe-core-4.9.1.tgz", + "integrity": "sha512-QbUdXJVTpvUTHU7871ppZkdOLBeGUKBQWHkHrvN2V9IQWGMt61zf3B45BtzjxEJzYuj0JBjBZP/hmYS/R9pmAw==", + "dev": true, + "peer": true, + "engines": { + "node": ">=4" + } + }, "node_modules/balanced-match": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", @@ -1919,6 +1930,19 @@ "node": "^16.0.0 || ^18.0.0 || >=20.0.0" } }, + "node_modules/cypress-axe": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/cypress-axe/-/cypress-axe-1.5.0.tgz", + "integrity": "sha512-Hy/owCjfj+25KMsecvDgo4fC/781ccL+e8p+UUYoadGVM2ogZF9XIKbiM6KI8Y3cEaSreymdD6ZzccbI2bY0lQ==", + "dev": true, + "engines": { + "node": ">=10" + }, + "peerDependencies": { + "axe-core": "^3 || ^4", + "cypress": "^10 || ^11 || ^12 || ^13" + } + }, "node_modules/cypress-file-upload": { "version": "5.0.8", "resolved": "https://registry.npmjs.org/cypress-file-upload/-/cypress-file-upload-5.0.8.tgz", diff --git a/backend/package.json b/backend/package.json index 7fd8c332a..69d0c9373 100644 --- a/backend/package.json +++ b/backend/package.json @@ -17,8 +17,7 @@ "fix:stylelint": "npx stylelint --fix './static/scss'", "check-all": "run-p check:*", "fix-all": "run-p fix:*", - "test:a11y:pa11y": "npx pa11y-ci -c ../.pa11yci", - "test:a11y:lighthouse": "npx lhci autorun --config='../lighthouserc.js'", + "test:a11y:cypress": "npx cypress run --spec 'cypress/e2e/accessibility.cy.js'", "test:e2e:ci": "npx cypress run" }, "author": "", @@ -26,6 +25,7 @@ "devDependencies": { "@4tw/cypress-drag-drop": "^2.2.5", "cypress": "^13.8.1", + "cypress-axe": "^1.5.0", "cypress-file-upload": "^5.0.8", "cypress-otp": "^1.0.3", "eslint": "8.57", diff --git a/backend/report_submission/templates/report_submission/delete-file-page.html b/backend/report_submission/templates/report_submission/delete-file-page.html index 5309dad22..3d5eea1eb 100644 --- a/backend/report_submission/templates/report_submission/delete-file-page.html +++ b/backend/report_submission/templates/report_submission/delete-file-page.html @@ -36,8 +36,7 @@

Confirm the deletion of the {{section_name}} worksheet by clicking the Delete Worksheet button below.

- Cancel + Cancel diff --git a/backend/report_submission/templates/report_submission/gen-form.html b/backend/report_submission/templates/report_submission/gen-form.html index 7e4de3bac..eb9cba5fa 100644 --- a/backend/report_submission/templates/report_submission/gen-form.html +++ b/backend/report_submission/templates/report_submission/gen-form.html @@ -35,7 +35,7 @@ method="post"> {% csrf_token %}
- General information +

General information

All Fields are required before a final submission can be made.

{% if errors %} There were errors when attempting to submit the form. Scroll down for more details. diff --git a/backend/report_submission/templates/report_submission/upload-page.html b/backend/report_submission/templates/report_submission/upload-page.html index 4c028423d..2f00d7b8e 100644 --- a/backend/report_submission/templates/report_submission/upload-page.html +++ b/backend/report_submission/templates/report_submission/upload-page.html @@ -10,9 +10,9 @@ method="post"> {% csrf_token %}
- {{ view_name }} + id_test="upload-page__legend">{{ view_name }}

{{ instructions }}

    @@ -31,7 +31,7 @@

    If you do not have findings:

    {% endif %} -

    Upload completed worksheet

    +

    Upload completed worksheet

    Save your completed worksheet as an XLSX file and upload it below.

    Upload completed worksheet - Cancel + Cancel
diff --git a/backend/static/scss/_search.scss b/backend/static/scss/_search.scss index c5c5fd729..0867a432c 100644 --- a/backend/static/scss/_search.scss +++ b/backend/static/scss/_search.scss @@ -47,13 +47,13 @@ .audit-search-results { padding: 1em 0 0 2em; - h2 { + h1 { border-bottom: 1px solid #a9aeb1; padding-bottom: 1em; } .search-instructions { - color: #a4a7ac; + color: #1b1b1b; font-size: 2em; margin: 0 auto; padding-top: 3em; diff --git a/backend/templates/home.html b/backend/templates/home.html index 3cadddb35..3086e6b70 100644 --- a/backend/templates/home.html +++ b/backend/templates/home.html @@ -27,14 +27,15 @@

I want -

or

+
  • or