bbc-a11y 0.0.12 → 0.0.13
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 +4 -4
- data/.ruby-version +1 -0
- data/README.md +9 -11
- data/Rakefile +2 -2
- data/a11y.rb +5 -0
- data/bbc-a11y.gemspec +3 -5
- data/bin/a11y +2 -2
- data/examples/bbc-pages/a11y.rb +2 -6
- data/examples/local-web-app/Gemfile +1 -1
- data/examples/local-web-app/a11y.rb +10 -22
- data/features/check_standards/focusable_controls.feature +62 -0
- data/features/check_standards/form_interactions.feature +45 -0
- data/features/check_standards/form_labels.feature +55 -0
- data/features/check_standards/headings.feature +154 -0
- data/features/check_standards/image_alt.feature +39 -0
- data/features/check_standards/language.feature +46 -0
- data/features/check_standards/main_landmark.feature +39 -0
- data/features/check_standards/tab_index.feature +54 -0
- data/features/cli/display_failing_result.feature +10 -0
- data/features/{exit_status.feature → cli/exit_status.feature} +1 -2
- data/features/cli/provide_muting_tips.feature +25 -0
- data/features/cli/report_configuration_errors.feature +43 -0
- data/features/cli/skipping_standards.feature +15 -0
- data/features/cli/specify_url.feature +9 -0
- data/features/cli/specify_url_via_config.feature +13 -0
- data/features/mute_errors.feature +118 -0
- data/features/step_definitions/steps.rb +25 -1
- data/lib/bbc/a11y/cli.rb +32 -44
- data/lib/bbc/a11y/configuration.rb +56 -22
- data/lib/bbc/a11y/linter.rb +42 -0
- data/lib/bbc/a11y/standards.rb +43 -0
- data/lib/bbc/a11y/standards/anchor_hrefs.rb +18 -0
- data/lib/bbc/a11y/standards/content_follows_headings.rb +22 -0
- data/lib/bbc/a11y/standards/exactly_one_main_heading.rb +20 -0
- data/lib/bbc/a11y/standards/exactly_one_main_landmark.rb +20 -0
- data/lib/bbc/a11y/standards/form_labels.rb +39 -0
- data/lib/bbc/a11y/standards/form_submit_buttons.rb +21 -0
- data/lib/bbc/a11y/standards/heading_hierarchy.rb +34 -0
- data/lib/bbc/a11y/standards/image_alt.rb +18 -0
- data/lib/bbc/a11y/standards/language_attribute.rb +19 -0
- data/lib/bbc/a11y/standards/tab_index.rb +22 -0
- data/lib/bbc/a11y/version +1 -1
- data/spec/bbc/a11y/cli_spec.rb +22 -15
- data/spec/bbc/a11y/configuration_spec.rb +15 -40
- data/standards/support/capybara.rb +1 -2
- metadata +62 -81
- data/features/specify_url_via_cli.feature +0 -10
- data/features/specify_url_via_config.feature +0 -16
- data/lib/bbc/a11y.rb +0 -17
- data/lib/bbc/a11y/cucumber_runner.rb +0 -208
- data/lib/bbc/a11y/cucumber_support.rb +0 -56
- data/lib/bbc/a11y/cucumber_support/disabled_w3c.rb +0 -37
- data/lib/bbc/a11y/cucumber_support/heading_hierarchy.rb +0 -94
- data/lib/bbc/a11y/cucumber_support/language_detector.rb +0 -26
- data/lib/bbc/a11y/cucumber_support/matchers.rb +0 -21
- data/lib/bbc/a11y/cucumber_support/page.rb +0 -94
- data/lib/bbc/a11y/cucumber_support/per_page_checks.rb +0 -28
- data/lib/bbc/a11y/cucumber_support/w3c.rb +0 -36
- data/spec/bbc/a11y/cucumber_support/heading_hierarchy_spec.rb +0 -162
- data/spec/bbc/a11y/cucumber_support/matchers_spec.rb +0 -52
- data/spec/bbc/a11y/cucumber_support/page_spec.rb +0 -197
@@ -1,10 +0,0 @@
|
|
1
|
-
Feature: Specify URL via the CLI
|
2
|
-
|
3
|
-
Scenario: No config, just pass page URL on command-line
|
4
|
-
Given a standards-compliant website running at http://localhost:54321
|
5
|
-
When I run `a11y http://localhost:54321/perfect.html`
|
6
|
-
Then it should pass with:
|
7
|
-
"""
|
8
|
-
BBC Accesibility: http://localhost:54321/perfect.html
|
9
|
-
-----------------------------------------------------
|
10
|
-
"""
|
@@ -1,16 +0,0 @@
|
|
1
|
-
Feature: Specify URL via config
|
2
|
-
|
3
|
-
Scenario: Specify a single page
|
4
|
-
Given a standards-compliant website running at http://localhost:54321
|
5
|
-
And a file named ".a11y.rb" with:
|
6
|
-
"""
|
7
|
-
BBC::A11y.configure do
|
8
|
-
page "http://localhost:54321/perfect.html"
|
9
|
-
end
|
10
|
-
"""
|
11
|
-
When I run `a11y`
|
12
|
-
Then it should pass with:
|
13
|
-
"""
|
14
|
-
BBC Accesibility: http://localhost:54321/perfect.html
|
15
|
-
-----------------------------------------------------
|
16
|
-
"""
|
data/lib/bbc/a11y.rb
DELETED
@@ -1,17 +0,0 @@
|
|
1
|
-
module BBC
|
2
|
-
module A11y
|
3
|
-
TestsFailed = Class.new(StandardError)
|
4
|
-
|
5
|
-
def self.current_version
|
6
|
-
@current_version ||= File.read(File.dirname(__FILE__) + '/a11y/version')
|
7
|
-
end
|
8
|
-
|
9
|
-
def self.until_version(max_version)
|
10
|
-
if current_version >= max_version
|
11
|
-
raise "DEAD CODE: #{caller[0]}"
|
12
|
-
end
|
13
|
-
yield
|
14
|
-
end
|
15
|
-
|
16
|
-
end
|
17
|
-
end
|
@@ -1,208 +0,0 @@
|
|
1
|
-
require 'forwardable'
|
2
|
-
require 'cucumber'
|
3
|
-
require 'bbc/a11y/cucumber_support'
|
4
|
-
require 'cucumber/core/filter'
|
5
|
-
require 'colorize'
|
6
|
-
|
7
|
-
module BBC
|
8
|
-
module A11y
|
9
|
-
module ConsoleWriter
|
10
|
-
def underline(text, character="-")
|
11
|
-
[text, character * text.length].join("\n")
|
12
|
-
end
|
13
|
-
|
14
|
-
def indent(spaces, text)
|
15
|
-
text.split("\n").map { |line| (" " * spaces) + line }.join("\n")
|
16
|
-
end
|
17
|
-
end
|
18
|
-
|
19
|
-
class CucumberFormatter
|
20
|
-
include ConsoleWriter
|
21
|
-
|
22
|
-
def initialize(*args)
|
23
|
-
@current_feature = nil
|
24
|
-
end
|
25
|
-
|
26
|
-
def before_test_case(test_case)
|
27
|
-
on_new_feature(test_case) do |feature|
|
28
|
-
puts
|
29
|
-
puts underline(feature.name)
|
30
|
-
puts
|
31
|
-
end
|
32
|
-
print " - #{test_case.name}... "
|
33
|
-
@step_results = []
|
34
|
-
end
|
35
|
-
|
36
|
-
def after_test_step(test_step, result)
|
37
|
-
@step_results << [test_step, result]
|
38
|
-
end
|
39
|
-
|
40
|
-
def after_test_case(test_case, result)
|
41
|
-
colour = ResultColour.new(result)
|
42
|
-
print colour.apply_to(result.to_s)
|
43
|
-
if !result.passed?
|
44
|
-
puts
|
45
|
-
puts
|
46
|
-
print_scenario
|
47
|
-
puts
|
48
|
-
puts indent(4, colour.apply_to(result.exception.message.to_s))
|
49
|
-
if result.failed?
|
50
|
-
puts indent(4, colour.apply_to(result.exception.backtrace.join("\n")))
|
51
|
-
end
|
52
|
-
end
|
53
|
-
puts
|
54
|
-
if result.failed?
|
55
|
-
CucumberRunner.test_case_failed
|
56
|
-
end
|
57
|
-
end
|
58
|
-
|
59
|
-
private
|
60
|
-
|
61
|
-
def print_scenario
|
62
|
-
@step_results.each do |step, result|
|
63
|
-
step.describe_source_to(StepsPrinter.new, result)
|
64
|
-
end
|
65
|
-
end
|
66
|
-
|
67
|
-
def colour(result)
|
68
|
-
ResultColour.new(result).apply_to(result.to_s)
|
69
|
-
end
|
70
|
-
|
71
|
-
def on_new_feature(test_case)
|
72
|
-
feature = test_case.source.first
|
73
|
-
if feature != @current_feature
|
74
|
-
@current_feature = feature
|
75
|
-
yield feature
|
76
|
-
end
|
77
|
-
end
|
78
|
-
|
79
|
-
class StepsPrinter
|
80
|
-
include ConsoleWriter
|
81
|
-
|
82
|
-
def before_hook(*)
|
83
|
-
end
|
84
|
-
|
85
|
-
def scenario(*)
|
86
|
-
end
|
87
|
-
|
88
|
-
def feature(*)
|
89
|
-
end
|
90
|
-
|
91
|
-
def after_hook(*)
|
92
|
-
end
|
93
|
-
|
94
|
-
def step(step, result)
|
95
|
-
colour = ResultColour.new(result)
|
96
|
-
puts indent(6, colour.apply_to("#{step.keyword}#{step.name}"))
|
97
|
-
end
|
98
|
-
end
|
99
|
-
|
100
|
-
class ResultColour
|
101
|
-
def initialize(result)
|
102
|
-
@color = :white
|
103
|
-
result.describe_to(self)
|
104
|
-
end
|
105
|
-
|
106
|
-
def passed
|
107
|
-
@color = :green
|
108
|
-
end
|
109
|
-
|
110
|
-
def skipped
|
111
|
-
@color = :blue
|
112
|
-
end
|
113
|
-
|
114
|
-
def pending(*)
|
115
|
-
@color = :yellow
|
116
|
-
end
|
117
|
-
|
118
|
-
def failed
|
119
|
-
@color = :red
|
120
|
-
end
|
121
|
-
|
122
|
-
def undefined
|
123
|
-
@color = :yellow
|
124
|
-
end
|
125
|
-
|
126
|
-
def duration(*)
|
127
|
-
end
|
128
|
-
|
129
|
-
def exception(*)
|
130
|
-
end
|
131
|
-
|
132
|
-
def apply_to(string)
|
133
|
-
string.send(@color)
|
134
|
-
end
|
135
|
-
end
|
136
|
-
|
137
|
-
end
|
138
|
-
|
139
|
-
class CucumberRunner
|
140
|
-
include ConsoleWriter
|
141
|
-
FEATURES_PATH = File.expand_path(File.dirname(__FILE__) + "/../../../standards")
|
142
|
-
|
143
|
-
class << self
|
144
|
-
# need to use a global stash-point for the formatter to talk back to use
|
145
|
-
# until Cucumber gives us a way to add formatter instances that we can
|
146
|
-
# share.
|
147
|
-
def failed_count
|
148
|
-
@failed_count ||= 0
|
149
|
-
end
|
150
|
-
|
151
|
-
def test_case_failed
|
152
|
-
@failed_count ||= 0
|
153
|
-
@failed_count += 1
|
154
|
-
end
|
155
|
-
end
|
156
|
-
|
157
|
-
def initialize(settings, cucumber_args)
|
158
|
-
@settings = settings
|
159
|
-
@cucumber_args = cucumber_args
|
160
|
-
end
|
161
|
-
|
162
|
-
def call
|
163
|
-
runtime = Cucumber::Runtime.new(configuration)
|
164
|
-
run_before_all_hooks
|
165
|
-
@settings.pages.each do |page_settings|
|
166
|
-
# stash the settings where the support code will find them
|
167
|
-
BBC::A11y::CucumberSupport.current_page_settings = page_settings
|
168
|
-
print_page_header page_settings
|
169
|
-
runtime.run!
|
170
|
-
end
|
171
|
-
ensure
|
172
|
-
run_after_all_hooks
|
173
|
-
raise BBC::A11y::TestsFailed if self.class.failed_count > 0
|
174
|
-
end
|
175
|
-
|
176
|
-
private
|
177
|
-
|
178
|
-
def configuration
|
179
|
-
return @configuration if @configuration
|
180
|
-
@configuration = Cucumber::Cli::Configuration.new
|
181
|
-
if !@cucumber_args.any?
|
182
|
-
@cucumber_args = [FEATURES_PATH, "--require", FEATURES_PATH]
|
183
|
-
end
|
184
|
-
# This is ugly, but until Cucumber offers a better API, we have to pass in our settings as though
|
185
|
-
# they were CLI arguments
|
186
|
-
@configuration.parse!(@cucumber_args +
|
187
|
-
["--format", "BBC::A11y::CucumberFormatter"])
|
188
|
-
@configuration
|
189
|
-
end
|
190
|
-
|
191
|
-
def run_before_all_hooks
|
192
|
-
@settings.before_all_hooks.each &:call
|
193
|
-
end
|
194
|
-
|
195
|
-
def run_after_all_hooks
|
196
|
-
@settings.after_all_hooks.each &:call
|
197
|
-
end
|
198
|
-
|
199
|
-
def print_page_header(page_settings)
|
200
|
-
puts
|
201
|
-
puts
|
202
|
-
puts underline("BBC Accesibility: #{page_settings.url}")
|
203
|
-
end
|
204
|
-
|
205
|
-
end
|
206
|
-
|
207
|
-
end
|
208
|
-
end
|
@@ -1,56 +0,0 @@
|
|
1
|
-
require 'bbc/a11y/cucumber_support/disabled_w3c'
|
2
|
-
require 'bbc/a11y/cucumber_support/language_detector'
|
3
|
-
require 'bbc/a11y/cucumber_support/matchers'
|
4
|
-
require 'bbc/a11y/cucumber_support/page'
|
5
|
-
require 'bbc/a11y/cucumber_support/per_page_checks'
|
6
|
-
require 'bbc/a11y/cucumber_support/w3c'
|
7
|
-
require 'bbc/a11y/configuration'
|
8
|
-
|
9
|
-
module BBC
|
10
|
-
module A11y
|
11
|
-
|
12
|
-
# These are the methods available to step definitions
|
13
|
-
# that test the specifications.
|
14
|
-
module CucumberSupport
|
15
|
-
include PerPageChecks
|
16
|
-
|
17
|
-
class << self
|
18
|
-
attr_accessor :current_page_settings
|
19
|
-
end
|
20
|
-
|
21
|
-
# Returns an object that can validate URLs
|
22
|
-
def w3c
|
23
|
-
@w3c ||= DisabledW3C.new
|
24
|
-
end
|
25
|
-
|
26
|
-
# An object that represents the current page being viewed in the browser
|
27
|
-
def page
|
28
|
-
@page ||= Page.new(browser)
|
29
|
-
end
|
30
|
-
|
31
|
-
# An object to detect the natural lanugage of the page
|
32
|
-
def language
|
33
|
-
@language ||= LanguageDetector.new
|
34
|
-
end
|
35
|
-
|
36
|
-
# Settings specified for this test run
|
37
|
-
def settings
|
38
|
-
CucumberSupport.current_page_settings
|
39
|
-
end
|
40
|
-
|
41
|
-
# Ask for a manual check
|
42
|
-
def confirm(question)
|
43
|
-
formatted_question = "\n #{question} [Y/n]"
|
44
|
-
answer = ask(formatted_question).strip
|
45
|
-
fail unless ["Y", "y", ""].include?(answer)
|
46
|
-
end
|
47
|
-
|
48
|
-
end
|
49
|
-
|
50
|
-
CucumberSupport.current_page_settings ||=
|
51
|
-
Configuration::PageSettings.new(ENV['A11Y_URL'])
|
52
|
-
end
|
53
|
-
|
54
|
-
end
|
55
|
-
|
56
|
-
|
@@ -1,37 +0,0 @@
|
|
1
|
-
module BBC
|
2
|
-
module A11y
|
3
|
-
module CucumberSupport
|
4
|
-
|
5
|
-
class DisabledW3C
|
6
|
-
|
7
|
-
def validate(url)
|
8
|
-
raise Cucumber::Core::Test::Result::Skipped.new <<-MESSAGE
|
9
|
-
W3C validation is disabled by default as it makes off-netowrk calls
|
10
|
-
to the W3C web service and can be slow and unreliable.
|
11
|
-
|
12
|
-
To enable these checks, include the following code in your .a11y.rb
|
13
|
-
config file:
|
14
|
-
|
15
|
-
BBC::A11y.configure do
|
16
|
-
page "my_page.html" do
|
17
|
-
|
18
|
-
customize_world do
|
19
|
-
def w3c
|
20
|
-
@w3c ||= BBC::A11y::CucumberSupport::W3C.new
|
21
|
-
end
|
22
|
-
end
|
23
|
-
|
24
|
-
end
|
25
|
-
end
|
26
|
-
MESSAGE
|
27
|
-
end
|
28
|
-
|
29
|
-
def errors
|
30
|
-
[]
|
31
|
-
end
|
32
|
-
|
33
|
-
end
|
34
|
-
|
35
|
-
end
|
36
|
-
end
|
37
|
-
end
|
@@ -1,94 +0,0 @@
|
|
1
|
-
module BBC
|
2
|
-
module A11y
|
3
|
-
module CucumberSupport
|
4
|
-
|
5
|
-
class HeadingHierarchy
|
6
|
-
include RSpec::Matchers
|
7
|
-
|
8
|
-
def initialize(page)
|
9
|
-
@page = page
|
10
|
-
end
|
11
|
-
|
12
|
-
def validate
|
13
|
-
return self unless headings.count > 1
|
14
|
-
adjacent_pairs = headings.zip(headings[1..-1])[0..-2]
|
15
|
-
errors = adjacent_pairs.reduce([]) { |errors, pair|
|
16
|
-
previous, current = *pair
|
17
|
-
if current > previous
|
18
|
-
delta = current.number - previous.number
|
19
|
-
if delta != 1
|
20
|
-
errors << current
|
21
|
-
end
|
22
|
-
end
|
23
|
-
errors
|
24
|
-
}
|
25
|
-
expect(errors).to be_empty, error_message(errors)
|
26
|
-
self
|
27
|
-
end
|
28
|
-
|
29
|
-
def to_s
|
30
|
-
headings.map { |h|
|
31
|
-
indent = " " * (h.number - 1)
|
32
|
-
indent + h.to_s
|
33
|
-
}.join("\n")
|
34
|
-
end
|
35
|
-
|
36
|
-
private
|
37
|
-
|
38
|
-
def error_message(errors)
|
39
|
-
"Headings were not in order: " +
|
40
|
-
headings.map { |h| errors.include?(h) ? "**#{h}**" : h }.
|
41
|
-
join(", ")
|
42
|
-
end
|
43
|
-
|
44
|
-
def headings
|
45
|
-
heading_elements.map { |h| Heading.new(h) }
|
46
|
-
end
|
47
|
-
|
48
|
-
def heading_elements
|
49
|
-
all_heading_elements = page.all('h1,h2,h3,h4', visible: false)
|
50
|
-
heading_elements_after_first_h1 = []
|
51
|
-
all_heading_elements.each do |node|
|
52
|
-
if node.tag_name == "h1" || heading_elements_after_first_h1.any?
|
53
|
-
heading_elements_after_first_h1 << node
|
54
|
-
end
|
55
|
-
end
|
56
|
-
heading_elements_after_first_h1
|
57
|
-
end
|
58
|
-
|
59
|
-
attr_reader :page
|
60
|
-
private :page
|
61
|
-
|
62
|
-
class Heading
|
63
|
-
attr_reader :number
|
64
|
-
|
65
|
-
def initialize(element)
|
66
|
-
@element = element
|
67
|
-
@tag_name = element.tag_name
|
68
|
-
@number = @tag_name[1].to_i
|
69
|
-
end
|
70
|
-
|
71
|
-
def > (other)
|
72
|
-
number > other.number
|
73
|
-
end
|
74
|
-
|
75
|
-
def == (other)
|
76
|
-
begin
|
77
|
-
other.element.path == @element.path
|
78
|
-
rescue NotSupportedByDriverError
|
79
|
-
other.element == @element
|
80
|
-
end
|
81
|
-
end
|
82
|
-
|
83
|
-
def to_s
|
84
|
-
@tag_name
|
85
|
-
end
|
86
|
-
|
87
|
-
attr_reader :element
|
88
|
-
protected :element
|
89
|
-
end
|
90
|
-
end
|
91
|
-
|
92
|
-
end
|
93
|
-
end
|
94
|
-
end
|
@@ -1,26 +0,0 @@
|
|
1
|
-
module BBC
|
2
|
-
module A11y
|
3
|
-
module CucumberSupport
|
4
|
-
|
5
|
-
module LanguageDetector
|
6
|
-
# factory for language detector, allows us to use different mechanisms (e.g. a hard-coded language passed from settings)
|
7
|
-
def self.new
|
8
|
-
CLDLanguageDetector.new
|
9
|
-
end
|
10
|
-
|
11
|
-
require 'cld'
|
12
|
-
class CLDLanguageDetector
|
13
|
-
InsufficientConfidence = Class.new(StandardError)
|
14
|
-
|
15
|
-
# returns the code of the language, or raises an error if insufficient confidence
|
16
|
-
def detect(text)
|
17
|
-
detected_language = CLD.detect_language(text.to_s)
|
18
|
-
raise InsufficientConfidence unless detected_language[:reliable]
|
19
|
-
detected_language[:code]
|
20
|
-
end
|
21
|
-
end
|
22
|
-
end
|
23
|
-
|
24
|
-
end
|
25
|
-
end
|
26
|
-
end
|