capybara-accessible 0.0.2 → 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.gitignore +0 -1
- data/Gemfile.lock +69 -0
- data/README.md +33 -8
- data/lib/capybara/accessible/auditor.rb +34 -10
- data/lib/capybara/accessible/axs_testing.js +452 -27
- data/lib/capybara/accessible/driver.rb +1 -1
- data/lib/capybara/accessible/element.rb +1 -1
- data/lib/capybara/accessible/version.rb +1 -1
- data/spec/accessible_app.rb +4 -0
- data/spec/driver_spec.rb +1 -1
- data/travis.yml +7 -0
- metadata +5 -4
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: ac095c1cbae4d4f383b2d82ff785b7c42297f467
|
4
|
+
data.tar.gz: 545f83a936a547ed9231b77513f4b48bdc9d0ec0
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 7cbd721bdd6d6db70718053a2b9a1e2ec37982db2d06df2620b50ee23072a5f51aba46adf5cf3b9efccb225a69be8940e0fac54cfee6f67c3ecbd9f63ab67f67
|
7
|
+
data.tar.gz: 73b0d915dbde5ce90f0d0b617e412f4b41666249dfba82dbf5b539d37c08129028bb25e3efc18877b0d54aeabcf5d2aac3ad6ee019752ad7204f9b498a94ff26
|
data/.gitignore
CHANGED
data/Gemfile.lock
ADDED
@@ -0,0 +1,69 @@
|
|
1
|
+
PATH
|
2
|
+
remote: .
|
3
|
+
specs:
|
4
|
+
capybara-accessible (0.0.1)
|
5
|
+
capybara (~> 2.0, >= 2.0.2)
|
6
|
+
selenium-webdriver
|
7
|
+
|
8
|
+
GEM
|
9
|
+
remote: https://rubygems.org/
|
10
|
+
specs:
|
11
|
+
capybara (2.1.0)
|
12
|
+
mime-types (>= 1.16)
|
13
|
+
nokogiri (>= 1.3.3)
|
14
|
+
rack (>= 1.0.0)
|
15
|
+
rack-test (>= 0.5.4)
|
16
|
+
xpath (~> 2.0)
|
17
|
+
childprocess (0.3.9)
|
18
|
+
ffi (~> 1.0, >= 1.0.11)
|
19
|
+
coderay (1.0.9)
|
20
|
+
diff-lcs (1.2.4)
|
21
|
+
ffi (1.8.1)
|
22
|
+
method_source (0.8.1)
|
23
|
+
mime-types (1.23)
|
24
|
+
multi_json (1.7.7)
|
25
|
+
nokogiri (1.5.9)
|
26
|
+
pry (0.9.12.2)
|
27
|
+
coderay (~> 1.0.5)
|
28
|
+
method_source (~> 0.8)
|
29
|
+
slop (~> 3.4)
|
30
|
+
rack (1.4.5)
|
31
|
+
rack-protection (1.4.0)
|
32
|
+
rack
|
33
|
+
rack-test (0.6.2)
|
34
|
+
rack (>= 1.0)
|
35
|
+
rake (10.0.4)
|
36
|
+
rspec (2.13.0)
|
37
|
+
rspec-core (~> 2.13.0)
|
38
|
+
rspec-expectations (~> 2.13.0)
|
39
|
+
rspec-mocks (~> 2.13.0)
|
40
|
+
rspec-core (2.13.1)
|
41
|
+
rspec-expectations (2.13.0)
|
42
|
+
diff-lcs (>= 1.1.3, < 2.0)
|
43
|
+
rspec-mocks (2.13.1)
|
44
|
+
rubyzip (0.9.9)
|
45
|
+
selenium-webdriver (2.32.1)
|
46
|
+
childprocess (>= 0.2.5)
|
47
|
+
multi_json (~> 1.0)
|
48
|
+
rubyzip
|
49
|
+
websocket (~> 1.0.4)
|
50
|
+
sinatra (1.3.6)
|
51
|
+
rack (~> 1.4)
|
52
|
+
rack-protection (~> 1.3)
|
53
|
+
tilt (~> 1.3, >= 1.3.3)
|
54
|
+
slop (3.4.5)
|
55
|
+
tilt (1.4.1)
|
56
|
+
websocket (1.0.7)
|
57
|
+
xpath (2.0.0)
|
58
|
+
nokogiri (~> 1.3)
|
59
|
+
|
60
|
+
PLATFORMS
|
61
|
+
ruby
|
62
|
+
|
63
|
+
DEPENDENCIES
|
64
|
+
bundler (~> 1.3)
|
65
|
+
capybara-accessible!
|
66
|
+
pry
|
67
|
+
rake
|
68
|
+
rspec
|
69
|
+
sinatra
|
data/README.md
CHANGED
@@ -1,16 +1,39 @@
|
|
1
1
|
# Capybara::Accessible
|
2
2
|
|
3
|
-
|
3
|
+
[![](https://api.tddium.com/pivotal-casebook/capybara-accessible/badges/25347.png?badge_token=1e276156136851abb3ca113d37e1cddca332e098)](https://api.tddium.com/pivotal-casebook/capybara-accessible/suites/25347)
|
4
4
|
|
5
|
-
|
5
|
+
Automated accessibility testing for Rails integration tests (Rspec + Capybara)
|
6
|
+
capybara-accessible automatically runs [Google's Accessibility Developer Tools](https://code.google.com/p/accessibility-developer-tools/) audits within your Ruby on Rails project integration test suite, using [RSpec feature specs](https://www.relishapp.com/rspec/rspec-rails/docs/feature-specs/feature-spec).
|
6
7
|
|
7
|
-
|
8
|
+
Some of the checks that are included:
|
9
|
+
* minimum color contrast
|
10
|
+
* label associations with inputs
|
11
|
+
* presence of alt attributes
|
12
|
+
* valid use of ARIA roles
|
13
|
+
|
14
|
+
The full list of accessibility audit rules is on the [Google Accessibility Developer Tools wiki](https://code.google.com/p/accessibility-developer-tools/wiki/AuditRules).
|
15
|
+
|
16
|
+
capybara-accessible extends Capybara and defines a custom webdriver that runs javascript assertions on every page visit and link/button click.
|
17
|
+
This way you do not need to make explicit assertions on accessibility. Instead, the test will simply fail with a message indicating the accessibility errors, like so:
|
18
|
+
|
19
|
+
Failure/Error: visit '/inaccessible'
|
20
|
+
Capybara::Accessible::InaccessibleError:
|
21
|
+
*** Begin accessibility audit results ***
|
22
|
+
An accessibility audit found
|
23
|
+
Errors:
|
24
|
+
Error: AX_ARIA_01 (Elements with ARIA roles must use a valid, non-abstract ARIA role) failed on the following element:
|
25
|
+
body > .outer > .standup > .block.buttons
|
26
|
+
See https://code.google.com/p/accessibility-developer-tools/wiki/AuditRules#AX_ARIA_01:_Elements_with_ARIA_roles_must_use_a_valid,_non-abstr for more information.
|
27
|
+
|
28
|
+
|
29
|
+
*** End accessibility audit results ***
|
30
|
+
|
31
|
+
Need help? Ask on the mailing list (please do not open an issue on GitHub): https://groups.google.com/forum/#!forum/capybara-accessible
|
8
32
|
|
9
33
|
## Installation
|
10
34
|
|
11
35
|
Add this line to your application's Gemfile:
|
12
36
|
|
13
|
-
gem 'selenium-webdriver'
|
14
37
|
gem 'capybara-accessible'
|
15
38
|
|
16
39
|
And then execute:
|
@@ -23,18 +46,20 @@ Or install it yourself as:
|
|
23
46
|
|
24
47
|
## Usage
|
25
48
|
|
26
|
-
|
49
|
+
You can use capybara-accessible as a drop-in replacement for Rack::Test, Selenium or capybara-webkit drivers for Capybara. Simply set the driver in spec_helper.rb:
|
27
50
|
|
28
51
|
require 'capybara/rspec'
|
29
52
|
require 'capybara/accessible'
|
30
53
|
|
31
54
|
Capybara.current_driver = :accessible
|
32
55
|
|
33
|
-
|
56
|
+
You can also optionally exclude individual accessibility assertions from being run:
|
57
|
+
|
58
|
+
Capybara::Accessible::Auditor.exclusions = ['AX_FOCUS_01', 'AX_TEXT_01']
|
34
59
|
|
35
|
-
|
60
|
+
You can use exclusions to supress failures for accessibility errors that you cannot immediately address, without turning off all assertions.
|
36
61
|
|
37
|
-
|
62
|
+
See the [Google Accessibility Developer Tools wiki](https://code.google.com/p/accessibility-developer-tools/wiki/AuditRules) for the complete list of codes.
|
38
63
|
|
39
64
|
## Contributing
|
40
65
|
|
@@ -14,23 +14,47 @@ module Capybara::Accessible
|
|
14
14
|
File.read(File.expand_path("../axs_testing.js", __FILE__))
|
15
15
|
end
|
16
16
|
|
17
|
-
def
|
18
|
-
|
17
|
+
def audit_failures
|
18
|
+
script =<<-JAVASCRIPT
|
19
|
+
var config = new axs.AuditConfiguration();
|
20
|
+
config.auditRulesToIgnore = #{excluded_rules.to_json};
|
21
|
+
results = axs.Audit.run(config);
|
22
|
+
return axs.Audit.auditResults(results).getErrors();
|
23
|
+
JAVASCRIPT
|
19
24
|
|
20
|
-
|
21
|
-
excluded_assertions.include?(r['rule']['code'])
|
22
|
-
end
|
25
|
+
run_script("#{audit_rules} #{script}")
|
23
26
|
end
|
24
27
|
|
25
28
|
def failure_messages
|
26
|
-
|
27
|
-
"<#{f['elements'].first.tag_name}> tag with text \"#{f['elements'].first.text}\" - #{f['rule']['heading']}" if f['result'] == 'FAIL'
|
28
|
-
end.join("\n")
|
29
|
+
run_script("#{audit_rules} var results = axs.Audit.run(); return axs.Audit.createReport(results)")
|
29
30
|
end
|
30
31
|
|
31
|
-
|
32
|
-
|
32
|
+
private
|
33
|
+
|
34
|
+
def excluded_rules
|
35
|
+
codes = Capybara::Accessible::Auditor.exclusions
|
36
|
+
mapping = {
|
37
|
+
'AX_ARIA_01' => 'badAriaRole',
|
38
|
+
'AX_ARIA_02' => 'nonExistentAriaLabelledbyElement',
|
39
|
+
'AX_ARIA_03' => 'requiredAriaAttributeMissing',
|
40
|
+
'AX_ARIA_04' => 'badAriaAttributeValue',
|
41
|
+
'AX_TEXT_01' => 'controlsWithoutLabel',
|
42
|
+
'AX_TEXT_02' => 'imagesWithoutAltText',
|
43
|
+
'AX_TITLE_01' => 'pageWithoutTitle',
|
44
|
+
'AX_IMAGE_01' => 'elementsWithMeaningfulBackgroundImage',
|
45
|
+
'AX_FOCUS_01' => 'focusableElementNotVisibleAndNotAriaHidden',
|
46
|
+
'AX_FOCUS_02' => 'unfocusableElementsWithOnClick',
|
47
|
+
'AX_COLOR_01' => 'lowContrastElements',
|
48
|
+
'AX_VIDEO_01' => 'videoWithoutCaptions',
|
49
|
+
'AX_AUDIO_01' => 'audioWithoutControls'
|
50
|
+
# 'AX_TITLE_01' => 'linkWithUnclearPurpose', # This has a duplicate name
|
51
|
+
# 'AX_ARIA_05' => '', # This has no rule associated with it
|
52
|
+
}
|
53
|
+
|
54
|
+
names = codes.map { |code| mapping[code]}
|
55
|
+
end
|
33
56
|
|
57
|
+
def run_script(script)
|
34
58
|
if @session
|
35
59
|
@session.driver.execute_script(script)
|
36
60
|
else
|
@@ -1,3 +1,391 @@
|
|
1
|
+
var COMPILED = !0, goog = goog || {};
|
2
|
+
goog.global = this;
|
3
|
+
goog.exportPath_ = function(a, b, c) {
|
4
|
+
a = a.split(".");
|
5
|
+
c = c || goog.global;
|
6
|
+
a[0] in c || !c.execScript || c.execScript("var " + a[0]);
|
7
|
+
for(var d;a.length && (d = a.shift());) {
|
8
|
+
a.length || void 0 === b ? c = c[d] ? c[d] : c[d] = {} : c[d] = b
|
9
|
+
}
|
10
|
+
};
|
11
|
+
goog.define = function(a, b) {
|
12
|
+
var c = b;
|
13
|
+
COMPILED || goog.global.CLOSURE_DEFINES && Object.prototype.hasOwnProperty.call(goog.global.CLOSURE_DEFINES, a) && (c = goog.global.CLOSURE_DEFINES[a]);
|
14
|
+
goog.exportPath_(a, c)
|
15
|
+
};
|
16
|
+
goog.DEBUG = !0;
|
17
|
+
goog.LOCALE = "en";
|
18
|
+
goog.TRUSTED_SITE = !0;
|
19
|
+
goog.provide = function(a) {
|
20
|
+
if(!COMPILED) {
|
21
|
+
if(goog.isProvided_(a)) {
|
22
|
+
throw Error('Namespace "' + a + '" already declared.');
|
23
|
+
}
|
24
|
+
delete goog.implicitNamespaces_[a];
|
25
|
+
for(var b = a;(b = b.substring(0, b.lastIndexOf("."))) && !goog.getObjectByName(b);) {
|
26
|
+
goog.implicitNamespaces_[b] = !0
|
27
|
+
}
|
28
|
+
}
|
29
|
+
goog.exportPath_(a)
|
30
|
+
};
|
31
|
+
goog.setTestOnly = function(a) {
|
32
|
+
if(COMPILED && !goog.DEBUG) {
|
33
|
+
throw a = a || "", Error("Importing test-only code into non-debug environment" + a ? ": " + a : ".");
|
34
|
+
}
|
35
|
+
};
|
36
|
+
COMPILED || (goog.isProvided_ = function(a) {
|
37
|
+
return!goog.implicitNamespaces_[a] && !!goog.getObjectByName(a)
|
38
|
+
}, goog.implicitNamespaces_ = {});
|
39
|
+
goog.getObjectByName = function(a, b) {
|
40
|
+
for(var c = a.split("."), d = b || goog.global, e;e = c.shift();) {
|
41
|
+
if(goog.isDefAndNotNull(d[e])) {
|
42
|
+
d = d[e]
|
43
|
+
}else {
|
44
|
+
return null
|
45
|
+
}
|
46
|
+
}
|
47
|
+
return d
|
48
|
+
};
|
49
|
+
goog.globalize = function(a, b) {
|
50
|
+
var c = b || goog.global, d;
|
51
|
+
for(d in a) {
|
52
|
+
c[d] = a[d]
|
53
|
+
}
|
54
|
+
};
|
55
|
+
goog.addDependency = function(a, b, c) {
|
56
|
+
if(goog.DEPENDENCIES_ENABLED) {
|
57
|
+
var d;
|
58
|
+
a = a.replace(/\\/g, "/");
|
59
|
+
for(var e = goog.dependencies_, f = 0;d = b[f];f++) {
|
60
|
+
e.nameToPath[d] = a, a in e.pathToNames || (e.pathToNames[a] = {}), e.pathToNames[a][d] = !0
|
61
|
+
}
|
62
|
+
for(d = 0;b = c[d];d++) {
|
63
|
+
a in e.requires || (e.requires[a] = {}), e.requires[a][b] = !0
|
64
|
+
}
|
65
|
+
}
|
66
|
+
};
|
67
|
+
goog.ENABLE_DEBUG_LOADER = !0;
|
68
|
+
goog.require = function(a) {
|
69
|
+
if(!COMPILED && !goog.isProvided_(a)) {
|
70
|
+
if(goog.ENABLE_DEBUG_LOADER) {
|
71
|
+
var b = goog.getPathFromDeps_(a);
|
72
|
+
if(b) {
|
73
|
+
goog.included_[b] = !0;
|
74
|
+
goog.writeScripts_();
|
75
|
+
return
|
76
|
+
}
|
77
|
+
}
|
78
|
+
a = "goog.require could not find: " + a;
|
79
|
+
goog.global.console && goog.global.console.error(a);
|
80
|
+
throw Error(a);
|
81
|
+
}
|
82
|
+
};
|
83
|
+
goog.basePath = "";
|
84
|
+
goog.nullFunction = function() {
|
85
|
+
};
|
86
|
+
goog.identityFunction = function(a, b) {
|
87
|
+
return a
|
88
|
+
};
|
89
|
+
goog.abstractMethod = function() {
|
90
|
+
throw Error("unimplemented abstract method");
|
91
|
+
};
|
92
|
+
goog.addSingletonGetter = function(a) {
|
93
|
+
a.getInstance = function() {
|
94
|
+
if(a.instance_) {
|
95
|
+
return a.instance_
|
96
|
+
}
|
97
|
+
goog.DEBUG && (goog.instantiatedSingletons_[goog.instantiatedSingletons_.length] = a);
|
98
|
+
return a.instance_ = new a
|
99
|
+
}
|
100
|
+
};
|
101
|
+
goog.instantiatedSingletons_ = [];
|
102
|
+
goog.DEPENDENCIES_ENABLED = !COMPILED && goog.ENABLE_DEBUG_LOADER;
|
103
|
+
goog.DEPENDENCIES_ENABLED && (goog.included_ = {}, goog.dependencies_ = {pathToNames:{}, nameToPath:{}, requires:{}, visited:{}, written:{}}, goog.inHtmlDocument_ = function() {
|
104
|
+
var a = goog.global.document;
|
105
|
+
return"undefined" != typeof a && "write" in a
|
106
|
+
}, goog.findBasePath_ = function() {
|
107
|
+
if(goog.global.CLOSURE_BASE_PATH) {
|
108
|
+
goog.basePath = goog.global.CLOSURE_BASE_PATH
|
109
|
+
}else {
|
110
|
+
if(goog.inHtmlDocument_()) {
|
111
|
+
for(var a = goog.global.document.getElementsByTagName("script"), b = a.length - 1;0 <= b;--b) {
|
112
|
+
var c = a[b].src, d = c.lastIndexOf("?"), d = -1 == d ? c.length : d;
|
113
|
+
if("base.js" == c.substr(d - 7, 7)) {
|
114
|
+
goog.basePath = c.substr(0, d - 7);
|
115
|
+
break
|
116
|
+
}
|
117
|
+
}
|
118
|
+
}
|
119
|
+
}
|
120
|
+
}, goog.importScript_ = function(a) {
|
121
|
+
var b = goog.global.CLOSURE_IMPORT_SCRIPT || goog.writeScriptTag_;
|
122
|
+
!goog.dependencies_.written[a] && b(a) && (goog.dependencies_.written[a] = !0)
|
123
|
+
}, goog.writeScriptTag_ = function(a) {
|
124
|
+
if(goog.inHtmlDocument_()) {
|
125
|
+
var b = goog.global.document;
|
126
|
+
if("complete" == b.readyState) {
|
127
|
+
if(/\bdeps.js$/.test(a)) {
|
128
|
+
return!1
|
129
|
+
}
|
130
|
+
throw Error('Cannot write "' + a + '" after document load');
|
131
|
+
}
|
132
|
+
b.write('<script type="text/javascript" src="' + a + '">\x3c/script>');
|
133
|
+
return!0
|
134
|
+
}
|
135
|
+
return!1
|
136
|
+
}, goog.writeScripts_ = function() {
|
137
|
+
function a(e) {
|
138
|
+
if(!(e in d.written)) {
|
139
|
+
if(!(e in d.visited) && (d.visited[e] = !0, e in d.requires)) {
|
140
|
+
for(var g in d.requires[e]) {
|
141
|
+
if(!goog.isProvided_(g)) {
|
142
|
+
if(g in d.nameToPath) {
|
143
|
+
a(d.nameToPath[g])
|
144
|
+
}else {
|
145
|
+
throw Error("Undefined nameToPath for " + g);
|
146
|
+
}
|
147
|
+
}
|
148
|
+
}
|
149
|
+
}
|
150
|
+
e in c || (c[e] = !0, b.push(e))
|
151
|
+
}
|
152
|
+
}
|
153
|
+
var b = [], c = {}, d = goog.dependencies_, e;
|
154
|
+
for(e in goog.included_) {
|
155
|
+
d.written[e] || a(e)
|
156
|
+
}
|
157
|
+
for(e = 0;e < b.length;e++) {
|
158
|
+
if(b[e]) {
|
159
|
+
goog.importScript_(goog.basePath + b[e])
|
160
|
+
}else {
|
161
|
+
throw Error("Undefined script input");
|
162
|
+
}
|
163
|
+
}
|
164
|
+
}, goog.getPathFromDeps_ = function(a) {
|
165
|
+
return a in goog.dependencies_.nameToPath ? goog.dependencies_.nameToPath[a] : null
|
166
|
+
}, goog.findBasePath_(), goog.global.CLOSURE_NO_DEPS || goog.importScript_(goog.basePath + "deps.js"));
|
167
|
+
goog.typeOf = function(a) {
|
168
|
+
var b = typeof a;
|
169
|
+
if("object" == b) {
|
170
|
+
if(a) {
|
171
|
+
if(a instanceof Array) {
|
172
|
+
return"array"
|
173
|
+
}
|
174
|
+
if(a instanceof Object) {
|
175
|
+
return b
|
176
|
+
}
|
177
|
+
var c = Object.prototype.toString.call(a);
|
178
|
+
if("[object Window]" == c) {
|
179
|
+
return"object"
|
180
|
+
}
|
181
|
+
if("[object Array]" == c || "number" == typeof a.length && "undefined" != typeof a.splice && "undefined" != typeof a.propertyIsEnumerable && !a.propertyIsEnumerable("splice")) {
|
182
|
+
return"array"
|
183
|
+
}
|
184
|
+
if("[object Function]" == c || "undefined" != typeof a.call && "undefined" != typeof a.propertyIsEnumerable && !a.propertyIsEnumerable("call")) {
|
185
|
+
return"function"
|
186
|
+
}
|
187
|
+
}else {
|
188
|
+
return"null"
|
189
|
+
}
|
190
|
+
}else {
|
191
|
+
if("function" == b && "undefined" == typeof a.call) {
|
192
|
+
return"object"
|
193
|
+
}
|
194
|
+
}
|
195
|
+
return b
|
196
|
+
};
|
197
|
+
goog.isDef = function(a) {
|
198
|
+
return void 0 !== a
|
199
|
+
};
|
200
|
+
goog.isNull = function(a) {
|
201
|
+
return null === a
|
202
|
+
};
|
203
|
+
goog.isDefAndNotNull = function(a) {
|
204
|
+
return null != a
|
205
|
+
};
|
206
|
+
goog.isArray = function(a) {
|
207
|
+
return"array" == goog.typeOf(a)
|
208
|
+
};
|
209
|
+
goog.isArrayLike = function(a) {
|
210
|
+
var b = goog.typeOf(a);
|
211
|
+
return"array" == b || "object" == b && "number" == typeof a.length
|
212
|
+
};
|
213
|
+
goog.isDateLike = function(a) {
|
214
|
+
return goog.isObject(a) && "function" == typeof a.getFullYear
|
215
|
+
};
|
216
|
+
goog.isString = function(a) {
|
217
|
+
return"string" == typeof a
|
218
|
+
};
|
219
|
+
goog.isBoolean = function(a) {
|
220
|
+
return"boolean" == typeof a
|
221
|
+
};
|
222
|
+
goog.isNumber = function(a) {
|
223
|
+
return"number" == typeof a
|
224
|
+
};
|
225
|
+
goog.isFunction = function(a) {
|
226
|
+
return"function" == goog.typeOf(a)
|
227
|
+
};
|
228
|
+
goog.isObject = function(a) {
|
229
|
+
var b = typeof a;
|
230
|
+
return"object" == b && null != a || "function" == b
|
231
|
+
};
|
232
|
+
goog.getUid = function(a) {
|
233
|
+
return a[goog.UID_PROPERTY_] || (a[goog.UID_PROPERTY_] = ++goog.uidCounter_)
|
234
|
+
};
|
235
|
+
goog.removeUid = function(a) {
|
236
|
+
"removeAttribute" in a && a.removeAttribute(goog.UID_PROPERTY_);
|
237
|
+
try {
|
238
|
+
delete a[goog.UID_PROPERTY_]
|
239
|
+
}catch(b) {
|
240
|
+
}
|
241
|
+
};
|
242
|
+
goog.UID_PROPERTY_ = "closure_uid_" + (1E9 * Math.random() >>> 0);
|
243
|
+
goog.uidCounter_ = 0;
|
244
|
+
goog.getHashCode = goog.getUid;
|
245
|
+
goog.removeHashCode = goog.removeUid;
|
246
|
+
goog.cloneObject = function(a) {
|
247
|
+
var b = goog.typeOf(a);
|
248
|
+
if("object" == b || "array" == b) {
|
249
|
+
if(a.clone) {
|
250
|
+
return a.clone()
|
251
|
+
}
|
252
|
+
var b = "array" == b ? [] : {}, c;
|
253
|
+
for(c in a) {
|
254
|
+
b[c] = goog.cloneObject(a[c])
|
255
|
+
}
|
256
|
+
return b
|
257
|
+
}
|
258
|
+
return a
|
259
|
+
};
|
260
|
+
goog.bindNative_ = function(a, b, c) {
|
261
|
+
return a.call.apply(a.bind, arguments)
|
262
|
+
};
|
263
|
+
goog.bindJs_ = function(a, b, c) {
|
264
|
+
if(!a) {
|
265
|
+
throw Error();
|
266
|
+
}
|
267
|
+
if(2 < arguments.length) {
|
268
|
+
var d = Array.prototype.slice.call(arguments, 2);
|
269
|
+
return function() {
|
270
|
+
var c = Array.prototype.slice.call(arguments);
|
271
|
+
Array.prototype.unshift.apply(c, d);
|
272
|
+
return a.apply(b, c)
|
273
|
+
}
|
274
|
+
}
|
275
|
+
return function() {
|
276
|
+
return a.apply(b, arguments)
|
277
|
+
}
|
278
|
+
};
|
279
|
+
goog.bind = function(a, b, c) {
|
280
|
+
Function.prototype.bind && -1 != Function.prototype.bind.toString().indexOf("native code") ? goog.bind = goog.bindNative_ : goog.bind = goog.bindJs_;
|
281
|
+
return goog.bind.apply(null, arguments)
|
282
|
+
};
|
283
|
+
goog.partial = function(a, b) {
|
284
|
+
var c = Array.prototype.slice.call(arguments, 1);
|
285
|
+
return function() {
|
286
|
+
var b = Array.prototype.slice.call(arguments);
|
287
|
+
b.unshift.apply(b, c);
|
288
|
+
return a.apply(this, b)
|
289
|
+
}
|
290
|
+
};
|
291
|
+
goog.mixin = function(a, b) {
|
292
|
+
for(var c in b) {
|
293
|
+
a[c] = b[c]
|
294
|
+
}
|
295
|
+
};
|
296
|
+
goog.now = goog.TRUSTED_SITE && Date.now || function() {
|
297
|
+
return+new Date
|
298
|
+
};
|
299
|
+
goog.globalEval = function(a) {
|
300
|
+
if(goog.global.execScript) {
|
301
|
+
goog.global.execScript(a, "JavaScript")
|
302
|
+
}else {
|
303
|
+
if(goog.global.eval) {
|
304
|
+
if(null == goog.evalWorksForGlobals_ && (goog.global.eval("var _et_ = 1;"), "undefined" != typeof goog.global._et_ ? (delete goog.global._et_, goog.evalWorksForGlobals_ = !0) : goog.evalWorksForGlobals_ = !1), goog.evalWorksForGlobals_) {
|
305
|
+
goog.global.eval(a)
|
306
|
+
}else {
|
307
|
+
var b = goog.global.document, c = b.createElement("script");
|
308
|
+
c.type = "text/javascript";
|
309
|
+
c.defer = !1;
|
310
|
+
c.appendChild(b.createTextNode(a));
|
311
|
+
b.body.appendChild(c);
|
312
|
+
b.body.removeChild(c)
|
313
|
+
}
|
314
|
+
}else {
|
315
|
+
throw Error("goog.globalEval not available");
|
316
|
+
}
|
317
|
+
}
|
318
|
+
};
|
319
|
+
goog.evalWorksForGlobals_ = null;
|
320
|
+
goog.getCssName = function(a, b) {
|
321
|
+
var c = function(a) {
|
322
|
+
return goog.cssNameMapping_[a] || a
|
323
|
+
}, d = function(a) {
|
324
|
+
a = a.split("-");
|
325
|
+
for(var b = [], d = 0;d < a.length;d++) {
|
326
|
+
b.push(c(a[d]))
|
327
|
+
}
|
328
|
+
return b.join("-")
|
329
|
+
}, d = goog.cssNameMapping_ ? "BY_WHOLE" == goog.cssNameMappingStyle_ ? c : d : function(a) {
|
330
|
+
return a
|
331
|
+
};
|
332
|
+
return b ? a + "-" + d(b) : d(a)
|
333
|
+
};
|
334
|
+
goog.setCssNameMapping = function(a, b) {
|
335
|
+
goog.cssNameMapping_ = a;
|
336
|
+
goog.cssNameMappingStyle_ = b
|
337
|
+
};
|
338
|
+
!COMPILED && goog.global.CLOSURE_CSS_NAME_MAPPING && (goog.cssNameMapping_ = goog.global.CLOSURE_CSS_NAME_MAPPING);
|
339
|
+
goog.getMsg = function(a, b) {
|
340
|
+
var c = b || {}, d;
|
341
|
+
for(d in c) {
|
342
|
+
var e = ("" + c[d]).replace(/\$/g, "$$$$");
|
343
|
+
a = a.replace(RegExp("\\{\\$" + d + "\\}", "gi"), e)
|
344
|
+
}
|
345
|
+
return a
|
346
|
+
};
|
347
|
+
goog.getMsgWithFallback = function(a, b) {
|
348
|
+
return a
|
349
|
+
};
|
350
|
+
goog.exportSymbol = function(a, b, c) {
|
351
|
+
goog.exportPath_(a, b, c)
|
352
|
+
};
|
353
|
+
goog.exportProperty = function(a, b, c) {
|
354
|
+
a[b] = c
|
355
|
+
};
|
356
|
+
goog.inherits = function(a, b) {
|
357
|
+
function c() {
|
358
|
+
}
|
359
|
+
c.prototype = b.prototype;
|
360
|
+
a.superClass_ = b.prototype;
|
361
|
+
a.prototype = new c;
|
362
|
+
a.prototype.constructor = a
|
363
|
+
};
|
364
|
+
goog.base = function(a, b, c) {
|
365
|
+
var d = arguments.callee.caller;
|
366
|
+
if(goog.DEBUG && !d) {
|
367
|
+
throw Error("arguments.caller not defined. goog.base() expects not to be running in strict mode. See http://www.ecma-international.org/ecma-262/5.1/#sec-C");
|
368
|
+
}
|
369
|
+
if(d.superClass_) {
|
370
|
+
return d.superClass_.constructor.apply(a, Array.prototype.slice.call(arguments, 1))
|
371
|
+
}
|
372
|
+
for(var e = Array.prototype.slice.call(arguments, 2), f = !1, g = a.constructor;g;g = g.superClass_ && g.superClass_.constructor) {
|
373
|
+
if(g.prototype[b] === d) {
|
374
|
+
f = !0
|
375
|
+
}else {
|
376
|
+
if(f) {
|
377
|
+
return g.prototype[b].apply(a, e)
|
378
|
+
}
|
379
|
+
}
|
380
|
+
}
|
381
|
+
if(a[b] === d) {
|
382
|
+
return a.constructor.prototype[b].apply(a, e)
|
383
|
+
}
|
384
|
+
throw Error("goog.base called from a method of one name to a method of a different name");
|
385
|
+
};
|
386
|
+
goog.scope = function(a) {
|
387
|
+
a.call(goog.global)
|
388
|
+
};
|
1
389
|
var axs = {};
|
2
390
|
axs.constants = {};
|
3
391
|
axs.constants.ARIA_ROLES = {alert:{namefrom:["author"], parent:["region"]}, alertdialog:{namefrom:["author"], namerequired:!0, parent:["alert", "dialog"]}, application:{namefrom:["author"], namerequired:!0, parent:["landmark"]}, article:{namefrom:["author"], parent:["document", "region"]}, banner:{namefrom:["author"], parent:["landmark"]}, button:{childpresentational:!0, namefrom:["contents", "author"], namerequired:!0, parent:["command"], properties:["aria-expanded", "aria-pressed"]}, checkbox:{namefrom:["contents",
|
@@ -33,8 +421,7 @@ axs.constants.addAllPropertiesToSet_ = function(a, b, c) {
|
|
33
421
|
}
|
34
422
|
}
|
35
423
|
if(a.parent) {
|
36
|
-
a = a.parent;
|
37
|
-
for(d = 0;d < a.length;d++) {
|
424
|
+
for(a = a.parent, d = 0;d < a.length;d++) {
|
38
425
|
axs.constants.addAllPropertiesToSet_(axs.constants.ARIA_ROLES[a[d]], b, c)
|
39
426
|
}
|
40
427
|
}
|
@@ -104,7 +491,7 @@ axs.utils.elementIsTransparent = function(a) {
|
|
104
491
|
axs.utils.elementHasZeroArea = function(a) {
|
105
492
|
a = a.getBoundingClientRect();
|
106
493
|
var b = a.top - a.bottom;
|
107
|
-
return
|
494
|
+
return a.right - a.left && b ? !1 : !0
|
108
495
|
};
|
109
496
|
axs.utils.elementIsOutsideScrollArea = function(a) {
|
110
497
|
a = a.getBoundingClientRect();
|
@@ -119,18 +506,14 @@ axs.utils.overlappingElement = function(a) {
|
|
119
506
|
return null
|
120
507
|
}
|
121
508
|
var c = a.getBoundingClientRect(), c = document.elementFromPoint((c.left + c.right) / 2, (c.top + c.bottom) / 2);
|
122
|
-
return null
|
509
|
+
return null == c || c == a || b(c, a) ? null : c
|
123
510
|
};
|
124
511
|
axs.utils.elementIsHtmlControl = function(a) {
|
125
|
-
|
512
|
+
var b = a.ownerDocument.defaultView;
|
513
|
+
return a instanceof b.HTMLButtonElement || a instanceof b.HTMLInputElement || a instanceof b.HTMLSelectElement || a instanceof b.HTMLTextAreaElement ? !0 : !1
|
126
514
|
};
|
127
515
|
axs.utils.elementIsAriaWidget = function(a) {
|
128
|
-
|
129
|
-
if((a = axs.constants.ARIA_ROLES[a]) && "widget" in a.allParentRolesSet) {
|
130
|
-
return!0
|
131
|
-
}
|
132
|
-
}
|
133
|
-
return!1
|
516
|
+
return a.hasAttribute("role") && (a = a.getAttribute("role")) && (a = axs.constants.ARIA_ROLES[a]) && "widget" in a.allParentRolesSet ? !0 : !1
|
134
517
|
};
|
135
518
|
axs.utils.elementIsVisible = function(a) {
|
136
519
|
if(axs.utils.elementIsTransparent(a) || axs.utils.elementHasZeroArea(a) || axs.utils.elementIsOutsideScrollArea(a)) {
|
@@ -178,11 +561,9 @@ axs.utils.getBgColor = function(a, b) {
|
|
178
561
|
if(f.backgroundImage && "none" != f.backgroundImage) {
|
179
562
|
return null
|
180
563
|
}
|
181
|
-
if((f = axs.utils.parseColor(f.backgroundColor)) && 0 != f.alpha) {
|
182
|
-
|
183
|
-
|
184
|
-
break
|
185
|
-
}
|
564
|
+
if((f = axs.utils.parseColor(f.backgroundColor)) && 0 != f.alpha && (e.push(f), 1 == f.alpha)) {
|
565
|
+
c = null;
|
566
|
+
break
|
186
567
|
}
|
187
568
|
}
|
188
569
|
}
|
@@ -239,7 +620,7 @@ axs.utils.getContrastRatioForElementWithComputedStyle = function(a, b) {
|
|
239
620
|
return null
|
240
621
|
}
|
241
622
|
var d = axs.utils.getFgColor(a, c);
|
242
|
-
return
|
623
|
+
return d ? axs.utils.calculateContrastRatio(d, c) : null
|
243
624
|
};
|
244
625
|
axs.utils.isNativeTextElement = function(a) {
|
245
626
|
var b = a.tagName.toLowerCase();
|
@@ -288,7 +669,7 @@ axs.utils.hasLabel = function(a) {
|
|
288
669
|
return!1
|
289
670
|
};
|
290
671
|
axs.utils.isElementHidden = function(a) {
|
291
|
-
if(!(a instanceof HTMLElement)) {
|
672
|
+
if(!(a instanceof a.ownerDocument.defaultView.HTMLElement)) {
|
292
673
|
return!1
|
293
674
|
}
|
294
675
|
if(a.hasAttribute("chromevoxignoreariahidden")) {
|
@@ -360,7 +741,7 @@ axs.utils.isValidTokenValue = function(a, b) {
|
|
360
741
|
return axs.utils.isPossibleValue(b, axs.constants.ARIA_PROPERTIES[c].valuesSet, a)
|
361
742
|
};
|
362
743
|
axs.utils.isPossibleValue = function(a, b, c) {
|
363
|
-
return
|
744
|
+
return b[a] ? {valid:!0, value:a} : {valid:!1, value:a, reason:'"' + a + '" is not a valid value for ' + c, possibleValues:Object.keys(b)}
|
364
745
|
};
|
365
746
|
axs.utils.isValidBoolean = function(a) {
|
366
747
|
try {
|
@@ -371,14 +752,15 @@ axs.utils.isValidBoolean = function(a) {
|
|
371
752
|
return"boolean" != typeof b ? {valid:!1, value:a, reason:'"' + a + '" is not a true/false value'} : {valid:!0, value:b}
|
372
753
|
};
|
373
754
|
axs.utils.isValidIDRefValue = function(a, b) {
|
374
|
-
return
|
755
|
+
return b.ownerDocument.getElementById(a) ? {valid:!0, idref:a} : {valid:!1, idref:a, reason:'No element with ID "' + a + '"'}
|
375
756
|
};
|
376
757
|
axs.utils.isValidNumber = function(a) {
|
377
758
|
var b = JSON.parse(a);
|
378
759
|
return"number" != typeof b ? {valid:!1, value:a, reason:'"' + a + '" is not a number'} : {valid:!0, value:b}
|
379
760
|
};
|
380
761
|
axs.utils.isElementImplicitlyFocusable = function(a) {
|
381
|
-
|
762
|
+
var b = a.ownerDocument.defaultView;
|
763
|
+
return a instanceof b.HTMLAnchorElement || a instanceof b.HTMLAreaElement ? a.hasAttribute("href") : a instanceof b.HTMLInputElement || a instanceof b.HTMLSelectElement || a instanceof b.HTMLTextAreaElement || a instanceof b.HTMLButtonElement || a instanceof b.HTMLIFrameElement ? !a.disabled : !1
|
382
764
|
};
|
383
765
|
axs.utils.values = function(a) {
|
384
766
|
var b = [], c;
|
@@ -413,7 +795,7 @@ axs.utils.getQuerySelectorText = function(a) {
|
|
413
795
|
if(a.parentNode) {
|
414
796
|
for(c = 0;c < a.parentNode.children.length;c++) {
|
415
797
|
var e = a.parentNode.children[c];
|
416
|
-
|
798
|
+
axs.browserUtils.matchSelector(e, b) && d++;
|
417
799
|
if(e === a) {
|
418
800
|
break
|
419
801
|
}
|
@@ -465,7 +847,7 @@ axs.AuditRule.prototype.addNode = function(a, b) {
|
|
465
847
|
axs.AuditRule.prototype.run = function(a, b) {
|
466
848
|
function c(a) {
|
467
849
|
for(var b = 0;b < d.length;b++) {
|
468
|
-
if(
|
850
|
+
if(axs.browserUtils.matchSelector(a, d[b])) {
|
469
851
|
return!0
|
470
852
|
}
|
471
853
|
}
|
@@ -510,24 +892,31 @@ axs.AuditResults = function() {
|
|
510
892
|
this.errors_ = [];
|
511
893
|
this.warnings_ = []
|
512
894
|
};
|
895
|
+
goog.exportSymbol("axs.AuditResults", axs.AuditResults);
|
513
896
|
axs.AuditResults.prototype.addError = function(a) {
|
514
897
|
"" != a && this.errors_.push(a)
|
515
898
|
};
|
899
|
+
goog.exportProperty(axs.AuditResults.prototype, "addError", axs.AuditResults.prototype.addError);
|
516
900
|
axs.AuditResults.prototype.addWarning = function(a) {
|
517
901
|
"" != a && this.warnings_.push(a)
|
518
902
|
};
|
903
|
+
goog.exportProperty(axs.AuditResults.prototype, "addWarning", axs.AuditResults.prototype.addWarning);
|
519
904
|
axs.AuditResults.prototype.numErrors = function() {
|
520
905
|
return this.errors_.length
|
521
906
|
};
|
907
|
+
goog.exportProperty(axs.AuditResults.prototype, "numErrors", axs.AuditResults.prototype.numErrors);
|
522
908
|
axs.AuditResults.prototype.numWarnings = function() {
|
523
909
|
return this.warnings_.length
|
524
910
|
};
|
911
|
+
goog.exportProperty(axs.AuditResults.prototype, "numWarnings", axs.AuditResults.prototype.numWarnings);
|
525
912
|
axs.AuditResults.prototype.getErrors = function() {
|
526
913
|
return this.errors_
|
527
914
|
};
|
915
|
+
goog.exportProperty(axs.AuditResults.prototype, "getErrors", axs.AuditResults.prototype.getErrors);
|
528
916
|
axs.AuditResults.prototype.getWarnings = function() {
|
529
917
|
return this.warnings_
|
530
918
|
};
|
919
|
+
goog.exportProperty(axs.AuditResults.prototype, "getWarnings", axs.AuditResults.prototype.getWarnings);
|
531
920
|
axs.AuditResults.prototype.toString = function() {
|
532
921
|
for(var a = "", b = 0;b < this.errors_.length;b++) {
|
533
922
|
0 == b && (a += "\nErrors:\n");
|
@@ -538,12 +927,18 @@ axs.AuditResults.prototype.toString = function() {
|
|
538
927
|
}
|
539
928
|
return a
|
540
929
|
};
|
930
|
+
goog.exportProperty(axs.AuditResults.prototype, "toString", axs.AuditResults.prototype.toString);
|
541
931
|
axs.Audit = {};
|
542
932
|
axs.AuditConfiguration = function() {
|
543
933
|
this.rules_ = {};
|
544
934
|
this.auditRulesToIgnore = this.auditRulesToRun = this.scope = null;
|
545
|
-
this.withConsoleApi = !1
|
935
|
+
this.withConsoleApi = !1;
|
936
|
+
goog.exportProperty(this, "scope", this.scope);
|
937
|
+
goog.exportProperty(this, "auditRulesToRun", this.auditRulesToRun);
|
938
|
+
goog.exportProperty(this, "auditRulesToIgnore", this.auditRulesToIgnore);
|
939
|
+
goog.exportProperty(this, "withConsoleApi", this.withConsoleApi)
|
546
940
|
};
|
941
|
+
goog.exportSymbol("axs.AuditConfiguration", axs.AuditConfiguration);
|
547
942
|
axs.AuditConfiguration.prototype = {ignoreSelectors:function(a, b) {
|
548
943
|
a in this.rules_ || (this.rules_[a] = {});
|
549
944
|
"ignore" in this.rules_[a] || (this.rules_[a].ignore = []);
|
@@ -551,6 +946,8 @@ axs.AuditConfiguration.prototype = {ignoreSelectors:function(a, b) {
|
|
551
946
|
}, getIgnoreSelectors:function(a) {
|
552
947
|
return a in this.rules_ && "ignore" in this.rules_[a] ? this.rules_[a].ignore : []
|
553
948
|
}};
|
949
|
+
goog.exportProperty(axs.AuditConfiguration.prototype, "ignoreSelectors", axs.AuditConfiguration.prototype.ignoreSelectors);
|
950
|
+
goog.exportProperty(axs.AuditConfiguration.prototype, "getIgnoreSelectors", axs.AuditConfiguration.prototype.getIgnoreSelectors);
|
554
951
|
axs.Audit.run = function(a) {
|
555
952
|
a = a || new axs.AuditConfiguration;
|
556
953
|
var b = a.withConsoleApi, c = [], d;
|
@@ -573,6 +970,7 @@ axs.Audit.run = function(a) {
|
|
573
970
|
}
|
574
971
|
return c
|
575
972
|
};
|
973
|
+
goog.exportSymbol("axs.Audit.run", axs.Audit.run);
|
576
974
|
axs.Audit.auditResults = function(a) {
|
577
975
|
for(var b = new axs.AuditResults, c = 0;c < a.length;c++) {
|
578
976
|
var d = a[c];
|
@@ -580,12 +978,14 @@ axs.Audit.auditResults = function(a) {
|
|
580
978
|
}
|
581
979
|
return b
|
582
980
|
};
|
981
|
+
goog.exportSymbol("axs.Audit.auditResults", axs.Audit.auditResults);
|
583
982
|
axs.Audit.createReport = function(a, b) {
|
584
983
|
var c;
|
585
984
|
c = "*** Begin accessibility audit results ***\nAn accessibility audit found " + axs.Audit.auditResults(a).toString();
|
586
985
|
b && (c += "\nFor more information, please see ", c += b);
|
587
986
|
return c += "\n*** End accessibility audit results ***"
|
588
987
|
};
|
988
|
+
goog.exportSymbol("axs.Audit.createReport", axs.Audit.createReport);
|
589
989
|
axs.Audit.accessibilityErrorMessage = function(a) {
|
590
990
|
for(var b = a.rule.severity == axs.constants.Severity.SEVERE ? "Error: " : "Warning: ", b = b + (a.rule.code + " (" + a.rule.heading + ") failed on the following " + (1 == a.elements.length ? "element" : "elements")), b = 1 == a.elements.length ? b + ":" : b + (" (1 - " + Math.min(5, a.elements.length) + " of " + a.elements.length + "):"), c = Math.min(a.elements.length, 5), d = 0;d < c;d++) {
|
591
991
|
var e = a.elements[d], b = b + "\n";
|
@@ -598,6 +998,11 @@ axs.Audit.accessibilityErrorMessage = function(a) {
|
|
598
998
|
"" != a.rule.url && (b += "\nSee " + a.rule.url + " for more information.");
|
599
999
|
return b
|
600
1000
|
};
|
1001
|
+
goog.exportSymbol("axs.Audit.accessibilityErrorMessage", axs.Audit.accessibilityErrorMessage);
|
1002
|
+
axs.browserUtils = {};
|
1003
|
+
axs.browserUtils.matchSelector = function(a, b) {
|
1004
|
+
return a.webkitMatchesSelector ? a.webkitMatchesSelector(b) : a.mozMatchesSelector(b)
|
1005
|
+
};
|
601
1006
|
axs.AuditRule.specs.audioWithoutControls = {name:"audioWithoutControls", heading:"Audio elements should have controls", url:"", severity:axs.constants.Severity.WARNING, relevantNodesSelector:function(a) {
|
602
1007
|
return a.querySelectorAll("audio[autoplay]")
|
603
1008
|
}, test:function(a) {
|
@@ -630,7 +1035,7 @@ axs.AuditRule.specs.badAriaRole = {name:"badAriaRole", heading:"Elements with AR
|
|
630
1035
|
axs.AuditRule.specs.controlsWithoutLabel = {name:"controlsWithoutLabel", heading:"Controls and media elements should have labels", url:"https://code.google.com/p/accessibility-developer-tools/wiki/AuditRules#AX_TEXT_01:_Controls_and_media_elements_should_have_labels", severity:axs.constants.Severity.SEVERE, relevantNodesSelector:function(a) {
|
631
1036
|
return a.querySelectorAll('input:not([type="hidden"]):not([disabled]), select:not([disabled]), textarea:not([disabled]), button:not([disabled]), video:not([disabled])')
|
632
1037
|
}, test:function(a) {
|
633
|
-
return axs.utils.isElementOrAncestorHidden(a) || "button" == a.tagName.toLowerCase() && a.textContent.replace(/^\s+|\s+$/g, "").length ? !1 :
|
1038
|
+
return axs.utils.isElementOrAncestorHidden(a) || "button" == a.tagName.toLowerCase() && a.textContent.replace(/^\s+|\s+$/g, "").length ? !1 : axs.utils.hasLabel(a) ? !1 : !0
|
634
1039
|
}, code:"AX_TEXT_01", ruleName:"Controls and media elements should have labels"};
|
635
1040
|
axs.AuditRule.specs.focusableElementNotVisibleAndNotAriaHidden = {name:"focusableElementNotVisibleAndNotAriaHidden", heading:"These elements are focusable but either invisible or obscured by another element", url:"https://code.google.com/p/accessibility-developer-tools/wiki/AuditRules#AX_FOCUS_01:_These_elements_are_focusable_but_either_invisible_o", severity:axs.constants.Severity.WARNING, relevantNodesSelector:function(a) {
|
636
1041
|
return a.querySelectorAll(axs.utils.FOCUSABLE_ELEMENTS_SELECTOR)
|
@@ -658,6 +1063,26 @@ axs.AuditRule.specs.lowContrastElements = {name:"lowContrastElements", heading:"
|
|
658
1063
|
var b = window.getComputedStyle(a, null);
|
659
1064
|
return(a = axs.utils.getContrastRatioForElementWithComputedStyle(b, a)) && axs.utils.isLowContrast(a, b)
|
660
1065
|
}, code:"AX_COLOR_01"};
|
1066
|
+
axs.AuditRule.specs.elementsWithMeaningfulBackgroundImage = {name:"elementsWithMeaningfulBackgroundImage", severity:axs.constants.Severity.WARNING, relevantNodesSelector:function(a) {
|
1067
|
+
a = a.querySelectorAll("*");
|
1068
|
+
for(var b = [], c = 0;c < a.length;c++) {
|
1069
|
+
var d = a[c];
|
1070
|
+
axs.utils.isElementOrAncestorHidden(d) || b.push(d)
|
1071
|
+
}
|
1072
|
+
return b
|
1073
|
+
}, heading:"Meaningful images should not be used in element backgrounds", url:"https://code.google.com/p/accessibility-developer-tools/wiki/AuditRules?ts=1368336558&updated=AuditRules#AX_IMAGE_01:_Meaningful_images_should_not_be_used_in_element_bac", test:function(a) {
|
1074
|
+
if(a.textContent && 0 < a.textContent.length) {
|
1075
|
+
return!1
|
1076
|
+
}
|
1077
|
+
a = window.getComputedStyle(a, null);
|
1078
|
+
var b = a.backgroundImage;
|
1079
|
+
if(!b || "undefined" === b || "none" === b) {
|
1080
|
+
return!1
|
1081
|
+
}
|
1082
|
+
b = parseInt(a.width, 10);
|
1083
|
+
a = parseInt(a.height, 10);
|
1084
|
+
return 150 > b && 150 > a
|
1085
|
+
}, code:"AX_IMAGE_01"};
|
661
1086
|
axs.AuditRule.specs.nonExistentAriaLabelledbyElement = {name:"nonExistentAriaLabelledbyElement", heading:"aria-labelledby attributes should refer to an element which exists in the DOM", url:"https://code.google.com/p/accessibility-developer-tools/wiki/AuditRules#AX_ARIA_02:__aria-labelledby_attributes_should_refer_to_an_eleme", severity:axs.constants.Severity.WARNING, relevantNodesSelector:function(a) {
|
662
1087
|
return a.querySelectorAll("[aria-labelledby]")
|
663
1088
|
}, test:function(a) {
|
@@ -669,7 +1094,7 @@ axs.AuditRule.specs.nonExistentAriaLabelledbyElement = {name:"nonExistentAriaLab
|
|
669
1094
|
}
|
670
1095
|
return!1
|
671
1096
|
}, code:"AX_ARIA_02"};
|
672
|
-
axs.AuditRule.specs.pageWithoutTitle = {name:"pageWithoutTitle", heading:"
|
1097
|
+
axs.AuditRule.specs.pageWithoutTitle = {name:"pageWithoutTitle", heading:"The web page should have a title that describes topic or purpose", url:"", severity:axs.constants.Severity.WARNING, relevantNodesSelector:function(a) {
|
673
1098
|
return a
|
674
1099
|
}, test:function(a) {
|
675
1100
|
a = a.querySelector("head");
|
@@ -701,7 +1126,7 @@ axs.AuditRule.specs.unfocusableElementsWithOnClick = {name:"unfocusableElementsW
|
|
701
1126
|
a = a.querySelectorAll("*");
|
702
1127
|
for(var b = [], c = 0;c < a.length;c++) {
|
703
1128
|
var d = a[c];
|
704
|
-
d instanceof HTMLBodyElement || axs.utils.isElementOrAncestorHidden(d) || "click" in getEventListeners(d) && b.push(d)
|
1129
|
+
d instanceof d.ownerDocument.defaultView.HTMLBodyElement || axs.utils.isElementOrAncestorHidden(d) || "click" in getEventListeners(d) && b.push(d)
|
705
1130
|
}
|
706
1131
|
return b
|
707
1132
|
}, test:function(a) {
|
@@ -6,7 +6,7 @@ module Capybara
|
|
6
6
|
def click
|
7
7
|
synchronize { base.click }
|
8
8
|
begin
|
9
|
-
if Capybara.current_driver == :accessible &&
|
9
|
+
if Capybara.current_driver == :accessible && audit_failures.any?
|
10
10
|
raise Capybara::Accessible::InaccessibleError, failure_messages
|
11
11
|
end
|
12
12
|
rescue ::Selenium::WebDriver::Error::UnhandledAlertError => e
|
data/spec/accessible_app.rb
CHANGED
data/spec/driver_spec.rb
CHANGED
@@ -27,7 +27,7 @@ describe Capybara::Accessible::Driver do
|
|
27
27
|
end
|
28
28
|
|
29
29
|
it 'does not raise an error on an excluded rule' do
|
30
|
-
expect { @session.visit('/
|
30
|
+
expect { @session.visit('/excluded') }.to_not raise_error(Capybara::Accessible::InaccessibleError)
|
31
31
|
end
|
32
32
|
end
|
33
33
|
end
|
data/travis.yml
ADDED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: capybara-accessible
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0
|
4
|
+
version: 0.1.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Case Commons
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2013-
|
11
|
+
date: 2013-07-02 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: capybara
|
@@ -123,6 +123,7 @@ extra_rdoc_files: []
|
|
123
123
|
files:
|
124
124
|
- .gitignore
|
125
125
|
- Gemfile
|
126
|
+
- Gemfile.lock
|
126
127
|
- LICENSE.txt
|
127
128
|
- README.md
|
128
129
|
- Rakefile
|
@@ -136,6 +137,7 @@ files:
|
|
136
137
|
- spec/accessible_app.rb
|
137
138
|
- spec/driver_spec.rb
|
138
139
|
- spec/spec_helper.rb
|
140
|
+
- travis.yml
|
139
141
|
homepage: https://github.com/Casecommons/capybara-accessible
|
140
142
|
licenses:
|
141
143
|
- MIT
|
@@ -156,7 +158,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
156
158
|
version: '0'
|
157
159
|
requirements: []
|
158
160
|
rubyforge_project:
|
159
|
-
rubygems_version: 2.0.
|
161
|
+
rubygems_version: 2.0.3
|
160
162
|
signing_key:
|
161
163
|
specification_version: 4
|
162
164
|
summary: A Selenium based webdriver and Capybara extension that runs Google Accessibility
|
@@ -165,4 +167,3 @@ test_files:
|
|
165
167
|
- spec/accessible_app.rb
|
166
168
|
- spec/driver_spec.rb
|
167
169
|
- spec/spec_helper.rb
|
168
|
-
has_rdoc:
|