bbc-a11y 0.1.2 → 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (120) hide show
  1. checksums.yaml +4 -4
  2. data/.travis.yml +9 -0
  3. data/CONTRIBUTING.md +70 -8
  4. data/GETTINGSTARTED.md +65 -0
  5. data/LICENSE +1 -1
  6. data/README.md +12 -55
  7. data/Rakefile +1 -1
  8. data/a11y.rb +1 -3
  9. data/bbc-a11y.gemspec +0 -1
  10. data/features/check_standards/01_core_purpose.feature +39 -0
  11. data/features/check_standards/02_validation.feature +1 -0
  12. data/features/check_standards/03_progressive_enhancement.feature +3 -0
  13. data/features/check_standards/{language.feature → 04_indicating_language.feature} +2 -2
  14. data/features/check_standards/05_page_titles.feature +1 -0
  15. data/features/check_standards/{main_landmark.feature → 06_main_landmark.feature} +6 -6
  16. data/features/check_standards/07_headings.feature +222 -0
  17. data/features/check_standards/{minimum_text_size.feature → 08_minimum_text_size.feature} +22 -2
  18. data/features/check_standards/09_resizable_text.feature +2 -0
  19. data/features/check_standards/{tab_index.feature → 10_tab_index.feature} +7 -7
  20. data/features/check_standards/{title_attribute.feature → 11_title_attributes.feature} +13 -5
  21. data/features/check_standards/{focusable_controls.feature → 12_focusable_controls.feature} +4 -4
  22. data/features/check_standards/13_visible_on_focus.feature +1 -0
  23. data/features/check_standards/14_control_styles.feature +1 -0
  24. data/features/check_standards/15_focus_styles.feature +1 -0
  25. data/features/check_standards/16_colour_contrast.feature +1 -0
  26. data/features/check_standards/17_colour_and_meaning.feature +1 -0
  27. data/features/check_standards/{image_alt.feature → 18_image_alternatives.feature} +5 -5
  28. data/features/check_standards/{form_labels.feature → 19_form_labels.feature} +7 -7
  29. data/features/check_standards/{form_interactions.feature → 20_form_interactions.feature} +6 -6
  30. data/features/check_standards/21_tables.feature +1 -0
  31. data/features/cli/display_failing_result.feature +14 -1
  32. data/features/cli/display_result_summary.feature +15 -1
  33. data/features/cli/provide_muting_tips.feature +1 -1
  34. data/features/cli/skipping_standards.feature +1 -1
  35. data/features/mute_errors.feature +2 -2
  36. data/features/step_definitions/steps.rb +10 -9
  37. data/features/support/web_server.rb +0 -11
  38. data/features/support/web_server/blank.html +7 -0
  39. data/features/support/web_server/two_headings_failures.html +11 -0
  40. data/karma.conf.js +1 -1
  41. data/lib/bbc/a11y/cli.rb +33 -6
  42. data/lib/bbc/a11y/js/bundle.js +139 -89
  43. data/lib/bbc/a11y/js/standards.js +67 -20
  44. data/lib/bbc/a11y/js/standards/{anchorsMustHaveHrefs.js → focusableControls/anchorsMustHaveHrefs.js} +0 -0
  45. data/lib/bbc/a11y/js/standards/{formsMustHaveSubmitButtons.js → formInteractions/formsMustHaveSubmitButtons.js} +0 -0
  46. data/lib/bbc/a11y/js/standards/{fieldsMustHaveLabelsOrTitles.js → formLabels/fieldsMustHaveLabelsOrTitles.js} +0 -0
  47. data/lib/bbc/a11y/js/standards/headings/contentMustFollowHeadings.js +18 -0
  48. data/lib/bbc/a11y/js/standards/{exactlyOneMainHeading.js → headings/exactlyOneMainHeading.js} +1 -1
  49. data/lib/bbc/a11y/js/standards/{headingsMustBeInAscendingOrder.js → headings/headingsMustBeInAscendingOrder.js} +0 -0
  50. data/lib/bbc/a11y/js/standards/{imagesMustHaveAltAttributes.js → imageAlternatives/imagesMustHaveAltAttributes.js} +0 -0
  51. data/lib/bbc/a11y/js/standards/{htmlMustHaveLangAttribute.js → indicatingLanguage/htmlMustHaveLangAttribute.js} +0 -0
  52. data/lib/bbc/a11y/js/standards/{exactlyOneMainLandmark.js → mainLandmark/exactlyOneMainLandmark.js} +0 -0
  53. data/lib/bbc/a11y/js/standards/{minimumTextSize.js → minimumTextSize/textCannotBeTooSmall.js} +3 -3
  54. data/lib/bbc/a11y/js/standards/{elementsWithZeroTabIndexMustBeFields.js → tabIndex/elementsWithZeroTabIndexMustBeFields.js} +0 -0
  55. data/lib/bbc/a11y/js/standards/{titleAttributesOnlyOnInputs.js → titleAttributes/titleAttributesOnlyOnInputs.js} +1 -1
  56. data/lib/bbc/a11y/linter.rb +16 -4
  57. data/lib/bbc/a11y/runner.rb +0 -1
  58. data/lib/bbc/a11y/string_colours.rb +15 -0
  59. data/lib/bbc/a11y/version +1 -1
  60. data/package.json +3 -0
  61. data/spec/bbc/a11y/js/a11ySpec.js +15 -6
  62. data/spec/bbc/a11y/js/minimumTextSizeStandardSpec.js +25 -0
  63. data/spec/bbc/a11y/js/standardsSpec.js +6 -6
  64. data/spec/bbc/a11y/string_colours_spec.rb +13 -0
  65. metadata +68 -102
  66. data/circle.yml +0 -3
  67. data/examples/bbc-pages/Gemfile +0 -3
  68. data/examples/bbc-pages/Rakefile +0 -3
  69. data/examples/bbc-pages/a11y.rb +0 -2
  70. data/examples/local-web-app/Gemfile +0 -4
  71. data/examples/local-web-app/Rakefile +0 -3
  72. data/examples/local-web-app/a11y.rb +0 -52
  73. data/examples/local-web-app/config.ru +0 -1
  74. data/examples/local-web-app/public/missing_header.html +0 -13
  75. data/examples/local-web-app/public/perfect.html +0 -14
  76. data/examples/local-web-app/readme.md +0 -0
  77. data/features/check_standards/headings.feature +0 -153
  78. data/lib/bbc/a11y/js/standards/contentMustFollowHeadings.js +0 -15
  79. data/lib/bbc/a11y/standards.rb +0 -45
  80. data/lib/bbc/a11y/standards/anchor_hrefs.rb +0 -18
  81. data/lib/bbc/a11y/standards/content_follows_headings.rb +0 -22
  82. data/lib/bbc/a11y/standards/exactly_one_main_heading.rb +0 -25
  83. data/lib/bbc/a11y/standards/exactly_one_main_landmark.rb +0 -20
  84. data/lib/bbc/a11y/standards/form_labels.rb +0 -39
  85. data/lib/bbc/a11y/standards/form_submit_buttons.rb +0 -21
  86. data/lib/bbc/a11y/standards/heading_hierarchy.rb +0 -34
  87. data/lib/bbc/a11y/standards/image_alt.rb +0 -18
  88. data/lib/bbc/a11y/standards/language_attribute.rb +0 -19
  89. data/lib/bbc/a11y/standards/tab_index.rb +0 -22
  90. data/lib/bbc/a11y/standards/title_attribute.rb +0 -31
  91. data/standards/01_core-purpose.md +0 -24
  92. data/standards/02_validation.feature +0 -31
  93. data/standards/03_javascript.feature +0 -40
  94. data/standards/04_language.feature +0 -58
  95. data/standards/05_page_title.feature +0 -45
  96. data/standards/06_main_landmark.feature +0 -24
  97. data/standards/07_headings.feature +0 -65
  98. data/standards/08_title_attribute.feature +0 -71
  99. data/standards/09_tabindex.feature +0 -51
  100. data/standards/10_form_labels.feature +0 -88
  101. data/standards/11_visible-on-focus.md +0 -58
  102. data/standards/13_colour-contrast.md +0 -27
  103. data/standards/14_colour-meaning.md +0 -19
  104. data/standards/15_focusable-controls.md +0 -45
  105. data/standards/16_table.md +0 -109
  106. data/standards/17_control-styles.md +0 -78
  107. data/standards/18_focus-styles.md +0 -36
  108. data/standards/19_form-interactions.md +0 -33
  109. data/standards/20_image-alt.md +0 -34
  110. data/standards/21_min-font-sizes.md +0 -64
  111. data/standards/22_resize-zoom.md +0 -80
  112. data/standards/step_definitions/core_content_steps.rb +0 -3
  113. data/standards/step_definitions/form_steps.rb +0 -6
  114. data/standards/step_definitions/language_steps.rb +0 -21
  115. data/standards/step_definitions/page_steps.rb +0 -50
  116. data/standards/step_definitions/w3c_steps.rb +0 -7
  117. data/standards/support/capybara.rb +0 -53
  118. data/standards/support/skipper.rb +0 -5
  119. data/standards/support/world.rb +0 -3
  120. data/standards/support/world_extender.rb +0 -5
data/circle.yml DELETED
@@ -1,3 +0,0 @@
1
- test:
2
- override:
3
- - bundle exec rake
@@ -1,3 +0,0 @@
1
- source 'https://rubygems.org'
2
-
3
- gem 'bbc-a11y', path: '../..'
@@ -1,3 +0,0 @@
1
- task :default do
2
- sh "bundle exec ../../bin/a11y"
3
- end
@@ -1,2 +0,0 @@
1
- page "http://bbc.co.uk/news"
2
- page "http://bbc.co.uk/mundo"
@@ -1,4 +0,0 @@
1
- source 'https://rubygems.org'
2
-
3
- gem 'rack'
4
- gem 'bbc-a11y', path: '../..'
@@ -1,3 +0,0 @@
1
- task :default do
2
- sh "bundle exec ../../bin/a11y"
3
- end
@@ -1,52 +0,0 @@
1
- class Server
2
- attr_reader :port
3
-
4
- def initialize
5
- @pid = nil
6
- @port = find_available_port
7
- end
8
-
9
- def start
10
- @pid = fork { `rackup -p #{port} -q` }
11
- @pid = @pid + 1 # because rackup starts a child process
12
- sleep(0.1) until responsive?
13
- end
14
-
15
- def stop
16
- return unless @pid
17
- Process.kill("SIGKILL", @pid)
18
- Process.wait(@pid)
19
- rescue Errno::ECHILD
20
- end
21
-
22
- private
23
-
24
- def find_available_port
25
- server = TCPServer.new('127.0.0.1', 0)
26
- server.addr[1]
27
- ensure
28
- server.close if server
29
- end
30
-
31
- def responsive?
32
- Net::HTTP.start('localhost', port) { |http| http.get("/perfect.html") }.is_a?(Net::HTTPSuccess)
33
- rescue SystemCallError
34
- false
35
- end
36
- end
37
-
38
- server = Server.new
39
- server.start
40
-
41
- at_exit do
42
- server.stop
43
- end
44
-
45
- page "http://localhost:#{server.port}/perfect.html" do
46
- skip_standard "W3C"
47
- end
48
-
49
- page "http://localhost:#{server.port}/missing_header.html" do
50
- skip_standard "W3C"
51
- skip_standard "Check headings"
52
- end
@@ -1 +0,0 @@
1
- run Rack::Directory.new("public")
@@ -1,13 +0,0 @@
1
- <html lang="en-gb">
2
- <head>
3
- <title>Missing header</title>
4
- </head>
5
-
6
- <body>
7
- <p role="main">
8
- Here is some text in British English. This is needed so that we can verify that
9
- the lang attribute in the html element at the top, which specified en-gb, is
10
- correct.
11
- </p>
12
- </body>
13
- </html>
@@ -1,14 +0,0 @@
1
- <html lang="en-gb">
2
- <head>
3
- <title>Perfect page - Main heading</title>
4
- </head>
5
-
6
- <body>
7
- <h1>Main heading</h1>
8
- <p role="main">
9
- Here is some text in British English. This is needed so that we can verify that
10
- the lang attribute in the html element at the top, which specified en-gb, is
11
- correct.
12
- </p>
13
- </body>
14
- </html>
File without changes
@@ -1,153 +0,0 @@
1
- Feature: Headings
2
-
3
- A document **must** have exactly one `<h1>` element.
4
-
5
- Heading levels after the document `<h1>` element **must** be sequential and **must not** skip heading levels.
6
-
7
- Heading elements **must** be followed by content.
8
-
9
- Rationale
10
- =========
11
-
12
- A logical heading structure is invaluable for users of screen readers and similar assistive technologies to help navigate content.
13
-
14
- Users should be able to use the document's `<h1>` identify its main content. Documents should have one main subject.
15
-
16
- Heading levels should not be skipped as a predictable document outline is an important factor for understandability.
17
-
18
- Headings should not be used for non-heading purposes, i.e. to identify blocks of content. A heading should always
19
- be followed either by non-heading content or by a heading of one level deeper.
20
-
21
- Scenario: No main heading
22
- Given a page with the HTML:
23
- """
24
- <h2>Heading 2</h2>
25
- """
26
- When I validate the "exactly one main heading" standard
27
- Then it fails with the message:
28
- """
29
- Found 0 h1 elements.
30
- """
31
-
32
- Scenario: More than one main heading
33
- Given a page with the HTML:
34
- """
35
- <h1>Heading 1</h1>
36
- <h2>Heading 2</h2>
37
- <h1>Heading 1</h1>
38
- """
39
- When I validate the "exactly one main heading" standard
40
- Then it fails with the message:
41
- """
42
- Found 2 h1 elements: /html/body/h1[1] /html/body/h1[2]
43
- """
44
-
45
- Scenario: Headings in ascending order
46
- Given a page with the HTML:
47
- """
48
- <h1>Heading 1</h1>
49
- <h2>Heading 2</h2>
50
- <h3>Heading 3</h3>
51
- <h4>Heading 4</h4>
52
- <h5>Heading 5</h5>
53
- <h6>Heading 6</h6>
54
- """
55
- When I validate the "headings must be in ascending order" standard
56
- Then it passes
57
-
58
- Scenario: Headings in invalid order
59
- Given a page with the HTML:
60
- """
61
- <h1>Heading 1</h1>
62
- <h3>Heading 3</h3>
63
- <h2>Heading 2</h2>
64
- """
65
- When I validate the "headings must be in ascending order" standard
66
- Then it fails with the message:
67
- """
68
- Headings are not in order: /html/body/h1 /html/body/h3
69
- """
70
-
71
- Scenario: Headings jump back up more than one level
72
- Given a page with the HTML:
73
- """
74
- <h1>Heading 1</h1>
75
- <h2>Heading 2</h2>
76
- <h3>Heading 3</h3>
77
- <h4>Heading 4</h4>
78
- <h2>Heading 2b</h2>
79
- <h3>Heading 3b</h3>
80
- """
81
- When I validate the "headings must be in ascending order" standard
82
- Then it passes
83
-
84
- Scenario: Heading is hidden
85
- Given a page with the HTML:
86
- """
87
- <h1>Heading 1</h1>
88
- <h3 style="display:none">Heading 3</h3>
89
- <h2>Heading 2</h2>
90
- """
91
- When I validate the "headings must be in ascending order" standard
92
- Then it fails with the message:
93
- """
94
- Headings are not in order: /html/body/h1 /html/body/h3
95
- """
96
-
97
- Scenario: Heading in a script tag
98
- Given a page with the HTML:
99
- """
100
- <h1>Heading 1</h1>
101
- <script>
102
- var stuff = "<h3>Heading 3</h3>";
103
- </script>
104
- <h2>Heading 2</h2>
105
- """
106
- When I validate the "headings must be in ascending order" standard
107
- Then it passes
108
-
109
- Scenario: Subheading before the first main heading
110
- Given a page with the HTML:
111
- """
112
- <h3>Ignore me</h3>
113
- <h1>Heading 1</h1>
114
- <h2>Heading 2</h2>
115
- """
116
- When I validate the "headings must be in ascending order" standard
117
- Then it passes
118
-
119
- Scenario: Content between headings
120
- Given a page with the HTML:
121
- """
122
- <div role="main">
123
- <h1>Main heading</h1>
124
- <p>non-heading content</p>
125
- <h2>Another heading</h2>
126
- <p>non-heading content</p>
127
- <h3>Main content</h3>
128
- non-heading content
129
- <h2>Secondary content</h2>
130
- <p>non-heading content</p>
131
- <h2>Tertiary content</h2>
132
- non-heading content
133
- </div>
134
- """
135
- When I validate the "content must follow headings" standard
136
- Then it passes
137
-
138
- Scenario: No content between headings
139
- Given a page with the HTML:
140
- """
141
- <div role="main">
142
- <h1>Main heading</h1>
143
- <p>non-heading content</p>
144
- <h2>Secondary content</h2>
145
- <h2>Tertiary content</h2>
146
- <p>non-heading content</p>
147
- </div>
148
- """
149
- When I validate the "content must follow headings" standard
150
- Then it fails with the message:
151
- """
152
- No content follows: /html/body/div/h2[1]
153
- """
@@ -1,15 +0,0 @@
1
- var headingSelector = 'h1, h2, h3, h4, h5, h6, h7, h8';
2
-
3
- module.exports = {
4
- name: 'Content must follow headings',
5
-
6
- validate: function($, fail) {
7
- $(headingSelector).each(function(index, heading) {
8
- if ($(heading.nextSibling).is(headingSelector) ||
9
- ($(heading.nextSibling).text().trim() == '' &&
10
- $(heading).next().is(headingSelector))) {
11
- fail("No content follows:", heading);
12
- }
13
- });
14
- }
15
- }
@@ -1,45 +0,0 @@
1
- require 'bbc/a11y/standards/anchor_hrefs'
2
- require 'bbc/a11y/standards/content_follows_headings'
3
- require 'bbc/a11y/standards/exactly_one_main_heading'
4
- require 'bbc/a11y/standards/exactly_one_main_landmark'
5
- require 'bbc/a11y/standards/form_labels'
6
- require 'bbc/a11y/standards/form_submit_buttons'
7
- require 'bbc/a11y/standards/heading_hierarchy'
8
- require 'bbc/a11y/standards/image_alt'
9
- require 'bbc/a11y/standards/language_attribute'
10
- require 'bbc/a11y/standards/tab_index'
11
- require 'bbc/a11y/standards/title_attribute'
12
-
13
- module BBC
14
- module A11y
15
- module Standards
16
- def self.for(page_settings)
17
- all.reject { |standard|
18
- page_settings.skip_standard?(standard)
19
- }
20
- end
21
-
22
- def self.matching(name)
23
- all.select { |standard|
24
- name.match(standard.name)
25
- }
26
- end
27
-
28
- def self.all
29
- [
30
- AnchorHrefs,
31
- ContentFollowsHeadings,
32
- FormLabels,
33
- FormSubmitButtons,
34
- HeadingHierarchy,
35
- ImageAlt,
36
- ExactlyOneMainHeading,
37
- ExactlyOneMainLandmark,
38
- LanguageAttribute,
39
- TabIndex,
40
- TitleAttribute
41
- ]
42
- end
43
- end
44
- end
45
- end
@@ -1,18 +0,0 @@
1
- module BBC
2
- module A11y
3
- module Standards
4
- class AnchorHrefs
5
- def initialize(page)
6
- @page = page
7
- end
8
-
9
- def call(errors)
10
- @page.all("a:not([href])").each do |anchor|
11
- errors << "Anchor has no href attribute: #{anchor.path}"
12
- end
13
- end
14
-
15
- end
16
- end
17
- end
18
- end
@@ -1,22 +0,0 @@
1
- module BBC
2
- module A11y
3
- module Standards
4
-
5
- class ContentFollowsHeadings
6
- def initialize(page)
7
- @page = page
8
- end
9
-
10
- def call(errors)
11
- ["h1", "h2", "h3", "h4", "h5", "h6"].each do |h|
12
- if @page.all("#{h} + #{h}").any?
13
- errors << "Heading elements must be followed by content. " +
14
- "No content follows a #{h}."
15
- end
16
- end
17
- end
18
- end
19
-
20
- end
21
- end
22
- end
@@ -1,25 +0,0 @@
1
- module BBC
2
- module A11y
3
- module Standards
4
- class ExactlyOneMainHeading
5
- def initialize(page)
6
- @page = page
7
- end
8
-
9
- def call(errors)
10
- main_headings = @page.all('h1')
11
- count = main_headings.size
12
- if count == 0
13
- errors << "A document must have exactly one main heading." +
14
- " Found 0 h1 elements."
15
- elsif count != 1
16
- errors << "A document must have exactly one main heading." +
17
- " Found #{count} h1 elements:\n" +
18
- main_headings.map { |h1| "* #{h1.path}" }.join("\n")
19
- end
20
- end
21
-
22
- end
23
- end
24
- end
25
- end
@@ -1,20 +0,0 @@
1
- module BBC
2
- module A11y
3
- module Standards
4
- class ExactlyOneMainLandmark
5
- def initialize(page)
6
- @page = page
7
- end
8
-
9
- def call(errors)
10
- count = @page.all('*[role=main]').size
11
- if count != 1
12
- errors << "A document must have exactly one main landmark." +
13
- " Found #{count} elements with role=\"main\"."
14
- end
15
- end
16
-
17
- end
18
- end
19
- end
20
- end
@@ -1,39 +0,0 @@
1
- module BBC
2
- module A11y
3
- module Standards
4
- class FormLabels
5
- def initialize(page)
6
- @page = page
7
- end
8
-
9
- def call(errors)
10
- fields = @page.all(potential_offenders)
11
- fields.each do |field|
12
- if offender? field
13
- errors << "Field has no label or title attribute: #{field.path}"
14
- end
15
- end
16
- end
17
-
18
- private
19
-
20
- def potential_offenders
21
- "input:not([title]):not([type='hidden']), textarea:not([title]), select:not([title])"
22
- end
23
-
24
- def offender?(field)
25
- (missing_id? field) or (missing_label? field)
26
- end
27
-
28
- def missing_id?(field)
29
- field['id'] == nil or field['id'] == ''
30
- end
31
-
32
- def missing_label?(field)
33
- @page.all("label[for='#{field['id']}']").empty?
34
- end
35
-
36
- end
37
- end
38
- end
39
- end