rails_accessibility_testing 1.2.0 → 1.4.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: a7f693b05664c88f5b08e1917caa94ad360754d3113787d89c8d7b59331c1b55
4
- data.tar.gz: 29bfe1bfa996b04d9f779c4d576c9a2b799a041ecc52d0d35c94b1cefb9b2f0a
3
+ metadata.gz: 5ed11e69796995b294eeaa17e2627e46b3122356d7383e2e1ad3b213c8f2ac78
4
+ data.tar.gz: 410f9e2c88b5e27e0a6319fb62bb854d832f8e74a25d7b3970aaf9b3f07a0fe5
5
5
  SHA512:
6
- metadata.gz: b525904eca4df9bf87d32fed9b27587681a45cfb73ae4f630ba8700929fa763fef316940e0b246c84eb2d7216b3def1df82e99d6375c859760d72f34e05ac1fa
7
- data.tar.gz: 750912a05ec0b37f24bdb2011052f140c827940ec1063f6beaf1d5974708e386d21399808ecc3133ed7899f4ba7202638292f20950de953dca9508f0a275af70
6
+ metadata.gz: b3c309bbcc50838e5a981cf997c74051e10a79ce0b03d67450ad3e0dac8486d6ed79cfad4cd201e2cd25abf432370d80799a67ca9f136e11e2c055724e5f9dee
7
+ data.tar.gz: d8f2aaaad2e708e83dc4c9f7fd283ec815904557549f33c2f4b491fdeeed6c196b55c759b79effe7074bfda21cb4e3d07ccdefedcf07e065cdf4302f4395b74d
data/CHANGELOG.md CHANGED
@@ -5,6 +5,44 @@ 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.4.0] - 2025-11-18
9
+
10
+ ### Changed
11
+ - Updated documentation examples to use clearer language ("runs accessibility checks" instead of "passes accessibility checks")
12
+ - Improved test descriptions to accurately reflect that tests will fail if accessibility issues are found
13
+ - Enhanced comments in examples to clarify when success messages appear
14
+
15
+ ### Improved
16
+ - Better clarity in documentation about when accessibility checks pass vs fail
17
+ - More accurate test descriptions that don't imply tests will pass when they may fail
18
+ - Improved user understanding of accessibility check behavior
19
+
20
+ ## [1.3.0] - 2025-11-18
21
+
22
+ ### Added
23
+ - CLI reports now use ErrorMessageBuilder for detailed, formatted error messages
24
+ - CLI reports include comprehensive remediation steps, element details, and WCAG references
25
+ - Better empty alt text detection (checks both Capybara attributes and JavaScript getAttribute)
26
+ - Improved server port detection with better error handling and timeout management
27
+
28
+ ### Changed
29
+ - CLI default profile changed to `:development` for faster checks (color contrast disabled by default)
30
+ - Improved server wait logic with longer retry times (up to 20 seconds) and better port re-detection
31
+ - Report generation now skips when no URLs are checked (cleaner output when server isn't ready)
32
+ - Port detection now prioritizes common Rails ports (3000, 3001, 4000, 5000) and excludes problematic ports
33
+
34
+ ### Fixed
35
+ - Fixed logger accessor compatibility issue - logger access is now optional to work with older gem versions
36
+ - Fixed CLI connection issues by improving port detection and server readiness checks
37
+ - Fixed CLI showing empty reports when server isn't ready - now shows informative message instead
38
+ - Improved error handling for connection timeouts and connection refused errors
39
+ - Better handling of interrupt signals during server wait operations
40
+
41
+ ### Improved
42
+ - CLI error messages are now more detailed and actionable with specific remediation steps
43
+ - Server detection is more reliable with improved timeout handling and error recovery
44
+ - Better user experience when running in Procfile.dev with automatic retries
45
+
8
46
  ## [1.2.0] - 2024-12-XX
9
47
 
10
48
  ### Changed
@@ -111,6 +149,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
111
149
  - Compatible with RSpec Rails 6.0+
112
150
  - Modular architecture with rule engine and check definitions
113
151
 
152
+ [1.3.0]: https://github.com/rayraycodes/rails-accessibility-testing/releases/tag/v1.3.0
114
153
  [1.2.0]: https://github.com/rayraycodes/rails-accessibility-testing/releases/tag/v1.2.0
115
154
  [1.1.6]: https://github.com/rayraycodes/rails-accessibility-testing/releases/tag/v1.1.6
116
155
  [1.1.5]: https://github.com/rayraycodes/rails-accessibility-testing/releases/tag/v1.1.5
@@ -54,7 +54,7 @@ Let's see it in action. Create a simple system spec:
54
54
 
55
55
  ```ruby
56
56
  # spec/system/home_spec.rb
57
- RSpec.describe "Home Page" do
57
+ RSpec.describe "Home Page", type: :system do
58
58
  it "displays the welcome message" do
59
59
  visit root_path
60
60
  expect(page).to have_content("Welcome")
@@ -63,6 +63,41 @@ RSpec.describe "Home Page" do
63
63
  end
64
64
  ```
65
65
 
66
+ ## Running Comprehensive Checks Explicitly
67
+
68
+ While checks run automatically after each `visit`, you can also run comprehensive checks explicitly at any point in your test:
69
+
70
+ ```ruby
71
+ # spec/system/home_page_accessibility_spec.rb
72
+ require 'rails_helper'
73
+
74
+ RSpec.describe 'Home Page Accessibility', type: :system do
75
+ it 'loads the page and runs comprehensive accessibility checks' do
76
+ visit root_path
77
+ expect(page).to have_content('Welcome')
78
+
79
+ # Run comprehensive accessibility checks explicitly
80
+ # This will fail the test if any accessibility issues are found
81
+ check_comprehensive_accessibility
82
+ # ✅ This runs all 11 comprehensive checks:
83
+ # - Form labels, Image alt text, Interactive elements
84
+ # - Heading hierarchy, Keyboard accessibility, ARIA landmarks
85
+ # - Form errors, Table structure, Duplicate IDs
86
+ # - Skip links, Color contrast (if enabled)
87
+ # If all checks pass, you'll see: "All comprehensive accessibility checks passed! (11 checks)"
88
+ end
89
+ end
90
+ ```
91
+
92
+ **When to use explicit checks:**
93
+ - When you want to run checks at a specific point in your test (e.g., after filling a form)
94
+ - When you want to ensure checks run even if the test might fail before the automatic check
95
+ - When you want to test multiple pages in one spec and check each one explicitly
96
+
97
+ **Note:** Even if you call `check_comprehensive_accessibility` explicitly, the automatic checks will still run after the test completes (unless the test fails before reaching the explicit check).
98
+
99
+ ### Example: Comprehensive Check Output
100
+
66
101
  If there are accessibility issues, you'll see detailed error messages like:
67
102
 
68
103
  ```
@@ -96,19 +131,33 @@ If there are accessibility issues, you'll see detailed error messages like:
96
131
 
97
132
  ## Understanding the Checks
98
133
 
99
- Rails A11y runs 11 comprehensive checks:
134
+ Rails A11y runs **11 comprehensive checks** automatically. These checks are WCAG 2.1 AA aligned:
100
135
 
101
- 1. **Form Labels** - All inputs have associated labels
102
- 2. **Image Alt Text** - All images have alt attributes
103
- 3. **Interactive Elements** - Buttons and links have accessible names
104
- 4. **Heading Hierarchy** - Proper h1-h6 structure
136
+ 1. **Form Labels** - All form inputs have associated labels
137
+ 2. **Image Alt Text** - All images have descriptive alt attributes (including empty alt="" detection)
138
+ 3. **Interactive Elements** - Buttons, links, and other interactive elements have accessible names
139
+ 4. **Heading Hierarchy** - Proper h1-h6 structure without skipping levels
105
140
  5. **Keyboard Accessibility** - All interactive elements are keyboard accessible
106
- 6. **ARIA Landmarks** - Proper use of ARIA landmark roles
107
- 7. **Form Error Associations** - Errors linked to form fields
108
- 8. **Table Structure** - Tables have proper headers
109
- 9. **Duplicate IDs** - No duplicate ID attributes
110
- 10. **Skip Links** - Skip navigation links present
111
- 11. **Color Contrast** - Text meets contrast requirements (optional)
141
+ 6. **ARIA Landmarks** - Proper use of ARIA landmark roles for page structure
142
+ 7. **Form Error Associations** - Form errors are properly linked to their form fields
143
+ 8. **Table Structure** - Tables have proper headers and structure
144
+ 9. **Duplicate IDs** - No duplicate ID attributes on the page
145
+ 10. **Skip Links** - Skip navigation links are present for keyboard users
146
+ 11. **Color Contrast** - Text meets WCAG contrast requirements (optional, disabled by default for performance)
147
+
148
+ ### What `check_comprehensive_accessibility` Does
149
+
150
+ When you call `check_comprehensive_accessibility`, it runs all 11 checks above and provides detailed error messages for any violations found. Each error includes:
151
+
152
+ - **File location hints** - Know exactly which view file to fix
153
+ - **Element details** - Tag, ID, classes, and visible text
154
+ - **Actionable fix instructions** - Code examples showing how to fix the issue
155
+ - **WCAG references** - Links to relevant WCAG guidelines
156
+
157
+ If all checks pass, you'll see:
158
+ ```
159
+ ✅ All comprehensive accessibility checks passed! (11 checks)
160
+ ```
112
161
 
113
162
  ## Configuration
114
163
 
@@ -171,6 +220,7 @@ end
171
220
 
172
221
  ## Next Steps
173
222
 
223
+ - **⭐ Read the [System Specs Guide](system_specs_for_accessibility.md)** - Recommended approach for reliable accessibility testing
174
224
  - **Read the [CI Integration Guide](continuous_integration.md)** to set up automated checks
175
225
  - **Check out [Writing Accessible Views](writing_accessible_views_in_rails.md)** for best practices
176
226
  - **See [Working with Designers](working_with_designers_and_content_authors.md)** for team collaboration
@@ -0,0 +1,248 @@
1
+ # Using System Specs for Accessibility Testing
2
+
3
+ System specs are the **recommended and most reliable** way to run accessibility checks in your Rails application. This guide shows you how to set up continuous accessibility testing using RSpec system specs.
4
+
5
+ ## Why System Specs?
6
+
7
+ ✅ **More Reliable** - Runs in the same test environment as your other specs
8
+ ✅ **Faster** - No need to wait for external server processes
9
+ ✅ **Better Integration** - Works seamlessly with your existing test suite
10
+ ✅ **Automatic** - Checks run automatically after each `visit` in system specs
11
+ ✅ **Clear Feedback** - Detailed error messages with file locations and fix instructions
12
+
13
+ ## Quick Setup
14
+
15
+ ### 1. Create System Specs
16
+
17
+ Create system specs for the pages you want to test. Name them with `_accessibility_spec.rb` suffix for clarity:
18
+
19
+ ```ruby
20
+ # spec/system/home_page_accessibility_spec.rb
21
+ require 'rails_helper'
22
+
23
+ RSpec.describe 'Home Page Accessibility', type: :system do
24
+ it 'loads the page and runs comprehensive accessibility checks' do
25
+ visit root_path
26
+ expect(page).to have_content('Biorepository').or have_content('Welcome')
27
+
28
+ # Run comprehensive accessibility checks
29
+ # This will fail the test if any accessibility issues are found
30
+ check_comprehensive_accessibility
31
+ # ✅ If all checks pass, you'll see: "All comprehensive accessibility checks passed! (11 checks)"
32
+ end
33
+ end
34
+ ```
35
+
36
+ ### 2. Automatic Checks
37
+
38
+ The gem automatically runs comprehensive accessibility checks after each `visit` in system specs. You don't need to call `check_comprehensive_accessibility` manually unless you want to run checks at a specific point in your test.
39
+
40
+ ### 3. Add to Procfile.dev (Optional)
41
+
42
+ For continuous testing during development, add to your `Procfile.dev`:
43
+
44
+ ```ruby
45
+ web: $(bundle show rails_accessibility_testing)/exe/rails_server_safe
46
+ css: bin/rails dartsass:watch
47
+ a11y: while true; do bundle exec rspec spec/system/*_accessibility_spec.rb; sleep 30; done
48
+ ```
49
+
50
+ This will run your accessibility specs every 30 seconds while you develop.
51
+
52
+ ## Example Specs
53
+
54
+ ### Basic Page Check
55
+
56
+ ```ruby
57
+ # spec/system/home_page_accessibility_spec.rb
58
+ require 'rails_helper'
59
+
60
+ RSpec.describe 'Home Page Accessibility', type: :system do
61
+ it 'runs accessibility checks on the home page' do
62
+ visit root_path
63
+ # ✅ Comprehensive accessibility checks run automatically after this test!
64
+ # The test will fail if any accessibility issues are found
65
+ end
66
+ end
67
+ ```
68
+
69
+ ### Multiple Pages
70
+
71
+ ```ruby
72
+ # spec/system/pages_accessibility_spec.rb
73
+ require 'rails_helper'
74
+
75
+ RSpec.describe 'Pages Accessibility', type: :system do
76
+ it 'runs accessibility checks on home page' do
77
+ visit root_path
78
+ # Checks run automatically - test fails if issues found
79
+ end
80
+
81
+ it 'runs accessibility checks on about page' do
82
+ visit about_path
83
+ # Checks run automatically - test fails if issues found
84
+ end
85
+
86
+ it 'runs accessibility checks on contact page' do
87
+ visit contact_path
88
+ # Checks run automatically - test fails if issues found
89
+ end
90
+ end
91
+ ```
92
+
93
+ ### With User Authentication
94
+
95
+ ```ruby
96
+ # spec/system/dashboard_accessibility_spec.rb
97
+ require 'rails_helper'
98
+
99
+ RSpec.describe 'Dashboard Accessibility', type: :system do
100
+ before do
101
+ user = FactoryBot.create(:user)
102
+ sign_in user
103
+ end
104
+
105
+ it 'runs accessibility checks on dashboard' do
106
+ visit dashboard_path
107
+ # ✅ Comprehensive accessibility checks run automatically after authentication!
108
+ # The test will fail if any accessibility issues are found
109
+ end
110
+ end
111
+ ```
112
+
113
+ ### Skip Checks for Specific Tests
114
+
115
+ ```ruby
116
+ it 'does something without accessibility checks', skip_a11y: true do
117
+ visit some_path
118
+ # Accessibility checks won't run for this test
119
+ end
120
+ ```
121
+
122
+ ## What Gets Checked
123
+
124
+ The gem automatically runs **11 comprehensive accessibility checks**:
125
+
126
+ 1. ✅ **Form Labels** - All form inputs have associated labels
127
+ 2. ✅ **Image Alt Text** - All images have descriptive alt attributes
128
+ 3. ✅ **Interactive Elements** - Buttons, links have accessible names
129
+ 4. ✅ **Heading Hierarchy** - Proper h1-h6 structure
130
+ 5. ✅ **Keyboard Accessibility** - All interactive elements keyboard accessible
131
+ 6. ✅ **ARIA Landmarks** - Proper use of ARIA landmark roles
132
+ 7. ✅ **Form Error Associations** - Errors linked to form fields
133
+ 8. ✅ **Table Structure** - Tables have proper headers
134
+ 9. ✅ **Duplicate IDs** - No duplicate ID attributes
135
+ 10. ✅ **Skip Links** - Skip navigation links present
136
+ 11. ✅ **Color Contrast** - Text meets contrast requirements (optional, disabled by default)
137
+
138
+ ## Success Messages
139
+
140
+ When all checks pass, you'll see:
141
+
142
+ ```
143
+ ✅ All comprehensive accessibility checks passed! (11 checks)
144
+ ```
145
+
146
+ ## Error Messages
147
+
148
+ When issues are found, you get detailed, actionable errors:
149
+
150
+ ```
151
+ ======================================================================
152
+ ❌ ACCESSIBILITY ERROR: Page missing H1 heading
153
+ ======================================================================
154
+
155
+ 📄 Page Being Tested:
156
+ URL: http://127.0.0.1:54384/
157
+ Path: /
158
+ 📝 Likely View File: app/views/home/about.html.erb
159
+
160
+ 📍 Element Details:
161
+ Tag: <page>
162
+ ID: (none)
163
+ Classes: (none)
164
+ Visible text: Page has no H1 heading
165
+
166
+ 🔧 HOW TO FIX:
167
+ Add an <h1> heading to your page:
168
+
169
+ <h1>Main Page Title</h1>
170
+
171
+ Or in Rails ERB:
172
+ <h1><%= @page_title || 'Default Title' %></h1>
173
+
174
+ 💡 Best Practice: Every page should have exactly one <h1>.
175
+ It should describe the main purpose of the page.
176
+
177
+ 📖 WCAG Reference: https://www.w3.org/WAI/WCAG21/Understanding/
178
+ ======================================================================
179
+ ```
180
+
181
+ ## Running Specs
182
+
183
+ ### Run All Accessibility Specs
184
+
185
+ ```bash
186
+ bundle exec rspec spec/system/*_accessibility_spec.rb
187
+ ```
188
+
189
+ ### Run Specific Spec
190
+
191
+ ```bash
192
+ bundle exec rspec spec/system/home_page_accessibility_spec.rb
193
+ ```
194
+
195
+ ### Run with Documentation Format
196
+
197
+ ```bash
198
+ bundle exec rspec spec/system/*_accessibility_spec.rb --format documentation
199
+ ```
200
+
201
+ ## Continuous Integration
202
+
203
+ Add to your CI configuration:
204
+
205
+ ```yaml
206
+ # .github/workflows/ci.yml
207
+ - name: Run Accessibility Tests
208
+ run: bundle exec rspec spec/system/*_accessibility_spec.rb
209
+ ```
210
+
211
+ ## Best Practices
212
+
213
+ 1. **Name your specs clearly** - Use `_accessibility_spec.rb` suffix
214
+ 2. **Test critical paths** - Focus on user-facing pages
215
+ 3. **Keep specs simple** - One page per spec is often enough
216
+ 4. **Use Procfile.dev** - For continuous testing during development
217
+ 5. **Run in CI** - Catch issues before they reach production
218
+
219
+ ## Troubleshooting
220
+
221
+ ### Checks Not Running
222
+
223
+ Make sure:
224
+ - Your spec has `type: :system`
225
+ - You call `visit` in your test
226
+ - The gem is properly configured in `spec/rails_helper.rb`
227
+
228
+ ### Success Message Not Showing
229
+
230
+ The success message appears when all checks pass. If you don't see it, there may be silent failures. Check your RSpec output for any exceptions.
231
+
232
+ ### Slow Tests
233
+
234
+ Disable color contrast checking in development:
235
+
236
+ ```yaml
237
+ # config/accessibility.yml
238
+ development:
239
+ checks:
240
+ color_contrast: false
241
+ ```
242
+
243
+ ## Next Steps
244
+
245
+ - See [Getting Started Guide](getting_started.md) for initial setup
246
+ - See [Continuous Integration Guide](continuous_integration.md) for CI/CD setup
247
+ - See [Writing Accessible Views](writing_accessible_views_in_rails.md) for best practices
248
+
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.4.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
 
@@ -82,13 +84,45 @@ RailsAccessibilityTesting::Integration::MinitestIntegration.setup!
82
84
 
83
85
  ## 📖 Usage
84
86
 
87
+ ### System Specs (Recommended)
88
+
89
+ **System specs are the recommended and most reliable way to run accessibility checks.** They're faster, more reliable, and integrate seamlessly with your test suite.
90
+
91
+ Create system specs for your pages:
92
+
93
+ ```ruby
94
+ # spec/system/home_page_accessibility_spec.rb
95
+ require 'rails_helper'
96
+
97
+ RSpec.describe 'Home Page Accessibility', type: :system do
98
+ it 'loads successfully and passes comprehensive accessibility checks' do
99
+ visit root_path
100
+ expect(page).to have_content('Biorepository').or have_content('Welcome')
101
+
102
+ # Run comprehensive accessibility checks
103
+ check_comprehensive_accessibility
104
+ # ✅ Comprehensive accessibility checks (11 checks) also run automatically after this test!
105
+ end
106
+ end
107
+ ```
108
+
109
+ **Accessibility checks run automatically after each `visit` in system specs!**
110
+
111
+ For continuous testing during development, add to your `Procfile.dev`:
112
+
113
+ ```ruby
114
+ a11y: while true; do bundle exec rspec spec/system/*_accessibility_spec.rb; sleep 30; done
115
+ ```
116
+
117
+ 📖 **[See the full System Specs Guide](GUIDES/system_specs_for_accessibility.md)** for detailed examples and best practices.
118
+
85
119
  ### Automatic Checks
86
120
 
87
121
  Just write your specs normally - checks run automatically:
88
122
 
89
123
  ```ruby
90
124
  # spec/system/home_page_spec.rb
91
- RSpec.describe "Home Page" do
125
+ RSpec.describe "Home Page", type: :system do
92
126
  it "displays welcome message" do
93
127
  visit root_path
94
128
  expect(page).to have_content("Welcome")
@@ -268,6 +302,7 @@ Complete documentation site with all guides, examples, and API reference. The do
268
302
 
269
303
  ### Guides
270
304
 
305
+ - **[System Specs for Accessibility](GUIDES/system_specs_for_accessibility.md)** - ⭐ **Recommended approach** - Using system specs for reliable accessibility testing
271
306
  - **[Getting Started](GUIDES/getting_started.md)** - Quick start guide
272
307
  - **[Continuous Integration](GUIDES/continuous_integration.md)** - CI/CD setup
273
308
  - **[Writing Accessible Views](GUIDES/writing_accessible_views_in_rails.md)** - Best practices
@@ -301,9 +336,13 @@ See [ARCHITECTURE.md](ARCHITECTURE.md) for detailed architecture documentation.
301
336
  - Ruby 3.0+ (3.1+ recommended)
302
337
  - Rails 6.0+ (7.1+ recommended)
303
338
  - RSpec Rails 6.0+ (for RSpec) or Minitest (for Minitest)
304
- - Capybara 3.0+
339
+ - Capybara 3.0+ (provided by your project)
340
+ - selenium-webdriver 4.0+ (provided by your project, for system specs)
341
+ - webdrivers (optional, provided by your project, for automatic driver management)
305
342
  - Chrome/Chromium browser
306
343
 
344
+ **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.
345
+
307
346
  ## 🤝 Contributing
308
347
 
309
348
  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
@@ -106,6 +106,9 @@ module RailsAccessibilityTesting
106
106
  end
107
107
 
108
108
  def run_checks(options, config)
109
+ # Reset wait attempts counter for each run
110
+ @wait_attempts = 0
111
+
109
112
  require 'capybara'
110
113
  require 'capybara/dsl'
111
114
  require 'selenium-webdriver'
@@ -135,12 +138,49 @@ module RailsAccessibilityTesting
135
138
  url = normalize_url(target)
136
139
 
137
140
  # Wait for server to be ready (with retries)
138
- wait_for_server(url) if url.match?(/\Ahttps?:\/\//)
141
+ # If wait fails, try to re-detect port and update URL
142
+ if url.match?(/\Ahttps?:\/\//)
143
+ # First, try to detect the port (might not be ready yet)
144
+ uri = URI.parse(url)
145
+ detected_port = detect_server_port
146
+
147
+ # If we detected a different port, update the URL
148
+ if detected_port != uri.port.to_s
149
+ url = "#{uri.scheme}://#{uri.host}:#{detected_port}#{uri.path}"
150
+ end
151
+
152
+ # Now wait for server to be ready
153
+ server_ready = wait_for_server(url, max_retries: 20, retry_delay: 1)
154
+
155
+ # If still not ready, try re-detecting port one more time
156
+ unless server_ready
157
+ new_port = detect_server_port
158
+ if new_port != uri.port.to_s
159
+ url = "#{uri.scheme}://#{uri.host}:#{new_port}#{uri.path}"
160
+ server_ready = wait_for_server(url, max_retries: 20, retry_delay: 1)
161
+ end
162
+
163
+ # If still not ready, skip this check and try next time
164
+ unless server_ready
165
+ # Server still starting - this is normal, will retry automatically
166
+ # Only show message occasionally to avoid spam (every 3rd attempt)
167
+ @wait_attempts ||= 0
168
+ @wait_attempts += 1
169
+ if @wait_attempts % 3 == 1
170
+ $stderr.puts "Waiting for server to start... (will retry automatically)"
171
+ end
172
+ next
173
+ end
174
+ end
175
+ end
139
176
 
140
177
  Capybara.visit(url)
141
178
  violations = engine.check(Capybara.current_session, context: { url: url })
142
179
  all_violations.concat(violations)
143
180
  checked_urls << { url: url, violations: violations.count }
181
+ rescue Interrupt
182
+ # Handle interrupt gracefully - exit the loop
183
+ break
144
184
  rescue StandardError => e
145
185
  $stderr.puts "Error checking #{target}: #{e.message}"
146
186
  end
@@ -180,8 +220,8 @@ module RailsAccessibilityTesting
180
220
  return target if target.match?(/\Ahttps?:\/\//)
181
221
 
182
222
  # If it's a path and we're using Selenium, construct a full URL
183
- # Default to localhost:3000 (standard Rails port)
184
- port = ENV['PORT'] || ENV['RAILS_PORT'] || '3000'
223
+ # Try to detect the actual port, or use environment variables, or default to 3000
224
+ port = detect_server_port
185
225
  base_url = ENV['RAILS_URL'] || "http://localhost:#{port}"
186
226
 
187
227
  # Ensure path starts with /
@@ -189,7 +229,42 @@ module RailsAccessibilityTesting
189
229
  "#{base_url}#{path}"
190
230
  end
191
231
 
192
- def wait_for_server(url, max_retries: 10, retry_delay: 2)
232
+ def detect_server_port
233
+ # Check environment variables first
234
+ return ENV['PORT'] if ENV['PORT']
235
+ return ENV['RAILS_PORT'] if ENV['RAILS_PORT']
236
+
237
+ # Try to detect port from Rails server - check common Rails ports
238
+ # Check in order: 3000 (most common), then others
239
+ # Prioritize 3000 first as it's the Rails default
240
+ common_ports = [3000, 3001, 4000, 5000]
241
+
242
+ common_ports.each do |port|
243
+ begin
244
+ require 'net/http'
245
+ http = Net::HTTP.new('localhost', port)
246
+ http.open_timeout = 1
247
+ http.read_timeout = 1
248
+ # Try to get a response - check if it looks like a Rails server
249
+ response = http.head('/')
250
+ # Accept 2xx, 3xx, or 4xx responses (server is responding)
251
+ # Reject 5xx as it might be a proxy or error
252
+ if response.code.to_i < 500
253
+ # Additional check: Rails servers usually have certain headers
254
+ # But for now, any HTTP response on these common ports is likely Rails
255
+ return port.to_s
256
+ end
257
+ rescue Errno::ECONNREFUSED, Net::OpenTimeout, Net::ReadTimeout, SocketError, Interrupt, Errno::EHOSTUNREACH, Errno::ETIMEDOUT
258
+ # Port not available or interrupted, try next
259
+ next
260
+ end
261
+ end
262
+
263
+ # Default to 3000 if nothing found
264
+ '3000'
265
+ end
266
+
267
+ def wait_for_server(url, max_retries: 15, retry_delay: 1)
193
268
  require 'net/http'
194
269
  require 'uri'
195
270
 
@@ -199,22 +274,31 @@ module RailsAccessibilityTesting
199
274
  max_retries.times do |attempt|
200
275
  begin
201
276
  http = Net::HTTP.new(uri.host, uri.port)
202
- http.open_timeout = 1
203
- http.read_timeout = 1
277
+ http.open_timeout = 2
278
+ http.read_timeout = 2
204
279
  response = http.head('/')
205
- return if response.code.to_i < 500 # Server is responding
206
- rescue Errno::ECONNREFUSED, Errno::ETIMEDOUT, Net::OpenTimeout, Net::ReadTimeout, SocketError
280
+ return true if response.code.to_i < 500 # Server is responding
281
+ rescue Errno::ECONNREFUSED, Errno::ETIMEDOUT, Net::OpenTimeout, Net::ReadTimeout, SocketError, Errno::EHOSTUNREACH
207
282
  # Server not ready yet
208
283
  if attempt < max_retries - 1
209
- sleep(retry_delay)
284
+ begin
285
+ sleep(retry_delay)
286
+ rescue Interrupt
287
+ # Handle interrupt gracefully - return false to indicate failure
288
+ return false
289
+ end
210
290
  next
211
291
  else
212
- # Last attempt failed, but we'll still try to visit (might be a different error)
213
- $stderr.puts "Warning: Server at #{base_url} not responding after #{max_retries} attempts, attempting anyway..."
214
- return
292
+ # Last attempt failed
293
+ return false
215
294
  end
295
+ rescue Interrupt
296
+ # Handle interrupt gracefully
297
+ return false
216
298
  end
217
299
  end
300
+
301
+ false
218
302
  end
219
303
 
220
304
  def resolve_routes(routes)
@@ -230,6 +314,11 @@ module RailsAccessibilityTesting
230
314
  end
231
315
 
232
316
  def generate_report(results, options)
317
+ # Don't generate report if no URLs were checked (server not ready)
318
+ if results[:summary][:urls_checked] == 0
319
+ return
320
+ end
321
+
233
322
  output = case options[:format]
234
323
  when :json
235
324
  generate_json_report(results)
@@ -262,11 +351,14 @@ module RailsAccessibilityTesting
262
351
  output << ""
263
352
 
264
353
  results[:violations].each_with_index do |violation, index|
265
- output << "#{index + 1}. #{violation.message}"
266
- output << " Rule: #{violation.rule_name}"
267
- output << " URL: #{violation.page_context[:url]}"
268
- output << " View: #{violation.page_context[:view_file]}" if violation.page_context[:view_file]
269
- output << ""
354
+ # Use ErrorMessageBuilder for detailed formatted messages
355
+ detailed_message = ErrorMessageBuilder.build(
356
+ error_type: violation.message,
357
+ element_context: violation.element_context || {},
358
+ page_context: violation.page_context || {}
359
+ )
360
+ output << detailed_message
361
+ output << "" if index < results[:violations].count - 1 # Add spacing between violations
270
362
  end
271
363
  else
272
364
  output << "✅ No accessibility violations found!"
@@ -300,7 +392,8 @@ module RailsAccessibilityTesting
300
392
  -v, --version Show version
301
393
 
302
394
  Examples:
303
- rails_a11y check /home /about
395
+ rails_a11y /home /about
396
+ rails_a11y /
304
397
  rails_a11y --urls https://example.com
305
398
  rails_a11y --routes home_path about_path --format json --output report.json
306
399
  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,7 +48,14 @@ module RailsAccessibilityTesting
48
48
  # Run comprehensive accessibility checks
49
49
  instance = example.example_group_instance
50
50
  instance.check_comprehensive_accessibility
51
+
52
+ # If we get here without an exception, all checks passed
53
+ # Note: check_comprehensive_accessibility will raise if there are errors,
54
+ # so if we reach this point, checks passed successfully
55
+ $stdout.puts "\n✅ All comprehensive accessibility checks passed! (11 checks)"
56
+ $stdout.flush
51
57
  rescue StandardError => e
58
+ # Accessibility check failed - set the exception so test fails
52
59
  example.set_exception(e)
53
60
  end
54
61
  end
@@ -1,4 +1,4 @@
1
1
  module RailsAccessibilityTesting
2
- VERSION = "1.2.0"
2
+ VERSION = "1.4.0"
3
3
  end
4
4
 
@@ -5,7 +5,7 @@
5
5
  # Automatically configures accessibility testing for Rails system specs with
6
6
  # comprehensive checks and detailed error messages.
7
7
  #
8
- # @version 1.1.0
8
+ # @version 1.4.0
9
9
  # @author Regan Maharjan
10
10
  #
11
11
  # @example Basic usage
@@ -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.4.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.2.0
4
+ version: 1.4.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Regan Maharjan
@@ -84,6 +84,7 @@ files:
84
84
  - CONTRIBUTING.md
85
85
  - GUIDES/continuous_integration.md
86
86
  - GUIDES/getting_started.md
87
+ - GUIDES/system_specs_for_accessibility.md
87
88
  - GUIDES/working_with_designers_and_content_authors.md
88
89
  - GUIDES/writing_accessible_views_in_rails.md
89
90
  - LICENSE