capybara-accessible 0.0.2 → 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.
- 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/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:
|