rails_accessibility_testing 1.1.4 → 1.3.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: d58abecc3e5e128753499732dea24b016875593af005f776a9e94002f5d779e9
4
- data.tar.gz: 51f98255eb1ae6042b55310532c59adfb9ebaeedb5a9415d4e1be652986df1c7
3
+ metadata.gz: 916b677bef5f630731d65df12bdd430949d70876ec0c5a9c047e15353084ae7b
4
+ data.tar.gz: 4960a239874be87bc96a74ba7804cfc3213ae0ea62a4b9a367d437fed337e0d0
5
5
  SHA512:
6
- metadata.gz: 4d554890949ccc6009accdcac4f6740e43237192f075fee2345b8468edab548a32da307577967409922fe2275361bbf19ef89de51f3fc0bd89dbf3f09c9747d3
7
- data.tar.gz: 6be04fb755999ece4ab602dbfe014257f61243542f3c36ffea3dd0984fd58f5d425a8f94d7d4fdc97664b275502c6a0c6220aae032db07c3e683e4888c49c79d
6
+ metadata.gz: f0d0e645d4d2929da2b0d2461459510759dab172192e7564be8dd2e57f2771ff86144f4fb8ece7fcbd04351ba2cc0ee08964ea7a0e07d65485171718e7608a5c
7
+ data.tar.gz: d8678b98bd3978104dd2158ff04f3494ff291d690b75ecee4bc29121eb76d283fd378a1cb6f763f14904060a1e8cc9e39288dcc4ca7f8ab8420253fcbc1273e1
data/CHANGELOG.md CHANGED
@@ -5,6 +5,55 @@ All notable changes to this project will be documented in this file.
5
5
  The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
6
6
  and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7
7
 
8
+ ## [1.3.0] - 2024-12-XX
9
+
10
+ ### Added
11
+ - CLI reports now use ErrorMessageBuilder for detailed, formatted error messages
12
+ - CLI reports include comprehensive remediation steps, element details, and WCAG references
13
+ - Better empty alt text detection (checks both Capybara attributes and JavaScript getAttribute)
14
+ - Improved server port detection with better error handling and timeout management
15
+
16
+ ### Changed
17
+ - CLI default profile changed to `:development` for faster checks (color contrast disabled by default)
18
+ - Improved server wait logic with longer retry times (up to 20 seconds) and better port re-detection
19
+ - Report generation now skips when no URLs are checked (cleaner output when server isn't ready)
20
+ - Port detection now prioritizes common Rails ports (3000, 3001, 4000, 5000) and excludes problematic ports
21
+
22
+ ### Fixed
23
+ - Fixed logger accessor compatibility issue - logger access is now optional to work with older gem versions
24
+ - Fixed CLI connection issues by improving port detection and server readiness checks
25
+ - Fixed CLI showing empty reports when server isn't ready - now shows informative message instead
26
+ - Improved error handling for connection timeouts and connection refused errors
27
+ - Better handling of interrupt signals during server wait operations
28
+
29
+ ### Improved
30
+ - CLI error messages are now more detailed and actionable with specific remediation steps
31
+ - Server detection is more reliable with improved timeout handling and error recovery
32
+ - Better user experience when running in Procfile.dev with automatic retries
33
+
34
+ ## [1.2.0] - 2024-12-XX
35
+
36
+ ### Changed
37
+ - **BREAKING**: Removed unnecessary dependencies (selenium-webdriver, capybara from gemspec)
38
+ - Gem now has minimal dependencies - only requires `axe-core-capybara`
39
+ - Users provide their own capybara, selenium-webdriver, webdrivers in their Gemfile
40
+ - This allows users to control their own driver configuration for RSpec system specs
41
+ - CLI tool still works but requires users to have selenium-webdriver in their Gemfile if they want to use it
42
+
43
+ ## [1.1.6] - 2024-12-XX
44
+
45
+ ### Fixed
46
+ - Added server readiness check with retry mechanism to prevent connection refused errors
47
+ - CLI tool now waits up to 20 seconds for Rails server to be ready before attempting accessibility checks
48
+ - Prevents race conditions when running in Procfile.dev where a11y process starts before web server
49
+
50
+ ## [1.1.5] - 2024-12-XX
51
+
52
+ ### Fixed
53
+ - Fixed CLI tool "invalid argument" error when visiting paths by automatically converting paths to full URLs
54
+ - CLI tool now properly constructs `http://localhost:PORT/path` URLs when using Selenium with Rails apps
55
+ - Respects PORT, RAILS_PORT, and RAILS_URL environment variables for server URL configuration
56
+
8
57
  ## [1.1.4] - 2024-12-XX
9
58
 
10
59
  ### Fixed
@@ -88,6 +137,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
88
137
  - Compatible with RSpec Rails 6.0+
89
138
  - Modular architecture with rule engine and check definitions
90
139
 
140
+ [1.3.0]: https://github.com/rayraycodes/rails-accessibility-testing/releases/tag/v1.3.0
141
+ [1.2.0]: https://github.com/rayraycodes/rails-accessibility-testing/releases/tag/v1.2.0
142
+ [1.1.6]: https://github.com/rayraycodes/rails-accessibility-testing/releases/tag/v1.1.6
143
+ [1.1.5]: https://github.com/rayraycodes/rails-accessibility-testing/releases/tag/v1.1.5
91
144
  [1.1.4]: https://github.com/rayraycodes/rails-accessibility-testing/releases/tag/v1.1.4
92
145
  [1.1.3]: https://github.com/rayraycodes/rails-accessibility-testing/releases/tag/v1.1.3
93
146
  [1.1.0]: https://github.com/rayraycodes/rails-accessibility-testing/releases/tag/v1.1.0
data/README.md CHANGED
@@ -7,7 +7,7 @@
7
7
 
8
8
  **The RSpec + RuboCop of accessibility for Rails. Catch WCAG violations before they reach production.**
9
9
 
10
- **Current Version:** 1.1.0
10
+ **Current Version:** 1.2.0
11
11
 
12
12
  šŸ“– **[šŸ“š Full Documentation](https://rayraycodes.github.io/rails-accessibility-testing/)** | [šŸ’» GitHub](https://github.com/rayraycodes/rails-accessibility-testing) | [šŸ’Ž RubyGems](https://rubygems.org/gems/rails_accessibility_testing)
13
13
 
@@ -40,6 +40,8 @@ Add to your `Gemfile`:
40
40
  group :development, :test do
41
41
  gem 'rails_accessibility_testing'
42
42
  gem 'axe-core-capybara', '~> 4.0'
43
+ # Your existing Capybara, selenium-webdriver, webdrivers gems
44
+ # The gem has minimal dependencies - you control your own driver setup
43
45
  end
44
46
  ```
45
47
 
@@ -301,9 +303,13 @@ See [ARCHITECTURE.md](ARCHITECTURE.md) for detailed architecture documentation.
301
303
  - Ruby 3.0+ (3.1+ recommended)
302
304
  - Rails 6.0+ (7.1+ recommended)
303
305
  - RSpec Rails 6.0+ (for RSpec) or Minitest (for Minitest)
304
- - Capybara 3.0+
306
+ - Capybara 3.0+ (provided by your project)
307
+ - selenium-webdriver 4.0+ (provided by your project, for system specs)
308
+ - webdrivers (optional, provided by your project, for automatic driver management)
305
309
  - Chrome/Chromium browser
306
310
 
311
+ **Note:** As of version 1.2.0, the gem has minimal dependencies. You provide and configure Capybara, selenium-webdriver, and webdrivers in your own Gemfile, giving you full control over your test driver setup.
312
+
307
313
  ## šŸ¤ Contributing
308
314
 
309
315
  We welcome contributions! Please see [CONTRIBUTING.md](CONTRIBUTING.md) for guidelines.
@@ -83,6 +83,9 @@ module AccessibilityHelper
83
83
  # If we collected any errors and this was called directly (not from comprehensive), raise them
84
84
  if @accessibility_errors.any? && !@in_comprehensive_check
85
85
  raise format_all_errors(@accessibility_errors)
86
+ elsif @accessibility_errors.empty? && !@in_comprehensive_check
87
+ # Show success message when all checks pass
88
+ puts "\nāœ… All basic accessibility checks passed! (5 checks: form labels, images, interactive elements, headings, keyboard)"
86
89
  end
87
90
  end
88
91
 
@@ -103,6 +106,9 @@ module AccessibilityHelper
103
106
  # If we collected any errors, raise them all together
104
107
  if @accessibility_errors.any?
105
108
  raise format_all_errors(@accessibility_errors)
109
+ else
110
+ # Show success message when all checks pass
111
+ puts "\nāœ… All comprehensive accessibility checks passed! (11 checks: form labels, images, interactive elements, headings, keyboard, ARIA landmarks, form errors, table structure, duplicate IDs, skip links, color contrast)"
106
112
  end
107
113
  end
108
114
 
@@ -17,6 +17,10 @@ module RailsAccessibilityTesting
17
17
 
18
18
  page.all('img', visible: :all).each do |img|
19
19
  has_alt_attribute = page.evaluate_script("arguments[0].hasAttribute('alt')", img.native)
20
+ # Get alt value - might be nil, empty string, or actual text
21
+ alt_value = img[:alt] || ""
22
+ # Also check via JavaScript to be sure
23
+ alt_value_js = page.evaluate_script("arguments[0].getAttribute('alt')", img.native) || ""
20
24
 
21
25
  if has_alt_attribute == false
22
26
  element_ctx = element_context(img)
@@ -27,6 +31,17 @@ module RailsAccessibilityTesting
27
31
  wcag_reference: "1.1.1",
28
32
  remediation: generate_remediation(element_ctx)
29
33
  )
34
+ elsif (alt_value.blank? || alt_value_js.blank?) && has_alt_attribute
35
+ # Image has alt attribute but it's empty - warn about this
36
+ # Empty alt is valid for decorative images, but we should check if it's actually decorative
37
+ element_ctx = element_context(img)
38
+
39
+ violations << violation(
40
+ message: "Image has empty alt attribute - ensure this image is purely decorative. If it conveys information, add descriptive alt text.",
41
+ element_context: element_ctx,
42
+ wcag_reference: "1.1.1",
43
+ remediation: generate_remediation(element_ctx)
44
+ )
30
45
  end
31
46
  end
32
47
 
@@ -53,7 +53,7 @@ module RailsAccessibilityTesting
53
53
 
54
54
  def parse_options(argv)
55
55
  options = {
56
- profile: :test,
56
+ profile: :development, # Use development profile by default (faster, no color contrast)
57
57
  format: :human,
58
58
  output: nil,
59
59
  debug: false
@@ -119,7 +119,8 @@ module RailsAccessibilityTesting
119
119
 
120
120
  # Setup Capybara
121
121
  Capybara.default_driver = :selenium_chrome_headless
122
- Capybara.app = Rails.application if defined?(Rails)
122
+ # Don't set Capybara.app when using Selenium - it needs a real HTTP server
123
+ # The Rails server should be running separately (e.g., via Procfile.dev)
123
124
 
124
125
  engine = Engine::RuleEngine.new(config: config)
125
126
  all_violations = []
@@ -130,10 +131,48 @@ module RailsAccessibilityTesting
130
131
 
131
132
  targets.each do |target|
132
133
  begin
133
- Capybara.visit(target)
134
- violations = engine.check(Capybara.current_session, context: { url: target })
134
+ # Convert path to full URL if needed (when using Selenium with Rails)
135
+ url = normalize_url(target)
136
+
137
+ # Wait for server to be ready (with retries)
138
+ # If wait fails, try to re-detect port and update URL
139
+ if url.match?(/\Ahttps?:\/\//)
140
+ # First, try to detect the port (might not be ready yet)
141
+ uri = URI.parse(url)
142
+ detected_port = detect_server_port
143
+
144
+ # If we detected a different port, update the URL
145
+ if detected_port != uri.port.to_s
146
+ url = "#{uri.scheme}://#{uri.host}:#{detected_port}#{uri.path}"
147
+ end
148
+
149
+ # Now wait for server to be ready
150
+ server_ready = wait_for_server(url, max_retries: 20, retry_delay: 1)
151
+
152
+ # If still not ready, try re-detecting port one more time
153
+ unless server_ready
154
+ new_port = detect_server_port
155
+ if new_port != uri.port.to_s
156
+ url = "#{uri.scheme}://#{uri.host}:#{new_port}#{uri.path}"
157
+ server_ready = wait_for_server(url, max_retries: 20, retry_delay: 1)
158
+ end
159
+
160
+ # If still not ready, skip this check and try next time
161
+ unless server_ready
162
+ # Server still starting - this is normal, will retry automatically
163
+ $stderr.puts "Waiting for server to start... (will retry automatically)"
164
+ next
165
+ end
166
+ end
167
+ end
168
+
169
+ Capybara.visit(url)
170
+ violations = engine.check(Capybara.current_session, context: { url: url })
135
171
  all_violations.concat(violations)
136
- checked_urls << { url: target, violations: violations.count }
172
+ checked_urls << { url: url, violations: violations.count }
173
+ rescue Interrupt
174
+ # Handle interrupt gracefully - exit the loop
175
+ break
137
176
  rescue StandardError => e
138
177
  $stderr.puts "Error checking #{target}: #{e.message}"
139
178
  end
@@ -168,6 +207,92 @@ module RailsAccessibilityTesting
168
207
  targets.uniq
169
208
  end
170
209
 
210
+ def normalize_url(target)
211
+ # If it's already a full URL, return as-is
212
+ return target if target.match?(/\Ahttps?:\/\//)
213
+
214
+ # If it's a path and we're using Selenium, construct a full URL
215
+ # Try to detect the actual port, or use environment variables, or default to 3000
216
+ port = detect_server_port
217
+ base_url = ENV['RAILS_URL'] || "http://localhost:#{port}"
218
+
219
+ # Ensure path starts with /
220
+ path = target.start_with?('/') ? target : "/#{target}"
221
+ "#{base_url}#{path}"
222
+ end
223
+
224
+ def detect_server_port
225
+ # Check environment variables first
226
+ return ENV['PORT'] if ENV['PORT']
227
+ return ENV['RAILS_PORT'] if ENV['RAILS_PORT']
228
+
229
+ # Try to detect port from Rails server - check common Rails ports
230
+ # Check in order: 3000 (most common), then others
231
+ # Prioritize 3000 first as it's the Rails default
232
+ common_ports = [3000, 3001, 4000, 5000]
233
+
234
+ common_ports.each do |port|
235
+ begin
236
+ require 'net/http'
237
+ http = Net::HTTP.new('localhost', port)
238
+ http.open_timeout = 1
239
+ http.read_timeout = 1
240
+ # Try to get a response - check if it looks like a Rails server
241
+ response = http.head('/')
242
+ # Accept 2xx, 3xx, or 4xx responses (server is responding)
243
+ # Reject 5xx as it might be a proxy or error
244
+ if response.code.to_i < 500
245
+ # Additional check: Rails servers usually have certain headers
246
+ # But for now, any HTTP response on these common ports is likely Rails
247
+ return port.to_s
248
+ end
249
+ rescue Errno::ECONNREFUSED, Net::OpenTimeout, Net::ReadTimeout, SocketError, Interrupt, Errno::EHOSTUNREACH, Errno::ETIMEDOUT
250
+ # Port not available or interrupted, try next
251
+ next
252
+ end
253
+ end
254
+
255
+ # Default to 3000 if nothing found
256
+ '3000'
257
+ end
258
+
259
+ def wait_for_server(url, max_retries: 15, retry_delay: 1)
260
+ require 'net/http'
261
+ require 'uri'
262
+
263
+ uri = URI.parse(url)
264
+ base_url = "#{uri.scheme}://#{uri.host}:#{uri.port}"
265
+
266
+ max_retries.times do |attempt|
267
+ begin
268
+ http = Net::HTTP.new(uri.host, uri.port)
269
+ http.open_timeout = 2
270
+ http.read_timeout = 2
271
+ response = http.head('/')
272
+ return true if response.code.to_i < 500 # Server is responding
273
+ rescue Errno::ECONNREFUSED, Errno::ETIMEDOUT, Net::OpenTimeout, Net::ReadTimeout, SocketError, Errno::EHOSTUNREACH
274
+ # Server not ready yet
275
+ if attempt < max_retries - 1
276
+ begin
277
+ sleep(retry_delay)
278
+ rescue Interrupt
279
+ # Handle interrupt gracefully - return false to indicate failure
280
+ return false
281
+ end
282
+ next
283
+ else
284
+ # Last attempt failed
285
+ return false
286
+ end
287
+ rescue Interrupt
288
+ # Handle interrupt gracefully
289
+ return false
290
+ end
291
+ end
292
+
293
+ false
294
+ end
295
+
171
296
  def resolve_routes(routes)
172
297
  return [] unless defined?(Rails) && Rails.application
173
298
 
@@ -181,6 +306,11 @@ module RailsAccessibilityTesting
181
306
  end
182
307
 
183
308
  def generate_report(results, options)
309
+ # Don't generate report if no URLs were checked (server not ready)
310
+ if results[:summary][:urls_checked] == 0
311
+ return
312
+ end
313
+
184
314
  output = case options[:format]
185
315
  when :json
186
316
  generate_json_report(results)
@@ -213,11 +343,14 @@ module RailsAccessibilityTesting
213
343
  output << ""
214
344
 
215
345
  results[:violations].each_with_index do |violation, index|
216
- output << "#{index + 1}. #{violation.message}"
217
- output << " Rule: #{violation.rule_name}"
218
- output << " URL: #{violation.page_context[:url]}"
219
- output << " View: #{violation.page_context[:view_file]}" if violation.page_context[:view_file]
220
- output << ""
346
+ # Use ErrorMessageBuilder for detailed formatted messages
347
+ detailed_message = ErrorMessageBuilder.build(
348
+ error_type: violation.message,
349
+ element_context: violation.element_context || {},
350
+ page_context: violation.page_context || {}
351
+ )
352
+ output << detailed_message
353
+ output << "" if index < results[:violations].count - 1 # Add spacing between violations
221
354
  end
222
355
  else
223
356
  output << "āœ… No accessibility violations found!"
@@ -251,7 +384,8 @@ module RailsAccessibilityTesting
251
384
  -v, --version Show version
252
385
 
253
386
  Examples:
254
- rails_a11y check /home /about
387
+ rails_a11y /home /about
388
+ rails_a11y /
255
389
  rails_a11y --urls https://example.com
256
390
  rails_a11y --routes home_path about_path --format json --output report.json
257
391
  HELP
@@ -38,7 +38,9 @@ module RailsAccessibilityTesting
38
38
 
39
39
  merge_profile_config(parsed, profile)
40
40
  rescue StandardError => e
41
- RailsAccessibilityTesting.config.logger&.warn("Failed to load config: #{e.message}") if defined?(RailsAccessibilityTesting)
41
+ if defined?(RailsAccessibilityTesting) && RailsAccessibilityTesting.config.respond_to?(:logger) && RailsAccessibilityTesting.config.logger
42
+ RailsAccessibilityTesting.config.logger.warn("Failed to load config: #{e.message}")
43
+ end
42
44
  default_config
43
45
  end
44
46
 
@@ -9,11 +9,13 @@ module RailsAccessibilityTesting
9
9
  # end
10
10
  #
11
11
  # @attr [Boolean] auto_run_checks Whether to automatically run checks after system specs
12
+ # @attr [Logger, nil] logger Optional logger for accessibility check output
12
13
  class Configuration
13
- attr_accessor :auto_run_checks
14
+ attr_accessor :auto_run_checks, :logger
14
15
 
15
16
  def initialize
16
17
  @auto_run_checks = true
18
+ @logger = nil
17
19
  end
18
20
  end
19
21
 
@@ -38,7 +38,9 @@ module RailsAccessibilityTesting
38
38
  @violation_collector.add(violations) if violations.any?
39
39
  rescue StandardError => e
40
40
  # Log but don't fail - one check error shouldn't stop others
41
- RailsAccessibilityTesting.config.logger&.error("Check #{check_class.rule_name} failed: #{e.message}") if defined?(RailsAccessibilityTesting)
41
+ if defined?(RailsAccessibilityTesting) && RailsAccessibilityTesting.config.respond_to?(:logger) && RailsAccessibilityTesting.config.logger
42
+ RailsAccessibilityTesting.config.logger.error("Check #{check_class.rule_name} failed: #{e.message}")
43
+ end
42
44
  end
43
45
  end
44
46
 
@@ -48,6 +48,12 @@ module RailsAccessibilityTesting
48
48
  # Run comprehensive accessibility checks
49
49
  instance = example.example_group_instance
50
50
  instance.check_comprehensive_accessibility
51
+
52
+ # Show success message if checks passed (no exception was raised)
53
+ unless example.exception
54
+ $stdout.puts "\nāœ… All comprehensive accessibility checks passed! (11 checks)"
55
+ $stdout.flush
56
+ end
51
57
  rescue StandardError => e
52
58
  example.set_exception(e)
53
59
  end
@@ -1,4 +1,4 @@
1
1
  module RailsAccessibilityTesting
2
- VERSION = "1.1.4"
2
+ VERSION = "1.3.0"
3
3
  end
4
4
 
@@ -38,7 +38,7 @@ begin
38
38
  require_relative 'rails_accessibility_testing/version'
39
39
  rescue LoadError
40
40
  module RailsAccessibilityTesting
41
- VERSION = '1.1.0'
41
+ VERSION = '1.3.0'
42
42
  end
43
43
  end
44
44
 
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: rails_accessibility_testing
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.1.4
4
+ version: 1.3.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Regan Maharjan
@@ -24,20 +24,6 @@ dependencies:
24
24
  - - "~>"
25
25
  - !ruby/object:Gem::Version
26
26
  version: '4.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: '3.0'
34
- type: :runtime
35
- prerelease: false
36
- version_requirements: !ruby/object:Gem::Requirement
37
- requirements:
38
- - - "~>"
39
- - !ruby/object:Gem::Version
40
- version: '3.0'
41
27
  - !ruby/object:Gem::Dependency
42
28
  name: bundler
43
29
  requirement: !ruby/object:Gem::Requirement
@@ -66,20 +52,6 @@ dependencies:
66
52
  - - "~>"
67
53
  - !ruby/object:Gem::Version
68
54
  version: '13.0'
69
- - !ruby/object:Gem::Dependency
70
- name: yard
71
- requirement: !ruby/object:Gem::Requirement
72
- requirements:
73
- - - "~>"
74
- - !ruby/object:Gem::Version
75
- version: '0.9'
76
- type: :development
77
- prerelease: false
78
- version_requirements: !ruby/object:Gem::Requirement
79
- requirements:
80
- - - "~>"
81
- - !ruby/object:Gem::Version
82
- version: '0.9'
83
55
  - !ruby/object:Gem::Dependency
84
56
  name: rspec
85
57
  requirement: !ruby/object:Gem::Requirement
@@ -94,34 +66,6 @@ dependencies:
94
66
  - - "~>"
95
67
  - !ruby/object:Gem::Version
96
68
  version: '3.12'
97
- - !ruby/object:Gem::Dependency
98
- name: capybara
99
- requirement: !ruby/object:Gem::Requirement
100
- requirements:
101
- - - "~>"
102
- - !ruby/object:Gem::Version
103
- version: '3.0'
104
- type: :development
105
- prerelease: false
106
- version_requirements: !ruby/object:Gem::Requirement
107
- requirements:
108
- - - "~>"
109
- - !ruby/object:Gem::Version
110
- version: '3.0'
111
- - !ruby/object:Gem::Dependency
112
- name: selenium-webdriver
113
- requirement: !ruby/object:Gem::Requirement
114
- requirements:
115
- - - "~>"
116
- - !ruby/object:Gem::Version
117
- version: '4.0'
118
- type: :development
119
- prerelease: false
120
- version_requirements: !ruby/object:Gem::Requirement
121
- requirements:
122
- - - "~>"
123
- - !ruby/object:Gem::Version
124
- version: '4.0'
125
69
  description: Comprehensive, opinionated but configurable accessibility testing gem
126
70
  for Rails. Integrates seamlessly into your test suite with RSpec and Minitest support.
127
71
  Includes CLI tool, Rails generator, YAML configuration, and 11+ WCAG 2.1 AA aligned