ada_matcher 0.1.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.
- data/.gitignore +4 -0
- data/Gemfile +4 -0
- data/README.md +0 -0
- data/Rakefile +17 -0
- data/ada_matcher.gemspec +27 -0
- data/lib/ada_matcher.rb +145 -0
- data/lib/ada_matcher/version.rb +3 -0
- data/resources/bad_page.html +49 -0
- data/resources/good_page.html +41 -0
- data/spec/ada_matcher_spec.rb +57 -0
- metadata +112 -0
data/.gitignore
ADDED
data/Gemfile
ADDED
data/README.md
ADDED
File without changes
|
data/Rakefile
ADDED
@@ -0,0 +1,17 @@
|
|
1
|
+
require "bundler/gem_tasks"
|
2
|
+
require 'rspec/core/rake_task'
|
3
|
+
|
4
|
+
desc 'Default: run specs.'
|
5
|
+
task :default => :spec
|
6
|
+
|
7
|
+
desc "Run specs"
|
8
|
+
RSpec::Core::RakeTask.new do |t|
|
9
|
+
t.pattern = "./spec/**/*_spec.rb"
|
10
|
+
end
|
11
|
+
|
12
|
+
desc "Generate code coverage"
|
13
|
+
RSpec::Core::RakeTask.new(:coverage) do |t|
|
14
|
+
t.pattern = "./spec/**/*_spec.rb" # don't need this, it's default.
|
15
|
+
t.rcov = true
|
16
|
+
t.rcov_opts = ['--exclude', 'spec']
|
17
|
+
end
|
data/ada_matcher.gemspec
ADDED
@@ -0,0 +1,27 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
$:.push File.expand_path("../lib", __FILE__)
|
3
|
+
require "ada_matcher/version"
|
4
|
+
|
5
|
+
Gem::Specification.new do |s|
|
6
|
+
s.name = "ada_matcher"
|
7
|
+
s.version = AdaMatcher::VERSION
|
8
|
+
s.authors = ["Aaron Brown"]
|
9
|
+
s.email = ["aaron@thebrownproject.com"]
|
10
|
+
s.homepage = ""
|
11
|
+
s.summary = %q{Adds custom rspec matchers that test watir Page objects for ADA compliance}
|
12
|
+
s.description = %q{Adds custom rspec matchers that test watir Page objects for ADA compliance}
|
13
|
+
s.license = 'MIT'
|
14
|
+
|
15
|
+
s.rubyforge_project = "ada_matcher"
|
16
|
+
|
17
|
+
s.files = `git ls-files`.split("\n")
|
18
|
+
s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
|
19
|
+
s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
|
20
|
+
s.require_paths = ["lib"]
|
21
|
+
|
22
|
+
# specify any dependencies here; for example:
|
23
|
+
s.add_development_dependency "rspec"
|
24
|
+
s.add_development_dependency "watir-webdriver", ">0"
|
25
|
+
s.add_development_dependency "headless"
|
26
|
+
# s.add_runtime_dependency "rspec"
|
27
|
+
end
|
data/lib/ada_matcher.rb
ADDED
@@ -0,0 +1,145 @@
|
|
1
|
+
require "ada_matcher/version"
|
2
|
+
|
3
|
+
module AdaMatcher
|
4
|
+
|
5
|
+
RSpec::Matchers.define :meet_ada_requirements do |reqs|
|
6
|
+
@errors = Array.new
|
7
|
+
|
8
|
+
match do |page|
|
9
|
+
browser = get_page_browser(page)
|
10
|
+
raise Exception.new("Can only test ADA on Watir::Browser and compatible objects (#{browser.class.name})") unless is_browser?(browser)
|
11
|
+
|
12
|
+
to_run = parse_args(reqs)
|
13
|
+
if (to_run.empty? || to_run.include?(:all))
|
14
|
+
@errors += image_alt(browser)
|
15
|
+
@errors += link_title(browser)
|
16
|
+
@errors += link_window_warning(browser)
|
17
|
+
@errors += htag_hierarchy(browser)
|
18
|
+
@errors += label_for(browser)
|
19
|
+
elsif to_run.include?(:image_alt)
|
20
|
+
@errors += image_alt(browser)
|
21
|
+
elsif to_run.include?(:link_title)
|
22
|
+
@errors += link_title(browser)
|
23
|
+
elsif to_run.include?(:link_window_warning)
|
24
|
+
@errors += link_window_warning(browser)
|
25
|
+
elsif to_run.include?(:htag_hierarchy)
|
26
|
+
@errors += htag_hierarchy(browser)
|
27
|
+
elsif to_run.include?(:label_for)
|
28
|
+
@errors += label_for(browser)
|
29
|
+
end
|
30
|
+
|
31
|
+
!(@errors.nil?) && (@errors.length == 0)
|
32
|
+
end
|
33
|
+
|
34
|
+
failure_message_for_should do |actual|
|
35
|
+
title = (actual.title || "Page")
|
36
|
+
"expected that page '#{title}' would meet ADA requirements, but errors were found:\n#{@errors.join("\n")}"
|
37
|
+
end
|
38
|
+
|
39
|
+
failure_message_for_should_not do |actual|
|
40
|
+
title = (actual.title || "Page")
|
41
|
+
"expected that page '#{title}' would not meet ADA requirements, but no errors were found.\n"
|
42
|
+
end
|
43
|
+
|
44
|
+
description do
|
45
|
+
"pass the following ADA requirements: #{to_run.inspect}"
|
46
|
+
end
|
47
|
+
|
48
|
+
def get_page_browser(page)
|
49
|
+
# "page" may be a browser object or, for example, a watir_page_helper object
|
50
|
+
# with a "browser" object embedded within it. Find the nested browser object
|
51
|
+
# and return it so we can continue in a consistent manner.
|
52
|
+
return page.browser if page.respond_to?(:browser)
|
53
|
+
return page.browser if page.instance_variable_defined?(:@browser)
|
54
|
+
return page.browser if page.instance_variable_defined?(:browser)
|
55
|
+
page
|
56
|
+
end
|
57
|
+
|
58
|
+
# We don't care what kind of web driver gave us "page" as long as it
|
59
|
+
# responds to all the API calls we need
|
60
|
+
def is_browser?(browser)
|
61
|
+
browser.respond_to?(:images) &&
|
62
|
+
browser.respond_to?(:links) &&
|
63
|
+
browser.respond_to?(:h1s) &&
|
64
|
+
browser.respond_to?(:h2s) &&
|
65
|
+
browser.respond_to?(:h3s) &&
|
66
|
+
browser.respond_to?(:h4s) &&
|
67
|
+
browser.respond_to?(:labels) &&
|
68
|
+
browser.respond_to?(:text_fields) &&
|
69
|
+
browser.respond_to?(:checkboxes) &&
|
70
|
+
browser.respond_to?(:file_fields) &&
|
71
|
+
browser.respond_to?(:radios) &&
|
72
|
+
browser.respond_to?(:select_lists)
|
73
|
+
end
|
74
|
+
|
75
|
+
def parse_args(args)
|
76
|
+
return [] if args.nil?
|
77
|
+
if args.respond_to? :to_a
|
78
|
+
args.to_a
|
79
|
+
else
|
80
|
+
[args]
|
81
|
+
end
|
82
|
+
end
|
83
|
+
|
84
|
+
def image_alt(page)
|
85
|
+
e = Array.new
|
86
|
+
page.images.each do |img|
|
87
|
+
e << "Image tag is missing 'alt' attribute: #{img.html}" if img.alt.empty?
|
88
|
+
end
|
89
|
+
e # return error message array
|
90
|
+
end
|
91
|
+
|
92
|
+
def link_title(page)
|
93
|
+
e = Array.new
|
94
|
+
page.links.each do |link|
|
95
|
+
e << "Link tag is missing 'title' attribute: #{link.html}" if link.title.empty?
|
96
|
+
end
|
97
|
+
e # return error message array
|
98
|
+
end
|
99
|
+
|
100
|
+
def link_window_warning(page)
|
101
|
+
e = Array.new
|
102
|
+
page.links.each do |link|
|
103
|
+
if !(link.target.empty?) && (link.title !~ /window/i)
|
104
|
+
e << "Link does not warn user about opening new window: #{link.html}"
|
105
|
+
end
|
106
|
+
end
|
107
|
+
e # return error message array
|
108
|
+
end
|
109
|
+
|
110
|
+
def htag_hierarchy(page)
|
111
|
+
e = Array.new
|
112
|
+
if page.h1s.to_a.empty? && !page.h2s.to_a.empty?
|
113
|
+
e << "H2 tag found but no H1 found on page"
|
114
|
+
end
|
115
|
+
if page.h2s.to_a.empty? && !page.h3s.to_a.empty?
|
116
|
+
e << "H3 tag found but no H2 found on page"
|
117
|
+
end
|
118
|
+
if page.h3s.to_a.empty? && !page.h4s.to_a.empty?
|
119
|
+
e << "H4 tag found but no H3 found on page"
|
120
|
+
end
|
121
|
+
|
122
|
+
e # return error message array
|
123
|
+
end
|
124
|
+
|
125
|
+
def label_for(page)
|
126
|
+
e = Array.new
|
127
|
+
fors = {}
|
128
|
+
page.labels.each do |label|
|
129
|
+
fors[label.for.to_s.to_sym] = 1 unless label.for.nil? || label.for.empty?
|
130
|
+
end
|
131
|
+
|
132
|
+
(page.text_fields.to_a + page.checkboxes.to_a + page.file_fields.to_a + page.radios.to_a + page.select_lists.to_a).each do |fld|
|
133
|
+
if fld.id.nil? || fld.id.to_s.empty?
|
134
|
+
e << "Form field without an ID or a corresponding Label: #{fld.html}"
|
135
|
+
else
|
136
|
+
e << "Form field without a corresponding Label: #{fld.html}" unless fors.has_key? fld.id.to_s.to_sym
|
137
|
+
end
|
138
|
+
end
|
139
|
+
|
140
|
+
e # return error message array
|
141
|
+
end
|
142
|
+
|
143
|
+
end
|
144
|
+
|
145
|
+
end
|
@@ -0,0 +1,49 @@
|
|
1
|
+
<html>
|
2
|
+
<head>
|
3
|
+
<title>Bad Page</title>
|
4
|
+
</head>
|
5
|
+
|
6
|
+
<body>
|
7
|
+
|
8
|
+
<h2>Section 1</h2>
|
9
|
+
<!-- img missing alt attribute -->
|
10
|
+
<a href="#" title="Stay here">
|
11
|
+
<img src="someimg.png"/>
|
12
|
+
</a>
|
13
|
+
<br/>
|
14
|
+
|
15
|
+
<h2>Section 2</h2>
|
16
|
+
<!-- link missing title attribute -->
|
17
|
+
<a href="#">
|
18
|
+
<img src="bad.png" alt="this is some alt text"/>
|
19
|
+
</a>
|
20
|
+
<br/>
|
21
|
+
|
22
|
+
<h4>H4 heading here</h4>
|
23
|
+
<!-- link opens in new window without warning -->
|
24
|
+
<a href="http://www.google.com/" title="another page" target="_blank">
|
25
|
+
Google
|
26
|
+
</a>
|
27
|
+
<br/>
|
28
|
+
|
29
|
+
<h2>Section 3</h2>
|
30
|
+
<form action="#">
|
31
|
+
<label for="someInput">textInput</label>
|
32
|
+
<input type="text" id="textInput" name="textInput"/> <br/>
|
33
|
+
<label for="checkboxInput">checkboxInput</label>
|
34
|
+
<input type="checkbox" id="checkboxInput" name="checkboxInput"/> <br/>
|
35
|
+
<label for="checkboxInput2">checkboxInput2</label>
|
36
|
+
<input type="checkbox" id="checkboxInput2" name="checkboxInput2"/> <br/>
|
37
|
+
<label for="doesNotMatch">selectInput</label>
|
38
|
+
<select id="selectInput" name="selectInput">
|
39
|
+
<option value="1">1</option>
|
40
|
+
<option value="2">2</option>
|
41
|
+
</select> <br/>
|
42
|
+
<label for="radioInput1">radioInput1</label>
|
43
|
+
<input type="radio" id="radioInput1" name="radioInput"/> <br/>
|
44
|
+
<label for="radioInput2">radioInput2</label>
|
45
|
+
<input type="radio" id="radioInput2" name="radioInput"/> <br/>
|
46
|
+
</form>
|
47
|
+
|
48
|
+
</body>
|
49
|
+
</html>
|
@@ -0,0 +1,41 @@
|
|
1
|
+
<html>
|
2
|
+
<head>
|
3
|
+
<title>Good Page</title>
|
4
|
+
</head>
|
5
|
+
|
6
|
+
<body>
|
7
|
+
<h1>Good Page</h1>
|
8
|
+
|
9
|
+
<h2>Section 1</h2>
|
10
|
+
<a href="#" title="Stay here">
|
11
|
+
<img src="someimg.png" alt="I am a test image"/>
|
12
|
+
</a>
|
13
|
+
<br/>
|
14
|
+
|
15
|
+
<h2>Section 2</h2>
|
16
|
+
<a href="http://www.google.com/" title="another page - opens in new window" target="_blank">
|
17
|
+
Google
|
18
|
+
</a>
|
19
|
+
<br/>
|
20
|
+
|
21
|
+
<h2>Section 3</h2>
|
22
|
+
<form action="#">
|
23
|
+
<label for="textInput">textInput</label>
|
24
|
+
<input type="text" id="textInput" name="textInput"/> <br/>
|
25
|
+
<label for="checkboxInput">checkboxInput</label>
|
26
|
+
<input type="checkbox" id="checkboxInput" name="checkboxInput"/> <br/>
|
27
|
+
<label for="checkboxInput2">checkboxInput2</label>
|
28
|
+
<input type="checkbox" id="checkboxInput2" name="checkboxInput2"/> <br/>
|
29
|
+
<label for="selectInput">selectInput</label>
|
30
|
+
<select id="selectInput" name="selectInput">
|
31
|
+
<option value="1">1</option>
|
32
|
+
<option value="2">2</option>
|
33
|
+
</select> <br/>
|
34
|
+
<label for="radioInput1">radioInput1</label>
|
35
|
+
<input type="radio" id="radioInput1" name="radioInput"/> <br/>
|
36
|
+
<label for="radioInput2">radioInput2</label>
|
37
|
+
<input type="radio" id="radioInput2" name="radioInput"/> <br/>
|
38
|
+
</form>
|
39
|
+
|
40
|
+
</body>
|
41
|
+
</html>
|
@@ -0,0 +1,57 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'ada_matcher'
|
3
|
+
require 'watir-webdriver'
|
4
|
+
require 'headless'
|
5
|
+
|
6
|
+
describe "AdaMatcher" do
|
7
|
+
before(:all) do
|
8
|
+
@project_base_path = File.expand_path File.dirname(__FILE__) << "../"
|
9
|
+
@headless = Headless.new
|
10
|
+
@headless.start
|
11
|
+
@browser = Watir::Browser.new
|
12
|
+
end
|
13
|
+
|
14
|
+
after(:all) do
|
15
|
+
@browser.close
|
16
|
+
@headless.destroy
|
17
|
+
end
|
18
|
+
|
19
|
+
before(:each) do
|
20
|
+
end
|
21
|
+
|
22
|
+
after(:each) do
|
23
|
+
end
|
24
|
+
|
25
|
+
it "should pass ADA compliant page" do
|
26
|
+
@browser.goto "file://#{@project_base_path}/../resources/good_page.html"
|
27
|
+
@browser.should meet_ada_requirements
|
28
|
+
# can also say:
|
29
|
+
# @browser.should meet_ada_requirements(:all)
|
30
|
+
end
|
31
|
+
|
32
|
+
it "should fail with bad image tags" do
|
33
|
+
@browser.goto "file://#{@project_base_path}/../resources/bad_page.html"
|
34
|
+
@browser.should_not meet_ada_requirements(:image_alt)
|
35
|
+
end
|
36
|
+
|
37
|
+
it "should fail with bad link tags" do
|
38
|
+
@browser.goto "file://#{@project_base_path}/../resources/bad_page.html"
|
39
|
+
@browser.should_not meet_ada_requirements(:link_title)
|
40
|
+
end
|
41
|
+
|
42
|
+
it "should fail with new window links without warnings" do
|
43
|
+
@browser.goto "file://#{@project_base_path}/../resources/bad_page.html"
|
44
|
+
@browser.should_not meet_ada_requirements(:link_window_warning)
|
45
|
+
end
|
46
|
+
|
47
|
+
it "should fail if H2-H4 tags exist without lower-index H tags" do
|
48
|
+
@browser.goto "file://#{@project_base_path}/../resources/bad_page.html"
|
49
|
+
@browser.should_not meet_ada_requirements(:htag_hierarchy)
|
50
|
+
end
|
51
|
+
|
52
|
+
it "should fail if a Form field exists without a corresponding Label" do
|
53
|
+
@browser.goto "file://#{@project_base_path}/../resources/bad_page.html"
|
54
|
+
@browser.should_not meet_ada_requirements(:label_for)
|
55
|
+
end
|
56
|
+
|
57
|
+
end
|
metadata
ADDED
@@ -0,0 +1,112 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: ada_matcher
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
prerelease: false
|
5
|
+
segments:
|
6
|
+
- 0
|
7
|
+
- 1
|
8
|
+
- 0
|
9
|
+
version: 0.1.0
|
10
|
+
platform: ruby
|
11
|
+
authors:
|
12
|
+
- Aaron Brown
|
13
|
+
autorequire:
|
14
|
+
bindir: bin
|
15
|
+
cert_chain: []
|
16
|
+
|
17
|
+
date: 2012-02-26 00:00:00 -05:00
|
18
|
+
default_executable:
|
19
|
+
dependencies:
|
20
|
+
- !ruby/object:Gem::Dependency
|
21
|
+
name: rspec
|
22
|
+
prerelease: false
|
23
|
+
requirement: &id001 !ruby/object:Gem::Requirement
|
24
|
+
none: false
|
25
|
+
requirements:
|
26
|
+
- - ">="
|
27
|
+
- !ruby/object:Gem::Version
|
28
|
+
segments:
|
29
|
+
- 0
|
30
|
+
version: "0"
|
31
|
+
type: :development
|
32
|
+
version_requirements: *id001
|
33
|
+
- !ruby/object:Gem::Dependency
|
34
|
+
name: watir-webdriver
|
35
|
+
prerelease: false
|
36
|
+
requirement: &id002 !ruby/object:Gem::Requirement
|
37
|
+
none: false
|
38
|
+
requirements:
|
39
|
+
- - ">"
|
40
|
+
- !ruby/object:Gem::Version
|
41
|
+
segments:
|
42
|
+
- 0
|
43
|
+
version: "0"
|
44
|
+
type: :development
|
45
|
+
version_requirements: *id002
|
46
|
+
- !ruby/object:Gem::Dependency
|
47
|
+
name: headless
|
48
|
+
prerelease: false
|
49
|
+
requirement: &id003 !ruby/object:Gem::Requirement
|
50
|
+
none: false
|
51
|
+
requirements:
|
52
|
+
- - ">="
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
segments:
|
55
|
+
- 0
|
56
|
+
version: "0"
|
57
|
+
type: :development
|
58
|
+
version_requirements: *id003
|
59
|
+
description: Adds custom rspec matchers that test watir Page objects for ADA compliance
|
60
|
+
email:
|
61
|
+
- aaron@thebrownproject.com
|
62
|
+
executables: []
|
63
|
+
|
64
|
+
extensions: []
|
65
|
+
|
66
|
+
extra_rdoc_files: []
|
67
|
+
|
68
|
+
files:
|
69
|
+
- .gitignore
|
70
|
+
- Gemfile
|
71
|
+
- README.md
|
72
|
+
- Rakefile
|
73
|
+
- ada_matcher.gemspec
|
74
|
+
- lib/ada_matcher.rb
|
75
|
+
- lib/ada_matcher/version.rb
|
76
|
+
- resources/bad_page.html
|
77
|
+
- resources/good_page.html
|
78
|
+
- spec/ada_matcher_spec.rb
|
79
|
+
has_rdoc: true
|
80
|
+
homepage: ""
|
81
|
+
licenses:
|
82
|
+
- MIT
|
83
|
+
post_install_message:
|
84
|
+
rdoc_options: []
|
85
|
+
|
86
|
+
require_paths:
|
87
|
+
- lib
|
88
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
89
|
+
none: false
|
90
|
+
requirements:
|
91
|
+
- - ">="
|
92
|
+
- !ruby/object:Gem::Version
|
93
|
+
segments:
|
94
|
+
- 0
|
95
|
+
version: "0"
|
96
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
97
|
+
none: false
|
98
|
+
requirements:
|
99
|
+
- - ">="
|
100
|
+
- !ruby/object:Gem::Version
|
101
|
+
segments:
|
102
|
+
- 0
|
103
|
+
version: "0"
|
104
|
+
requirements: []
|
105
|
+
|
106
|
+
rubyforge_project: ada_matcher
|
107
|
+
rubygems_version: 1.3.7
|
108
|
+
signing_key:
|
109
|
+
specification_version: 3
|
110
|
+
summary: Adds custom rspec matchers that test watir Page objects for ADA compliance
|
111
|
+
test_files: []
|
112
|
+
|