axe-matchers 1.0.0 → 1.1.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 +110 -42
- data/lib/axe/api/a11y_check.rb +6 -2
- data/lib/axe/api/audit.rb +1 -1
- data/lib/axe/api/context.rb +10 -34
- data/lib/axe/api/options.rb +2 -7
- data/lib/axe/api/rules.rb +8 -14
- data/lib/axe/api/selector.rb +18 -0
- data/lib/axe/cucumber.rb +3 -0
- data/lib/axe/cucumber/step.rb +17 -26
- data/lib/axe/cucumber/step_definitions.rb +1 -1
- data/lib/axe/dsl.rb +15 -0
- data/lib/axe/expectation.rb +32 -0
- data/lib/axe/matchers/be_accessible.rb +7 -36
- data/lib/axe/version.rb +1 -1
- data/lib/chain_mail/chainable.rb +19 -0
- data/lib/strip_heredoc.rb +6 -0
- data/node_modules/axe-core/axe.min.js +5 -15
- metadata +19 -5
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: a4ce283bc01f3e57d9c2d68192f9c81063cd1244
|
4
|
+
data.tar.gz: 98200dc0c23e1e0d90bae077591b747ad4e087cd
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 1c04838dedcf4eb65c9461b4115d2b814ec7a463f66a92f47ef003164be6dcb447f8917d33114ff75cbcef2222afae3980cd838a65e0f28c20aad520f9da0d3c
|
7
|
+
data.tar.gz: 6d3cf4f14f44191c53e5e6af6a3526f7e28ca9e32ea94afaeedd36636fd8c27c248a39ef136753cb1e8c7234f3065d2fa1eff7247b240bedde81e8411af8889c
|
data/README.md
CHANGED
@@ -1,72 +1,140 @@
|
|
1
|
+
# Installation
|
1
2
|
|
3
|
+
`gem install axe-matchers`
|
2
4
|
|
3
|
-
|
5
|
+
or add `gem 'axe-matchers'` to your `Gemfile` and `bundle install`
|
4
6
|
|
5
|
-
|
6
|
-
2. Bundler for gem dependencies
|
7
|
-
3. Brewdler for system dependencies (phantomjs, chromedriver, etc)
|
8
|
-
4. Rake as task runner
|
9
|
-
5. RSpec for unit tests
|
10
|
-
6. Cucumber for end to end tests
|
11
|
-
7. Node/npm are necessary for pulling down the axe-core package
|
7
|
+
# Cucumber Configuration
|
12
8
|
|
13
|
-
|
9
|
+
1. Require step definitions
|
14
10
|
|
15
|
-
|
11
|
+
Require the axe-matcher step definitions in `features/support/env.rb` or similar.
|
16
12
|
|
17
|
-
|
13
|
+
``` ruby
|
14
|
+
require 'axe/cucumber/step_definitions'
|
15
|
+
```
|
16
|
+
|
17
|
+
2. Configure Browser/WebDriver
|
18
|
+
|
19
|
+
If there exists a `page` method on the Cucumber `World` (as is provided by the Capybara DSL), or if one of `@page`, `@browser`, `@driver` or `@webdriver` exist, then no configuration is necessary. Otherwise, the browser object must be configurate manually.
|
20
|
+
|
21
|
+
The browser/page object can be provided directly. Or in cases where it hasn't been instantiated yet, the variable name can be given as a String/Symbol.
|
22
|
+
|
23
|
+
``` ruby
|
24
|
+
@firefox = Selenium::WebDriver.for :firefox
|
25
|
+
|
26
|
+
Axe::Cucumber.configure do |c|
|
27
|
+
# browser object
|
28
|
+
c.page = @firefox
|
29
|
+
|
30
|
+
# or variable name
|
31
|
+
c.page = :@firefox
|
32
|
+
end
|
33
|
+
```
|
34
|
+
|
35
|
+
# Accessibility Steps
|
36
|
+
|
37
|
+
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:
|
38
|
+
|
39
|
+
`Then the page should be accessible [including] [excluding] [according-to] [checking-rules/checking-only-rules] [skipping-rules]`
|
40
|
+
|
41
|
+
## Base Step
|
42
|
+
|
43
|
+
``` gherkin
|
44
|
+
Then the page should be accessible
|
45
|
+
```
|
46
|
+
|
47
|
+
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](https://github.com/dequelabs/axe-core/blob/master/doc/API.md#api-name-axea11ycheck) (the entire document is checked using the default rules).
|
48
|
+
|
49
|
+
## Inclusion clause
|
50
|
+
|
51
|
+
``` gherkin
|
52
|
+
Then the page should be accessible within "#selector"
|
53
|
+
```
|
18
54
|
|
19
|
-
|
55
|
+
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"`
|
20
56
|
|
21
|
-
|
22
|
-
`bundle install` to install necessary gems
|
57
|
+
Additional [context parameter documentation](https://github.com/dequelabs/axe-core/blob/master/doc/API.md#a-context-parameter)
|
23
58
|
|
24
|
-
|
59
|
+
## Exclusion clause
|
25
60
|
|
26
|
-
|
61
|
+
``` gherkin
|
62
|
+
Then the page should be accessible excluding "#selector"
|
63
|
+
```
|
27
64
|
|
28
|
-
|
65
|
+
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"`
|
29
66
|
|
30
|
-
|
67
|
+
Additional [context parameter documentation](https://github.com/dequelabs/axe-core/blob/master/doc/API.md#a-context-parameter)
|
31
68
|
|
32
|
-
|
33
|
-
`brew bundle` to install phantomjs and chromedriver
|
69
|
+
If desired, a semicolon (`;`) or the word `but` may be used to separate the exclusion clause from the inclusion clause (if present).
|
34
70
|
|
35
|
-
|
71
|
+
``` gherkin
|
72
|
+
Then the page should be accessible within "#header"; excluding "#footer"
|
73
|
+
Then the page should be accessible within "#header" but excluding "#footer"
|
74
|
+
```
|
36
75
|
|
37
|
-
##
|
76
|
+
## Accessibility Standard (Tag) clause
|
38
77
|
|
39
|
-
|
78
|
+
``` gherkin
|
79
|
+
Then the page should be accessible according to: wcag2a
|
80
|
+
```
|
40
81
|
|
41
|
-
-
|
42
|
-
- `rake cucumber` to run end to end tests
|
43
|
-
- `rake build` to build and package the gem
|
44
|
-
- `rake clean` and `rake clobber` to clean up build assets
|
82
|
+
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`
|
45
83
|
|
46
|
-
#
|
84
|
+
The acceptable [tag names are documented](https://github.com/dequelabs/axe-core/blob/master/doc/API.md#b-options-parameter) as well as a [complete listing of rules](https://github.com/dequelabs/axe-core/blob/master/doc/rule-descriptions.md) that correspond to each tag/standard.
|
47
85
|
|
48
|
-
|
86
|
+
If desired, a semicolon (`;`) may be used to separate the tag clause from the preceding clause.
|
49
87
|
|
50
|
-
|
88
|
+
``` gherkin
|
89
|
+
Then the page should be accessible within "#header"; according to: best-practice
|
90
|
+
```
|
51
91
|
|
52
|
-
|
92
|
+
## Checking Rules clause
|
53
93
|
|
94
|
+
``` gherkin
|
95
|
+
Then the page should be accessible checking: ruleId
|
54
96
|
```
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
97
|
+
|
98
|
+
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. (see [rules documentation](https://github.com/dequelabs/axe-core/blob/master/doc/rule-descriptions.md) for a list of valid rule IDs)
|
99
|
+
|
100
|
+
If desired, a semicolon (`;`) or the word `and` may be used to separate the checking-rules clause from the preceding clause.
|
101
|
+
|
102
|
+
``` gherkin
|
103
|
+
Then the page should be accessible according to: wcag2aa; checking: color-contrast
|
104
|
+
Then the page should be accessible according to: wcag2aa and checking: color-contrast
|
105
|
+
```
|
106
|
+
|
107
|
+
### Exclusive Rules clause
|
108
|
+
|
109
|
+
``` gherkin
|
110
|
+
Then the page should be accessible checking only: ruleId
|
60
111
|
```
|
61
112
|
|
62
|
-
|
113
|
+
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.
|
63
114
|
|
64
|
-
|
115
|
+
## Skipping Rules clause
|
65
116
|
|
117
|
+
``` gherkin
|
118
|
+
Then the page should be accessible skipping: ruleId
|
66
119
|
```
|
67
|
-
|
68
|
-
|
69
|
-
|
120
|
+
|
121
|
+
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. (see [rules documentation](https://github.com/dequelabs/axe-core/blob/master/doc/rule-descriptions.md) for a list of valid rule IDs)
|
122
|
+
|
123
|
+
If desired, a semicolon (`;`) or the word `but` may be used to separate the skipping-rules clause from the preceding clause.
|
124
|
+
|
125
|
+
``` gherkin
|
126
|
+
Then the page should be accessible according to: wcag2a; skipping: accesskeys
|
127
|
+
Then the page should be accessible according to: wcag2a but skipping: accesskeys
|
70
128
|
```
|
71
129
|
|
72
|
-
|
130
|
+
# Examples
|
131
|
+
|
132
|
+
``` gherkin
|
133
|
+
Then the page should be accessible within "main, header" but excluding "footer"
|
134
|
+
|
135
|
+
Then the page should be accessible excluding "#sidebar" according to: wcag2a, wcag2aa but skipping: color-contrast
|
136
|
+
|
137
|
+
Then the page should be accessible checking only: document-title, label
|
138
|
+
|
139
|
+
Then the page should be accessible according to: best-practice and checking: aria-roles, definition-list
|
140
|
+
```
|
data/lib/axe/api/a11y_check.rb
CHANGED
@@ -1,6 +1,8 @@
|
|
1
1
|
require 'forwardable'
|
2
2
|
require 'json'
|
3
3
|
|
4
|
+
require 'chain_mail/chainable'
|
5
|
+
|
4
6
|
require 'axe/api'
|
5
7
|
require 'axe/api/audit'
|
6
8
|
require 'axe/api/context'
|
@@ -14,9 +16,11 @@ module Axe
|
|
14
16
|
METHOD_NAME = "#{LIBRARY_IDENTIFIER}.a11yCheck"
|
15
17
|
|
16
18
|
extend Forwardable
|
19
|
+
def_delegators :@context, :within, :excluding
|
20
|
+
def_delegators :@options, :according_to, :checking, :checking_only, :skipping, :with_options
|
17
21
|
|
18
|
-
|
19
|
-
|
22
|
+
extend ChainMail::Chainable
|
23
|
+
chainable :within, :excluding, :according_to, :checking, :checking_only, :skipping, :with_options
|
20
24
|
|
21
25
|
def initialize
|
22
26
|
@context = Context.new
|
data/lib/axe/api/audit.rb
CHANGED
@@ -18,7 +18,7 @@ module Axe
|
|
18
18
|
end
|
19
19
|
|
20
20
|
def failure_message_when_negated
|
21
|
-
"Expected to find accessibility violations. None were detected.\nInvocation: #{invocation}"
|
21
|
+
"Expected to find accessibility violations. None were detected.\n\nInvocation: #{invocation}"
|
22
22
|
end
|
23
23
|
end
|
24
24
|
end
|
data/lib/axe/api/context.rb
CHANGED
@@ -1,60 +1,36 @@
|
|
1
|
-
require '
|
1
|
+
require 'axe/api/selector'
|
2
2
|
|
3
3
|
module Axe
|
4
4
|
module API
|
5
5
|
class Context
|
6
|
-
extend Forwardable
|
7
|
-
|
8
|
-
attr_reader :inclusion, :exclusion
|
9
|
-
|
10
6
|
def initialize
|
11
7
|
@inclusion = []
|
12
8
|
@exclusion = []
|
13
9
|
end
|
14
10
|
|
15
|
-
def
|
16
|
-
@inclusion.concat
|
17
|
-
self
|
11
|
+
def within(*selectors)
|
12
|
+
@inclusion.concat selectors.map { |s| Array(Selector.new s) }
|
18
13
|
end
|
19
14
|
|
20
|
-
def
|
21
|
-
@exclusion.concat
|
22
|
-
self
|
15
|
+
def excluding(*selectors)
|
16
|
+
@exclusion.concat selectors.map { |s| Array(Selector.new s) }
|
23
17
|
end
|
24
18
|
|
25
19
|
def to_hash
|
26
20
|
{}.tap do |context_param|
|
27
|
-
#
|
28
|
-
# (
|
21
|
+
# omit empty arrays
|
22
|
+
# (include must not be present if empty)
|
23
|
+
# (exclude is allowed to be empty; but meh)
|
29
24
|
context_param[:include] = @inclusion unless @inclusion.empty?
|
30
|
-
|
31
|
-
# exclude array allowed to be empty
|
32
|
-
# and must exist in case `include` is omitted
|
33
|
-
# because context_param cannot be empty object ({})
|
34
|
-
context_param[:exclude] = @exclusion
|
25
|
+
context_param[:exclude] = @exclusion unless @exclusion.empty?
|
35
26
|
end
|
36
27
|
end
|
37
28
|
|
38
29
|
def to_json
|
39
|
-
|
40
|
-
if @exclusion.empty?
|
41
|
-
"document"
|
42
|
-
else
|
43
|
-
%Q({"include":document,"exclude":#{@exclusion.to_json}})
|
44
|
-
end
|
45
|
-
else
|
46
|
-
to_hash.to_json
|
47
|
-
end
|
30
|
+
to_hash.to_json
|
48
31
|
end
|
49
32
|
|
50
33
|
alias :to_s :to_json
|
51
|
-
|
52
|
-
private
|
53
|
-
|
54
|
-
def ensure_nested_array(selector)
|
55
|
-
Array(selector).map { |s| Array(s) }
|
56
|
-
end
|
57
|
-
|
58
34
|
end
|
59
35
|
end
|
60
36
|
end
|
data/lib/axe/api/options.rb
CHANGED
@@ -6,13 +6,8 @@ module Axe
|
|
6
6
|
class Options
|
7
7
|
extend Forwardable
|
8
8
|
|
9
|
-
|
10
|
-
def_delegator :@
|
11
|
-
def_delegator :@rules, :run, :run_rules
|
12
|
-
def_delegator :@rules, :skip, :skip_rules
|
13
|
-
def_delegator :@custom, :merge!, :custom_options
|
14
|
-
|
15
|
-
attr_reader :rules, :custom
|
9
|
+
def_delegators :@rules, :according_to, :checking, :checking_only, :skipping
|
10
|
+
def_delegator :@custom, :merge!, :with_options
|
16
11
|
|
17
12
|
def initialize
|
18
13
|
@rules = Rules.new
|
data/lib/axe/api/rules.rb
CHANGED
@@ -1,8 +1,6 @@
|
|
1
1
|
module Axe
|
2
2
|
module API
|
3
3
|
class Rules
|
4
|
-
attr_reader :tags, :included, :excluded, :exclusive
|
5
|
-
|
6
4
|
def initialize
|
7
5
|
@tags = []
|
8
6
|
@included = []
|
@@ -10,24 +8,20 @@ module Axe
|
|
10
8
|
@exclusive = []
|
11
9
|
end
|
12
10
|
|
13
|
-
def
|
14
|
-
@tags
|
15
|
-
self
|
11
|
+
def according_to(*tags)
|
12
|
+
@tags.concat tags.flatten
|
16
13
|
end
|
17
14
|
|
18
|
-
def
|
19
|
-
@
|
20
|
-
self
|
15
|
+
def checking(*rules)
|
16
|
+
@included.concat rules.flatten
|
21
17
|
end
|
22
18
|
|
23
|
-
def
|
24
|
-
@
|
25
|
-
self
|
19
|
+
def checking_only(*rules)
|
20
|
+
@exclusive.concat rules.flatten
|
26
21
|
end
|
27
22
|
|
28
|
-
def
|
29
|
-
@excluded
|
30
|
-
self
|
23
|
+
def skipping(*rules)
|
24
|
+
@excluded.concat rules.flatten
|
31
25
|
end
|
32
26
|
|
33
27
|
def to_hash
|
@@ -0,0 +1,18 @@
|
|
1
|
+
module Axe
|
2
|
+
module API
|
3
|
+
class Selector
|
4
|
+
def initialize(s)
|
5
|
+
@selector = case s
|
6
|
+
when Array then s
|
7
|
+
when String, Symbol then [ String(s) ]
|
8
|
+
when Hash then Selector.new(s[:selector]).to_a.unshift s[:iframe]
|
9
|
+
else Selector.new(s.selector).to_a.unshift s.iframe
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
def to_a
|
14
|
+
@selector
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
data/lib/axe/cucumber.rb
ADDED
data/lib/axe/cucumber/step.rb
CHANGED
@@ -1,11 +1,11 @@
|
|
1
1
|
require 'yaml'
|
2
2
|
|
3
3
|
require 'axe'
|
4
|
-
require 'axe/matchers
|
4
|
+
require 'axe/matchers'
|
5
|
+
require 'axe/expectation'
|
5
6
|
|
6
|
-
# The purpose of this class is to
|
7
|
-
#
|
8
|
-
# cucumber World object.
|
7
|
+
# The purpose of this class is to support private helpers for argument parsing
|
8
|
+
# without leaking the helper methods into the cucumber World object.
|
9
9
|
# Further, using these helper methods for assert/refute removes the dependency
|
10
10
|
# on rspec. So end users may choose to use any (or non) assertion/expectation
|
11
11
|
# library, as this class uses the Axe Accessibility Matcher directly, without
|
@@ -48,23 +48,22 @@ module Axe
|
|
48
48
|
@page = page
|
49
49
|
end
|
50
50
|
|
51
|
-
def
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
51
|
+
def assert_accessibility(negate=false, inclusion="", exclusion="", tags="", run_only=false, run_rules="", skip_rules="", options=nil)
|
52
|
+
is_accessible = Axe::Matchers::BeAccessible.new.tap do |a|
|
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
|
+
a.with_options to_hash(options)
|
60
|
+
end
|
61
|
+
|
62
|
+
Axe::AccessibilityExpectation.create(negate).assert @page, is_accessible
|
62
63
|
end
|
63
64
|
|
64
65
|
private
|
65
66
|
|
66
|
-
attr_reader :page
|
67
|
-
|
68
67
|
def selector(selector)
|
69
68
|
split(selector)
|
70
69
|
end
|
@@ -74,15 +73,7 @@ module Axe
|
|
74
73
|
end
|
75
74
|
|
76
75
|
def to_hash(string)
|
77
|
-
YAML.load
|
78
|
-
end
|
79
|
-
|
80
|
-
def assert(matcher)
|
81
|
-
raise matcher.failure_message unless matcher.matches? page
|
82
|
-
end
|
83
|
-
|
84
|
-
def refute(matcher)
|
85
|
-
raise matcher.failure_message_when_negated if matcher.matches? page
|
76
|
+
(string && !string.empty?) ? YAML.load(String(string)) : {}
|
86
77
|
end
|
87
78
|
end
|
88
79
|
end
|