axe-matchers 1.1.1 → 1.2.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +57 -23
- data/lib/axe.rb +3 -12
- data/lib/axe/api.rb +0 -1
- data/lib/axe/api/a11y_check.rb +4 -8
- data/lib/axe/configuration.rb +35 -21
- data/lib/axe/core.rb +46 -0
- data/lib/axe/cucumber/step.rb +8 -8
- data/lib/axe/finds_page.rb +55 -0
- data/lib/axe/hooks.rb +32 -0
- data/lib/axe/loader.rb +29 -0
- data/lib/axe/matchers/be_accessible.rb +2 -3
- data/lib/axe/version.rb +27 -1
- data/lib/webdriver_script_adapter/frame_adapter.rb +63 -0
- data/lib/webdriver_script_adapter/query_selector_adapter.rb +18 -0
- metadata +24 -6
- data/lib/axe/javascript_library.rb +0 -26
- data/lib/axe/page.rb +0 -26
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 88bbc1b711577affa1153852e892ed18fc531265
|
4
|
+
data.tar.gz: d22ece7e3486a010e178fc94398a14c50a79290c
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: ca6f9503838d38f9eeb918e5d9571f0ea487e68ac9e9ee868f24e0fd5fe52b041bf61c89450dead8e75ece88fe8a2e15da9c4037a46de3a83e78664db2899238
|
7
|
+
data.tar.gz: fce3a5257299ebf5f9d674c410d19b6979766435e8ec473788ad5fa257c3fc4947f35050beec8d4773a0685d764c702aff48690e640b89a0920e03cff668d376
|
data/README.md
CHANGED
@@ -1,26 +1,32 @@
|
|
1
|
-
#
|
1
|
+
# axe-matchers
|
2
|
+
|
3
|
+
Automated accessibility testing powered by aXe.
|
2
4
|
|
3
|
-
|
5
|
+
Provides Cucumber step definitions and RSpec matchers for auditing accessibility.
|
6
|
+
|
7
|
+
# Installation
|
4
8
|
|
5
|
-
|
9
|
+
Add this line to your application's Gemfile:
|
6
10
|
|
7
11
|
``` ruby
|
8
12
|
gem 'axe-matchers'
|
9
13
|
```
|
10
14
|
|
11
|
-
|
15
|
+
And then execute:
|
12
16
|
|
13
17
|
``` sh
|
14
|
-
bundle install
|
18
|
+
$ bundle install
|
15
19
|
```
|
16
20
|
|
17
|
-
|
21
|
+
Or install it yourself as:
|
18
22
|
|
19
23
|
``` sh
|
20
|
-
gem install axe-matchers
|
24
|
+
$ gem install axe-matchers
|
21
25
|
```
|
22
26
|
|
23
|
-
# Cucumber
|
27
|
+
# Cucumber
|
28
|
+
|
29
|
+
## Configuration
|
24
30
|
|
25
31
|
1. Require step definitions: in `features/support/env.rb` or similar.
|
26
32
|
|
@@ -46,21 +52,21 @@ gem install axe-matchers
|
|
46
52
|
end
|
47
53
|
```
|
48
54
|
|
49
|
-
|
55
|
+
## Built-In Accessibility Cucumber Steps
|
50
56
|
|
51
57
|
To construct an axe accessibility Cucumber step, begin with the base step, and append any clauses necessary. All of the following clauses may be mixed and matched; however, they must appear in the specified order:
|
52
58
|
|
53
59
|
`Then the page should be accessible [including] [excluding] [according-to] [checking-rules/checking-only-rules] [skipping-rules]`
|
54
60
|
|
55
|
-
|
61
|
+
### Base Step
|
56
62
|
|
57
63
|
``` gherkin
|
58
64
|
Then the page should be accessible
|
59
65
|
```
|
60
66
|
|
61
|
-
The base step is the core component of the step. It is a complete step on its own and will verify the currently loaded page is accessible using the default configuration of [axe.a11yCheck]
|
67
|
+
The base step is the core component of the step. It is a complete step on its own and will verify the currently loaded page is accessible using the default configuration of [axe.a11yCheck][a11ycheck] (the entire document is checked using the default rules).
|
62
68
|
|
63
|
-
|
69
|
+
### Inclusion clause
|
64
70
|
|
65
71
|
``` gherkin
|
66
72
|
Then the page should be accessible within "#selector"
|
@@ -68,9 +74,9 @@ Then the page should be accessible within "#selector"
|
|
68
74
|
|
69
75
|
The inclusion clause (`within "#selector"`) specifies which elements of the page should be checked. A valid CSS selector must be provided, and is surrounded in double quotes. Compound selectors may be used to select multiple elements. e.g. `within "#header, .footer"`
|
70
76
|
|
71
|
-
|
77
|
+
*see additional [context parameter documentation][context-param]*
|
72
78
|
|
73
|
-
|
79
|
+
### Exclusion clause
|
74
80
|
|
75
81
|
``` gherkin
|
76
82
|
Then the page should be accessible excluding "#selector"
|
@@ -78,7 +84,7 @@ Then the page should be accessible excluding "#selector"
|
|
78
84
|
|
79
85
|
The exclusion clause (`excluding "#selector"`) specifies which elements of the document should be ignored. A valid CSS selector must be provided, and is surrounded in double quotes. Compound selectors may be used to select multiple elements. e.g. `excluding "#widget, .ad"`
|
80
86
|
|
81
|
-
|
87
|
+
*see additional [context parameter documentation][context-param]*
|
82
88
|
|
83
89
|
If desired, a semicolon (`;`) or the word `but` may be used to separate the exclusion clause from the inclusion clause (if present).
|
84
90
|
|
@@ -87,7 +93,7 @@ Then the page should be accessible within "main"; excluding "aside"
|
|
87
93
|
Then the page should be accessible within "main" but excluding "aside"
|
88
94
|
```
|
89
95
|
|
90
|
-
|
96
|
+
### Accessibility Standard (Tag) clause
|
91
97
|
|
92
98
|
``` gherkin
|
93
99
|
Then the page should be accessible according to: tag-name
|
@@ -95,7 +101,7 @@ Then the page should be accessible according to: tag-name
|
|
95
101
|
|
96
102
|
The tag clause specifies which accessibility standard (or standards) should be used to check the page. The accessibility standards are specified by name (tag). Multiple standards can be specified when comma-separated. e.g. `according to: wcag2a, section508`
|
97
103
|
|
98
|
-
The acceptable [tag names are documented]
|
104
|
+
The acceptable [tag names are documented][options-param] as well as a [complete listing of rules][rules] that correspond to each tag.
|
99
105
|
|
100
106
|
If desired, a semicolon (`;`) may be used to separate the tag clause from the preceding clause.
|
101
107
|
|
@@ -103,13 +109,15 @@ If desired, a semicolon (`;`) may be used to separate the tag clause from the pr
|
|
103
109
|
Then the page should be accessible within "#header"; according to: best-practice
|
104
110
|
```
|
105
111
|
|
106
|
-
|
112
|
+
### Checking Rules clause
|
107
113
|
|
108
114
|
``` gherkin
|
109
115
|
Then the page should be accessible checking: ruleId
|
110
116
|
```
|
111
117
|
|
112
|
-
The checking-rules clause specifies which *additional* rules to run (in addition to the specified tags, if any, or the default ruleset). The rules are specified by comma-separated rule IDs.
|
118
|
+
The checking-rules clause specifies which *additional* rules to run (in addition to the specified tags, if any, or the default ruleset). The rules are specified by comma-separated rule IDs.
|
119
|
+
|
120
|
+
*see [rules documentation][rules] for a list of valid rule IDs*
|
113
121
|
|
114
122
|
If desired, a semicolon (`;`) or the word `and` may be used to separate the checking-rules clause from the preceding clause.
|
115
123
|
|
@@ -118,7 +126,7 @@ Then the page should be accessible according to: wcag2a; checking: color-contras
|
|
118
126
|
Then the page should be accessible according to: wcag2a and checking: color-contrast
|
119
127
|
```
|
120
128
|
|
121
|
-
|
129
|
+
#### Exclusive Rules clause
|
122
130
|
|
123
131
|
``` gherkin
|
124
132
|
Then the page should be accessible checking only: ruleId
|
@@ -126,13 +134,15 @@ Then the page should be accessible checking only: ruleId
|
|
126
134
|
|
127
135
|
This clause is not really a separate clause. But rather, by adding the word `only` to the checking-rules clause, the meaning of the step can be changed. As described above, by default the checking-rules clause specifies *additional* rules to run. If the word `only` is used, then *only* the specified rules are checked.
|
128
136
|
|
129
|
-
|
137
|
+
### Skipping Rules clause
|
130
138
|
|
131
139
|
``` gherkin
|
132
140
|
Then the page should be accessible skipping: ruleId
|
133
141
|
```
|
134
142
|
|
135
|
-
The skipping-rules clause specifies which rules to skip. This allows an accessibility standard to be provided (via the tag clause) while ignoring a particular rule. The rules are specified by comma-separated rule IDs.
|
143
|
+
The skipping-rules clause specifies which rules to skip. This allows an accessibility standard to be provided (via the tag clause) while ignoring a particular rule. The rules are specified by comma-separated rule IDs.
|
144
|
+
|
145
|
+
*see [rules documentation][rules] for a list of valid rule IDs*
|
136
146
|
|
137
147
|
If desired, a semicolon (`;`) or the word `but` may be used to separate the skipping-rules clause from the preceding clause.
|
138
148
|
|
@@ -141,7 +151,7 @@ Then the page should be accessible according to: wcag2a; skipping: accesskeys
|
|
141
151
|
Then the page should be accessible according to: wcag2a but skipping: accesskeys
|
142
152
|
```
|
143
153
|
|
144
|
-
|
154
|
+
## Examples
|
145
155
|
|
146
156
|
``` gherkin
|
147
157
|
Then the page should be accessible within "main, header" but excluding "footer"
|
@@ -152,3 +162,27 @@ Then the page should be accessible checking only: document-title, label
|
|
152
162
|
|
153
163
|
Then the page should be accessible according to: best-practice and checking: aria-roles, definition-list
|
154
164
|
```
|
165
|
+
|
166
|
+
# WebDrivers
|
167
|
+
|
168
|
+
axe-matchers supports Capybara, Selenium, and Watir webdrivers; each tested with Firefox, Chrome, Safari, and PhantomJS. Additionally, capybara-webkit and poltergeist are supported.
|
169
|
+
|
170
|
+
*__Notes:__*
|
171
|
+
|
172
|
+
- Auditing IFrames is not suppored in Poltergeist < 1.8.0. Upgrade to 1.8.0+ or set `skip_iframes=true` in `Axe.configure`
|
173
|
+
- Chrome requires [ChromeDriver](https://sites.google.com/a/chromium.org/chromedriver/) (tested with 2.21)
|
174
|
+
- Safari requires [SafariDriver](https://code.google.com/p/selenium/wiki/SafariDriver) (tested with 2.48)
|
175
|
+
|
176
|
+
|
177
|
+
|
178
|
+
[inclusion-clause]: #inclusion-clause
|
179
|
+
[exclusion-clause]: #exclusion-clause
|
180
|
+
[tag-clause]: #accessibility-standard-tag-clause
|
181
|
+
[rules-clause]: #checking-rules-clause
|
182
|
+
[exclusive-rules-clause]: #exclusive-rules-clause
|
183
|
+
[skipping-rules-clause]: #skipping-rules-clause
|
184
|
+
|
185
|
+
[a11ycheck]: https://github.com/dequelabs/axe-core/blob/master/doc/API.md#api-name-axea11ycheck
|
186
|
+
[context-param]: https://github.com/dequelabs/axe-core/blob/master/doc/API.md#a-context-parameter
|
187
|
+
[options-param]: https://github.com/dequelabs/axe-core/blob/master/doc/API.md#b-options-parameter
|
188
|
+
[rules]: https://github.com/dequelabs/axe-core/blob/master/doc/rule-descriptions.md
|
data/lib/axe.rb
CHANGED
@@ -1,17 +1,8 @@
|
|
1
|
-
require 'forwardable'
|
2
1
|
require 'axe/configuration'
|
2
|
+
require 'axe/version'
|
3
3
|
|
4
4
|
module Axe
|
5
|
-
|
6
|
-
|
7
|
-
def_delegator :configuration, :page_from
|
8
|
-
|
9
|
-
def configuration
|
10
|
-
@configuration ||= Configuration.new
|
11
|
-
end
|
12
|
-
|
13
|
-
def configure
|
14
|
-
yield configuration if block_given?
|
15
|
-
end
|
5
|
+
def self.configure
|
6
|
+
yield Configuration.instance if block_given?
|
16
7
|
end
|
17
8
|
end
|
data/lib/axe/api.rb
CHANGED
data/lib/axe/api/a11y_check.rb
CHANGED
@@ -8,12 +8,13 @@ require 'axe/api/audit'
|
|
8
8
|
require 'axe/api/context'
|
9
9
|
require 'axe/api/options'
|
10
10
|
require 'axe/api/results'
|
11
|
-
require 'axe/
|
11
|
+
require 'axe/core'
|
12
12
|
|
13
13
|
module Axe
|
14
14
|
module API
|
15
15
|
class A11yCheck
|
16
|
-
|
16
|
+
JS_NAME = "a11yCheck"
|
17
|
+
METHOD_NAME = "#{Core::JS_NAME}.#{JS_NAME}"
|
17
18
|
|
18
19
|
extend Forwardable
|
19
20
|
def_delegators :@context, :within, :excluding
|
@@ -28,7 +29,6 @@ module Axe
|
|
28
29
|
end
|
29
30
|
|
30
31
|
def call(page)
|
31
|
-
inject_axe_lib page
|
32
32
|
audit page do |results|
|
33
33
|
Audit.new to_js, Results.new(results)
|
34
34
|
end
|
@@ -36,12 +36,8 @@ module Axe
|
|
36
36
|
|
37
37
|
private
|
38
38
|
|
39
|
-
def inject_axe_lib(page)
|
40
|
-
JavaScriptLibrary.new.inject_into page
|
41
|
-
end
|
42
|
-
|
43
39
|
def audit(page)
|
44
|
-
yield page.execute_async_script "#{METHOD_NAME}.apply(#{
|
40
|
+
yield page.execute_async_script "#{METHOD_NAME}.apply(#{Core::JS_NAME}, arguments)", @context.to_json, @options.to_json
|
45
41
|
end
|
46
42
|
|
47
43
|
def to_js
|
data/lib/axe/configuration.rb
CHANGED
@@ -1,42 +1,56 @@
|
|
1
1
|
require 'forwardable'
|
2
|
+
require 'pathname'
|
3
|
+
require 'rubygems'
|
4
|
+
require 'singleton'
|
5
|
+
require 'yaml'
|
6
|
+
|
7
|
+
require 'axe/hooks'
|
2
8
|
require 'webdriver_script_adapter/execute_async_script_adapter'
|
3
9
|
|
4
10
|
module Axe
|
5
11
|
class Configuration
|
12
|
+
include Singleton
|
13
|
+
include Hooks
|
6
14
|
extend Forwardable
|
7
15
|
|
8
|
-
|
16
|
+
attr_writer :jslib
|
17
|
+
attr_accessor :page, :jslib_path, :skip_iframes
|
18
|
+
|
9
19
|
def_delegators ::WebDriverScriptAdapter,
|
10
20
|
:async_results_identifier, :async_results_identifier=,
|
11
21
|
:max_wait_time, :max_wait_time=,
|
12
22
|
:wait_interval, :wait_interval=
|
13
23
|
|
14
|
-
def
|
15
|
-
|
16
|
-
|
17
|
-
default_page_from(world) ||
|
18
|
-
from_ivar(:@page, world) ||
|
19
|
-
from_ivar(:@browser, world) ||
|
20
|
-
from_ivar(:@driver, world) ||
|
21
|
-
from_ivar(:@webdriver, world) ||
|
22
|
-
NullWebDriver.new
|
24
|
+
def initialize
|
25
|
+
@page = :page
|
26
|
+
@jslib_path = gem_root + '/node_modules/axe-core/axe.min.js'
|
23
27
|
end
|
24
28
|
|
25
|
-
|
26
|
-
|
27
|
-
def page_from_eval(world)
|
28
|
-
world.instance_eval "#{page}" if page.is_a?(String) || page.is_a?(Symbol)
|
29
|
+
def jslib
|
30
|
+
@jslib ||= Pathname.new(@jslib_path).read
|
29
31
|
end
|
30
32
|
|
31
|
-
|
32
|
-
|
33
|
+
class << self
|
34
|
+
def from_yaml(path="config/axe.yml")
|
35
|
+
file = Pathname.new(path)
|
36
|
+
from_hash(YAML.load_file(file)) if file.exist?
|
37
|
+
instance
|
38
|
+
end
|
39
|
+
|
40
|
+
def from_hash(attributes)
|
41
|
+
attributes.each do |k, v|
|
42
|
+
instance.__send__("#{k}=", v)
|
43
|
+
end
|
44
|
+
instance
|
45
|
+
end
|
33
46
|
end
|
34
47
|
|
35
|
-
|
36
|
-
|
37
|
-
|
48
|
+
private
|
49
|
+
|
50
|
+
def gem_root
|
51
|
+
Gem::Specification.find_by_name('axe-matchers').gem_dir
|
38
52
|
end
|
39
53
|
end
|
40
|
-
|
41
|
-
class NullWebDriver; end
|
42
54
|
end
|
55
|
+
|
56
|
+
Axe::Configuration.from_yaml
|
data/lib/axe/core.rb
ADDED
@@ -0,0 +1,46 @@
|
|
1
|
+
require 'webdriver_script_adapter/execute_async_script_adapter'
|
2
|
+
require 'webdriver_script_adapter/frame_adapter'
|
3
|
+
require 'webdriver_script_adapter/query_selector_adapter'
|
4
|
+
|
5
|
+
require 'axe/configuration'
|
6
|
+
require 'axe/loader'
|
7
|
+
|
8
|
+
module Axe
|
9
|
+
class Core
|
10
|
+
JS_NAME = "axe"
|
11
|
+
|
12
|
+
def initialize(page)
|
13
|
+
@page = wrap_driver page
|
14
|
+
load_axe_core Axe::Configuration.instance.jslib
|
15
|
+
end
|
16
|
+
|
17
|
+
def call(callable)
|
18
|
+
callable.call(@page)
|
19
|
+
end
|
20
|
+
|
21
|
+
private
|
22
|
+
|
23
|
+
def load_axe_core(source)
|
24
|
+
Loader.new(@page, self).call(source) unless already_loaded?
|
25
|
+
end
|
26
|
+
|
27
|
+
def already_loaded?
|
28
|
+
@page.evaluate_script <<-JS
|
29
|
+
window.#{JS_NAME} &&
|
30
|
+
typeof #{JS_NAME}.a11yCheck === 'function'
|
31
|
+
JS
|
32
|
+
end
|
33
|
+
|
34
|
+
def wrap_driver(driver)
|
35
|
+
::WebDriverScriptAdapter::QuerySelectorAdapter.wrap(
|
36
|
+
::WebDriverScriptAdapter::FrameAdapter.wrap(
|
37
|
+
::WebDriverScriptAdapter::ExecuteAsyncScriptAdapter.wrap(
|
38
|
+
::WebDriverScriptAdapter::ExecEvalScriptAdapter.wrap(
|
39
|
+
driver
|
40
|
+
)
|
41
|
+
)
|
42
|
+
)
|
43
|
+
)
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
data/lib/axe/cucumber/step.rb
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
require 'yaml'
|
2
2
|
|
3
|
-
require 'axe'
|
3
|
+
require 'axe/finds_page'
|
4
4
|
require 'axe/matchers'
|
5
5
|
require 'axe/expectation'
|
6
6
|
|
@@ -41,7 +41,7 @@ module Axe
|
|
41
41
|
$/x
|
42
42
|
|
43
43
|
def self.create_for(world)
|
44
|
-
new
|
44
|
+
new(FindsPage.in(world).page)
|
45
45
|
end
|
46
46
|
|
47
47
|
def initialize(page)
|
@@ -50,12 +50,12 @@ module Axe
|
|
50
50
|
|
51
51
|
def assert_accessibility(negate=false, inclusion="", exclusion="", tags="", run_only=false, run_rules="", skip_rules="", options=nil)
|
52
52
|
is_accessible = Axe::Matchers::BeAccessible.new.tap do |a|
|
53
|
-
a.within
|
54
|
-
a.excluding
|
55
|
-
a.according_to
|
56
|
-
a.checking
|
57
|
-
a.checking_only
|
58
|
-
a.skipping
|
53
|
+
a.within(*selector(inclusion))
|
54
|
+
a.excluding(*selector(exclusion))
|
55
|
+
a.according_to(*split(tags))
|
56
|
+
a.checking(*split(run_rules)) unless run_only
|
57
|
+
a.checking_only(*split(run_rules)) if run_only
|
58
|
+
a.skipping(*split(skip_rules))
|
59
59
|
a.with_options to_hash(options)
|
60
60
|
end
|
61
61
|
|
@@ -0,0 +1,55 @@
|
|
1
|
+
require 'axe/configuration'
|
2
|
+
|
3
|
+
module Axe
|
4
|
+
class FindsPage
|
5
|
+
WEBDRIVER_NAMES = [ :page, :browser, :driver, :webdriver ]
|
6
|
+
|
7
|
+
class << self
|
8
|
+
alias :in :new
|
9
|
+
end
|
10
|
+
|
11
|
+
def initialize(world)
|
12
|
+
@world = world
|
13
|
+
end
|
14
|
+
|
15
|
+
def page
|
16
|
+
from_configuration || implicit or raise "A page/browser/webdriver must be configured"
|
17
|
+
end
|
18
|
+
|
19
|
+
private
|
20
|
+
|
21
|
+
def configuration
|
22
|
+
Axe::Configuration.instance
|
23
|
+
end
|
24
|
+
|
25
|
+
def from_configuration
|
26
|
+
if configuration.page.is_a?(String) || configuration.page.is_a?(Symbol)
|
27
|
+
from_world(configuration.page)
|
28
|
+
else
|
29
|
+
configuration.page
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
def implicit
|
34
|
+
WEBDRIVER_NAMES.map(&method(:from_world)).drop_while(&:nil?).first
|
35
|
+
end
|
36
|
+
|
37
|
+
def from_world(name)
|
38
|
+
via_method(name) || via_ivar(name)
|
39
|
+
end
|
40
|
+
|
41
|
+
def via_method(name)
|
42
|
+
@world.__send__(name) if @world.respond_to?(name)
|
43
|
+
end
|
44
|
+
|
45
|
+
def via_ivar(name)
|
46
|
+
name = ensure_ivar_format(name)
|
47
|
+
@world.instance_variable_get(name) if @world.instance_variables.include?(name)
|
48
|
+
end
|
49
|
+
|
50
|
+
def ensure_ivar_format(name)
|
51
|
+
# ensure leading '@'
|
52
|
+
name.to_s.sub(/^([^@])/, '@\1').to_sym
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
data/lib/axe/hooks.rb
ADDED
@@ -0,0 +1,32 @@
|
|
1
|
+
module Axe
|
2
|
+
module Hooks
|
3
|
+
HOOKS = [ :after_load ]
|
4
|
+
|
5
|
+
HOOKS.each do |hook_name|
|
6
|
+
# define instance-level registration method per hook
|
7
|
+
define_method hook_name do |callable=nil, &block|
|
8
|
+
callable ||= block
|
9
|
+
Hooks.callbacks.fetch(hook_name) << callable if callable
|
10
|
+
end
|
11
|
+
|
12
|
+
# define singleton-level run_* method per hook
|
13
|
+
define_singleton_method "run_#{hook_name}" do |*args|
|
14
|
+
callbacks.fetch(hook_name).each do |callback|
|
15
|
+
callback.call(*args)
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
# beware, the callbacks hash is a single shared instance tied to this module
|
21
|
+
def self.callbacks
|
22
|
+
@callbacks ||= initialize_callbacks_array_per_hook
|
23
|
+
end
|
24
|
+
|
25
|
+
private
|
26
|
+
|
27
|
+
def self.initialize_callbacks_array_per_hook
|
28
|
+
Hash[ HOOKS.map{|name| [name, []]} ]
|
29
|
+
end
|
30
|
+
|
31
|
+
end
|
32
|
+
end
|
data/lib/axe/loader.rb
ADDED
@@ -0,0 +1,29 @@
|
|
1
|
+
require 'axe/configuration'
|
2
|
+
require 'axe/hooks'
|
3
|
+
|
4
|
+
module Axe
|
5
|
+
class Loader
|
6
|
+
def initialize(page, lib)
|
7
|
+
@page = page
|
8
|
+
@lib = lib
|
9
|
+
end
|
10
|
+
|
11
|
+
def call(source)
|
12
|
+
@page.execute_script source
|
13
|
+
Axe::Hooks.run_after_load @lib
|
14
|
+
load_into_iframes(source) unless Axe::Configuration.instance.skip_iframes
|
15
|
+
end
|
16
|
+
|
17
|
+
private
|
18
|
+
|
19
|
+
def load_into_iframes(source)
|
20
|
+
iframes.each do |iframe|
|
21
|
+
@page.within_frame(iframe) { call source }
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
def iframes
|
26
|
+
@page.find_elements(:tag_name, "iframe")
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
@@ -1,6 +1,6 @@
|
|
1
1
|
require 'forwardable'
|
2
2
|
require 'chain_mail/chainable'
|
3
|
-
require 'axe/
|
3
|
+
require 'axe/core'
|
4
4
|
require 'axe/api/a11y_check'
|
5
5
|
|
6
6
|
module Axe
|
@@ -18,8 +18,7 @@ module Axe
|
|
18
18
|
end
|
19
19
|
|
20
20
|
def matches?(page)
|
21
|
-
@audit =
|
22
|
-
|
21
|
+
@audit = Core.new(page).call @a11y_check
|
23
22
|
@audit.passed?
|
24
23
|
end
|
25
24
|
end
|
data/lib/axe/version.rb
CHANGED
@@ -1,3 +1,29 @@
|
|
1
1
|
module Axe
|
2
|
-
VERSION
|
2
|
+
module VERSION
|
3
|
+
MAJOR=1
|
4
|
+
MINOR=2
|
5
|
+
PATCH=0
|
6
|
+
PRE=nil
|
7
|
+
BUILD=nil
|
8
|
+
|
9
|
+
class << self
|
10
|
+
def to_s
|
11
|
+
[MAJOR, MINOR, PATCH].join(".") + pre + build
|
12
|
+
end
|
13
|
+
|
14
|
+
private
|
15
|
+
|
16
|
+
def pre
|
17
|
+
empty?(PRE) ? "" : "-#{PRE}"
|
18
|
+
end
|
19
|
+
|
20
|
+
def build
|
21
|
+
empty?(BUILD) ? "" : "+#{BUILD}"
|
22
|
+
end
|
23
|
+
|
24
|
+
def empty?(v)
|
25
|
+
v.nil? || v.empty?
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
3
29
|
end
|
@@ -0,0 +1,63 @@
|
|
1
|
+
require 'dumb_delegator'
|
2
|
+
|
3
|
+
module WebDriverScriptAdapter
|
4
|
+
class FrameAdapter < ::DumbDelegator
|
5
|
+
|
6
|
+
def self.wrap(driver)
|
7
|
+
if driver.respond_to?(:within_frame)
|
8
|
+
driver #capybara already supports within_frame
|
9
|
+
elsif !driver.respond_to?(:switch_to)
|
10
|
+
WatirAdapter.new driver
|
11
|
+
elsif driver.switch_to.respond_to?(:parent_frame)
|
12
|
+
new driver # add within_frame to selenium
|
13
|
+
else
|
14
|
+
ParentlessFrameAdapter.new driver # old selenium doesn't support parent_frame
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
def within_frame(frame)
|
19
|
+
switch_to.frame(frame)
|
20
|
+
yield
|
21
|
+
ensure
|
22
|
+
begin
|
23
|
+
switch_to.parent_frame
|
24
|
+
rescue => e
|
25
|
+
if /switchToParentFrame|frame\/parent/.match(e.message)
|
26
|
+
::Kernel.warn "WARNING: This browser only supports first-level iframes. Second-level iframes and beyond will not be audited. To skip auditing all iframes, set Axe::Configuration#skip_iframes=true"
|
27
|
+
end
|
28
|
+
switch_to.default_content
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
private
|
33
|
+
|
34
|
+
class WatirAdapter < ::DumbDelegator
|
35
|
+
# delegate to Watir's Selenium #driver
|
36
|
+
def within_frame(frame, &block)
|
37
|
+
FrameAdapter.instance_method(:within_frame).bind(FrameAdapter.wrap driver).call(frame.wd, &block)
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
# Selenium Webdriver < 2.43 doesnt support moving back to the parent
|
42
|
+
class ParentlessFrameAdapter < ::DumbDelegator
|
43
|
+
|
44
|
+
# storage of frame stack (for reverting to parent) taken from Capybara
|
45
|
+
# : https://github.com/jnicklas/capybara/blob/2.6.2/lib/capybara/selenium/driver.rb#L117-L147
|
46
|
+
#
|
47
|
+
# There doesnt appear to be any way in Selenium Webdriver < 2.43 to move back to a parent frame
|
48
|
+
# other than going back to the root and then reiterating down
|
49
|
+
def within_frame(frame)
|
50
|
+
@frame_stack[window_handle] ||= []
|
51
|
+
@frame_stack[window_handle] << frame
|
52
|
+
|
53
|
+
switch_to.frame(frame)
|
54
|
+
yield
|
55
|
+
ensure
|
56
|
+
@frame_stack[window_handle].pop
|
57
|
+
switch_to.default_content
|
58
|
+
@frame_stack[window_handle].each { |f| switch_to.frame(f) }
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
end
|
63
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
require 'dumb_delegator'
|
2
|
+
|
3
|
+
module WebDriverScriptAdapter
|
4
|
+
class QuerySelectorAdapter < ::DumbDelegator
|
5
|
+
|
6
|
+
def self.wrap(driver)
|
7
|
+
# capybara: all(<tag>) but also seems to support all(:tag_name, <tag>)
|
8
|
+
# watir: elements(:tag_name); also supports #iframes
|
9
|
+
# selenium: find_elements(:tag_name, <tag>); aliased as all
|
10
|
+
|
11
|
+
driver.respond_to?(:find_elements) ? driver : new(driver)
|
12
|
+
end
|
13
|
+
|
14
|
+
def find_elements(*args)
|
15
|
+
respond_to?(:elements) ? elements(*args) : all(*args)
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: axe-matchers
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.
|
4
|
+
version: 1.2.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Deque Systems
|
@@ -9,7 +9,7 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date:
|
12
|
+
date: 2016-03-11 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: dumb_delegator
|
@@ -67,6 +67,20 @@ dependencies:
|
|
67
67
|
- - ~>
|
68
68
|
- !ruby/object:Gem::Version
|
69
69
|
version: '1.3'
|
70
|
+
- !ruby/object:Gem::Dependency
|
71
|
+
name: pry
|
72
|
+
requirement: !ruby/object:Gem::Requirement
|
73
|
+
requirements:
|
74
|
+
- - '>='
|
75
|
+
- !ruby/object:Gem::Version
|
76
|
+
version: '0'
|
77
|
+
type: :development
|
78
|
+
prerelease: false
|
79
|
+
version_requirements: !ruby/object:Gem::Requirement
|
80
|
+
requirements:
|
81
|
+
- - '>='
|
82
|
+
- !ruby/object:Gem::Version
|
83
|
+
version: '0'
|
70
84
|
- !ruby/object:Gem::Dependency
|
71
85
|
name: rake
|
72
86
|
requirement: !ruby/object:Gem::Requirement
|
@@ -171,14 +185,14 @@ dependencies:
|
|
171
185
|
requirements:
|
172
186
|
- - ~>
|
173
187
|
- !ruby/object:Gem::Version
|
174
|
-
version: '1.
|
188
|
+
version: '1.8'
|
175
189
|
type: :development
|
176
190
|
prerelease: false
|
177
191
|
version_requirements: !ruby/object:Gem::Requirement
|
178
192
|
requirements:
|
179
193
|
- - ~>
|
180
194
|
- !ruby/object:Gem::Version
|
181
|
-
version: '1.
|
195
|
+
version: '1.8'
|
182
196
|
- !ruby/object:Gem::Dependency
|
183
197
|
name: selenium-webdriver
|
184
198
|
requirement: !ruby/object:Gem::Requirement
|
@@ -232,21 +246,25 @@ files:
|
|
232
246
|
- lib/axe/api/value_object.rb
|
233
247
|
- lib/axe/api.rb
|
234
248
|
- lib/axe/configuration.rb
|
249
|
+
- lib/axe/core.rb
|
235
250
|
- lib/axe/cucumber/step.rb
|
236
251
|
- lib/axe/cucumber/step_definitions.rb
|
237
252
|
- lib/axe/cucumber.rb
|
238
253
|
- lib/axe/dsl.rb
|
239
254
|
- lib/axe/expectation.rb
|
240
|
-
- lib/axe/
|
255
|
+
- lib/axe/finds_page.rb
|
256
|
+
- lib/axe/hooks.rb
|
257
|
+
- lib/axe/loader.rb
|
241
258
|
- lib/axe/matchers/be_accessible.rb
|
242
259
|
- lib/axe/matchers.rb
|
243
|
-
- lib/axe/page.rb
|
244
260
|
- lib/axe/rspec.rb
|
245
261
|
- lib/axe/version.rb
|
246
262
|
- lib/axe.rb
|
247
263
|
- lib/chain_mail/chainable.rb
|
248
264
|
- lib/webdriver_script_adapter/exec_eval_script_adapter.rb
|
249
265
|
- lib/webdriver_script_adapter/execute_async_script_adapter.rb
|
266
|
+
- lib/webdriver_script_adapter/frame_adapter.rb
|
267
|
+
- lib/webdriver_script_adapter/query_selector_adapter.rb
|
250
268
|
- node_modules/axe-core/axe.min.js
|
251
269
|
- LICENSE
|
252
270
|
- README.md
|
@@ -1,26 +0,0 @@
|
|
1
|
-
require 'pathname'
|
2
|
-
require 'rubygems'
|
3
|
-
|
4
|
-
module Axe
|
5
|
-
class JavaScriptLibrary
|
6
|
-
|
7
|
-
def inject_into(page)
|
8
|
-
page.execute_script source
|
9
|
-
end
|
10
|
-
|
11
|
-
def source
|
12
|
-
axe_lib.read
|
13
|
-
end
|
14
|
-
|
15
|
-
private
|
16
|
-
|
17
|
-
def axe_lib
|
18
|
-
gem_root + 'node_modules/axe-core/axe.min.js'
|
19
|
-
end
|
20
|
-
|
21
|
-
def gem_root
|
22
|
-
Pathname.new Gem::Specification.find_by_name('axe-matchers').gem_dir
|
23
|
-
end
|
24
|
-
|
25
|
-
end
|
26
|
-
end
|
data/lib/axe/page.rb
DELETED
@@ -1,26 +0,0 @@
|
|
1
|
-
require 'forwardable'
|
2
|
-
require 'webdriver_script_adapter/exec_eval_script_adapter'
|
3
|
-
require 'webdriver_script_adapter/execute_async_script_adapter'
|
4
|
-
|
5
|
-
module Axe
|
6
|
-
class Page
|
7
|
-
extend Forwardable
|
8
|
-
def_delegators :@driver, :execute_script, :execute_async_script
|
9
|
-
|
10
|
-
def initialize(driver)
|
11
|
-
@driver = wrap_exec_async wrap_exec_eval driver
|
12
|
-
end
|
13
|
-
|
14
|
-
private
|
15
|
-
|
16
|
-
# ensure driver has #execute_async_script
|
17
|
-
def wrap_exec_async(driver)
|
18
|
-
::WebDriverScriptAdapter::ExecuteAsyncScriptAdapter.wrap driver
|
19
|
-
end
|
20
|
-
|
21
|
-
# ensure driver has #execute_script and #evaluate_script
|
22
|
-
def wrap_exec_eval(driver)
|
23
|
-
::WebDriverScriptAdapter::ExecEvalScriptAdapter.wrap driver
|
24
|
-
end
|
25
|
-
end
|
26
|
-
end
|