Class: AxeCuprite::Injector
- Inherits:
-
Object
- Object
- AxeCuprite::Injector
- Defined in:
- lib/axe/cuprite/injector.rb
Overview
Handles getting axe-core onto the page and running it — entirely through Capybara’s driver-neutral JS API, with a Ferrum fast-path for Cuprite.
This is the make-or-break class. Two things are easy to get wrong:
1. The async-callback convention. Ferrum's `evaluate_async` (and Capybara's
`evaluate_async_script`, which Cuprite delegates to it) wraps your
expression in a Promise and appends the resolve callback as the LAST
entry of `arguments`. So we resolve via `arguments[arguments.length - 1]`.
2. The timeout. Ferrum wraps the promise in a `setTimeout(reject, wait*1000)`.
Through `evaluate_async_script` that `wait` is Capybara.default_max_wait_time
(often 2s) — far too short for `axe.run` on a real page. So on Cuprite we
call Ferrum's `page.evaluate_async(expr, explicit_wait, *args)` DIRECTLY
with our own timeout, decoupled from default_max_wait_time entirely.
Constant Summary collapse
- RUN_JS =
<<~JS var ctx = arguments[0]; var opts = arguments[1] || {}; var done = arguments[arguments.length - 1]; if (typeof window.axe === 'undefined' || typeof window.axe.run !== 'function') { done({ error: 'axe-core is not present on the page' }); return; } var promise = ctx ? window.axe.run(ctx, opts) : window.axe.run(opts); promise.then(function (results) { done({ violations: results.violations, incomplete: results.incomplete, url: results.url, timestamp: results.timestamp, testEngine: results.testEngine }); }).catch(function (err) { done({ error: (err && err.message) ? err.message : String(err) }); }); JS
- PRESENCE_JS =
JS expression that reports whether axe is loaded and runnable.
"typeof window.axe !== 'undefined' && typeof window.axe.run === 'function'"
Instance Method Summary collapse
-
#ensure_injected!(force: false) ⇒ Object
Ensure axe is present.
-
#initialize(page, configuration = AxeCuprite.configuration) ⇒ Injector
constructor
A new instance of Injector.
-
#inject_source! ⇒ Object
Inject the vendored axe-core source into the page.
-
#injected? ⇒ Boolean
Is axe-core present and runnable on the current page?.
-
#run(context:, options:, timeout: nil) ⇒ Object
Run axe and return a Results object.
Constructor Details
#initialize(page, configuration = AxeCuprite.configuration) ⇒ Injector
Returns a new instance of Injector.
49 50 51 52 |
# File 'lib/axe/cuprite/injector.rb', line 49 def initialize(page, configuration = AxeCuprite.configuration) @page = page @config = configuration end |
Instance Method Details
#ensure_injected!(force: false) ⇒ Object
Ensure axe is present. Idempotent: does nothing if already injected (so repeated assertions on one page don’t re-send ~500KB), unless force:. Returns true if it actually injected, false if it was already there.
62 63 64 65 66 67 |
# File 'lib/axe/cuprite/injector.rb', line 62 def ensure_injected!(force: false) return false if !force && injected? inject_source! true end |
#inject_source! ⇒ Object
Inject the vendored axe-core source into the page. Primary path is Capybara’s driver-neutral execute_script (which, on Cuprite, runs via CDP Runtime.evaluate and is not subject to the page’s CSP). If that path fails to land axe — e.g. a strict Content-Security-Policy — we fall back to Ferrum’s add_script_tag.
74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 |
# File 'lib/axe/cuprite/injector.rb', line 74 def inject_source! source = AxeCuprite.axe_source begin @page.execute_script(source) rescue StandardError => e raise InjectionError, "Failed to inject axe-core: #{e.}" unless try_add_script_tag(source) end return true if injected? # execute_script silently no-op'd (CSP, sandbox, ...). Try the tag fallback. if try_add_script_tag(source) && injected? true else raise InjectionError, "axe-core did not load after injection. A strict Content-Security-Policy " \ "may be blocking script injection on the page under test." end end |
#injected? ⇒ Boolean
Is axe-core present and runnable on the current page?
55 56 57 |
# File 'lib/axe/cuprite/injector.rb', line 55 def injected? @page.evaluate_script(PRESENCE_JS) == true end |
#run(context:, options:, timeout: nil) ⇒ Object
Run axe and return a Results object. Injects on demand if needed.
96 97 98 99 100 101 102 103 104 105 106 |
# File 'lib/axe/cuprite/injector.rb', line 96 def run(context:, options:, timeout: nil) timeout ||= @config.timeout ensure_present! raw = evaluate_axe(context, , timeout) if raw.is_a?(Hash) && raw["error"] raise AxeRunError, "axe.run failed: #{raw["error"]}" end Results.new(raw) end |