acop 1.0.0 → 1.0.1
Sign up to get free protection for your applications and to get access to all the features.
- data/README.md +76 -11
- data/acop.gemspec +1 -1
- data/lib/acop/accessibility.rb +170 -0
- data/lib/acop/version.rb +1 -1
- data/spec/acop_spec.rb +238 -0
- metadata +2 -2
data/README.md
CHANGED
@@ -31,6 +31,42 @@ Specify the url you want accessibility tested
|
|
31
31
|
|
32
32
|
Checkpoints
|
33
33
|
-----------
|
34
|
+
### Standard Web Programming
|
35
|
+
`<!DOCTYPE Resource SYSTEM 'foo.dtd'>`
|
36
|
+
* Doctype should be specified if frame or iframe elements exist on the page
|
37
|
+
|
38
|
+
### Appropriate Markup
|
39
|
+
* HTML visual formatting elements like `<b></b>`, `<i></i>`, `<center></center>`, `<font></font>`, `<u></u>` should not be used. Use CSS for formatting instead
|
40
|
+
|
41
|
+
### Title
|
42
|
+
`<title></title>`
|
43
|
+
* Page title element should not be empty or missing
|
44
|
+
* There should not be more than one page title
|
45
|
+
|
46
|
+
`<frameset><frame title=""...`
|
47
|
+
* Frame elements should have title attributes
|
48
|
+
* Frame elements should not have title attributes empty
|
49
|
+
|
50
|
+
`<iframe title=""...`
|
51
|
+
* iFrame elements should have title attributes
|
52
|
+
* iFrame elements should not have title attributes empty
|
53
|
+
|
54
|
+
### Headings
|
55
|
+
`<body><p><h1>Heading 1</h1><h2>Heading 2</h2><p><h3>Heading 3</h3></p></body>`
|
56
|
+
* Page must contain atleast one h1 element
|
57
|
+
* The h1 element should have non empty text
|
58
|
+
* Heading elements that follow the h1 element should be properly nested
|
59
|
+
* All subheadings(h2..h6) should have non empty text
|
60
|
+
|
61
|
+
### HTML lang
|
62
|
+
`<html lang='en'></html>`
|
63
|
+
* You should declare the primary language of a page with the html lang attribute
|
64
|
+
|
65
|
+
### Hyperlinks
|
66
|
+
`<body><a href="www.google.com">Go to Google</a></body>`
|
67
|
+
* Hyperlinks should always have text associated with them
|
68
|
+
* There should not be duplicate text for hyperlinks on the same page
|
69
|
+
|
34
70
|
### Images
|
35
71
|
`<input type='image'...`
|
36
72
|
* Image inputs elements should have alt attributes
|
@@ -43,20 +79,49 @@ Checkpoints
|
|
43
79
|
`<a><img...`
|
44
80
|
* Image elements inside anchor tags should have empty alt attributes
|
45
81
|
|
46
|
-
###
|
47
|
-
`<
|
48
|
-
*
|
82
|
+
### Area
|
83
|
+
`<area shape='rect' coords='0,0,82,126' href='sun.htm' alt='Sun'>`
|
84
|
+
* Area elements should have an alt attribute
|
85
|
+
* Area element alt attribute cannot be empty
|
49
86
|
|
50
|
-
|
51
|
-
*
|
87
|
+
### Flashing content
|
88
|
+
* The blink and marquee elements must not be used. Blinking and moving text are an accessibility problems for people with photosenstive epilepsy and visual impairments.
|
52
89
|
|
53
|
-
|
54
|
-
|
55
|
-
*
|
90
|
+
### Forms
|
91
|
+
`<form><textarea id='area' rows='3' cols='3'></textarea><label for='area'/></form>`
|
92
|
+
* Every form element should have a corresponding label (the label 'for' attribute should match the form field 'id' attribute)
|
56
93
|
|
57
|
-
`<
|
58
|
-
*
|
59
|
-
|
94
|
+
`<form><input id='in' type='text' value="input_value"/></form>`
|
95
|
+
* Form input elements of type submit|reset|button should not have labels, instead have a non empty 'value' attribute
|
96
|
+
|
97
|
+
`<label for="label1">Label 1</label><label for="label2">Label 2</label>`
|
98
|
+
* Labels for form controls should have non-empty text
|
99
|
+
|
100
|
+
`<legend>Legend 1</legend>`
|
101
|
+
* Legends specified for fieldsets or otherwise should have non-empty text
|
102
|
+
|
103
|
+
`<button type="button">Button 1</button>`
|
104
|
+
* Buttons should have non-empty text
|
105
|
+
|
106
|
+
### Tables
|
107
|
+
`<table summary="summary"><th>Table Heading</th><tr><td>Data 1</td></tr></table>`
|
108
|
+
* Table should have a table header
|
109
|
+
* Table should have a non empty summary attribute
|
110
|
+
* Table headers should have a non empty scope attribute specifying whether it is for a row or column
|
111
|
+
|
112
|
+
Tests
|
113
|
+
-----
|
114
|
+
Running the rspec tests
|
115
|
+
|
116
|
+
`cd spec; rspec acop_spec.rb`
|
117
|
+
|
118
|
+
Additional Resources
|
119
|
+
--------------------
|
120
|
+
While this gem does attempt to flag as many accessibility concerns as possible, there are numerous others that cannot be automated or are difficult to automate. Below are excellent resources that I would encourage anybody using this gem to also go through
|
121
|
+
|
122
|
+
* [Illinois Center for Information Technology and Web Accessibility](http://html.cita.illinois.edu/iitaa.php)
|
123
|
+
* [PennState AccessAbility](http://accessibility.psu.edu/)
|
124
|
+
* [Web Accessibility in Mind](http://webaim.org/)
|
60
125
|
|
61
126
|
Bugs
|
62
127
|
----
|
data/acop.gemspec
CHANGED
@@ -5,7 +5,7 @@ Gem::Specification.new do |s|
|
|
5
5
|
s.version = Acop::VERSION
|
6
6
|
s.date = '2013-04-22'
|
7
7
|
s.summary = "Accessibility Cop"
|
8
|
-
s.description = "A gem to enforce accessibility
|
8
|
+
s.description = "A gem to enforce webpage accessibility'"
|
9
9
|
s.authors = ["Avinash Padmanabhan"]
|
10
10
|
s.email = ['avinashpadmanabhan@gmail.com']
|
11
11
|
s.files = `git ls-files`.split("\n")
|
data/lib/acop/accessibility.rb
CHANGED
@@ -3,6 +3,7 @@ require 'nokogiri'
|
|
3
3
|
require 'rexml/document'
|
4
4
|
|
5
5
|
module Acop
|
6
|
+
|
6
7
|
class Enforcer
|
7
8
|
attr_reader :ah, :source, :contents
|
8
9
|
|
@@ -58,6 +59,17 @@ module Acop
|
|
58
59
|
end
|
59
60
|
error_messages
|
60
61
|
end
|
62
|
+
|
63
|
+
def check_areas(source=@contents)
|
64
|
+
area_elements = source.css("area")
|
65
|
+
error_messages = []
|
66
|
+
area_elements.each do |element|
|
67
|
+
if (@ah.attribute_empty_or_nil(element, "alt"))
|
68
|
+
error_messages.push("Missing alt text/attribute for area element with id/name: " + (element['name'] || element['id'] || ""))
|
69
|
+
end
|
70
|
+
end
|
71
|
+
error_messages
|
72
|
+
end
|
61
73
|
|
62
74
|
def check_page_title(source=@contents)
|
63
75
|
title_element = source.css('title')
|
@@ -69,6 +81,14 @@ module Acop
|
|
69
81
|
error_messages
|
70
82
|
end
|
71
83
|
|
84
|
+
def check_visual_formatting(source=@contents)
|
85
|
+
error_messages = []
|
86
|
+
%w{b i font center u}.each do |markup_element|
|
87
|
+
error_messages.push("HTML visual formatting elements being used. Use CSS instead") unless source.css(markup_element).empty?
|
88
|
+
end
|
89
|
+
error_messages
|
90
|
+
end
|
91
|
+
|
72
92
|
def check_doctype(source=@contents)
|
73
93
|
frame_elements = source.css("frame")
|
74
94
|
iframe_elements = source.css("iframe")
|
@@ -80,6 +100,32 @@ module Acop
|
|
80
100
|
error_messages
|
81
101
|
end
|
82
102
|
|
103
|
+
def check_html_lang(source=@contents)
|
104
|
+
html = source.css("html")
|
105
|
+
error_messages = []
|
106
|
+
error_messages.push("Missing lang attribute for html") if(html.attr('lang') == nil or html.attr('lang').value == "")
|
107
|
+
error_messages
|
108
|
+
end
|
109
|
+
|
110
|
+
def check_hyperlinks(source=@contents)
|
111
|
+
hyperlinks = source.css("a")
|
112
|
+
error_messages = []
|
113
|
+
hyperlinks.each do |link|
|
114
|
+
error_messages.push("Missing link text for link with href: #{link['href']}") if(link.text==nil or link.text=="")
|
115
|
+
end
|
116
|
+
hyperlink_text = hyperlinks.collect {|link| link.text }
|
117
|
+
error_messages.push("Links should not have duplicate text") if(hyperlink_text != hyperlink_text.uniq)
|
118
|
+
error_messages
|
119
|
+
end
|
120
|
+
|
121
|
+
def check_flashing_content(source=@contents)
|
122
|
+
error_messages = []
|
123
|
+
%w{blink marquee}.each do |flashing_element|
|
124
|
+
error_messages.push("Flashing elements such as 'blink' or 'marquee' should not be used") unless source.css(flashing_element).empty?
|
125
|
+
end
|
126
|
+
error_messages
|
127
|
+
end
|
128
|
+
|
83
129
|
def check_frame_title(source=@contents)
|
84
130
|
return [] if source.css("frameset").length < 1
|
85
131
|
frame_elements = source.css("frame")
|
@@ -100,6 +146,130 @@ module Acop
|
|
100
146
|
end
|
101
147
|
error_messages
|
102
148
|
end
|
149
|
+
|
150
|
+
# each form element should have a corresponding label
|
151
|
+
def check_form_labels(source=@contents)
|
152
|
+
forms = source.css("form")
|
153
|
+
error_messages = []
|
154
|
+
forms.each do |form|
|
155
|
+
labels = form.css("label")
|
156
|
+
form_fields = []
|
157
|
+
%w{input select textarea button}.each do |element|
|
158
|
+
element_fields = form.css(element)
|
159
|
+
form_fields << form.css(element) unless element_fields.empty?
|
160
|
+
end
|
161
|
+
form_fields.flatten!
|
162
|
+
form_fields.reject {|field| field.attr('submit') || field.attr('reset') || field.attr('button') }
|
163
|
+
|
164
|
+
form_fields.each do |field|
|
165
|
+
id = field.attr('id')
|
166
|
+
error_messages.push("Missing label for form field with id/name: " + (id || field.attr('name') || "")) if (labels.select {|label| label['for'].to_s == id.to_s }.size < 1)
|
167
|
+
end
|
168
|
+
end
|
169
|
+
error_messages
|
170
|
+
end
|
171
|
+
|
172
|
+
# each input element of type (submit||reset||button) should have include a "value" attribute and should not have a corresponding label
|
173
|
+
def check_form_inputs(source=@contents)
|
174
|
+
forms = source.css("form")
|
175
|
+
error_messages = []
|
176
|
+
forms.each do |form|
|
177
|
+
input_fields = form.css("input").select {|field| field.attr('type') == 'submit' || field.attr('type') == 'reset' || field.attr('type') == 'button' }
|
178
|
+
labels = form.css("label")
|
179
|
+
|
180
|
+
input_fields.each do |field|
|
181
|
+
value_present = field.attr('value') != nil and field.attr('value') != ""
|
182
|
+
label_absent = (labels.select {|label| label['for'].to_s == field.attr('id').to_s }.size) < 1
|
183
|
+
error_messages.push("Missing value attribute/label present for input element with id/name: " + (field.attr('id').to_s || field.attr('name') || "")) unless(value_present and label_absent)
|
184
|
+
end
|
185
|
+
end
|
186
|
+
error_messages
|
187
|
+
end
|
188
|
+
|
189
|
+
def check_form_control_text(source=@contents)
|
190
|
+
error_messages = []
|
191
|
+
labels = source.css("label")
|
192
|
+
labels.each do |label|
|
193
|
+
error_messages.push("Missing label text for label with for attribute: #{label['for']}") if (label.text==nil or label.text=="")
|
194
|
+
end
|
195
|
+
|
196
|
+
%w{legend button}.each do |control|
|
197
|
+
fields = source.css(control)
|
198
|
+
fields.each do |field|
|
199
|
+
error_messages.push("Missing #{control} text for #{control}") if (field.text==nil or field.text=="")
|
200
|
+
end
|
201
|
+
end
|
202
|
+
error_messages
|
203
|
+
end
|
204
|
+
|
205
|
+
def check_h1(source=@contents)
|
206
|
+
h1_elements = source.css("h1")
|
207
|
+
error_messages = []
|
208
|
+
error_messages.push("Missing heading level 1. Provide atleast one level 1 heading for document content") if h1_elements.empty?
|
209
|
+
error_messages
|
210
|
+
end
|
211
|
+
|
212
|
+
def check_heading_text(source=@contents)
|
213
|
+
error_messages = []
|
214
|
+
headings = []
|
215
|
+
%w{h1 h2 h3 h4 h5 h6}.each do |heading|
|
216
|
+
headings << source.css(heading)
|
217
|
+
end
|
218
|
+
headings.flatten!
|
219
|
+
headings.each do |heading|
|
220
|
+
error_messages.push("Missing text for #{heading.name} element") if(heading.text==nil or heading.text=="")
|
221
|
+
end
|
222
|
+
error_messages
|
223
|
+
end
|
224
|
+
|
225
|
+
def check_heading_structure(source=@contents)
|
226
|
+
error_messages = []
|
227
|
+
all_nodes = []
|
228
|
+
source.at_css('body').traverse {|node| all_nodes << node.name }
|
229
|
+
headings = all_nodes.select {|node| node =~ /^h\d/ }
|
230
|
+
if headings.first != "h1"
|
231
|
+
error_messages.push("First heading level should be h1")
|
232
|
+
return error_messages
|
233
|
+
end
|
234
|
+
|
235
|
+
prev_heading_level = 0
|
236
|
+
headings.each do |heading|
|
237
|
+
heading_level = heading[1]
|
238
|
+
error_messages.push("Incorrect document heading structure") if (heading_level.to_i - prev_heading_level.to_i > 1)
|
239
|
+
prev_heading_level = heading[1]
|
240
|
+
end
|
241
|
+
error_messages
|
242
|
+
end
|
243
|
+
|
244
|
+
def check_table_summary(source=@contents)
|
245
|
+
error_messages = []
|
246
|
+
tables = source.css("table")
|
247
|
+
|
248
|
+
tables.each do |table|
|
249
|
+
error_messages.push("Missing table summary") if(table['summary'] == nil or table['summary'] == "")
|
250
|
+
end
|
251
|
+
error_messages
|
252
|
+
end
|
253
|
+
|
254
|
+
def check_table_headers(source=@contents)
|
255
|
+
error_messages = []
|
256
|
+
tables = source.css("table")
|
257
|
+
|
258
|
+
tables.each do |table|
|
259
|
+
if table.css("th").empty?
|
260
|
+
error_messages.push("Missing table headers for table with summary: " + (table['summary'] || ""))
|
261
|
+
end
|
262
|
+
end
|
263
|
+
|
264
|
+
tables.each do |table|
|
265
|
+
th_elements = table.css("th")
|
266
|
+
th_elements.each do |th|
|
267
|
+
error_messages.push("Missing scope for table header") if (th['scope'] == nil || th['scope'] == "")
|
268
|
+
end
|
269
|
+
end
|
270
|
+
error_messages
|
271
|
+
end
|
272
|
+
|
103
273
|
end
|
104
274
|
|
105
275
|
class Helpers
|
data/lib/acop/version.rb
CHANGED
data/spec/acop_spec.rb
CHANGED
@@ -81,6 +81,29 @@ RSpec.configure do |config|
|
|
81
81
|
error_messages[0].should match("Alt Text not empty or nil for image link with src")
|
82
82
|
end
|
83
83
|
|
84
|
+
it "should return error messages when alt tags not present in area elements" do
|
85
|
+
stub_request(:any, "www.example.com").to_return(:body => "<area shape='rect' coords='0,0,82,126' href='sun.htm'>", :status => 200)
|
86
|
+
enf = Acop::Enforcer.new({:url => "www.example.com"})
|
87
|
+
error_messages = enf.check_areas
|
88
|
+
error_messages.should_not be_empty
|
89
|
+
error_messages[0].should match("Missing alt text/attribute for area element")
|
90
|
+
end
|
91
|
+
|
92
|
+
it "should return error messages when alt tags empty in area elements" do
|
93
|
+
stub_request(:any, "www.example.com").to_return(:body => "<area shape='rect' coords='0,0,82,126' href='sun.htm' alt=''>", :status => 200)
|
94
|
+
enf = Acop::Enforcer.new({:url => "www.example.com"})
|
95
|
+
error_messages = enf.check_areas
|
96
|
+
error_messages.should_not be_empty
|
97
|
+
error_messages[0].should match("Missing alt text/attribute for area element")
|
98
|
+
end
|
99
|
+
|
100
|
+
it "should not return error messages when alt tags present in area elements" do
|
101
|
+
stub_request(:any, "www.example.com").to_return(:body => "<area shape='rect' coords='0,0,82,126' href='sun.htm' alt='Sun'>", :status => 200)
|
102
|
+
enf = Acop::Enforcer.new({:url => "www.example.com"})
|
103
|
+
error_messages = enf.check_areas
|
104
|
+
error_messages.should be_empty
|
105
|
+
end
|
106
|
+
|
84
107
|
it "should return error messages when title not present" do
|
85
108
|
stub_request(:any, "www.example.com").to_return(:body => "<html><head></head></html>", :status => 200)
|
86
109
|
enf = Acop::Enforcer.new({:url => "www.example.com"})
|
@@ -151,5 +174,220 @@ RSpec.configure do |config|
|
|
151
174
|
error_messages.should_not be_empty
|
152
175
|
error_messages[0].should match("Empty iframe title element")
|
153
176
|
end
|
177
|
+
|
178
|
+
it "should not return error messages when form elements have labels" do
|
179
|
+
stub_request(:any, "www.example.com").to_return(:body => '<form><textarea id="area" rows="3" cols="10" /><label for="area" /></form>', :status => 200)
|
180
|
+
enf = Acop::Enforcer.new({:url => "www.example.com"})
|
181
|
+
error_messages = enf.check_form_labels
|
182
|
+
error_messages.should be_empty
|
183
|
+
end
|
184
|
+
|
185
|
+
it "should return error messages when form elements have labels" do
|
186
|
+
stub_request(:any, "www.example.com").to_return(:body => '<form><textarea id="area" rows="3" cols="10" /></form>', :status => 200)
|
187
|
+
enf = Acop::Enforcer.new({:url => "www.example.com"})
|
188
|
+
error_messages = enf.check_form_labels
|
189
|
+
error_messages.should_not be_empty
|
190
|
+
end
|
191
|
+
|
192
|
+
it "should not return error messages when form input (submit|reset|button) elements have value attribs and no labels" do
|
193
|
+
stub_request(:any, "www.example.com").to_return(:body => '<form><input id="in" type="button" value="input_value"/></form>', :status => 200)
|
194
|
+
enf = Acop::Enforcer.new({:url => "www.example.com"})
|
195
|
+
error_messages = enf.check_form_inputs
|
196
|
+
error_messages.should be_empty
|
197
|
+
end
|
198
|
+
|
199
|
+
it "should return error messages when form input (submit|reset|button) elements have value attribs and labels" do
|
200
|
+
stub_request(:any, "www.example.com").to_return(:body => '<form><input id="in" type="button" value="input_value"/><label for="in" /></form>', :status => 200)
|
201
|
+
enf = Acop::Enforcer.new({:url => "www.example.com"})
|
202
|
+
error_messages = enf.check_form_inputs
|
203
|
+
error_messages.should_not be_empty
|
204
|
+
error_messages[0].should match("Missing value attribute/label present for input element")
|
205
|
+
end
|
206
|
+
|
207
|
+
it "should return error messages when form input (submit|reset|button) elements have no value attribs and labels" do
|
208
|
+
stub_request(:any, "www.example.com").to_return(:body => '<form><input id="in" type="button" /><label for="in" /></form>', :status => 200)
|
209
|
+
enf = Acop::Enforcer.new({:url => "www.example.com"})
|
210
|
+
error_messages = enf.check_form_inputs
|
211
|
+
error_messages.should_not be_empty
|
212
|
+
error_messages[0].should match("Missing value attribute/label present for input element")
|
213
|
+
end
|
214
|
+
|
215
|
+
it "should return error messages when form input (submit|reset|button) elements have no value attribs and no labels" do
|
216
|
+
stub_request(:any, "www.example.com").to_return(:body => '<form><input id="in" type="button" /><label for="in" /></form>', :status => 200)
|
217
|
+
enf = Acop::Enforcer.new({:url => "www.example.com"})
|
218
|
+
error_messages = enf.check_form_inputs
|
219
|
+
error_messages.should_not be_empty
|
220
|
+
error_messages[0].should match("Missing value attribute/label present for input element")
|
221
|
+
end
|
222
|
+
|
223
|
+
it "should not return error messages when form input other than (submit|reset|button) elements have no value attribs and no labels" do
|
224
|
+
stub_request(:any, "www.example.com").to_return(:body => '<form><input id="in" type="text" /><label for="in" /></form>', :status => 200)
|
225
|
+
enf = Acop::Enforcer.new({:url => "www.example.com"})
|
226
|
+
error_messages = enf.check_form_inputs
|
227
|
+
error_messages.should be_empty
|
228
|
+
end
|
229
|
+
|
230
|
+
it "should not return error messages when labels have text" do
|
231
|
+
stub_request(:any, "www.example.com").to_return(:body => '<label for="label1">Label 1</label><label for="label2">Label 2</label>', :status => 200)
|
232
|
+
enf = Acop::Enforcer.new({:url => "www.example.com"})
|
233
|
+
error_messages = enf.check_form_control_text
|
234
|
+
error_messages.should be_empty
|
235
|
+
end
|
236
|
+
|
237
|
+
it "should return error messages when labels do not have text" do
|
238
|
+
stub_request(:any, "www.example.com").to_return(:body => '<label for="label1">Label 1</label><label for="label2"></label>', :status => 200)
|
239
|
+
enf = Acop::Enforcer.new({:url => "www.example.com"})
|
240
|
+
error_messages = enf.check_form_control_text
|
241
|
+
error_messages.should_not be_empty
|
242
|
+
error_messages[0].should match("Missing label text for label")
|
243
|
+
end
|
244
|
+
|
245
|
+
it "should not return error messages when legends have text" do
|
246
|
+
stub_request(:any, "www.example.com").to_return(:body => '<legend>Legend 1</legend>', :status => 200)
|
247
|
+
enf = Acop::Enforcer.new({:url => "www.example.com"})
|
248
|
+
error_messages = enf.check_form_control_text
|
249
|
+
error_messages.should be_empty
|
250
|
+
end
|
251
|
+
|
252
|
+
it "should return error messages when legends do not have text" do
|
253
|
+
stub_request(:any, "www.example.com").to_return(:body => '<legend></legend>', :status => 200)
|
254
|
+
enf = Acop::Enforcer.new({:url => "www.example.com"})
|
255
|
+
error_messages = enf.check_form_control_text
|
256
|
+
error_messages.should_not be_empty
|
257
|
+
error_messages[0].should match("Missing legend text for legend")
|
258
|
+
end
|
259
|
+
|
260
|
+
it "should not return error messages when buttons have text" do
|
261
|
+
stub_request(:any, "www.example.com").to_return(:body => '<button type="button">Button 1</button>', :status => 200)
|
262
|
+
enf = Acop::Enforcer.new({:url => "www.example.com"})
|
263
|
+
error_messages = enf.check_form_control_text
|
264
|
+
error_messages.should be_empty
|
265
|
+
end
|
266
|
+
|
267
|
+
it "should return error messages when buttons do not have text" do
|
268
|
+
stub_request(:any, "www.example.com").to_return(:body => '<button type="button"></button>', :status => 200)
|
269
|
+
enf = Acop::Enforcer.new({:url => "www.example.com"})
|
270
|
+
error_messages = enf.check_form_control_text
|
271
|
+
error_messages.should_not be_empty
|
272
|
+
error_messages[0].should match("Missing button text for button")
|
273
|
+
end
|
274
|
+
|
275
|
+
it "should return error_messages when document does not have level 1 heading" do
|
276
|
+
stub_request(:any, "www.example.com").to_return(:body => '<h2>Heading 2</h2><p><h3>Heading 3</h3></p>', :status => 200)
|
277
|
+
enf = Acop::Enforcer.new({:url => "www.example.com"})
|
278
|
+
error_messages = enf.check_h1
|
279
|
+
error_messages.should_not be_empty
|
280
|
+
error_messages[0].should match("Missing heading level 1")
|
281
|
+
end
|
282
|
+
|
283
|
+
it "should return error_messages when headings do not have text" do
|
284
|
+
stub_request(:any, "www.example.com").to_return(:body => '<p><h1></h1></p><h2>Heading 2</h2><h3></h3>', :status => 200)
|
285
|
+
enf = Acop::Enforcer.new({:url => "www.example.com"})
|
286
|
+
error_messages = enf.check_heading_text
|
287
|
+
error_messages.should_not be_empty
|
288
|
+
error_messages[0].should match("Missing text for h1 element")
|
289
|
+
error_messages[1].should match("Missing text for h3 element")
|
290
|
+
end
|
291
|
+
|
292
|
+
it "should not return error_messages when document has correct heading structure" do
|
293
|
+
stub_request(:any, "www.example.com").to_return(:body => '<body><p><h1>Heading 1</h1><h2>Heading 2</h2><p><h3>Heading 3</h3></p></body>', :status => 200)
|
294
|
+
enf = Acop::Enforcer.new({:url => "www.example.com"})
|
295
|
+
error_messages = enf.check_heading_structure
|
296
|
+
error_messages.should be_empty
|
297
|
+
end
|
298
|
+
|
299
|
+
it "should return error_messages when document has incorrect heading structure" do
|
300
|
+
stub_request(:any, "www.example.com").to_return(:body => '<p><h2>Heading 2</h2><h1>Heading 1</h1><p><h3>Heading 3</h3></p>', :status => 200)
|
301
|
+
enf = Acop::Enforcer.new({:url => "www.example.com"})
|
302
|
+
error_messages = enf.check_heading_structure
|
303
|
+
error_messages.should_not be_empty
|
304
|
+
error_messages[0].should match("First heading level should be h1")
|
305
|
+
end
|
306
|
+
|
307
|
+
it "should return error_messages when document has incorrect heading structure" do
|
308
|
+
stub_request(:any, "www.example.com").to_return(:body => '<p><h1>Heading 1</h1><h3>Heading 3</h3><p><h4>Heading 4</h4></p><h2>Heading 2</h2>', :status => 200)
|
309
|
+
enf = Acop::Enforcer.new({:url => "www.example.com"})
|
310
|
+
error_messages = enf.check_heading_structure
|
311
|
+
error_messages.should_not be_empty
|
312
|
+
error_messages[0].should match("Incorrect document heading structure")
|
313
|
+
end
|
314
|
+
|
315
|
+
it "should return error_messages when table is missing table headers" do
|
316
|
+
stub_request(:any, "www.example.com").to_return(:body => '<table summary="summary"><tr><td>Data 1</td></tr></table>', :status => 200)
|
317
|
+
enf = Acop::Enforcer.new({:url => "www.example.com"})
|
318
|
+
error_messages = enf.check_table_headers
|
319
|
+
error_messages.should_not be_empty
|
320
|
+
error_messages[0].should match("Missing table headers for table with summary: summary")
|
321
|
+
end
|
322
|
+
|
323
|
+
it "should return error_messages when table headers are missing scope" do
|
324
|
+
stub_request(:any, "www.example.com").to_return(:body => '<table summary="summary"><th>Table Heading</th><tr><td>Data 1</td></tr></table>', :status => 200)
|
325
|
+
enf = Acop::Enforcer.new({:url => "www.example.com"})
|
326
|
+
error_messages = enf.check_table_headers
|
327
|
+
error_messages.should_not be_empty
|
328
|
+
error_messages[0].should match("Missing scope for table header")
|
329
|
+
end
|
330
|
+
|
331
|
+
it "should not return error_messages when table headers has scope" do
|
332
|
+
stub_request(:any, "www.example.com").to_return(:body => '<table summary="summary"><th scope="row">Table Heading</th><tr><td>Data 1</td></tr></table>', :status => 200)
|
333
|
+
enf = Acop::Enforcer.new({:url => "www.example.com"})
|
334
|
+
error_messages = enf.check_table_headers
|
335
|
+
error_messages.should be_empty
|
336
|
+
end
|
337
|
+
|
338
|
+
it "should return error_messages when table is missing summary" do
|
339
|
+
stub_request(:any, "www.example.com").to_return(:body => '<table><th>Table Heading</th><tr><td>Data 1</td></tr></table>', :status => 200)
|
340
|
+
enf = Acop::Enforcer.new({:url => "www.example.com"})
|
341
|
+
error_messages = enf.check_table_summary
|
342
|
+
error_messages.should_not be_empty
|
343
|
+
error_messages[0].should match("Missing table summary")
|
344
|
+
end
|
345
|
+
|
346
|
+
it "should return error_messages when html lang attribute is not specified" do
|
347
|
+
stub_request(:any, "www.example.com").to_return(:body => '<html></html>', :status => 200)
|
348
|
+
enf = Acop::Enforcer.new({:url => "www.example.com"})
|
349
|
+
error_messages = enf.check_html_lang
|
350
|
+
error_messages.should_not be_empty
|
351
|
+
error_messages[0].should match("Missing lang attribute for html")
|
352
|
+
end
|
353
|
+
|
354
|
+
it "should not return error_messages when html lang attribute is specified" do
|
355
|
+
stub_request(:any, "www.example.com").to_return(:body => '<html lang="en"></html>', :status => 200)
|
356
|
+
enf = Acop::Enforcer.new({:url => "www.example.com"})
|
357
|
+
error_messages = enf.check_html_lang
|
358
|
+
error_messages.should be_empty
|
359
|
+
end
|
360
|
+
|
361
|
+
it "should return error_messages when html visual formatting elements are used" do
|
362
|
+
stub_request(:any, "www.example.com").to_return(:body => '<body><b>Bold text</b><p><i>Italicized Text</i></p></body>', :status => 200)
|
363
|
+
enf = Acop::Enforcer.new({:url => "www.example.com"})
|
364
|
+
error_messages = enf.check_visual_formatting
|
365
|
+
error_messages.should_not be_empty
|
366
|
+
error_messages[0].should match("HTML visual formatting elements being used")
|
367
|
+
end
|
368
|
+
|
369
|
+
it "should return error_messages when html flashing elements are used" do
|
370
|
+
stub_request(:any, "www.example.com").to_return(:body => '<body><blink>Blinking text</blink><p><marquee>Marquee Text</marquee></p></body>', :status => 200)
|
371
|
+
enf = Acop::Enforcer.new({:url => "www.example.com"})
|
372
|
+
error_messages = enf.check_flashing_content
|
373
|
+
error_messages.should_not be_empty
|
374
|
+
error_messages[0].should match("Flashing elements")
|
375
|
+
end
|
376
|
+
|
377
|
+
it "should return error messages when link text is absent" do
|
378
|
+
stub_request(:any, "www.example.com").to_return(:body => '<body><a href="www.google.com"></a></body>', :status => 200)
|
379
|
+
enf = Acop::Enforcer.new({:url => "www.example.com"})
|
380
|
+
error_messages = enf.check_hyperlinks
|
381
|
+
error_messages.should_not be_empty
|
382
|
+
error_messages[0].should match("Missing link text for link")
|
383
|
+
end
|
384
|
+
|
385
|
+
it "should return error messages when there is duplicate link text" do
|
386
|
+
stub_request(:any, "www.example.com").to_return(:body => '<body><a href="www.google.com">Go to Google</a><p><a href="www.google.com">Go to Google</a></p></body>', :status => 200)
|
387
|
+
enf = Acop::Enforcer.new({:url => "www.example.com"})
|
388
|
+
error_messages = enf.check_hyperlinks
|
389
|
+
error_messages.should_not be_empty
|
390
|
+
error_messages[0].should match("Links should not have duplicate text")
|
391
|
+
end
|
154
392
|
end
|
155
393
|
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: acop
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.0.
|
4
|
+
version: 1.0.1
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -75,7 +75,7 @@ dependencies:
|
|
75
75
|
- - ! '>='
|
76
76
|
- !ruby/object:Gem::Version
|
77
77
|
version: '0'
|
78
|
-
description: A gem to enforce accessibility
|
78
|
+
description: A gem to enforce webpage accessibility'
|
79
79
|
email:
|
80
80
|
- avinashpadmanabhan@gmail.com
|
81
81
|
executables:
|