axe-core-api 4.0.0.pre.287120d → 4.2.0.pre.6beb600
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/lib/axe/api/context.rb +10 -4
- data/lib/axe/api/options.rb +6 -2
- data/lib/axe/api/results.rb +10 -0
- data/lib/axe/api/run.rb +155 -6
- data/lib/axe/configuration.rb +1 -1
- data/lib/axe/core.rb +22 -2
- data/lib/loader.rb +10 -2
- data/lib/webdriver_script_adapter/exec_eval_script_adapter.rb +14 -1
- data/lib/webdriver_script_adapter/execute_async_script_adapter.rb +13 -1
- data/lib/webdriver_script_adapter/frame_adapter.rb +42 -8
- data/node_modules/axe-core/axe.min.js +3 -3
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 93fdbc2f4d06b670e6a36caa7489196b44f3b2a09e939ec16b541ae7c45c47d9
|
4
|
+
data.tar.gz: 7dcc16ad9bd768830d7a5a227ba72af18e7ec67d0612397cd8cd391547049c0d
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: e359e128c431a0345906f64bdf29444f35f5128cb67c728cf4af58bbbe55e88b6d600b4766800e886e16d225f6746f3e0c31f911e9fd21e895f9de740356e13b
|
7
|
+
data.tar.gz: daa3fcd4f2c12b81e560e19771151ae3010977153f74423b31b16e5567dac224ce89825ed53da5556ea56bf5991a8db4120f144a333e983b29870ba6ea4fd6d9
|
data/lib/axe/api/context.rb
CHANGED
@@ -16,13 +16,19 @@ module Axe
|
|
16
16
|
@exclusion.concat selectors.map { |s| Array(Selector.new s) }
|
17
17
|
end
|
18
18
|
|
19
|
+
def to_h
|
20
|
+
to_hash
|
21
|
+
end
|
19
22
|
def to_hash
|
20
|
-
{
|
21
|
-
|
23
|
+
return { exclude: @exclusion } if @inclusion.empty?
|
24
|
+
h = {}
|
25
|
+
h["include"] = @inclusion unless @inclusion.empty?
|
26
|
+
h["exclude"] = @exclusion unless @exclusion.empty?
|
27
|
+
h
|
22
28
|
end
|
23
29
|
|
24
|
-
def to_json
|
25
|
-
to_hash.to_json
|
30
|
+
def to_json(options = nil)
|
31
|
+
to_hash.to_json options
|
26
32
|
end
|
27
33
|
|
28
34
|
def empty?
|
data/lib/axe/api/options.rb
CHANGED
@@ -14,12 +14,16 @@ module Axe
|
|
14
14
|
@custom = {}
|
15
15
|
end
|
16
16
|
|
17
|
+
def to_h
|
18
|
+
to_hash
|
19
|
+
end
|
20
|
+
|
17
21
|
def to_hash
|
18
22
|
@rules.to_hash.merge(@custom)
|
19
23
|
end
|
20
24
|
|
21
|
-
def to_json
|
22
|
-
to_hash.to_json
|
25
|
+
def to_json(options = nil)
|
26
|
+
to_hash.to_json options
|
23
27
|
end
|
24
28
|
|
25
29
|
def empty?
|
data/lib/axe/api/results.rb
CHANGED
@@ -10,6 +10,10 @@ module Axe
|
|
10
10
|
attribute :incomplete, ::Array[Rule]
|
11
11
|
attribute :passes, ::Array[Rule]
|
12
12
|
attribute :timestamp
|
13
|
+
attribute :testEngine
|
14
|
+
attribute :testEnvironment
|
15
|
+
attribute :testRunner
|
16
|
+
attribute :toolOptions
|
13
17
|
attribute :url, ::String
|
14
18
|
attribute :violations, ::Array[Rule]
|
15
19
|
end
|
@@ -28,12 +32,18 @@ module Axe
|
|
28
32
|
inapplicable: inapplicable.map(&:to_h),
|
29
33
|
incomplete: incomplete.map(&:to_h),
|
30
34
|
passes: passes.map(&:to_h),
|
35
|
+
testEngine: testEngine,
|
31
36
|
timestamp: timestamp,
|
32
37
|
url: url,
|
33
38
|
violations: violations.map(&:to_h),
|
34
39
|
}
|
35
40
|
end
|
36
41
|
|
42
|
+
def timestamp=(ts)
|
43
|
+
timestamp = ts
|
44
|
+
end
|
45
|
+
|
46
|
+
|
37
47
|
private
|
38
48
|
|
39
49
|
def violation_count_message
|
data/lib/axe/api/run.rb
CHANGED
@@ -27,21 +27,170 @@ module Axe
|
|
27
27
|
end
|
28
28
|
|
29
29
|
def call(page)
|
30
|
-
audit page
|
31
|
-
|
32
|
-
|
30
|
+
results = audit page
|
31
|
+
Audit.new to_js, Results.new(results)
|
32
|
+
end
|
33
|
+
|
34
|
+
def analyze_post_43x(page, lib)
|
35
|
+
@original_window = window_handle page
|
36
|
+
partial_results = run_partial_recursive(page, @context, lib, true)
|
37
|
+
throw partial_results if partial_results.respond_to?("key?") and partial_results.key?("errorMessage")
|
38
|
+
results = within_about_blank_context(page) { |page|
|
39
|
+
Common::Loader.new(page, lib).load_top_level Axe::Configuration.instance.jslib
|
40
|
+
axe_finish_run page, partial_results
|
41
|
+
}
|
42
|
+
Audit.new to_js, Results.new(results)
|
33
43
|
end
|
34
44
|
|
35
45
|
private
|
36
46
|
|
37
47
|
def audit(page)
|
38
|
-
|
48
|
+
script = <<-JS
|
49
|
+
var callback = arguments[arguments.length - 1];
|
50
|
+
var context = arguments[0] || document;
|
51
|
+
var options = arguments[1] || {};
|
52
|
+
#{METHOD_NAME}(context, options).then(callback);
|
53
|
+
JS
|
54
|
+
page.execute_async_script_fixed script, *js_args
|
55
|
+
end
|
56
|
+
|
57
|
+
def switch_to_frame_by_handle(page, handle)
|
58
|
+
page = get_selenium page
|
59
|
+
page.switch_to.frame handle
|
60
|
+
end
|
61
|
+
|
62
|
+
def switch_to_parent_frame(page)
|
63
|
+
page = get_selenium page
|
64
|
+
page.switch_to.parent_frame
|
65
|
+
end
|
66
|
+
|
67
|
+
def within_about_blank_context(page)
|
68
|
+
driver = get_selenium page
|
69
|
+
|
70
|
+
driver.execute_script("window.open('about:blank'), '_blank'")
|
71
|
+
driver.switch_to.window page.window_handles[-1]
|
72
|
+
driver.get "about:blank"
|
73
|
+
|
74
|
+
ret = yield page
|
75
|
+
|
76
|
+
driver.switch_to.window page.window_handles[-1]
|
77
|
+
driver.close
|
78
|
+
driver.switch_to.window @original_window
|
79
|
+
|
80
|
+
ret
|
81
|
+
end
|
82
|
+
def window_handle(page)
|
83
|
+
page = get_selenium page
|
84
|
+
|
85
|
+
return page.window_handle if page.respond_to?("window_handle")
|
86
|
+
page.current_window_handle
|
87
|
+
end
|
88
|
+
|
89
|
+
def run_partial_recursive(page, context, lib, top_level = false)
|
90
|
+
begin
|
91
|
+
if not top_level
|
92
|
+
begin
|
93
|
+
Common::Loader.new(page, lib).load_top_level Axe::Configuration.instance.jslib
|
94
|
+
rescue
|
95
|
+
return [nil]
|
96
|
+
end
|
97
|
+
|
98
|
+
end
|
99
|
+
|
100
|
+
frame_contexts = get_frame_context_script page
|
101
|
+
if frame_contexts.respond_to?("key?") and frame_contexts.key?("errorMessage")
|
102
|
+
throw frame_contexts if top_level
|
103
|
+
return [nil]
|
104
|
+
end
|
105
|
+
|
106
|
+
res = axe_run_partial page, context
|
107
|
+
if res.key?("errorMessage")
|
108
|
+
throw res if top_level
|
109
|
+
return [nil]
|
110
|
+
else
|
111
|
+
results = [res]
|
112
|
+
end
|
113
|
+
|
114
|
+
for frame_context in frame_contexts
|
115
|
+
frame_selector = frame_context["frameSelector"]
|
116
|
+
frame_context = frame_context["frameContext"]
|
117
|
+
frame = axe_shadow_select page, frame_selector
|
118
|
+
switch_to_frame_by_handle page, frame
|
119
|
+
res = run_partial_recursive page, frame_context, lib
|
120
|
+
results += res
|
121
|
+
end
|
122
|
+
|
123
|
+
ensure
|
124
|
+
switch_to_parent_frame page if not top_level
|
125
|
+
end
|
126
|
+
return results
|
127
|
+
end
|
128
|
+
|
129
|
+
def axe_finish_run(page, partial_results)
|
130
|
+
script = <<-JS
|
131
|
+
const partialResults = arguments[0];
|
132
|
+
return axe.finishRun(partialResults);
|
133
|
+
JS
|
134
|
+
page.execute_script_fixed script, partial_results
|
135
|
+
end
|
136
|
+
|
137
|
+
def axe_shadow_select(page, frame_selector)
|
138
|
+
script = <<-JS
|
139
|
+
const frameSelector = arguments[0];
|
140
|
+
return axe.utils.shadowSelect(frameSelector);
|
141
|
+
JS
|
142
|
+
page.execute_script_fixed script, frame_selector
|
143
|
+
end
|
144
|
+
|
145
|
+
def axe_run_partial(page, context)
|
146
|
+
script = <<-JS
|
147
|
+
const context = arguments[0];
|
148
|
+
const options = arguments[1];
|
149
|
+
const cb = arguments[arguments.length - 1];
|
150
|
+
try {
|
151
|
+
const ret = window.axe.runPartial(context, options);
|
152
|
+
cb(ret);
|
153
|
+
} catch (err) {
|
154
|
+
const ret = {
|
155
|
+
violations: [],
|
156
|
+
passes: [],
|
157
|
+
url: '',
|
158
|
+
timestamp: new Date().toString(),
|
159
|
+
errorMessage: err.message
|
160
|
+
};
|
161
|
+
cb(ret);
|
162
|
+
}
|
163
|
+
JS
|
164
|
+
page.execute_async_script_fixed script, context, @options
|
165
|
+
end
|
166
|
+
|
167
|
+
def get_frame_context_script(page)
|
168
|
+
script = <<-JS
|
169
|
+
const context = arguments[0];
|
170
|
+
try {
|
171
|
+
return window.axe.utils.getFrameContexts(context);
|
172
|
+
} catch (err) {
|
173
|
+
return {
|
174
|
+
violations: [],
|
175
|
+
passes: [],
|
176
|
+
url: '',
|
177
|
+
timestamp: new Date().toString(),
|
178
|
+
errorMessage: err.message
|
179
|
+
};
|
180
|
+
}
|
181
|
+
JS
|
182
|
+
page.execute_script_fixed script, @context
|
183
|
+
end
|
184
|
+
|
185
|
+
def get_selenium(page)
|
186
|
+
page = page.driver if page.respond_to?("driver")
|
187
|
+
page = page.browser if page.respond_to?("browser") and not page.browser.is_a?(::Symbol)
|
188
|
+
page
|
39
189
|
end
|
40
190
|
|
41
191
|
def js_args
|
42
192
|
[@context, @options]
|
43
|
-
.
|
44
|
-
.map(&:to_json)
|
193
|
+
.map(&:to_h)
|
45
194
|
end
|
46
195
|
|
47
196
|
def to_js
|
data/lib/axe/configuration.rb
CHANGED
data/lib/axe/core.rb
CHANGED
@@ -14,13 +14,32 @@ module Axe
|
|
14
14
|
end
|
15
15
|
|
16
16
|
def call(callable)
|
17
|
-
|
17
|
+
if has_run_partial?
|
18
|
+
callable.analyze_post_43x @page, self
|
19
|
+
else
|
20
|
+
callable.call @page
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
def call_verbatim(callable)
|
25
|
+
callable.call @page
|
18
26
|
end
|
19
27
|
|
20
28
|
private
|
21
29
|
|
22
30
|
def load_axe_core(source)
|
23
|
-
|
31
|
+
return if already_loaded?
|
32
|
+
loader = Common::Loader.new(@page, self)
|
33
|
+
loader.load_top_level source
|
34
|
+
return if has_run_partial?
|
35
|
+
|
36
|
+
loader.call source
|
37
|
+
end
|
38
|
+
|
39
|
+
def has_run_partial?
|
40
|
+
@page.evaluate_script <<-JS
|
41
|
+
typeof window.axe.runPartial === 'function'
|
42
|
+
JS
|
24
43
|
end
|
25
44
|
|
26
45
|
def already_loaded?
|
@@ -31,6 +50,7 @@ module Axe
|
|
31
50
|
end
|
32
51
|
|
33
52
|
def wrap_driver(driver)
|
53
|
+
driver = driver.driver if driver.respond_to? :driver
|
34
54
|
::WebDriverScriptAdapter::QuerySelectorAdapter.wrap(
|
35
55
|
::WebDriverScriptAdapter::FrameAdapter.wrap(
|
36
56
|
::WebDriverScriptAdapter::ExecuteAsyncScriptAdapter.wrap(
|
data/lib/loader.rb
CHANGED
@@ -6,10 +6,18 @@ module Common
|
|
6
6
|
def initialize(page, lib)
|
7
7
|
@page = page
|
8
8
|
@lib = lib
|
9
|
+
@loaded_top_level = false
|
9
10
|
end
|
10
11
|
|
11
|
-
def
|
12
|
+
def load_top_level(source)
|
12
13
|
@page.execute_script source
|
14
|
+
@loaded_top_level = true
|
15
|
+
Common::Hooks.run_after_load @lib
|
16
|
+
end
|
17
|
+
|
18
|
+
def call(source, is_top_level = true)
|
19
|
+
@page.execute_script source unless (@loaded_top_level and is_top_level)
|
20
|
+
@page.execute_script "axe.configure({ allowedOrigins: ['<unsafe_all_origins>'] });"
|
13
21
|
Common::Hooks.run_after_load @lib
|
14
22
|
load_into_iframes(source) unless Axe::Configuration.instance.skip_iframes
|
15
23
|
end
|
@@ -18,7 +26,7 @@ module Common
|
|
18
26
|
|
19
27
|
def load_into_iframes(source)
|
20
28
|
@page.find_frames.each do |iframe|
|
21
|
-
@page.within_frame(iframe) { call source }
|
29
|
+
@page.within_frame(iframe) { call source, false }
|
22
30
|
end
|
23
31
|
end
|
24
32
|
end
|
@@ -8,7 +8,7 @@ module WebDriverScriptAdapter
|
|
8
8
|
def self.wrap(driver)
|
9
9
|
raise WebDriverError, "WebDriver must respond to #execute_script" unless driver.respond_to? :execute_script
|
10
10
|
|
11
|
-
driver.respond_to?(:evaluate_script) ? driver : new(driver)
|
11
|
+
driver.respond_to?(:evaluate_script) ? ExecEvalScriptAdapter2.new(driver) : new(driver)
|
12
12
|
end
|
13
13
|
|
14
14
|
# executes script without returning result
|
@@ -21,6 +21,19 @@ module WebDriverScriptAdapter
|
|
21
21
|
def evaluate_script(script)
|
22
22
|
__getobj__.execute_script "return #{script}"
|
23
23
|
end
|
24
|
+
|
25
|
+
def execute_script_fixed(script, *args)
|
26
|
+
page = __getobj__
|
27
|
+
page.execute_script(script, *args)
|
28
|
+
end
|
29
|
+
end
|
30
|
+
class ExecEvalScriptAdapter2 < ::DumbDelegator
|
31
|
+
def execute_script_fixed(script, *args)
|
32
|
+
page = __getobj__
|
33
|
+
page = page.driver if page.respond_to?("driver")
|
34
|
+
page = page.browser if page.respond_to?("browser") and not page.browser.is_a?(::Symbol)
|
35
|
+
page.execute_script(script, *args)
|
36
|
+
end
|
24
37
|
end
|
25
38
|
|
26
39
|
class WebDriverError < TypeError; end
|
@@ -3,6 +3,11 @@ require "securerandom"
|
|
3
3
|
require "timeout"
|
4
4
|
require_relative "./exec_eval_script_adapter"
|
5
5
|
|
6
|
+
def get_selenium(page)
|
7
|
+
page = page.driver if page.respond_to?("driver")
|
8
|
+
page = page.browser if page.respond_to?("browser") and not page.browser.is_a?(Symbol)
|
9
|
+
page
|
10
|
+
end
|
6
11
|
module WebDriverScriptAdapter
|
7
12
|
class << self
|
8
13
|
attr_accessor :async_results_identifier,
|
@@ -76,7 +81,7 @@ module WebDriverScriptAdapter
|
|
76
81
|
|
77
82
|
class ExecuteAsyncScriptAdapter < ::DumbDelegator
|
78
83
|
def self.wrap(driver)
|
79
|
-
new
|
84
|
+
new driver
|
80
85
|
end
|
81
86
|
|
82
87
|
def execute_async_script(script, *args)
|
@@ -84,6 +89,13 @@ module WebDriverScriptAdapter
|
|
84
89
|
execute_script ScriptWriter.async_wrapper(script, *args, ScriptWriter.callback(results))
|
85
90
|
Patiently.wait_until { evaluate_script results }
|
86
91
|
end
|
92
|
+
|
93
|
+
def execute_async_script_fixed(script, *args)
|
94
|
+
page = __getobj__
|
95
|
+
page = page.driver if page.respond_to?("driver")
|
96
|
+
page = page.browser if page.respond_to?("browser") and not page.browser.is_a?(::Symbol)
|
97
|
+
page.execute_async_script(script, *args)
|
98
|
+
end
|
87
99
|
end
|
88
100
|
|
89
101
|
configure do |c|
|
@@ -3,7 +3,7 @@ require "dumb_delegator"
|
|
3
3
|
module WebDriverScriptAdapter
|
4
4
|
class FrameAdapter < ::DumbDelegator
|
5
5
|
def self.wrap(driver)
|
6
|
-
if driver.respond_to?(:
|
6
|
+
if driver.respond_to?(:find_css)
|
7
7
|
CapybaraAdapter.new driver
|
8
8
|
elsif !driver.respond_to?(:switch_to)
|
9
9
|
WatirAdapter.new driver
|
@@ -16,20 +16,19 @@ module WebDriverScriptAdapter
|
|
16
16
|
|
17
17
|
private
|
18
18
|
|
19
|
-
class
|
20
|
-
def
|
21
|
-
|
19
|
+
class WatirAdapter < ::DumbDelegator
|
20
|
+
def initialize(driver)
|
21
|
+
super(driver)
|
22
|
+
@driver = driver
|
22
23
|
end
|
23
|
-
end
|
24
24
|
|
25
|
-
class WatirAdapter < ::DumbDelegator
|
26
25
|
# delegate to Watir's Selenium #driver
|
27
26
|
def within_frame(frame, &block)
|
28
|
-
SeleniumAdapter.instance_method(:within_frame).bind(FrameAdapter.wrap driver).call(frame, &block)
|
27
|
+
SeleniumAdapter.instance_method(:within_frame).bind(FrameAdapter.wrap @driver).call(frame, &block)
|
29
28
|
end
|
30
29
|
|
31
30
|
def find_frames
|
32
|
-
|
31
|
+
find_elements(:css, "iframe")
|
33
32
|
end
|
34
33
|
end
|
35
34
|
|
@@ -57,6 +56,41 @@ module WebDriverScriptAdapter
|
|
57
56
|
end
|
58
57
|
end
|
59
58
|
|
59
|
+
class CapybaraAdapter < ::DumbDelegator
|
60
|
+
def initialize(driver)
|
61
|
+
super(driver)
|
62
|
+
@driver = driver
|
63
|
+
end
|
64
|
+
|
65
|
+
def within_frame(frame)
|
66
|
+
# Patch the `Symbol` class to respond to the :native method.
|
67
|
+
# Will be fixed in https://github.com/teamcapybara/capybara/pull/2462
|
68
|
+
(:parent).class.define_method(:native) do
|
69
|
+
nil
|
70
|
+
end
|
71
|
+
switch_to_frame frame
|
72
|
+
yield
|
73
|
+
ensure
|
74
|
+
begin
|
75
|
+
switch_to_frame :parent
|
76
|
+
rescue => e
|
77
|
+
if /switchToParentFrame|frame\/parent/.match(e.message)
|
78
|
+
::Kernel.warn "WARNING:
|
79
|
+
This browser only supports first-level iframes.
|
80
|
+
Second-level iframes and beyond will not be audited.
|
81
|
+
To skip auditing all iframes,
|
82
|
+
set Axe::Configuration#skip_iframes=true"
|
83
|
+
end
|
84
|
+
switch_to_frame :top
|
85
|
+
end
|
86
|
+
end
|
87
|
+
|
88
|
+
def find_frames
|
89
|
+
find_css("iframe")
|
90
|
+
end
|
91
|
+
end
|
92
|
+
|
93
|
+
|
60
94
|
# Selenium Webdriver < 2.43 doesnt support moving back to the parent
|
61
95
|
class ParentlessFrameAdapter < ::DumbDelegator
|
62
96
|
# storage of frame stack (for reverting to parent) taken from Capybara
|