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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 33ad37f031298e34a65bb01d494d4a26ab600a41
4
- data.tar.gz: 4deb2f97a3ccac73730c1dd4d8572d1cd45bf2ed
3
+ metadata.gz: a4ce283bc01f3e57d9c2d68192f9c81063cd1244
4
+ data.tar.gz: 98200dc0c23e1e0d90bae077591b747ad4e087cd
5
5
  SHA512:
6
- metadata.gz: e57a6e8793fc43ff030efaeb39664365aecbcd86ed55a66742e40d0c4749c177669619bf6a5479312b0fe28c89cef68baeb8a7436ce230971931b05efb125dc6
7
- data.tar.gz: 4def6e8ca24af43a8cc89e85d6c7c3b3ec64d0724ad4e332acd92e04d4c2f718408be6367606122a88a83f9a9b8067b4424766bf6f07d58cba6065f4528ef6bd
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
- # Requirements
5
+ or add `gem 'axe-matchers'` to your `Gemfile` and `bundle install`
4
6
 
5
- 1. Ruby 2.0.0 or later.
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
- ## Ruby Version management
9
+ 1. Require step definitions
14
10
 
15
- [rbenv](https://github.com/sstephenson/rbenv) is recommended but you may also use [rvm](https://rvm.io/), [chruby](https://github.com/postmodern/chruby) or other ruby version manager of your choice. 2.0.0-p481 is the official minimum version, as it is the default Ruby bundled with OS X Mavericks, but the gem *ought* to support 1.9 and above.
11
+ Require the axe-matcher step definitions in `features/support/env.rb` or similar.
16
12
 
17
- The `.ruby-version` is intentionally ignored from the repo for the same reason that `Gemfile.lock` should not be committed. See http://yehudakatz.com/2010/12/16/clarifying-the-roles-of-the-gemspec-and-gemfile/ for more clarification.
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
- ## Bundler
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
- `gem install bundler` to install bundler
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
- All subsequent commands (when invoking rake, rspec, cucumber, etc) must be prefixed with `bundle exec` unless you are using bundler binstubs or rbenv-bundle-exec or similar. (Elsewhere in this readme, the `bundle exec` prefix will be omitted.)
59
+ ## Exclusion clause
25
60
 
26
- ## Brewdler
61
+ ``` gherkin
62
+ Then the page should be accessible excluding "#selector"
63
+ ```
27
64
 
28
- Most of the dependencies necessary for running the various test suite configurations are provided via gems and managed by bundler.
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
- However, to run the tests against phantomjs, you will need phantomjs installed. And to run the tests against chrome, you will need chromedriver. Both of these are system dependencies. These can be installed manually, or through homebrew. To ease installation of these non-gem dependencies, a `Brewfile` is provided.
67
+ Additional [context parameter documentation](https://github.com/dequelabs/axe-core/blob/master/doc/API.md#a-context-parameter)
31
68
 
32
- `brew tap homebrew/bundle` to install brewdler
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
- Additionally, to test against Safari, the SafariDriver extension is needed. Install it (using Safari) from http://selenium-release.storage.googleapis.com/2.45/SafariDriver.safariextz.
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
- ## Rake Tasks
76
+ ## Accessibility Standard (Tag) clause
38
77
 
39
- Rake is the standard task runner. For a list of configured tasks, run `rake -T`. Briefly:
78
+ ``` gherkin
79
+ Then the page should be accessible according to: wcag2a
80
+ ```
40
81
 
41
- - `rake spec` to run unit tests
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
- # RSpec Unit Tests
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
- These confirm the proper behavior of the matchers. These are located in the `spec` directory and may be run with `rake spec` or `rspec`.
86
+ If desired, a semicolon (`;`) may be used to separate the tag clause from the preceding clause.
49
87
 
50
- # Cucumber Features
88
+ ``` gherkin
89
+ Then the page should be accessible within "#header"; according to: best-practice
90
+ ```
51
91
 
52
- There is a single feature that does a minimal test of the matchers + cucumber steps + axe js library. It is intended as a smoke test to validate against multiple webdrivers. Unless a specific profile is specified, cucumber will run the default profile which drives webkit (headless) with capybara. Alternatiely, you may specify the driver and browser combination:
92
+ ## Checking Rules clause
53
93
 
94
+ ``` gherkin
95
+ Then the page should be accessible checking: ruleId
54
96
  ```
55
- cucumber -p capybara -p poltergeist
56
- cucumber -p capybara -p webkit
57
- cucumber -p capybara -p firefox
58
- cucumber -p selenium -p phantomjs
59
- cucumber -p watir -p chrome
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
- First specify the driver (one of: `capybara`, `selenium` or `watir`), and choose the browser (`firefox`, `chrome`, `safari`, `phantomjs`). All three drivers support all four browsers. Capybara alone also supports `webkit` and `poltergeist`.
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
- These profile configurations can also be run via rake: `rake cucumber:{driver}:{browser}`. For example:
115
+ ## Skipping Rules clause
65
116
 
117
+ ``` gherkin
118
+ Then the page should be accessible skipping: ruleId
66
119
  ```
67
- rake cucumber:capybara:firefox
68
- rake cucumber:selenium:chrome
69
- rake cucumber:watir:phantomjs
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
- You may omit the browser (`rake cucumber:capybara`) and it will run the features under every configured browser for the given driver. Alternatively, you may omit the driver (`rake cucumber:firefox`) and it will run the features with each driver for the given browser. Additionally, there are special options to run every driver/browser combination `rake cucumber:all`, or only the headless browsers `rake cucumber:headless`. (Headless combinations are capybara+poltergeist, capybara+webkit, capybara+phantomjs, selenium+phantomjs, watir+phantomjs)
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
+ ```
@@ -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
- def_delegators :@context, :include, :exclude
19
- def_delegators :@options, :rules_by_tags, :run_rules, :skip_rules, :run_only_rules, :custom_options
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
@@ -1,60 +1,36 @@
1
- require 'forwardable'
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 include(selector)
16
- @inclusion.concat ensure_nested_array(selector)
17
- self
11
+ def within(*selectors)
12
+ @inclusion.concat selectors.map { |s| Array(Selector.new s) }
18
13
  end
19
14
 
20
- def exclude(selector)
21
- @exclusion.concat ensure_nested_array(selector)
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
- # include key must not be included if empty
28
- # (when undefined, defaults to `document`)
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
- if @inclusion.empty?
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
@@ -6,13 +6,8 @@ module Axe
6
6
  class Options
7
7
  extend Forwardable
8
8
 
9
- def_delegator :@rules, :by_tags, :rules_by_tags
10
- def_delegator :@rules, :run_only, :run_only_rules
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 by_tags(tags)
14
- @tags += tags
15
- self
11
+ def according_to(*tags)
12
+ @tags.concat tags.flatten
16
13
  end
17
14
 
18
- def run_only(rules)
19
- @exclusive += rules
20
- self
15
+ def checking(*rules)
16
+ @included.concat rules.flatten
21
17
  end
22
18
 
23
- def run(rules)
24
- @included += rules
25
- self
19
+ def checking_only(*rules)
20
+ @exclusive.concat rules.flatten
26
21
  end
27
22
 
28
- def skip(rules)
29
- @excluded += rules
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
@@ -0,0 +1,3 @@
1
+ require 'axe/dsl'
2
+
3
+ World Axe::DSL
@@ -1,11 +1,11 @@
1
1
  require 'yaml'
2
2
 
3
3
  require 'axe'
4
- require 'axe/matchers/be_accessible'
4
+ require 'axe/matchers'
5
+ require 'axe/expectation'
5
6
 
6
- # The purpose of this class is to enable private helper methods for assertion
7
- # and cucumber argument parsing without leaking the helper methods into the
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 be_accessible(negate, inclusion, exclusion, tags, run_only, run_rules, skip_rules, options)
52
- accessibility = Matchers::BeAccessible.new
53
-
54
- accessibility.within(selector inclusion) if inclusion
55
- accessibility.excluding(selector exclusion) if exclusion
56
- accessibility.according_to(split tags) if tags
57
- run_only ? accessibility.checking_only(split run_rules) : accessibility.checking(split run_rules) if run_rules
58
- accessibility.skipping(split skip_rules) if skip_rules
59
- accessibility.with_options(to_hash options) if options
60
-
61
- if negate then refute accessibility else assert accessibility end
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 string
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