axe-core-api 4.2.1 → 4.3.0.pre.4296492

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
  SHA256:
3
- metadata.gz: 1bf8794e0bc1dfac77330f245362e620033433dcf549b946bebce5843681457b
4
- data.tar.gz: 3c583ccd52930208e247b97423b1b70702d0836682f1cd7e4f72e9e105cef5e4
3
+ metadata.gz: f71b2281e1eb828c1429fadfc386d28e9b5c27efdd3902d7d92474a67087eab9
4
+ data.tar.gz: 2ce1d0250dc89e3273208642d56e28eef7a3e722abfc164907464229513ef3e9
5
5
  SHA512:
6
- metadata.gz: badda2f0b6132ff4d10f021b1751ca9776e8fa0a11614deb4b5489b70478f4ff83845509137dc06479ae9e20693a9f1c348d89ba44535a5e9d6c4b780699da1c
7
- data.tar.gz: 708864e46c1f14803907f4afad28545b83a93c10d7842374ab9b6153364835e3b6519f841b1a13d7d12e68c6d5a2e609ebc6b31fdc18eb18ce9a2859b1770e21
6
+ metadata.gz: 2c35ca71f95034e3c4416673c6ceb8e49fd693dc35f51838709881b97ffe05362eaa34f4d770205dcd3f7604c482a7643c22bc6951cbe0156cc191fed630ef6c
7
+ data.tar.gz: 94dd617cdd382adb01f3ec822512ec3f25b97faacf1aa391077981b9353ef6a32495e9d1695f6d1436df36591f0c602373dc2cdecba8e3139fea3d5596e0a1d8
@@ -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
- { include: @inclusion, exclude: @exclusion }
21
- .reject { |k, v| v.empty? }
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?
@@ -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?
@@ -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,182 @@ module Axe
27
27
  end
28
28
 
29
29
  def call(page)
30
- audit page do |results|
31
- Audit.new to_js, Results.new(results)
32
- end
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
+ begin
41
+ axe_finish_run page, partial_results
42
+ rescue
43
+ raise StandardError.new "axe.finishRun failed. Please check out https://github.com/dequelabs/axe-core-gems/error-handling.md`"
44
+ end
45
+ }
46
+ Audit.new to_js, Results.new(results)
33
47
  end
34
48
 
35
49
  private
36
50
 
37
51
  def audit(page)
38
- yield page.execute_async_script "#{METHOD_NAME}.apply(#{Core::JS_NAME}, arguments)", *js_args
52
+ script = <<-JS
53
+ var callback = arguments[arguments.length - 1];
54
+ var context = arguments[0] || document;
55
+ var options = arguments[1] || {};
56
+ #{METHOD_NAME}(context, options).then(callback);
57
+ JS
58
+ page.execute_async_script_fixed script, *js_args
59
+ end
60
+
61
+ def switch_to_frame_by_handle(page, handle)
62
+ page = get_selenium page
63
+ page.switch_to.frame handle
64
+ end
65
+
66
+ def switch_to_parent_frame(page)
67
+ page = get_selenium page
68
+ page.switch_to.parent_frame
69
+ end
70
+
71
+ def within_about_blank_context(page)
72
+ driver = get_selenium page
73
+
74
+ num_handles = page.window_handles.length
75
+ begin
76
+ driver.execute_script("window.open('about:blank'), '_blank'")
77
+ if num_handles == page.window_handles.length
78
+ raise StandardError.new "Could not open new window. Please make sure that you have popup blockers disabled."
79
+ end
80
+ driver.switch_to.window page.window_handles[-1]
81
+ rescue
82
+ raise StandardError.new "switchToWindow failed. Are you using updated browser drivers?"
83
+ end
84
+ driver.get "about:blank"
85
+
86
+ ret = yield page
87
+
88
+ driver.switch_to.window page.window_handles[-1]
89
+ driver.close
90
+ driver.switch_to.window @original_window
91
+
92
+ ret
93
+ end
94
+ def window_handle(page)
95
+ page = get_selenium page
96
+
97
+ return page.window_handle if page.respond_to?("window_handle")
98
+ page.current_window_handle
99
+ end
100
+
101
+ def run_partial_recursive(page, context, lib, top_level = false)
102
+ begin
103
+ if not top_level
104
+ begin
105
+ Common::Loader.new(page, lib).load_top_level Axe::Configuration.instance.jslib
106
+ rescue
107
+ return [nil]
108
+ end
109
+
110
+ end
111
+
112
+ frame_contexts = get_frame_context_script page
113
+ if frame_contexts.respond_to?("key?") and frame_contexts.key?("errorMessage")
114
+ throw frame_contexts if top_level
115
+ return [nil]
116
+ end
117
+
118
+ res = axe_run_partial page, context
119
+ if res.key?("errorMessage")
120
+ throw res if top_level
121
+ return [nil]
122
+ else
123
+ results = [res]
124
+ end
125
+
126
+ for frame_context in frame_contexts
127
+ frame_selector = frame_context["frameSelector"]
128
+ frame_context = frame_context["frameContext"]
129
+ frame = axe_shadow_select page, frame_selector
130
+ switch_to_frame_by_handle page, frame
131
+ res = run_partial_recursive page, frame_context, lib
132
+ results += res
133
+ end
134
+
135
+ ensure
136
+ switch_to_parent_frame page if not top_level
137
+ end
138
+ return results
139
+ end
140
+
141
+ def axe_finish_run(page, partial_results)
142
+ script = <<-JS
143
+ const partialResults = arguments[0];
144
+ return axe.finishRun(partialResults);
145
+ JS
146
+ page.execute_script_fixed script, partial_results
147
+ end
148
+
149
+ def axe_shadow_select(page, frame_selector)
150
+ script = <<-JS
151
+ const frameSelector = arguments[0];
152
+ return axe.utils.shadowSelect(frameSelector);
153
+ JS
154
+ page.execute_script_fixed script, frame_selector
155
+ end
156
+
157
+ def axe_run_partial(page, context)
158
+ script = <<-JS
159
+ const context = arguments[0];
160
+ const options = arguments[1];
161
+ const cb = arguments[arguments.length - 1];
162
+ try {
163
+ const ret = window.axe.runPartial(context, options);
164
+ cb(ret);
165
+ } catch (err) {
166
+ const ret = {
167
+ violations: [],
168
+ passes: [],
169
+ url: '',
170
+ timestamp: new Date().toString(),
171
+ errorMessage: err.message
172
+ };
173
+ cb(ret);
174
+ }
175
+ JS
176
+ page.execute_async_script_fixed script, context, @options
177
+ end
178
+
179
+ def get_frame_context_script(page)
180
+ script = <<-JS
181
+ const context = arguments[0];
182
+ try {
183
+ return window.axe.utils.getFrameContexts(context);
184
+ } catch (err) {
185
+ return {
186
+ violations: [],
187
+ passes: [],
188
+ url: '',
189
+ timestamp: new Date().toString(),
190
+ errorMessage: err.message
191
+ };
192
+ }
193
+ JS
194
+ page.execute_script_fixed script, @context
195
+ end
196
+
197
+ def get_selenium(page)
198
+ page = page.driver if page.respond_to?("driver")
199
+ page = page.browser if page.respond_to?("browser") and not page.browser.is_a?(::Symbol)
200
+ page
39
201
  end
40
202
 
41
203
  def js_args
42
204
  [@context, @options]
43
- .reject(&:empty?)
44
- .map(&:to_json)
205
+ .map(&:to_h)
45
206
  end
46
207
 
47
208
  def to_js
@@ -14,7 +14,8 @@ module Axe
14
14
  attr_writer :jslib
15
15
  attr_accessor :page,
16
16
  :jslib_path,
17
- :skip_iframes
17
+ :skip_iframes,
18
+ :legacy_mode
18
19
  def_delegators ::WebDriverScriptAdapter,
19
20
  :async_results_identifier,
20
21
  :async_results_identifier=,
data/lib/axe/core.rb CHANGED
@@ -14,13 +14,36 @@ module Axe
14
14
  end
15
15
 
16
16
  def call(callable)
17
- callable.call(@page)
17
+ if use_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
 
30
+ def use_run_partial
31
+ has_run_partial? and not Axe::Configuration.instance.legacy_mode
32
+ end
33
+
22
34
  def load_axe_core(source)
23
- Common::Loader.new(@page, self).call(source) unless already_loaded?
35
+ return if already_loaded?
36
+ loader = Common::Loader.new(@page, self)
37
+ loader.load_top_level source
38
+ return if use_run_partial
39
+
40
+ loader.call source
41
+ end
42
+
43
+ def has_run_partial?
44
+ @page.evaluate_script <<-JS
45
+ typeof window.axe.runPartial === 'function'
46
+ JS
24
47
  end
25
48
 
26
49
  def already_loaded?
data/lib/loader.rb CHANGED
@@ -6,20 +6,33 @@ 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 call(source)
12
+ def load_top_level(source)
12
13
  @page.execute_script source
13
- @page.execute_script "axe.configure({ allowedOrigins: ['<unsafe_all_origins>'] });"
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
+ set_allowed_origins
14
21
  Common::Hooks.run_after_load @lib
15
22
  load_into_iframes(source) unless Axe::Configuration.instance.skip_iframes
16
23
  end
17
24
 
18
25
  private
19
26
 
27
+ def set_allowed_origins
28
+ allowed_origins = "<unsafe_all_origins>"
29
+ allowed_origins = "<same_origin>" if Axe::Configuration.instance.legacy_mode
30
+ @page.execute_script "axe.configure({ allowedOrigins: ['#{allowed_origins}'] });"
31
+ end
32
+
20
33
  def load_into_iframes(source)
21
34
  @page.find_frames.each do |iframe|
22
- @page.within_frame(iframe) { call source }
35
+ @page.within_frame(iframe) { call source, false }
23
36
  end
24
37
  end
25
38
  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 ExecEvalScriptAdapter.wrap driver
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|