axe-cuprite 0.2.0 → 1.0.0
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/CHANGELOG.md +48 -0
- data/README.md +46 -7
- data/lib/axe/cuprite/results.rb +7 -0
- data/lib/axe/cuprite/runner.rb +1 -5
- data/lib/axe/cuprite/version.rb +1 -1
- data/lib/axe/cuprite.rb +4 -2
- data/lib/axe-cuprite.rb +0 -2
- metadata +5 -156
- data/lib/axe/cuprite/tasks/axe.rake +0 -169
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: e56b57fadf23c8ab623bd1a3005d94ce158832160a9ec3bfd1591b070312d953
|
|
4
|
+
data.tar.gz: 2cdad2a18469b669266938df7476040d702b53419bb8a80076e918c282bf218e
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 2262ff93c4a88cda10c54d650b74f9353d8d1ae98f0f0a4df82b1483cc755e6347e23be923dedee4fffa726de3229ae4f4e9a00d18f704750f21513038d2515e
|
|
7
|
+
data.tar.gz: 8f001777544278d53266271b66c9d93bb32e0ed2d9e8969fa2413d69342649028be46c78c40706ec526e863c2fa13106a451856b7dfa7db8d23e0852b3367a9f
|
data/CHANGELOG.md
CHANGED
|
@@ -7,6 +7,48 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
7
7
|
|
|
8
8
|
## [Unreleased]
|
|
9
9
|
|
|
10
|
+
## [1.0.0] - 2026-06-16
|
|
11
|
+
|
|
12
|
+
First stable release. No functional changes since `0.2.0` — this tags the API as
|
|
13
|
+
stable under [Semantic Versioning](https://semver.org/spec/v2.0.0.html). The
|
|
14
|
+
public API is the documented surface of `AxeCuprite`, `AxeCuprite::Configuration`,
|
|
15
|
+
`AxeCuprite::Runner`, the result wrappers (`Results`/`Violation`/`Node`/
|
|
16
|
+
`ContrastData`), the error classes, and the RSpec matcher DSL; classes such as
|
|
17
|
+
`AxeCuprite::Injector` and `AxeCuprite::DeepFreeze` are internal and not covered
|
|
18
|
+
by the stability guarantee.
|
|
19
|
+
|
|
20
|
+
### Fixed
|
|
21
|
+
- The packaged gem no longer ships the dev-only vendoring rake tasks
|
|
22
|
+
(`lib/axe/cuprite/tasks/*.rake` — `axe:update` / `axe:verify` / `axe:version`).
|
|
23
|
+
These operate on the gem's read-only, version-pinned `vendor/` directory and
|
|
24
|
+
`axe:verify` couldn't even run from an installed gem (its recorded checksum
|
|
25
|
+
isn't shipped), so they were broken for consumers. They remain available in a
|
|
26
|
+
repo checkout for development
|
|
27
|
+
([#32](https://github.com/Guided-Rails/axe-cuprite/issues/32)).
|
|
28
|
+
|
|
29
|
+
### Changed
|
|
30
|
+
- Repositioned as **Cuprite-only**: the README, gemspec `description`, and
|
|
31
|
+
`AGENTS.md` no longer advertise support for "any real-browser Capybara driver."
|
|
32
|
+
Cuprite is the only supported and tested driver; the driver-neutral JS approach
|
|
33
|
+
is described as the implementation detail that makes the gem work on Cuprite
|
|
34
|
+
(and avoid Selenium internals), not a promise of broad driver support. The
|
|
35
|
+
best-effort non-Ferrum fallback in `Injector#evaluate_axe` is kept but is now
|
|
36
|
+
documented as unsupported/untested
|
|
37
|
+
([#31](https://github.com/Guided-Rails/axe-cuprite/issues/31)).
|
|
38
|
+
- Documented that `AxeCuprite::Results` (and its nested wrappers) takes ownership
|
|
39
|
+
of the hash passed in and deep-freezes it **in place** — so a caller
|
|
40
|
+
constructing `Results.new(hash)` shouldn't pass or reuse a hash they intend to
|
|
41
|
+
mutate afterward. No behavior change; clarifies the existing read-only contract
|
|
42
|
+
in the `Results` doc comment and the README runner section
|
|
43
|
+
([#33](https://github.com/Guided-Rails/axe-cuprite/issues/33)).
|
|
44
|
+
|
|
45
|
+
### Added
|
|
46
|
+
- A unit test pinning the best-effort non-Ferrum fallback in
|
|
47
|
+
`Injector#evaluate_axe`: with no Ferrum page it routes through
|
|
48
|
+
`evaluate_async_script` under the explicit timeout rather than
|
|
49
|
+
`Capybara.default_max_wait_time`, so the path can't silently break
|
|
50
|
+
([#31](https://github.com/Guided-Rails/axe-cuprite/issues/31)).
|
|
51
|
+
|
|
10
52
|
## [0.2.0] - 2026-06-10
|
|
11
53
|
|
|
12
54
|
### Added
|
|
@@ -97,3 +139,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
97
139
|
auto-inject toggle, and report-only mode.
|
|
98
140
|
- `rake axe:update[VERSION]` to refresh the vendored axe-core engine and bump
|
|
99
141
|
the `AXE_CORE_VERSION` constant.
|
|
142
|
+
|
|
143
|
+
[Unreleased]: https://github.com/Guided-Rails/axe-cuprite/compare/v1.0.0...HEAD
|
|
144
|
+
[1.0.0]: https://github.com/Guided-Rails/axe-cuprite/compare/v0.2.0...v1.0.0
|
|
145
|
+
[0.2.0]: https://github.com/Guided-Rails/axe-cuprite/compare/v0.1.1...v0.2.0
|
|
146
|
+
[0.1.1]: https://github.com/Guided-Rails/axe-cuprite/compare/v0.1.0...v0.1.1
|
|
147
|
+
[0.1.0]: https://github.com/Guided-Rails/axe-cuprite/releases/tag/v0.1.0
|
data/README.md
CHANGED
|
@@ -1,5 +1,9 @@
|
|
|
1
1
|
# axe-cuprite
|
|
2
2
|
|
|
3
|
+
[](https://rubygems.org/gems/axe-cuprite)
|
|
4
|
+
[](https://github.com/Guided-Rails/axe-cuprite/actions/workflows/ci.yml)
|
|
5
|
+
[](LICENSE.txt)
|
|
6
|
+
|
|
3
7
|
Run the [axe-core](https://github.com/dequelabs/axe-core) accessibility engine
|
|
4
8
|
against pages in your **Capybara system/feature tests driven by
|
|
5
9
|
[Cuprite](https://github.com/rubycdp/cuprite)** (the CDP/Ferrum headless-Chrome
|
|
@@ -23,13 +27,24 @@ they break.
|
|
|
23
27
|
just a JavaScript file — a driver-agnostic engine — so we drive it exclusively
|
|
24
28
|
through Capybara's **driver-neutral** JavaScript API (`execute_script`,
|
|
25
29
|
`evaluate_async_script`), which Cuprite fully implements. That single decision is
|
|
26
|
-
what makes this gem work where the official one doesn't.
|
|
27
|
-
|
|
28
|
-
the
|
|
30
|
+
what makes this gem work where the official one doesn't.
|
|
31
|
+
|
|
32
|
+
**Cuprite is the only supported and tested driver.** Driving axe through the
|
|
33
|
+
driver-neutral JS API is what lets the gem avoid Selenium internals — not a promise
|
|
34
|
+
of broad driver support. Other real-browser drivers may happen to work (a
|
|
35
|
+
best-effort fallback exists), but they are unsupported and untested.
|
|
29
36
|
|
|
30
37
|
There is no runtime dependency on Selenium, Cuprite, or Ferrum — the only runtime
|
|
31
38
|
dependency is Capybara. You bring your own driver.
|
|
32
39
|
|
|
40
|
+
## Requirements
|
|
41
|
+
|
|
42
|
+
- **Ruby** >= 3.0
|
|
43
|
+
- **Capybara** >= 3.0, < 4.0 (the only runtime dependency)
|
|
44
|
+
- **[Cuprite](https://github.com/rubycdp/cuprite)** as the Capybara driver — the
|
|
45
|
+
only supported and tested driver
|
|
46
|
+
- **Chrome or Chromium**, which Cuprite drives over CDP
|
|
47
|
+
|
|
33
48
|
## Installation
|
|
34
49
|
|
|
35
50
|
```ruby
|
|
@@ -146,6 +161,13 @@ on top of your configured defaults. Only `violations` and `incomplete` are
|
|
|
146
161
|
carried back across the CDP boundary — the full results object (with
|
|
147
162
|
`passes`/`inapplicable`) can be huge.
|
|
148
163
|
|
|
164
|
+
`AxeCuprite::Results` is read-only: it **takes ownership of the hash it's given
|
|
165
|
+
and deep-freezes it in place** (this backs the read-only `raw`/`to_h` contract).
|
|
166
|
+
You normally get one straight from `Runner#run`, so this is transparent — but if
|
|
167
|
+
you construct `Results.new(hash)` yourself, don't pass or keep a reference to a
|
|
168
|
+
hash you intend to mutate afterward, or you'll hit a `FrozenError`. Dup it first
|
|
169
|
+
if you need a mutable copy.
|
|
170
|
+
|
|
149
171
|
## Configuration
|
|
150
172
|
|
|
151
173
|
```ruby
|
|
@@ -197,10 +219,11 @@ page routinely exceeds.
|
|
|
197
219
|
axe-cuprite avoids this trap: on Cuprite it calls Ferrum's
|
|
198
220
|
`page.evaluate_async(script, explicit_wait, *args)` **directly**, with its own
|
|
199
221
|
timeout (default **30s**, configurable) that is completely **decoupled from
|
|
200
|
-
`default_max_wait_time`**. On non-Ferrum
|
|
201
|
-
`evaluate_async_script` under a temporarily-raised wait time
|
|
202
|
-
doesn't finish, you get a
|
|
203
|
-
the timeout or scope the run
|
|
222
|
+
`default_max_wait_time`**. (On a non-Ferrum driver there is a best-effort,
|
|
223
|
+
untested fallback to `evaluate_async_script` under a temporarily-raised wait time —
|
|
224
|
+
but Cuprite is the only supported driver.) If axe still doesn't finish, you get a
|
|
225
|
+
clear `AxeCuprite::TimeoutError` telling you to raise the timeout or scope the run
|
|
226
|
+
with `.within`.
|
|
204
227
|
|
|
205
228
|
Tune it globally (`c.timeout = 60`) or per assertion (`.with_timeout(60)`).
|
|
206
229
|
|
|
@@ -252,6 +275,22 @@ sha512 of the vendored engine is recorded in
|
|
|
252
275
|
with `rake axe:verify` (CI does this on every run). Note the bump in
|
|
253
276
|
`CHANGELOG.md`.
|
|
254
277
|
|
|
278
|
+
## Versioning
|
|
279
|
+
|
|
280
|
+
axe-cuprite follows [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
281
|
+
The **public API** covered by that guarantee is the documented surface:
|
|
282
|
+
`AxeCuprite` (module methods), `AxeCuprite::Configuration`, `AxeCuprite::Runner`,
|
|
283
|
+
the result wrappers (`Results` / `Violation` / `Node` / `ContrastData`), the error
|
|
284
|
+
classes, and the RSpec matcher DSL (`be_axe_clean` / `be_accessible` and its
|
|
285
|
+
chainers). Classes like `AxeCuprite::Injector` and `AxeCuprite::DeepFreeze` are
|
|
286
|
+
internal implementation details and may change in any release. Breaking changes to
|
|
287
|
+
the public API only land in a major version, and every release is noted in
|
|
288
|
+
[`CHANGELOG.md`](CHANGELOG.md).
|
|
289
|
+
|
|
290
|
+
Note that the **vendored axe-core version** can change in a minor release; axe may
|
|
291
|
+
report new violations after an engine bump, which can turn a previously-green
|
|
292
|
+
suite red. Such bumps are always called out in the changelog.
|
|
293
|
+
|
|
255
294
|
## Licensing
|
|
256
295
|
|
|
257
296
|
- **axe-cuprite's own code** is licensed under the **MIT** license — see
|
data/lib/axe/cuprite/results.rb
CHANGED
|
@@ -26,6 +26,13 @@ module AxeCuprite
|
|
|
26
26
|
#
|
|
27
27
|
# The wrappers are read-only: `@raw` is deep-frozen at construction, so `raw`
|
|
28
28
|
# and `to_h` expose the live underlying hash safely (callers cannot mutate it).
|
|
29
|
+
#
|
|
30
|
+
# Ownership note: `Results` (and the nested `Violation`/`Node`/`ContrastData`
|
|
31
|
+
# wrappers) **take ownership of the hash passed in and deep-freeze it in place**
|
|
32
|
+
# — they do not copy first. For the internal flow this is safe (the payload
|
|
33
|
+
# comes fresh off the CDP boundary), but if you construct `Results.new(hash)`
|
|
34
|
+
# yourself, don't pass — or hold onto — a hash you intend to mutate afterward,
|
|
35
|
+
# or you'll hit a `FrozenError`. Dup it first if you need a mutable copy.
|
|
29
36
|
class Results
|
|
30
37
|
attr_reader :raw
|
|
31
38
|
|
data/lib/axe/cuprite/runner.rb
CHANGED
|
@@ -73,11 +73,7 @@ module AxeCuprite
|
|
|
73
73
|
# axe accepts a selector string, or {include:, exclude:}. Stringify hash
|
|
74
74
|
# keys so Ferrum serializes them predictably across CDP.
|
|
75
75
|
def normalize_context(context)
|
|
76
|
-
|
|
77
|
-
when nil, String then context
|
|
78
|
-
when Hash then deep_stringify(context)
|
|
79
|
-
else context
|
|
80
|
-
end
|
|
76
|
+
context.is_a?(Hash) ? deep_stringify(context) : context
|
|
81
77
|
end
|
|
82
78
|
|
|
83
79
|
def deep_stringify(value)
|
data/lib/axe/cuprite/version.rb
CHANGED
data/lib/axe/cuprite.rb
CHANGED
|
@@ -8,8 +8,10 @@ require "axe/cuprite/injector"
|
|
|
8
8
|
require "axe/cuprite/runner"
|
|
9
9
|
|
|
10
10
|
# axe-cuprite drives the axe-core accessibility engine through Capybara's
|
|
11
|
-
# driver-neutral JavaScript API
|
|
12
|
-
#
|
|
11
|
+
# driver-neutral JavaScript API. That approach is what lets it avoid Selenium
|
|
12
|
+
# internals and work on Cuprite (Ferrum/CDP) — the only supported and tested
|
|
13
|
+
# driver. Other real-browser drivers may work via a best-effort, untested
|
|
14
|
+
# fallback, but they are unsupported.
|
|
13
15
|
#
|
|
14
16
|
# Quick start:
|
|
15
17
|
#
|
data/lib/axe-cuprite.rb
CHANGED
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: axe-cuprite
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.
|
|
4
|
+
version: 1.0.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Abdullah Hashim
|
|
@@ -29,165 +29,13 @@ dependencies:
|
|
|
29
29
|
- - "<"
|
|
30
30
|
- !ruby/object:Gem::Version
|
|
31
31
|
version: '4.0'
|
|
32
|
-
- !ruby/object:Gem::Dependency
|
|
33
|
-
name: cuprite
|
|
34
|
-
requirement: !ruby/object:Gem::Requirement
|
|
35
|
-
requirements:
|
|
36
|
-
- - "~>"
|
|
37
|
-
- !ruby/object:Gem::Version
|
|
38
|
-
version: '0.17'
|
|
39
|
-
type: :development
|
|
40
|
-
prerelease: false
|
|
41
|
-
version_requirements: !ruby/object:Gem::Requirement
|
|
42
|
-
requirements:
|
|
43
|
-
- - "~>"
|
|
44
|
-
- !ruby/object:Gem::Version
|
|
45
|
-
version: '0.17'
|
|
46
|
-
- !ruby/object:Gem::Dependency
|
|
47
|
-
name: ferrum
|
|
48
|
-
requirement: !ruby/object:Gem::Requirement
|
|
49
|
-
requirements:
|
|
50
|
-
- - "~>"
|
|
51
|
-
- !ruby/object:Gem::Version
|
|
52
|
-
version: '0.17'
|
|
53
|
-
type: :development
|
|
54
|
-
prerelease: false
|
|
55
|
-
version_requirements: !ruby/object:Gem::Requirement
|
|
56
|
-
requirements:
|
|
57
|
-
- - "~>"
|
|
58
|
-
- !ruby/object:Gem::Version
|
|
59
|
-
version: '0.17'
|
|
60
|
-
- !ruby/object:Gem::Dependency
|
|
61
|
-
name: puma
|
|
62
|
-
requirement: !ruby/object:Gem::Requirement
|
|
63
|
-
requirements:
|
|
64
|
-
- - ">="
|
|
65
|
-
- !ruby/object:Gem::Version
|
|
66
|
-
version: '5.0'
|
|
67
|
-
- - "<"
|
|
68
|
-
- !ruby/object:Gem::Version
|
|
69
|
-
version: '9.0'
|
|
70
|
-
type: :development
|
|
71
|
-
prerelease: false
|
|
72
|
-
version_requirements: !ruby/object:Gem::Requirement
|
|
73
|
-
requirements:
|
|
74
|
-
- - ">="
|
|
75
|
-
- !ruby/object:Gem::Version
|
|
76
|
-
version: '5.0'
|
|
77
|
-
- - "<"
|
|
78
|
-
- !ruby/object:Gem::Version
|
|
79
|
-
version: '9.0'
|
|
80
|
-
- !ruby/object:Gem::Dependency
|
|
81
|
-
name: rack
|
|
82
|
-
requirement: !ruby/object:Gem::Requirement
|
|
83
|
-
requirements:
|
|
84
|
-
- - ">="
|
|
85
|
-
- !ruby/object:Gem::Version
|
|
86
|
-
version: '2.0'
|
|
87
|
-
- - "<"
|
|
88
|
-
- !ruby/object:Gem::Version
|
|
89
|
-
version: '4.0'
|
|
90
|
-
type: :development
|
|
91
|
-
prerelease: false
|
|
92
|
-
version_requirements: !ruby/object:Gem::Requirement
|
|
93
|
-
requirements:
|
|
94
|
-
- - ">="
|
|
95
|
-
- !ruby/object:Gem::Version
|
|
96
|
-
version: '2.0'
|
|
97
|
-
- - "<"
|
|
98
|
-
- !ruby/object:Gem::Version
|
|
99
|
-
version: '4.0'
|
|
100
|
-
- !ruby/object:Gem::Dependency
|
|
101
|
-
name: rackup
|
|
102
|
-
requirement: !ruby/object:Gem::Requirement
|
|
103
|
-
requirements:
|
|
104
|
-
- - "~>"
|
|
105
|
-
- !ruby/object:Gem::Version
|
|
106
|
-
version: '2.0'
|
|
107
|
-
type: :development
|
|
108
|
-
prerelease: false
|
|
109
|
-
version_requirements: !ruby/object:Gem::Requirement
|
|
110
|
-
requirements:
|
|
111
|
-
- - "~>"
|
|
112
|
-
- !ruby/object:Gem::Version
|
|
113
|
-
version: '2.0'
|
|
114
|
-
- !ruby/object:Gem::Dependency
|
|
115
|
-
name: rake
|
|
116
|
-
requirement: !ruby/object:Gem::Requirement
|
|
117
|
-
requirements:
|
|
118
|
-
- - "~>"
|
|
119
|
-
- !ruby/object:Gem::Version
|
|
120
|
-
version: '13.0'
|
|
121
|
-
type: :development
|
|
122
|
-
prerelease: false
|
|
123
|
-
version_requirements: !ruby/object:Gem::Requirement
|
|
124
|
-
requirements:
|
|
125
|
-
- - "~>"
|
|
126
|
-
- !ruby/object:Gem::Version
|
|
127
|
-
version: '13.0'
|
|
128
|
-
- !ruby/object:Gem::Dependency
|
|
129
|
-
name: rspec
|
|
130
|
-
requirement: !ruby/object:Gem::Requirement
|
|
131
|
-
requirements:
|
|
132
|
-
- - "~>"
|
|
133
|
-
- !ruby/object:Gem::Version
|
|
134
|
-
version: '3.0'
|
|
135
|
-
type: :development
|
|
136
|
-
prerelease: false
|
|
137
|
-
version_requirements: !ruby/object:Gem::Requirement
|
|
138
|
-
requirements:
|
|
139
|
-
- - "~>"
|
|
140
|
-
- !ruby/object:Gem::Version
|
|
141
|
-
version: '3.0'
|
|
142
|
-
- !ruby/object:Gem::Dependency
|
|
143
|
-
name: rubocop
|
|
144
|
-
requirement: !ruby/object:Gem::Requirement
|
|
145
|
-
requirements:
|
|
146
|
-
- - "~>"
|
|
147
|
-
- !ruby/object:Gem::Version
|
|
148
|
-
version: '1.86'
|
|
149
|
-
type: :development
|
|
150
|
-
prerelease: false
|
|
151
|
-
version_requirements: !ruby/object:Gem::Requirement
|
|
152
|
-
requirements:
|
|
153
|
-
- - "~>"
|
|
154
|
-
- !ruby/object:Gem::Version
|
|
155
|
-
version: '1.86'
|
|
156
|
-
- !ruby/object:Gem::Dependency
|
|
157
|
-
name: rubocop-rake
|
|
158
|
-
requirement: !ruby/object:Gem::Requirement
|
|
159
|
-
requirements:
|
|
160
|
-
- - "~>"
|
|
161
|
-
- !ruby/object:Gem::Version
|
|
162
|
-
version: '0.7'
|
|
163
|
-
type: :development
|
|
164
|
-
prerelease: false
|
|
165
|
-
version_requirements: !ruby/object:Gem::Requirement
|
|
166
|
-
requirements:
|
|
167
|
-
- - "~>"
|
|
168
|
-
- !ruby/object:Gem::Version
|
|
169
|
-
version: '0.7'
|
|
170
|
-
- !ruby/object:Gem::Dependency
|
|
171
|
-
name: rubocop-rspec
|
|
172
|
-
requirement: !ruby/object:Gem::Requirement
|
|
173
|
-
requirements:
|
|
174
|
-
- - "~>"
|
|
175
|
-
- !ruby/object:Gem::Version
|
|
176
|
-
version: '3.0'
|
|
177
|
-
type: :development
|
|
178
|
-
prerelease: false
|
|
179
|
-
version_requirements: !ruby/object:Gem::Requirement
|
|
180
|
-
requirements:
|
|
181
|
-
- - "~>"
|
|
182
|
-
- !ruby/object:Gem::Version
|
|
183
|
-
version: '3.0'
|
|
184
32
|
description: |
|
|
185
33
|
axe-cuprite runs the axe-core accessibility engine against pages rendered in
|
|
186
34
|
Capybara system/feature tests and exposes the results as RSpec matchers
|
|
187
35
|
(be_axe_clean / be_accessible). Unlike Deque's official axe-core-capybara gem,
|
|
188
36
|
it never touches Selenium-specific driver internals: axe is driven entirely
|
|
189
|
-
through Capybara's driver-neutral JavaScript API,
|
|
190
|
-
|
|
37
|
+
through Capybara's driver-neutral JavaScript API, which is what makes it work
|
|
38
|
+
on Cuprite. Cuprite is the only supported and tested driver.
|
|
191
39
|
email:
|
|
192
40
|
- abdullah@guidedrails.com
|
|
193
41
|
executables: []
|
|
@@ -207,7 +55,6 @@ files:
|
|
|
207
55
|
- lib/axe/cuprite/rspec.rb
|
|
208
56
|
- lib/axe/cuprite/rspec/matchers.rb
|
|
209
57
|
- lib/axe/cuprite/runner.rb
|
|
210
|
-
- lib/axe/cuprite/tasks/axe.rake
|
|
211
58
|
- lib/axe/cuprite/vendor/axe-core-LICENSE.txt
|
|
212
59
|
- lib/axe/cuprite/vendor/axe.min.js
|
|
213
60
|
- lib/axe/cuprite/version.rb
|
|
@@ -217,6 +64,8 @@ licenses:
|
|
|
217
64
|
metadata:
|
|
218
65
|
source_code_uri: https://github.com/Guided-Rails/axe-cuprite
|
|
219
66
|
changelog_uri: https://github.com/Guided-Rails/axe-cuprite/blob/main/CHANGELOG.md
|
|
67
|
+
bug_tracker_uri: https://github.com/Guided-Rails/axe-cuprite/issues
|
|
68
|
+
documentation_uri: https://github.com/Guided-Rails/axe-cuprite/blob/main/README.md
|
|
220
69
|
rubygems_mfa_required: 'true'
|
|
221
70
|
rdoc_options: []
|
|
222
71
|
require_paths:
|
|
@@ -1,169 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
require "digest"
|
|
4
|
-
require "fileutils"
|
|
5
|
-
require "json"
|
|
6
|
-
require "open-uri"
|
|
7
|
-
require "rubygems/package"
|
|
8
|
-
require "stringio"
|
|
9
|
-
require "zlib"
|
|
10
|
-
|
|
11
|
-
namespace :axe do
|
|
12
|
-
desc "Refresh the vendored axe-core engine. Usage: rake 'axe:update[4.12.0]' or VERSION=4.12.0 rake axe:update (default: latest)"
|
|
13
|
-
task :update, [:version] do |_task, args|
|
|
14
|
-
version = args[:version] || ENV["VERSION"] || AxeCupriteVendor.latest_version
|
|
15
|
-
AxeCupriteVendor.update!(version)
|
|
16
|
-
end
|
|
17
|
-
|
|
18
|
-
desc "Print the currently vendored axe-core version"
|
|
19
|
-
task :version do
|
|
20
|
-
puts AxeCupriteVendor.vendored_version
|
|
21
|
-
end
|
|
22
|
-
|
|
23
|
-
desc "Verify the vendored axe.min.js against its recorded sha512 checksum"
|
|
24
|
-
task :verify do
|
|
25
|
-
AxeCupriteVendor.verify_vendored!
|
|
26
|
-
end
|
|
27
|
-
end
|
|
28
|
-
|
|
29
|
-
# Helpers for vendoring axe-core. Kept in a module so the rake tasks stay thin
|
|
30
|
-
# and the logic is easy to read. Never used at runtime — vendoring is a
|
|
31
|
-
# development-time step; the engine ships in the gem.
|
|
32
|
-
#
|
|
33
|
-
# Supply-chain note: the vendored axe.min.js is the JavaScript this gem injects
|
|
34
|
-
# into every consumer's browser session, so its integrity is the gem's most
|
|
35
|
-
# important supply-chain property. We therefore download the *official npm
|
|
36
|
-
# tarball* from registry.npmjs.org (not a CDN re-serving it), verify it against
|
|
37
|
-
# the sha512 integrity the registry publishes in its metadata, and abort on any
|
|
38
|
-
# mismatch before a single byte is written. The verified content hash of the
|
|
39
|
-
# extracted axe.min.js is recorded next to it (axe.min.js.sha512) so reviewers
|
|
40
|
-
# and CI can re-check the vendored artifact without re-downloading.
|
|
41
|
-
module AxeCupriteVendor
|
|
42
|
-
VENDOR_DIR = File.expand_path("../vendor", __dir__)
|
|
43
|
-
AXE_JS = File.join(VENDOR_DIR, "axe.min.js")
|
|
44
|
-
AXE_SHA512 = File.join(VENDOR_DIR, "axe.min.js.sha512")
|
|
45
|
-
AXE_LICENSE = File.join(VENDOR_DIR, "axe-core-LICENSE.txt")
|
|
46
|
-
VERSION_FILE = File.expand_path("../version.rb", __dir__)
|
|
47
|
-
REGISTRY = "https://registry.npmjs.org/axe-core"
|
|
48
|
-
|
|
49
|
-
module_function
|
|
50
|
-
|
|
51
|
-
# A semver-ish version string: three dot-separated numbers with an optional
|
|
52
|
-
# prerelease suffix. Rejecting anything else keeps the value safe to splice
|
|
53
|
-
# into the registry URL and into version.rb, and catches honest typos before
|
|
54
|
-
# they corrupt the vendor directory.
|
|
55
|
-
VERSION_FORMAT = /\A\d+\.\d+\.\d+(-[\w.]+)?\z/
|
|
56
|
-
|
|
57
|
-
def update!(version)
|
|
58
|
-
abort "Invalid version: #{version.inspect}" unless version.match?(VERSION_FORMAT)
|
|
59
|
-
|
|
60
|
-
FileUtils.mkdir_p(VENDOR_DIR)
|
|
61
|
-
|
|
62
|
-
dist = registry_dist(version)
|
|
63
|
-
puts "Downloading #{dist.fetch("tarball")} ..."
|
|
64
|
-
tarball = download(dist.fetch("tarball"))
|
|
65
|
-
verify_tarball!(tarball, dist)
|
|
66
|
-
|
|
67
|
-
js, license = extract(tarball, "package/axe.min.js", "package/LICENSE")
|
|
68
|
-
verify_banner!(js, version)
|
|
69
|
-
|
|
70
|
-
File.write(AXE_JS, js)
|
|
71
|
-
File.write(AXE_LICENSE, license)
|
|
72
|
-
File.write(AXE_SHA512, "#{Digest::SHA512.hexdigest(js)} axe.min.js\n")
|
|
73
|
-
bump_version_constant(version)
|
|
74
|
-
|
|
75
|
-
report(version, js)
|
|
76
|
-
end
|
|
77
|
-
|
|
78
|
-
def latest_version
|
|
79
|
-
JSON.parse(download("#{REGISTRY}/latest")).fetch("version")
|
|
80
|
-
end
|
|
81
|
-
|
|
82
|
-
def vendored_version
|
|
83
|
-
File.read(AXE_JS)[/axe v([0-9][0-9.]*)/, 1] || "unknown"
|
|
84
|
-
end
|
|
85
|
-
|
|
86
|
-
def verify_vendored!
|
|
87
|
-
abort "No recorded checksum at #{AXE_SHA512} — run rake axe:update first." unless File.exist?(AXE_SHA512)
|
|
88
|
-
|
|
89
|
-
expected = File.read(AXE_SHA512).split.first
|
|
90
|
-
actual = Digest::SHA512.hexdigest(File.binread(AXE_JS))
|
|
91
|
-
unless actual == expected
|
|
92
|
-
abort <<~MSG
|
|
93
|
-
MISMATCH: #{AXE_JS} does not match its recorded sha512.
|
|
94
|
-
recorded: #{expected}
|
|
95
|
-
actual: #{actual}
|
|
96
|
-
MSG
|
|
97
|
-
end
|
|
98
|
-
|
|
99
|
-
puts "OK: axe.min.js matches its recorded sha512."
|
|
100
|
-
end
|
|
101
|
-
|
|
102
|
-
def registry_dist(version)
|
|
103
|
-
puts "Fetching axe-core@#{version} metadata from #{REGISTRY} ..."
|
|
104
|
-
JSON.parse(download("#{REGISTRY}/#{version}")).fetch("dist")
|
|
105
|
-
end
|
|
106
|
-
|
|
107
|
-
# Abort unless the downloaded tarball matches the integrity values the npm
|
|
108
|
-
# registry publishes for it (dist.integrity sha512, plus legacy dist.shasum).
|
|
109
|
-
def verify_tarball!(tarball, dist)
|
|
110
|
-
integrity = dist["integrity"]
|
|
111
|
-
abort "Registry metadata has no sha512 integrity for the tarball — refusing to vendor." unless integrity&.start_with?("sha512-")
|
|
112
|
-
|
|
113
|
-
actual = "sha512-#{Digest::SHA512.base64digest(tarball)}"
|
|
114
|
-
unless actual == integrity
|
|
115
|
-
abort <<~MSG
|
|
116
|
-
Tarball integrity check FAILED — refusing to vendor.
|
|
117
|
-
expected (registry dist.integrity): #{integrity}
|
|
118
|
-
actual (downloaded tarball): #{actual}
|
|
119
|
-
MSG
|
|
120
|
-
end
|
|
121
|
-
|
|
122
|
-
shasum = dist["shasum"]
|
|
123
|
-
if shasum && Digest::SHA1.hexdigest(tarball) != shasum
|
|
124
|
-
abort "Tarball sha1 shasum mismatch (registry says #{shasum}) — refusing to vendor."
|
|
125
|
-
end
|
|
126
|
-
|
|
127
|
-
puts " tarball integrity verified (#{integrity})"
|
|
128
|
-
end
|
|
129
|
-
|
|
130
|
-
# Secondary sanity check on the extracted engine itself; fatal on mismatch.
|
|
131
|
-
def verify_banner!(engine_js, version)
|
|
132
|
-
return if engine_js.match?(/axe v#{Regexp.escape(version)}\b/)
|
|
133
|
-
|
|
134
|
-
abort "Extracted axe.min.js banner does not mention v#{version} — refusing to vendor."
|
|
135
|
-
end
|
|
136
|
-
|
|
137
|
-
def extract(tarball, *paths)
|
|
138
|
-
found = {}
|
|
139
|
-
Zlib::GzipReader.wrap(StringIO.new(tarball)) do |gz|
|
|
140
|
-
Gem::Package::TarReader.new(gz) do |tar|
|
|
141
|
-
tar.each { |entry| found[entry.full_name] = entry.read if paths.include?(entry.full_name) }
|
|
142
|
-
end
|
|
143
|
-
end
|
|
144
|
-
missing = paths - found.keys
|
|
145
|
-
abort "Tarball is missing expected file(s): #{missing.join(", ")}" unless missing.empty?
|
|
146
|
-
found.values_at(*paths)
|
|
147
|
-
end
|
|
148
|
-
|
|
149
|
-
def download(url)
|
|
150
|
-
URI.parse(url).open("rb", &:read)
|
|
151
|
-
rescue OpenURI::HTTPError => e
|
|
152
|
-
abort "Failed to download #{url}: #{e.message}"
|
|
153
|
-
end
|
|
154
|
-
|
|
155
|
-
def bump_version_constant(version)
|
|
156
|
-
contents = File.read(VERSION_FILE)
|
|
157
|
-
updated = contents.sub(/(AXE_CORE_VERSION\s*=\s*)"[^"]*"/, %(\\1"#{version}"))
|
|
158
|
-
File.write(VERSION_FILE, updated)
|
|
159
|
-
end
|
|
160
|
-
|
|
161
|
-
def report(version, engine_js)
|
|
162
|
-
puts "Vendored axe-core #{version} (tarball integrity verified):"
|
|
163
|
-
puts " #{AXE_JS} (#{engine_js.bytesize} bytes)"
|
|
164
|
-
puts " #{AXE_SHA512}"
|
|
165
|
-
puts " #{AXE_LICENSE}"
|
|
166
|
-
puts " updated AXE_CORE_VERSION in #{VERSION_FILE}"
|
|
167
|
-
puts "Remember to note the version bump in CHANGELOG.md."
|
|
168
|
-
end
|
|
169
|
-
end
|