capybara_accessibility_audit 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: c9d57dbd535b2d7c257f1dd19975788449178add55e5368aa3dec36ca7239612
4
+ data.tar.gz: 0a5b06b7b534d613052590db1242ef6c1d28db0820fcea718588c297aebcbc0b
5
+ SHA512:
6
+ metadata.gz: 6d143c9f23767f861bc58481e0bd2513285de60b8fae286153c269ff52c36da9cf2aeca9a834d15e629bcd12b8dd49d364aadd6a25b5fcebdc861e42090ca395
7
+ data.tar.gz: 423c722319bcc0118f8ee87ca4927626a24f3a327f92469b8f8f1fb4540c4a871213bba05fd439d234fe49553057a3317094c8139ed8ebdfccb332bcba7f2563
data/MIT-LICENSE ADDED
@@ -0,0 +1,20 @@
1
+ Copyright 2022 Sean Doyle
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,228 @@
1
+ # CapybaraAccessibilityAudit
2
+
3
+ :warning: This is pre-release software. :warning:
4
+
5
+ Extend your Capybara-powered System Tests to automatically audit the page for
6
+ WCAG Stardards-based accessibility violations.
7
+
8
+ ## Usage
9
+
10
+ ```
11
+ Failure:
12
+ Found 1 accessibility violation:
13
+
14
+ 1) label: Form elements must have labels (critical)
15
+ https://dequeuniversity.com/rules/axe/4.4/label?application=axeAPI
16
+ The following 1 node violate this rule:
17
+
18
+ Selector: input
19
+ HTML: <input>
20
+ Fix any of the following:
21
+ - Form element does not have an implicit (wrapped) <label>
22
+ - Form element does not have an explicit <label>
23
+ - aria-label attribute does not exist or is empty
24
+ - aria-labelledby attribute does not exist, references elements that do not exist or references elements that are empty
25
+ - Element has no title attribute
26
+ - Element has no placeholder attribute
27
+ - Element's default semantics were not overridden with role="none" or role="presentation"
28
+
29
+ Invocation: axe.run({:exclude=>[]}, {}, callback);
30
+ ```
31
+
32
+ Installing the gem will automatically configure your System Tests to audit for
33
+ accessibility violations after common actions, including:
34
+
35
+ * [`visit`](https://rubydoc.info/github/teamcapybara/capybara/master/Capybara/Session:visit)
36
+ * [`click_button`](https://rubydoc.info/github/teamcapybara/capybara/master/Capybara/Node/Actions#click_button-instance_method)
37
+ * [`click_link`](https://rubydoc.info/github/teamcapybara/capybara/master/Capybara/Node/Actions#click_link-instance_method)
38
+ * [`click_link_or_button`](https://rubydoc.info/github/teamcapybara/capybara/master/Capybara/Node/Actions#click_link_or_button-instance_method)
39
+ * [`click_on`](https://rubydoc.info/github/teamcapybara/capybara/master/Capybara/Node/Actions:click_on)
40
+
41
+ Under the hood, `capybara_accessibility_audit` relies on [axe-core-rspec][], which uses [aXe][]
42
+ to audit for accessibility violations. To configure which options are passed to
43
+ the `be_axe_clean` matcher, override the class-level
44
+ `accessibility_audit_options`. Supported keys include:
45
+
46
+ * [`according_to:`](https://github.com/dequelabs/axe-core-gems/blob/develop/packages/axe-core-rspec/README.md#according_to---accessibility-standard-tag-clause)
47
+ * [`checking_only:`](https://github.com/dequelabs/axe-core-gems/blob/develop/packages/axe-core-rspec/README.md#checking_only---exclusive-rules-clause)
48
+ * [`checking:`](https://github.com/dequelabs/axe-core-gems/blob/develop/packages/axe-core-rspec/README.md#checking---checking-rules-clause)
49
+ * [`excluding:`](https://github.com/dequelabs/axe-core-gems/blob/develop/packages/axe-core-rspec/README.md#excluding---exclusion-clause)
50
+ * [`skipping:`](https://github.com/dequelabs/axe-core-gems/blob/develop/packages/axe-core-rspec/README.md#skipping---skipping-rules-clause)
51
+ * [`within:`](https://github.com/dequelabs/axe-core-gems/blob/develop/packages/axe-core-rspec/README.md#within---inclusion-clause)
52
+
53
+ To override the class-level setting, wrap code in calls to the
54
+ `with_accessibility_audit_options` method:
55
+
56
+ ```ruby
57
+ with_accessibility_audit_options according_to: :wcag21aaa do
58
+ visit page_with_violations_path
59
+ end
60
+ ```
61
+
62
+ [aXe]: https://www.deque.com/axe/
63
+ [axe-core-rspec]: https://github.com/dequelabs/axe-core-gems/blob/develop/packages/axe-core-rspec/README.md#matcher
64
+
65
+ ## Frequently Asked Questions
66
+
67
+ My application already exists, will automated accessibility audits are uncovering violations left and right. Do I have to fix them all at once?
68
+ ---
69
+
70
+ Your suite has control over which rules are skipped and which rules are
71
+ enforced through the `accessibility_audit_options` configuration.
72
+
73
+ Configuration overrides can occur at any scope, ranging from class-wide to
74
+ block-wide.
75
+
76
+ For example, to skip a rule at the suite-level, override it in your
77
+ `ApplicationSystemTestCase`:
78
+
79
+ ```ruby
80
+ class ApplicationSystemTestCase < ActionDispatch::SystemTestCase
81
+ accessibility_audit_options.skipping = %w[label button-name image-alt]
82
+ end
83
+ ```
84
+
85
+ To skip a rule at the test-level, wrap the test in a
86
+ `with_accessibility_audit_options` block:
87
+
88
+ ```ruby
89
+ class MySystemTest < ApplicationSystemTestCase
90
+ test "with overridden accessibility audit options" do
91
+ with_accessibility_audit_options skipping: %w[label button-name image-alt] do
92
+ visit examples_path
93
+ # ...
94
+ end
95
+ end
96
+ end
97
+ ```
98
+
99
+ To skip a rule at the block-level, wrap the code in a
100
+ `with_accessibility_audit_options` block:
101
+
102
+ ```ruby
103
+ class MySystemTest < ApplicationSystemTestCase
104
+ test "with overridden accessibility audit options" do
105
+ visit examples_path
106
+
107
+ with_accessibility_audit_options skipping: %w[label button-name image-alt] do
108
+ click_on "A link to a page with a violation"
109
+ end
110
+
111
+ # ...
112
+ end
113
+ end
114
+ ```
115
+
116
+ As you resolve the violations, you can remove entries from the list of skipped
117
+ rules.
118
+
119
+ I've implemented a custom Capybara action to toggle a disclosure element. How can I automatically audit for violations after it's called?
120
+ ---
121
+
122
+ You can add the method to the list of methods that will initiate an automated
123
+ audit:
124
+
125
+ ```ruby
126
+ class ApplicationSystemTestCase < ActionDispatch::SystemTestCase
127
+ def toggle_disclosure(locator)
128
+ # ...
129
+ end
130
+
131
+ accessibility_audit_after :toggle_disclosure
132
+ end
133
+ ```
134
+
135
+ How can I turn off auditing for the entire suite?
136
+ ---
137
+
138
+ You can disable automated auditing within your `ApplicationSystemTestCase`:
139
+
140
+ ```ruby
141
+ class ApplicationSystemTestCase < ActionDispatch::SystemTestCase
142
+ self.accessibility_audit_enabled = false
143
+ end
144
+ ```
145
+
146
+ How can I turn off auditing for a block of code?
147
+ ---
148
+
149
+ You can disable automated auditing temporarily by wrapping code in a
150
+ `skip_accessibility_audits` block:
151
+
152
+ ```ruby
153
+ class MySystemTest < ApplicationSystemTestCase
154
+ test "with overridden accessibility audit options" do
155
+ skip_accessibility_audits do
156
+ visit a_page_with_violations_path
157
+
158
+ click_on "A link to a page with a violation"
159
+ end
160
+
161
+ # ...
162
+ end
163
+ end
164
+ ```
165
+
166
+ How can I turn off auditing hooks for a method?
167
+ ---
168
+
169
+ You can remove the method from the test's [Set][] of
170
+ `accessibility_audit_after_methods` configuration by calling
171
+ `skip_accessibility_audit_after`:
172
+
173
+ ```ruby
174
+ class ApplicationSystemTestCase < ActionDispatch::SystemTestCase
175
+ skip_accessibility_audit_after :visit
176
+ end
177
+ ```
178
+
179
+ [Set]: https://ruby-doc.org/stdlib-3.0.1/libdoc/set/rdoc/Set.html
180
+
181
+ How can I turn off auditing for a test file?
182
+ ---
183
+
184
+ You can disable automated auditing at the class-level:
185
+
186
+ ```ruby
187
+ class MySystemTest < ApplicationSystemTestCase
188
+ self.accessibility_audit_enabled = false
189
+ end
190
+ ```
191
+
192
+ As you gradually address violations, you can re-enable audits within
193
+ `with_accessibility_audits` blocks:
194
+
195
+ ```ruby
196
+ class MySystemTest < ApplicationSystemTestCase
197
+ self.accessibility_audit_enabled = false
198
+
199
+ test "a test with a violation" do
200
+ visit examples_path
201
+
202
+ with_accessibility_audits do
203
+ click_on "A link to a page with violations"
204
+ end
205
+ end
206
+ end
207
+ ```
208
+
209
+ ## Installation
210
+ Add this line to your application's Gemfile:
211
+
212
+ ```ruby
213
+ gem "capybara_accessibility_audit",
214
+ github: "thoughtbot/capybara_accessibility_audit",
215
+ branch: "main"
216
+ ```
217
+
218
+ And then execute:
219
+ ```bash
220
+ $ bundle
221
+ ```
222
+
223
+ ## Contributing
224
+
225
+ Please read [CONTRIBUTING.md](./CONTRIBUTING.md).
226
+
227
+ ## License
228
+ The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
data/Rakefile ADDED
@@ -0,0 +1,21 @@
1
+ require "bundler/setup"
2
+
3
+ load "rails/tasks/statistics.rake"
4
+
5
+ require "bundler/gem_tasks"
6
+ require "standard/rake"
7
+
8
+ namespace :test do
9
+ task :prepare do
10
+ end
11
+
12
+ desc "Runs all tests, including system tests"
13
+ task all: %w[test test:system]
14
+
15
+ desc "Run system tests only"
16
+ task system: %w[test:prepare] do
17
+ $: << "test"
18
+
19
+ Rails::TestUnit::Runner.rake_run(["test/system"])
20
+ end
21
+ end
@@ -0,0 +1,96 @@
1
+ require "axe-capybara"
2
+ require "axe/matchers/be_axe_clean"
3
+
4
+ module CapybaraAccessibilityAudit
5
+ module AuditSystemTestExtensions
6
+ extend ActiveSupport::Concern
7
+
8
+ included do
9
+ class_attribute :accessibility_audit_after_methods, default: Set.new
10
+ class_attribute :accessibility_audit_enabled, default: true
11
+ class_attribute :accessibility_audit_options, default: ActiveSupport::OrderedOptions.new
12
+ end
13
+
14
+ class_methods do
15
+ def inherited(descendant)
16
+ super
17
+
18
+ descendant.accessibility_audit_options = accessibility_audit_options.deep_dup
19
+ descendant.accessibility_audit_after_methods = accessibility_audit_after_methods.dup
20
+ end
21
+
22
+ def accessibility_audit_after(*methods)
23
+ (methods.flatten.to_set - accessibility_audit_after_methods).each do |method|
24
+ define_method method do |*arguments, **options, &block|
25
+ super(*arguments, **options, &block).tap { Auditor.new(self).audit!(method) }
26
+ end
27
+
28
+ accessibility_audit_after_methods << method
29
+ end
30
+ end
31
+
32
+ def skip_accessibility_audit_after(*methods)
33
+ methods.each { |method| accessibility_audit_after_methods.delete(method) }
34
+ end
35
+ end
36
+
37
+ delegate :accessibility_audit_after, :skip_accessibility_audit_after, to: :class
38
+
39
+ def with_accessibility_audits(**options, &block)
40
+ accessibility_audit_enabled = self.accessibility_audit_enabled
41
+ self.accessibility_audit_enabled = true
42
+
43
+ if options.present?
44
+ with_accessibility_audit_options(**options, &block)
45
+ else
46
+ block.call
47
+ end
48
+ ensure
49
+ self.accessibility_audit_enabled = accessibility_audit_enabled
50
+ end
51
+
52
+ def with_accessibility_audit_options(**options, &block)
53
+ accessibility_audit_options = self.accessibility_audit_options
54
+ self.accessibility_audit_options = accessibility_audit_options.merge(options)
55
+
56
+ block.call
57
+ ensure
58
+ self.accessibility_audit_options = accessibility_audit_options
59
+ end
60
+
61
+ def skip_accessibility_audits(&block)
62
+ accessibility_audit_enabled = self.accessibility_audit_enabled
63
+ self.accessibility_audit_enabled = false
64
+
65
+ block.call
66
+ ensure
67
+ self.accessibility_audit_enabled = accessibility_audit_enabled
68
+ end
69
+
70
+ def skip_accessibility_violations(value, &block)
71
+ skipping = accessibility_audit_options.skipping
72
+ accessibility_audit_options.skipping = Array(value)
73
+
74
+ block.call
75
+ ensure
76
+ accessibility_audit_options.skipping = skipping
77
+ end
78
+
79
+ def assert_no_accessibility_violations(**options)
80
+ options.assert_valid_keys(
81
+ :according_to,
82
+ :checking,
83
+ :checking_only,
84
+ :excluding,
85
+ :skipping,
86
+ :within
87
+ )
88
+ options.compact_blank!
89
+
90
+ axe_matcher = Axe::Matchers::BeAxeClean.new
91
+ axe_matcher = options.inject(axe_matcher) { |matcher, option| matcher.public_send(*option) }
92
+
93
+ assert axe_matcher.matches?(page), axe_matcher.failure_message
94
+ end
95
+ end
96
+ end
@@ -0,0 +1,24 @@
1
+ module CapybaraAccessibilityAudit
2
+ class Auditor
3
+ delegate_missing_to :@test
4
+
5
+ def initialize(test)
6
+ @test = test
7
+ end
8
+
9
+ def audit!(method)
10
+ if accessibility_audit_enabled && method.in?(accessibility_audit_after_methods) && javascript_enabled?
11
+ assert_no_accessibility_violations(**accessibility_audit_options)
12
+ end
13
+ end
14
+
15
+ private
16
+
17
+ def javascript_enabled?
18
+ case page.driver
19
+ when Capybara::RackTest::Driver then false
20
+ else true
21
+ end
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,37 @@
1
+ module CapybaraAccessibilityAudit
2
+ class Engine < ::Rails::Engine
3
+ config.capybara_accessibility_audit = ActiveSupport::OrderedOptions.new
4
+ config.capybara_accessibility_audit.audit_after = %i[
5
+ visit
6
+ click_button
7
+ click_link
8
+ click_link_or_button
9
+ click_on
10
+ ]
11
+ config.capybara_accessibility_audit.audit_enabled = true
12
+
13
+ ActiveSupport.on_load :action_dispatch_system_test_case do
14
+ include CapybaraAccessibilityAudit::AuditSystemTestExtensions
15
+
16
+ self.accessibility_audit_enabled = Rails.configuration.capybara_accessibility_audit.audit_enabled
17
+
18
+ accessibility_audit_after Rails.configuration.capybara_accessibility_audit.audit_after
19
+ end
20
+
21
+ if defined?(RSpec)
22
+ RSpec.configure do |config|
23
+ config.include CapybaraAccessibilityAudit::AuditSystemTestExtensions, type: :system
24
+ config.include CapybaraAccessibilityAudit::AuditSystemTestExtensions, type: :feature
25
+
26
+ configure = proc do
27
+ self.accessibility_audit_enabled = Rails.configuration.capybara_accessibility_audit.audit_enabled
28
+
29
+ accessibility_audit_after Rails.configuration.capybara_accessibility_audit.audit_after
30
+ end
31
+
32
+ config.before(type: :system, &configure)
33
+ config.before(type: :feature, &configure)
34
+ end
35
+ end
36
+ end
37
+ end
@@ -0,0 +1,3 @@
1
+ module CapybaraAccessibilityAudit
2
+ VERSION = "0.1.0"
3
+ end
@@ -0,0 +1,9 @@
1
+ require "zeitwerk"
2
+ loader = Zeitwerk::Loader.for_gem
3
+ loader.setup
4
+
5
+ module CapybaraAccessibilityAudit
6
+ # Your code goes here...
7
+ end
8
+
9
+ loader.eager_load
@@ -0,0 +1,4 @@
1
+ # desc "Explaining what the task does"
2
+ # task :capybara_accessibility_audit do
3
+ # # Task goes here
4
+ # end
metadata ADDED
@@ -0,0 +1,111 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: capybara_accessibility_audit
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Sean Doyle
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2022-07-29 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: rails
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ">="
18
+ - !ruby/object:Gem::Version
19
+ version: '0'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ">="
25
+ - !ruby/object:Gem::Version
26
+ version: '0'
27
+ - !ruby/object:Gem::Dependency
28
+ name: capybara
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ">="
32
+ - !ruby/object:Gem::Version
33
+ version: '0'
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - ">="
39
+ - !ruby/object:Gem::Version
40
+ version: '0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: axe-core-capybara
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - ">="
46
+ - !ruby/object:Gem::Version
47
+ version: '0'
48
+ type: :runtime
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - ">="
53
+ - !ruby/object:Gem::Version
54
+ version: '0'
55
+ - !ruby/object:Gem::Dependency
56
+ name: zeitwerk
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - ">="
60
+ - !ruby/object:Gem::Version
61
+ version: '0'
62
+ type: :runtime
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - ">="
67
+ - !ruby/object:Gem::Version
68
+ version: '0'
69
+ description: Accessibility tooling for Capybara
70
+ email:
71
+ - sean.p.doyle24@gmail.com
72
+ executables: []
73
+ extensions: []
74
+ extra_rdoc_files: []
75
+ files:
76
+ - MIT-LICENSE
77
+ - README.md
78
+ - Rakefile
79
+ - lib/capybara_accessibility_audit.rb
80
+ - lib/capybara_accessibility_audit/audit_system_test_extensions.rb
81
+ - lib/capybara_accessibility_audit/auditor.rb
82
+ - lib/capybara_accessibility_audit/engine.rb
83
+ - lib/capybara_accessibility_audit/version.rb
84
+ - lib/tasks/capybara_accessibility_audit.rake
85
+ homepage: https://github.com/thoughtbot/capybara_accessibility_audit
86
+ licenses:
87
+ - MIT
88
+ metadata:
89
+ homepage_uri: https://github.com/thoughtbot/capybara_accessibility_audit
90
+ source_code_uri: https://github.com/thoughtbot/capybara_accessibility_audit
91
+ changelog_uri: https://github.com/thoughtbot/capybara_accessibility_audit/blob/main/CHANGELOG.md
92
+ post_install_message:
93
+ rdoc_options: []
94
+ require_paths:
95
+ - lib
96
+ required_ruby_version: !ruby/object:Gem::Requirement
97
+ requirements:
98
+ - - ">="
99
+ - !ruby/object:Gem::Version
100
+ version: '0'
101
+ required_rubygems_version: !ruby/object:Gem::Requirement
102
+ requirements:
103
+ - - ">="
104
+ - !ruby/object:Gem::Version
105
+ version: '0'
106
+ requirements: []
107
+ rubygems_version: 3.3.3
108
+ signing_key:
109
+ specification_version: 4
110
+ summary: Accessibility tooling for Capybara
111
+ test_files: []