diff --git a/spec/features/accessibility/user_pages_spec.rb b/spec/features/accessibility/user_pages_spec.rb index 70b03958e06..e71e4bda1f2 100644 --- a/spec/features/accessibility/user_pages_spec.rb +++ b/spec/features/accessibility/user_pages_spec.rb @@ -7,7 +7,20 @@ sign_up_with(email) expect(current_path).to eq(sign_up_verify_email_path) - expect_page_to_have_no_accessibility_violations(page) + # We can't validate markup here, since markup validation requires a page reload when using the + # JS driver, but the sign up verify email path can only be visited once before redirecting to + # the account creation form. Instead, we validate markup separately with the non-JS driver. + expect_page_to_have_no_accessibility_violations(page, validate_markup: false) + end + + context 'with javascript disabled', js: false do + scenario 'user registration page' do + email = 'test@example.com' + sign_up_with(email) + + expect(current_path).to eq(sign_up_verify_email_path) + expect(page).to have_valid_markup + end end describe 'user confirmation page' do diff --git a/spec/support/matchers/accessibility.rb b/spec/support/matchers/accessibility.rb index 5727c1fc1b1..5a991666b5b 100644 --- a/spec/support/matchers/accessibility.rb +++ b/spec/support/matchers/accessibility.rb @@ -48,6 +48,50 @@ end end +RSpec::Matchers.define :have_valid_markup do + def page_html + if page.driver.is_a?(Capybara::Selenium::Driver) + # Here be dragons: + # 1. ChromeDriver repairs most invalid HTML, thus defeating the purpose of this test + # 2. Switching drivers does not preserve the original session, hence the cookie management + # 3. The domain name differs between Rack and ChromeDriver testing environments + # 4. Methods stubbed for JS tests carry over when switching drivers (see `rails_helper.rb`) + cookies = page.driver.browser.send(:bridge).cookies + session_value = cookies.find { |c| c['name'] == '_identity_idp_session' }&.[]('value') + original_path_with_params = URI.parse(current_url).request_uri + Capybara.using_driver(:desktop_rack_test) do + domain_name = IdentityConfig.store.domain_name + default_url_options = Rails.application.routes.default_url_options + allow(IdentityConfig.store).to receive(:domain_name).and_call_original + allow(Rails.application.routes).to receive(:default_url_options).and_call_original + page.driver.browser.set_cookie "_identity_idp_session=#{session_value}" if session_value + page.driver.get(original_path_with_params) + allow(IdentityConfig.store).to receive(:domain_name).and_return(domain_name) + allow(Rails.application.routes).to receive(:default_url_options). + and_return(default_url_options) + page.html + end + else + page.html + end + end + + def page_markup_syntax_errors + return @page_markup_syntax_errors if defined?(@page_markup_syntax_errors) + @page_markup_syntax_errors = Nokogiri::HTML5(page_html, max_errors: 20).errors + end + + match { |_page| page_markup_syntax_errors.blank? } + + failure_message do |page| + <<~STR + Expected page to have valid markup. Found syntax errors: + + #{page_markup_syntax_errors.map(&:inspect).join("\n")} + STR + end +end + RSpec::Matchers.define :have_description do |description| def descriptors(element) element['aria-describedby']&. @@ -82,9 +126,10 @@ def descriptors(element) end end -def expect_page_to_have_no_accessibility_violations(page) +def expect_page_to_have_no_accessibility_violations(page, validate_markup: true) expect(page).to be_axe_clean.according_to :section508, :"best-practice", :wcag21aa expect(page).to have_valid_idrefs expect(page).to label_required_fields expect(page).to be_uniquely_titled + expect(page).to have_valid_markup if validate_markup end