Skip to content

Commit 255625b

Browse files
committed
Configure reporting with :raise and :log
Related to #21 Related to #22 Related to #30 Related to #30 Rather than immediately fail tests (like the `:raise` reporter), log the output through a new `:log` reporter. --- > How many I conduct a preliminary audit that comprehensively exercises > my system test suite without failing tests that result in violations You can configure the audit's reporting mechanism. By default, the `config.capybara_accessibility_audit.reporter` value is set to `:raise`, which will raise violation errors that will fail the test suite. To log violations, rather than raise them, you can configure `config.capybara_accessibility_audit.reporter` to `:log`: ```ruby class MySystemTest < ApplicationSystemTestCase self.accessibility_audit_reporter = :log test "with overridden accessibility :log reporter" do visit examples_path # ... end end ``` --- `report.capybara_accessibility_audit` notification --- Subscribe to `report.capybara_accessibility_audit` notifications emitted when an accessibility audit detects violations. The `payload` includes: | Payload | Type | Description | | ------------- | ---------------------------------- | ----------- | | report | [Axe::API::Results][] | The underlying axe.js [Results][] object | options | [ActiveSupport::OrderedOptions][] | The audit's configuration | test | [ActionDispatch::SystemTestCase][] | The test case that triggered the audit [ActiveSupport::OrderedOptions]: https://api.rubyonrails.org/classes/ActiveSupport/OrderedOptions.html [ActionDispatch::SystemTestCase]: https://api.rubyonrails.org/classes/ActionDispatch/SystemTestCase.html [Axe::API::Results]: https://github.com/dequelabs/axe-core-gems/blob/v4.11.0/packages/axe-core-api/lib/axe/api/results.rb [Results]: https://www.deque.com/axe/core-documentation/api-documentation/#results-object > [!NOTE] > The `report.capybara_accessibility_audit` notifications are only published when > `config.capybara_accessibility_audit.reporter` is configured with > `:notification` or `:log`.
1 parent 9e779f4 commit 255625b

14 files changed

+315
-25
lines changed

Gemfile

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,4 +23,5 @@ gem "puma"
2323
gem "standard", "~> 1.12"
2424
gem "capybara-playwright-driver", require: "capybara/playwright"
2525
gem "cuprite", require: "capybara/cuprite"
26+
gem "rspec-matchers-active_support-notifications", github: "josegomezr/rspec-matchers-active_support-notifications"
2627
gem "selenium-webdriver"

README.md

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -89,6 +89,27 @@ the `payload` includes additional information:
8989
[ActiveSupport::OrderedOptions]: https://api.rubyonrails.org/classes/ActiveSupport/OrderedOptions.html
9090
[ActionDispatch::SystemTestCase]: https://api.rubyonrails.org/classes/ActionDispatch/SystemTestCase.html
9191

92+
### `report.capybara_accessibility_audit` notification
93+
94+
Subscribe to `report.capybara_accessibility_audit` notifications emitted when an
95+
accessibility audit detects violations. The `payload` includes:
96+
97+
| Payload | Type | Description |
98+
| ------------- | ---------------------------------- | ----------- |
99+
| report | [Axe::API::Results][] | The underlying axe.js [Results][] object
100+
| options | [ActiveSupport::OrderedOptions][] | The audit's configuration
101+
| test | [ActionDispatch::SystemTestCase][] | The test case that triggered the audit
102+
103+
[ActiveSupport::OrderedOptions]: https://api.rubyonrails.org/classes/ActiveSupport/OrderedOptions.html
104+
[ActionDispatch::SystemTestCase]: https://api.rubyonrails.org/classes/ActionDispatch/SystemTestCase.html
105+
[Axe::API::Results]: https://github.com/dequelabs/axe-core-gems/blob/v4.11.0/packages/axe-core-api/lib/axe/api/results.rb
106+
[Results]: https://www.deque.com/axe/core-documentation/api-documentation/#results-object
107+
108+
> [!NOTE]
109+
> The `report.capybara_accessibility_audit` notifications are only published when
110+
> `config.capybara_accessibility_audit.reporter` is configured with
111+
> `:notification` or `:log`.
112+
92113
## Frequently Asked Questions
93114

94115
My application already exists, automated accessibility audits are uncovering violations left and right. Do I have to fix them all at once?
@@ -143,6 +164,28 @@ end
143164
As you resolve the violations, you can remove entries from the list of skipped
144165
rules.
145166

167+
How many I conduct a preliminary audit that comprehensively exercises my system test suite without failing tests that result in violations?
168+
---
169+
170+
You can configure the audit's reporting mechanism. By default, the
171+
`config.capybara_accessibility_audit.reporter` value is set to `:raise`, which
172+
will raise violation errors that will fail the test suite.
173+
174+
To log violations, rather than raise them, you can configure
175+
`config.capybara_accessibility_audit.reporter` to `:log`:
176+
177+
178+
```ruby
179+
class MySystemTest < ApplicationSystemTestCase
180+
self.accessibility_audit_reporter = :log
181+
182+
test "with overridden accessibility :log reporter" do
183+
visit examples_path
184+
# ...
185+
end
186+
end
187+
```
188+
146189
I've implemented a custom Capybara action to toggle a disclosure element. How can I automatically audit for violations after it's called?
147190
---
148191

lib/capybara_accessibility_audit.rb

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,20 @@
33
loader.setup
44

55
module CapybaraAccessibilityAudit
6-
# Your code goes here...
6+
extend self
7+
8+
def reporter_class(name)
9+
case name
10+
when :notification, :log
11+
NotificationReporter
12+
when :raise
13+
RaiseReporter
14+
when ::Class
15+
name
16+
else
17+
raise ArgumentError.new("unsupported reporter: #{name}")
18+
end
19+
end
720
end
821

922
loader.eager_load

lib/capybara_accessibility_audit/audit_system_test_extensions.rb

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,9 @@ module AuditSystemTestExtensions
1313
class_attribute :accessibility_audit_after_methods, default: Set.new
1414
class_attribute :accessibility_audit_enabled, default: true
1515
class_attribute :accessibility_audit_options, default: ActiveSupport::OrderedOptions.new
16+
class_attribute :accessibility_audit_reporter, default: :raise
17+
18+
attr_accessor :accessibility_audit_auditor
1619

1720
MODAL_METHODS.each do |method|
1821
define_method method do |*arguments, **options, &block|
@@ -87,8 +90,7 @@ def skip_accessibility_violations(value, &block)
8790
accessibility_audit_options.skipping = skipping
8891
end
8992

90-
def assert_no_accessibility_violations(auditor: accessibility_audit_options.auditor, **options)
91-
options = options.with_defaults(accessibility_audit_options.except(:auditor))
93+
def assert_no_accessibility_violations(auditor: accessibility_audit_auditor, **options)
9294
options.assert_valid_keys(
9395
:according_to,
9496
:checking,

lib/capybara_accessibility_audit/engine.rb

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -10,21 +10,22 @@ class Engine < ::Rails::Engine
1010
click_on
1111
]
1212
config.capybara_accessibility_audit.audit_enabled = true
13-
config.capybara_accessibility_audit.reporter = RaiseReporter
13+
config.capybara_accessibility_audit.reporter = :raise
1414

1515
initializer "capybara_accessibility_audit.minitest" do |app|
1616
ActiveSupport.on_load :action_dispatch_system_test_case do
1717
include CapybaraAccessibilityAudit::AuditSystemTestExtensions
1818

1919
self.accessibility_audit_enabled = app.config.capybara_accessibility_audit.audit_enabled
20+
self.accessibility_audit_reporter = app.config.capybara_accessibility_audit.reporter
2021

2122
accessibility_audit_after app.config.capybara_accessibility_audit.audit_after
2223

2324
setup do
2425
auditor_class = app.config.capybara_accessibility_audit.auditor
25-
reporter_class = app.config.capybara_accessibility_audit.reporter
26+
reporter_class = CapybaraAccessibilityAudit.reporter_class(accessibility_audit_reporter)
2627

27-
accessibility_audit_options.auditor = auditor_class.new(page, reporter_class.new(self))
28+
self.accessibility_audit_auditor = auditor_class.new(page, reporter_class.new(self))
2829
end
2930
end
3031
end
@@ -43,9 +44,9 @@ class Engine < ::Rails::Engine
4344
accessibility_audit_after app.config.capybara_accessibility_audit.audit_after
4445

4546
auditor_class = app.config.capybara_accessibility_audit.auditor
46-
reporter_class = app.config.capybara_accessibility_audit.reporter
47+
reporter_class = CapybaraAccessibilityAudit.reporter_class(accessibility_audit_reporter)
4748

48-
accessibility_audit_options.auditor = auditor_class.new(page, reporter_class.new(self))
49+
self.accessibility_audit_auditor = auditor_class.new(page, reporter_class.new(self))
4950
end
5051

5152
config.before(type: :system, &configure)
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
module CapybaraAccessibilityAudit
2+
class LogSubscriber < ActiveSupport::LogSubscriber
3+
def report(event)
4+
report = event.payload[:report]
5+
test = event.payload[:test]
6+
7+
if test.accessibility_audit_reporter == :log
8+
error color(<<~ERROR, :red)
9+
[capybara_accessibility_audit] Accessibility audit detected violations in "#{test.class.name}##{test.name}":
10+
#{report.failure_message}
11+
ERROR
12+
end
13+
end
14+
15+
attach_to :capybara_accessibility_audit
16+
end
17+
end
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
module CapybaraAccessibilityAudit
2+
class NotificationReporter
3+
def initialize(test)
4+
@test = test
5+
end
6+
7+
def report(results)
8+
if results.violations.present?
9+
publish(results)
10+
end
11+
end
12+
13+
private
14+
15+
def publish(report)
16+
ActiveSupport::Notifications.instrument "report.capybara_accessibility_audit", {
17+
auditor: @test.accessibility_audit_auditor,
18+
options: @test.accessibility_audit_options,
19+
report: report,
20+
test: @test
21+
}
22+
end
23+
end
24+
end

spec/spec_helper.rb

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,10 +7,31 @@
77

88
Dummy::Application.initialize!
99

10+
module ReporterSpecHelpers
11+
def capture_report(&block)
12+
assert_raises(Minitest::Assertion, &block).message
13+
end
14+
15+
def assert_rule_violation(rule = nil, with: rule, without: nil, &block)
16+
report = capture_report(&block)
17+
18+
Array(with).flatten.each do |included|
19+
expect(report).to include(included)
20+
end
21+
22+
Array(without).flatten.each do |excluded|
23+
expect(report).not_to include(excluded)
24+
end
25+
end
26+
end
27+
1028
RSpec.configure do |config|
1129
config.use_active_record = false
1230

1331
config.filter_rails_from_backtrace!
32+
33+
config.include Rspec::Matchers::ActiveSupport::Notifications
34+
config.include ReporterSpecHelpers, type: :system
1435
end
1536

1637
Capybara.javascript_driver = ENV.fetch("DRIVER", "selenium_chrome_headless").to_sym
Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
require "spec_helper"
2+
3+
RSpec.describe "Audit assertions", type: :system, js: true do
4+
before :all do
5+
driven_by Capybara.javascript_driver
6+
self.accessibility_audit_reporter = :notification
7+
end
8+
9+
it "does not report when there are no violations detected" do
10+
expect do
11+
visit violations_path
12+
13+
expect(page).to have_link("Violate rule: label")
14+
end.to emit_event("report.capybara_accessibility_audit").exactly(0).times
15+
end
16+
17+
it "reports on violations detected after #visit" do
18+
assert_rule_violation "label: Form elements must have labels" do
19+
visit violations_path(rules: %w[label])
20+
end
21+
end
22+
23+
it "reports on violations detected after #click_on" do
24+
visit violations_path
25+
26+
assert_rule_violation "label: Form elements must have labels" do
27+
click_on "Violate rule: label"
28+
end
29+
end
30+
31+
it "reports on violations detected after #click_link" do
32+
visit violations_path
33+
34+
assert_rule_violation "label: Form elements must have labels" do
35+
click_link "Violate rule: label"
36+
end
37+
end
38+
39+
describe "Skipping Audit After Method" do
40+
skip_accessibility_audit_after :visit, :click_on
41+
42+
it "does not audit after a skipped method" do
43+
visit violations_path
44+
click_on "Violate rule: label"
45+
go_back
46+
47+
assert_rule_violation("label: Form elements must have labels") do
48+
click_link "Violate rule: label"
49+
end
50+
end
51+
end
52+
53+
def capture_report(&block)
54+
notification = nil
55+
56+
ActiveSupport::Notifications.subscribed(->(event) { notification = event }, "report.capybara_accessibility_audit", &block)
57+
58+
assert_equal self, notification.payload[:test]
59+
assert_kind_of CapybaraAccessibilityAudit::AxeAuditor, notification.payload[:auditor]
60+
61+
notification.payload[:report].failure_message
62+
end
63+
end
Lines changed: 0 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -157,16 +157,4 @@
157157
click_on "Violate rule: label"
158158
end
159159
end
160-
161-
def assert_rule_violation(rule = nil, with: rule, without: nil, &block)
162-
exception = assert_raises(Minitest::Assertion, &block)
163-
164-
Array(with).flatten.each do |included|
165-
expect(exception.message).to include(included)
166-
end
167-
168-
Array(without).flatten.each do |excluded|
169-
expect(exception.message).not_to include(excluded)
170-
end
171-
end
172160
end

0 commit comments

Comments
 (0)