capybara_accessibility_audit 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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: []