diff --git a/Gemfile b/Gemfile index f43a1b7d9..2d77d694c 100644 --- a/Gemfile +++ b/Gemfile @@ -105,6 +105,7 @@ group :test do gem "simplecov", require: false gem "timecop", "~> 0.9.4" gem "webmock", require: false + gem 'axe-core-rspec' end # Windows does not include zoneinfo files, so bundle the tzinfo-data gem diff --git a/Gemfile.lock b/Gemfile.lock index fa09aeb53..c8d1edd1a 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -91,6 +91,17 @@ GEM aws-sigv4 (~> 1.8) aws-sigv4 (1.8.0) aws-eventstream (~> 1, >= 1.0.2) + axe-core-api (4.9.1) + dumb_delegator + virtus + axe-core-rspec (4.9.1) + axe-core-api (= 4.9.1) + dumb_delegator + virtus + axiom-types (0.1.1) + descendants_tracker (~> 0.0.4) + ice_nine (~> 0.11.0) + thread_safe (~> 0.3, >= 0.3.1) base64 (0.2.0) bcrypt (3.1.20) better_html (2.0.2) @@ -128,6 +139,8 @@ GEM launchy childprocess (5.0.0) coderay (1.1.3) + coercible (1.0.0) + descendants_tracker (~> 0.0.1) concurrent-ruby (1.3.3) connection_pool (2.4.1) crack (1.0.0) @@ -137,6 +150,8 @@ GEM cssbundling-rails (1.4.0) railties (>= 6.0.0) date (3.3.4) + descendants_tracker (0.0.4) + thread_safe (~> 0.3, >= 0.3.1) devise (4.9.3) bcrypt (~> 3.0) orm_adapter (~> 0.1) @@ -155,6 +170,7 @@ GEM dotenv-rails (3.0.2) dotenv (= 3.0.2) railties (>= 6.1) + dumb_delegator (1.0.0) encryptor (3.0.0) erb_lint (0.5.0) activesupport @@ -204,6 +220,7 @@ GEM activesupport (>= 6.1.4.4) i18n (1.14.5) concurrent-ruby (~> 1.0) + ice_nine (0.11.2) iniparse (1.5.0) jmespath (1.6.2) jsbundling-rails (1.3.0) @@ -450,6 +467,7 @@ GEM railties (>= 6.0.0) strscan (3.1.0) thor (1.3.1) + thread_safe (0.3.6) timecop (0.9.8) timeout (0.4.1) turbo-rails (1.5.0) @@ -467,6 +485,10 @@ GEM activesupport (>= 5.2.0, < 8.0) concurrent-ruby (~> 1.0) method_source (~> 1.0) + virtus (2.0.0) + axiom-types (~> 0.1) + coercible (~> 1.0) + descendants_tracker (~> 0.0, >= 0.0.3) warden (1.2.9) rack (>= 2.0.9) web-console (4.2.1) @@ -499,6 +521,7 @@ PLATFORMS DEPENDENCIES auto_strip_attributes aws-sdk-s3 + axe-core-rspec bootsnap (>= 1.4.4) bundler-audit byebug diff --git a/config/routes.rb b/config/routes.rb index a6de59a65..382174f25 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -72,7 +72,6 @@ Rails.application.routes.draw do get "details", to: "schemes#details" get "check-answers", to: "schemes#check_answers" get "edit-name", to: "schemes#edit_name" - get "support-services-provider", to: "schemes#support_services_provider" get "new-deactivation", to: "schemes#new_deactivation" get "deactivate-confirm", to: "schemes#deactivate_confirm" get "reactivate", to: "schemes#reactivate" diff --git a/spec/features/accessibility_spec.rb b/spec/features/accessibility_spec.rb new file mode 100644 index 000000000..ce1e89d18 --- /dev/null +++ b/spec/features/accessibility_spec.rb @@ -0,0 +1,187 @@ +require "rails_helper" + +RSpec.describe "Accessibility", js: true do + let(:user) { create(:user, :support) } + let!(:other_user) { create(:user, name: "new user", organisation: user.organisation, email: "new_user@example.com", confirmation_token: "abc") } + + before do + allow(user).to receive(:need_two_factor_authentication?).and_return(false) + sign_in(user) + end + + context "when viewing user pages" do + let(:user_paths) { + Rails.application.routes.routes.select {|route| route.verb == "GET" && route.path.spec.to_s.start_with?("/user")} .map do |route| + route_path = route.path.spec.to_s + route_path.gsub(":id", other_user.id.to_s).gsub(":user_id", other_user.id.to_s).gsub("(.:format)", "") + end.uniq + } + + it "is has accessible pages" do + user_paths.each do |path| + visit(path) + expect(page).to have_current_path(path) + expect(page).to be_axe_clean.according_to :wcag2aa + end + end + end + + context "when viewing organisation pages" do + let(:organisation_paths) { + Rails.application.routes.routes.select {|route| route.verb == "GET" && route.path.spec.to_s.start_with?("/organisation")} .map do |route| + route_path = route.path.spec.to_s + route_path.gsub(":id", other_user.organisation_id.to_s) + .gsub(":organisation_id", other_user.organisation_id.to_s) + .gsub("(.:format)", "") + .gsub(/\A\/organisations\/#{other_user.organisation_id}\z/, "/organisations/#{other_user.organisation_id}/details") + end.uniq + } + + it "is has accessible pages" do + organisation_paths.each do |path| + next if path.include?("invite") # needs to be fixed + next if path.include?("csv") # needs to be fixed, needs codes_only + next if path.include?("lettings-logs") # needs to be fixed + next if path.include?("sales-logs") # needs to be fixed + next if path.include?("stock-owner") # needs to be fixed, needs target_organisation + next if path.include?("managing-agent") # needs to be fixed, needs target_organisation + + visit(path) + expect(page).to have_current_path(path) + expect(page).to be_axe_clean.according_to :wcag2aa + end + end + end + + context "when viewing lettings log pages" do + let(:bulk_upload) { create(:bulk_upload) } + let(:lettings_log) { create(:lettings_log, :completed, assigned_to: other_user, bulk_upload_id: bulk_upload.id) } + let(:organisation_relationship) {create(:organisation_relationship, parent_organisation: user.organisation) } + + let(:lettings_log_paths) do + all_page_ids = FormHandler.instance.lettings_forms.values.flat_map(&:pages).map(&:id).uniq + lettings_log_pages = lettings_log.form.pages + other_form_page_ids = all_page_ids - lettings_log_pages.map(&:id) + + filtered_routes = Rails.application.routes.routes.select do |route| + route.verb == "GET" && route.path.spec.to_s.start_with?("/lettings-log") + end + + filtered_routes.map do |route| + route_path = route.path.spec.to_s + route_path + .gsub("/lettings-logs/:id", "/lettings-logs/#{lettings_log.id}") + .gsub(":lettings_log_id", "#{lettings_log.id}") + .gsub(":id", "#{bulk_upload.id}") + .gsub("(.:format)", "") + end.reject do |path| + path.include?("/edit") || path.include?("/new") || path.include?("*page") || + path.include?("local-authority/check-answers") || path.include?("declaration/check-answers") || + other_form_page_ids.any? { |page_id| path.include?(page_id.dasherize) } || + lettings_log_pages.any? { |page| path.include?(page.id.dasherize) && !page.routed_to?(lettings_log, user) } + end.uniq + end + + + before do + allow(FormHandler.instance).to receive(:in_crossover_period?).and_return(true) + end + + it "is has accessible pages" do + lettings_log_paths.each do |path| + next if path.include?("duplicate") # needs to be fixed, add a duplicate? + next if path.include?("csv") # needs to be fixed, needs codes_only + next if path.include?("filters/update") # needs to be fixed, needs codes_only + next if path.include?("bulk") # needs to be fixed + + visit(path) + expect(page).to have_current_path(path) + expect(page).to be_axe_clean.according_to :wcag2aa + end + end + end + + context "when viewing sales log pages" do + let(:bulk_upload) { create(:bulk_upload) } + let(:sales_log) { create(:sales_log, :completed, assigned_to: other_user, bulk_upload_id: bulk_upload.id) } + let(:organisation_relationship) {create(:organisation_relationship, parent_organisation: user.organisation) } + + let(:sales_log_paths) do + all_page_ids = FormHandler.instance.sales_forms.values.flat_map(&:pages).map(&:id).uniq + sales_log_pages = sales_log.form.pages + other_form_page_ids = all_page_ids - sales_log_pages.map(&:id) + + filtered_routes = Rails.application.routes.routes.select do |route| + route.verb == "GET" && route.path.spec.to_s.start_with?("/sales-log") + end + + filtered_routes.map do |route| + route_path = route.path.spec.to_s + route_path + .gsub("/sales-logs/:id", "/sales-logs/#{sales_log.id}") + .gsub(":sales_log_id", "#{sales_log.id}") + .gsub(":id", "#{bulk_upload.id}") + .gsub("(.:format)", "") + end.reject do |path| + path.include?("/edit") || path.include?("/new") || path.include?("*page") || + other_form_page_ids.any? { |page_id| path.include?(page_id.dasherize) } || + sales_log_pages.any? { |page| path.include?(page.id.dasherize) && !page.routed_to?(sales_log, user) } + end.uniq + end + + + before do + allow(FormHandler.instance).to receive(:in_crossover_period?).and_return(true) + end + + it "is has accessible pages" do + sales_log_paths.each do |path| + next if path.include?("duplicate") # needs to be fixed, add a duplicate? + next if path.include?("csv") # needs to be fixed, needs codes_only + next if path.include?("filters/update") # needs to be fixed, needs codes_only + next if path.include?("bulk") # needs to be fixed + + visit(path) + expect(page).to have_current_path(path) + expect(page).to be_axe_clean.according_to :wcag2aa + end + end + end + + context "when viewing scheme pages" do + let(:scheme) { create(:scheme, owning_organisation: other_user.organisation) } + let!(:location) { create(:location, scheme:) } + let(:scheme_paths) do + filtered_routes = Rails.application.routes.routes.select do |route| + route.verb == "GET" && route.path.spec.to_s.start_with?("/scheme") + end + + filtered_routes.map do |route| + route_path = route.path.spec.to_s + route_path + .gsub("/scheme/:id", "/scheme/#{scheme.id}") + .gsub(":scheme_id", "#{scheme.id}") + .gsub(":id", "#{location.id}") + .gsub("(.:format)", "") + end.reject do |path| + path.include?("/edit") || path.include?("/new") || path.include?("*page") + end.uniq + end + + + before do + allow(FormHandler.instance).to receive(:in_crossover_period?).and_return(true) + end + + it "is has accessible pages" do + scheme_paths.each do |path| + next if path.include?("reactivate") # needs to be fixed + next if path.include?("deactivate") # needs to be fixed + + visit(path) + expect(page).to have_current_path(path) + expect(page).to be_axe_clean.according_to :wcag2aa + end + end + end +end diff --git a/spec/rails_helper.rb b/spec/rails_helper.rb index 36cf81b99..c70d4b717 100644 --- a/spec/rails_helper.rb +++ b/spec/rails_helper.rb @@ -10,10 +10,14 @@ require "capybara-screenshot/rspec" require "selenium-webdriver" require "view_component/test_helpers" require "pundit/rspec" +require 'axe-rspec' Capybara.register_driver :headless do |app| options = Selenium::WebDriver::Firefox::Options.new options.add_argument("--headless") + options.add_argument('--no-sandbox') + options.add_argument('--disable-dev-shm-usage') + options.add_argument('--window-size=1400,1400') Capybara::Selenium::Driver.new(app, browser: :firefox, options:) end