stimulus_spec 0.1.0 → 0.3.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 +11 -1
- data/README.md +33 -1
- data/ROADMAP.md +0 -17
- data/lib/stimulus_spec/matchers/have_stimulus_class.rb +65 -0
- data/lib/stimulus_spec/matchers/have_stimulus_value.rb +65 -0
- data/lib/stimulus_spec/matchers.rb +2 -0
- data/lib/stimulus_spec/version.rb +1 -1
- data/sig/stimulus_spec.rbs +20 -0
- metadata +3 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: ffe129c9b92bcc3dfe29e7dd6e9be508fd7c14b99603ca7524f964679fe42b6c
|
|
4
|
+
data.tar.gz: a254bb73a1d5e275637e477a333e3bea6ab4248b7555e1d1d3f4bf74de9f213c
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: '038f5d80f4a0b38bc9e0664bb3d476db0af508580bc6476095d2791fe65cb7f515e06d1116fecdc8570f559aaf77331fac6c06e5fa2e0b41b7616b6eb652e069'
|
|
7
|
+
data.tar.gz: cc7ed0ded4e4db2426532aeef9a2dcf2ab92aee3b27bf5af792921a70e3ccdfefc84cb679a7004e0231b385a1c6c3254178a5142eccc499dadc31b947711f73a
|
data/CHANGELOG.md
CHANGED
|
@@ -7,6 +7,15 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
7
7
|
|
|
8
8
|
## [Unreleased]
|
|
9
9
|
|
|
10
|
+
## [0.3.0] - 2026-06-22
|
|
11
|
+
|
|
12
|
+
### Added
|
|
13
|
+
|
|
14
|
+
- `have_stimulus_value(controller, name)` matcher — asserts `data-{controller}-{name}-value` exists
|
|
15
|
+
- `have_stimulus_value(controller, name, expected)` matcher — asserts attribute equals expected value
|
|
16
|
+
- `have_stimulus_class(controller, name)` matcher — asserts `data-{controller}-{name}-class` exists
|
|
17
|
+
- `have_stimulus_class(controller, name, expected)` matcher — asserts attribute equals expected class
|
|
18
|
+
|
|
10
19
|
## [0.1.0] - 2026-06-22
|
|
11
20
|
|
|
12
21
|
### Added
|
|
@@ -19,5 +28,6 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
19
28
|
- `have_stimulus_action(descriptor)` matcher — full descriptor (`~=`) and shorthand without event (`*=`)
|
|
20
29
|
- `have_stimulus_target(controller, target)` matcher — asserts `[data-{controller}-target~="target"]`
|
|
21
30
|
|
|
22
|
-
[Unreleased]: https://github.com/eclectic-coding/stimulus_spec/compare/v0.
|
|
31
|
+
[Unreleased]: https://github.com/eclectic-coding/stimulus_spec/compare/v0.3.0...HEAD
|
|
32
|
+
[0.3.0]: https://github.com/eclectic-coding/stimulus_spec/releases/tag/v0.3.0
|
|
23
33
|
[0.1.0]: https://github.com/eclectic-coding/stimulus_spec/releases/tag/v0.1.0
|
data/README.md
CHANGED
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
|
|
9
9
|
Drop-in RSpec matchers for [hotwired/stimulus-rails](https://github.com/hotwired/stimulus-rails) — stop hand-rolling `data-controller` assertions and test your Stimulus wiring with expressive, purpose-built matchers.
|
|
10
10
|
|
|
11
|
-
- **Request/controller specs** — `have_stimulus_controller`, `have_stimulus_action`, `have_stimulus_target`
|
|
11
|
+
- **Request/controller specs** — `have_stimulus_controller`, `have_stimulus_action`, `have_stimulus_target`, `have_stimulus_value`, `have_stimulus_class`
|
|
12
12
|
- **Auto-included** — zero setup required when `stimulus-rails` is in your bundle
|
|
13
13
|
- **Configurable** — disable auto-include when you need manual control
|
|
14
14
|
|
|
@@ -22,6 +22,8 @@ Companion gem to [turbo_rspec](https://github.com/eclectic-coding/turbo_rspec)
|
|
|
22
22
|
- [have\_stimulus\_controller](#have_stimulus_controller)
|
|
23
23
|
- [have\_stimulus\_action](#have_stimulus_action)
|
|
24
24
|
- [have\_stimulus\_target](#have_stimulus_target)
|
|
25
|
+
- [have\_stimulus\_value](#have_stimulus_value)
|
|
26
|
+
- [have\_stimulus\_class](#have_stimulus_class)
|
|
25
27
|
- [Example](#example)
|
|
26
28
|
- [Relationship to turbo\_rspec](#relationship-to-turbo_rspec)
|
|
27
29
|
- [Contributing](#contributing)
|
|
@@ -112,6 +114,36 @@ expect(response).to have_stimulus_target("hello", "output")
|
|
|
112
114
|
expect(response).not_to have_stimulus_target("hello", "missing")
|
|
113
115
|
```
|
|
114
116
|
|
|
117
|
+
### `have_stimulus_value`
|
|
118
|
+
|
|
119
|
+
Assert that rendered HTML contains a `data-{controller}-{name}-value` attribute, optionally with a specific value.
|
|
120
|
+
|
|
121
|
+
```ruby
|
|
122
|
+
# Assert the value attribute exists
|
|
123
|
+
expect(response).to have_stimulus_value("search", "url")
|
|
124
|
+
|
|
125
|
+
# Assert a specific value
|
|
126
|
+
expect(response).to have_stimulus_value("search", "url", "/results")
|
|
127
|
+
|
|
128
|
+
# Negation
|
|
129
|
+
expect(response).not_to have_stimulus_value("search", "url")
|
|
130
|
+
```
|
|
131
|
+
|
|
132
|
+
### `have_stimulus_class`
|
|
133
|
+
|
|
134
|
+
Assert that rendered HTML contains a `data-{controller}-{name}-class` attribute, optionally with a specific class.
|
|
135
|
+
|
|
136
|
+
```ruby
|
|
137
|
+
# Assert the class attribute exists
|
|
138
|
+
expect(response).to have_stimulus_class("search", "loading")
|
|
139
|
+
|
|
140
|
+
# Assert a specific class value
|
|
141
|
+
expect(response).to have_stimulus_class("search", "loading", "opacity-50")
|
|
142
|
+
|
|
143
|
+
# Negation
|
|
144
|
+
expect(response).not_to have_stimulus_class("search", "loading")
|
|
145
|
+
```
|
|
146
|
+
|
|
115
147
|
[Back to top](#stimulusspec)
|
|
116
148
|
|
|
117
149
|
## Example
|
data/ROADMAP.md
CHANGED
|
@@ -11,23 +11,6 @@ RSpec matchers for [Stimulus](https://github.com/hotwired/stimulus-rails): contr
|
|
|
11
11
|
|
|
12
12
|
---
|
|
13
13
|
|
|
14
|
-
## 0.2.0 — Values and Classes
|
|
15
|
-
|
|
16
|
-
- `have_stimulus_value("search", "url")` — assert `data-search-url-value` attribute exists
|
|
17
|
-
- `have_stimulus_value("search", "url", "/results")` — assert attribute equals expected value
|
|
18
|
-
- `have_stimulus_class("search", "loading")` — assert `data-search-loading-class` exists
|
|
19
|
-
- `have_stimulus_class("search", "loading", "opacity-50")` — assert attribute equals expected class
|
|
20
|
-
|
|
21
|
-
---
|
|
22
|
-
|
|
23
|
-
## 0.3.0 — Outlets and Capybara Foundation
|
|
24
|
-
|
|
25
|
-
- `have_stimulus_outlet("search", "results")` — assert `data-search-results-outlet` exists
|
|
26
|
-
- `have_stimulus_outlet("search", "results", "#results-list")` — assert selector value
|
|
27
|
-
- Capybara matchers: `have_stimulus_controller`, `have_stimulus_action`, `have_stimulus_target` using `has_css?` / `has_no_css?` with `wait: 0`
|
|
28
|
-
- Auto-include `StimulusSpec::Capybara::Matchers` into `type: :system` and `type: :feature` (gated on `capybara`)
|
|
29
|
-
|
|
30
|
-
---
|
|
31
14
|
|
|
32
15
|
## 0.4.0 — Capybara Values and Rich Failures
|
|
33
16
|
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module StimulusSpec
|
|
4
|
+
module Matchers
|
|
5
|
+
class HaveStimulusClass
|
|
6
|
+
def initialize(controller, name, expected = nil)
|
|
7
|
+
@controller = controller.to_s
|
|
8
|
+
@name = name.to_s
|
|
9
|
+
@expected = expected
|
|
10
|
+
@attr = "data-#{@controller}-#{@name}-class"
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
def matches?(subject)
|
|
14
|
+
@body = extract_body(subject)
|
|
15
|
+
element = document.at_css("[#{@attr}]")
|
|
16
|
+
return false unless element
|
|
17
|
+
|
|
18
|
+
if @expected
|
|
19
|
+
@actual = element[@attr]
|
|
20
|
+
@actual == @expected.to_s
|
|
21
|
+
else
|
|
22
|
+
true
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
def does_not_match?(subject)
|
|
27
|
+
!matches?(subject)
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
def failure_message
|
|
31
|
+
if @expected && @actual
|
|
32
|
+
"expected #{@attr} to be \"#{@expected}\" but was \"#{@actual}\""
|
|
33
|
+
else
|
|
34
|
+
"expected to find an element with #{@attr} but found none in:\n#{@body}"
|
|
35
|
+
end
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
def failure_message_when_negated
|
|
39
|
+
"expected not to find an element with #{@attr} but found one"
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
def description
|
|
43
|
+
if @expected
|
|
44
|
+
"have Stimulus class \"#{@name}\" with \"#{@expected}\" for controller \"#{@controller}\""
|
|
45
|
+
else
|
|
46
|
+
"have Stimulus class \"#{@name}\" for controller \"#{@controller}\""
|
|
47
|
+
end
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
private
|
|
51
|
+
|
|
52
|
+
def extract_body(subject)
|
|
53
|
+
subject.respond_to?(:body) ? subject.body : subject.to_s
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
def document
|
|
57
|
+
Nokogiri::HTML5.fragment(@body)
|
|
58
|
+
end
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
def have_stimulus_class(controller, name, expected = nil)
|
|
62
|
+
HaveStimulusClass.new(controller, name, expected)
|
|
63
|
+
end
|
|
64
|
+
end
|
|
65
|
+
end
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module StimulusSpec
|
|
4
|
+
module Matchers
|
|
5
|
+
class HaveStimulusValue
|
|
6
|
+
def initialize(controller, name, expected = nil)
|
|
7
|
+
@controller = controller.to_s
|
|
8
|
+
@name = name.to_s
|
|
9
|
+
@expected = expected
|
|
10
|
+
@attr = "data-#{@controller}-#{@name}-value"
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
def matches?(subject)
|
|
14
|
+
@body = extract_body(subject)
|
|
15
|
+
element = document.at_css("[#{@attr}]")
|
|
16
|
+
return false unless element
|
|
17
|
+
|
|
18
|
+
if @expected
|
|
19
|
+
@actual = element[@attr]
|
|
20
|
+
@actual == @expected.to_s
|
|
21
|
+
else
|
|
22
|
+
true
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
def does_not_match?(subject)
|
|
27
|
+
!matches?(subject)
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
def failure_message
|
|
31
|
+
if @expected && @actual
|
|
32
|
+
"expected #{@attr} to be \"#{@expected}\" but was \"#{@actual}\""
|
|
33
|
+
else
|
|
34
|
+
"expected to find an element with #{@attr} but found none in:\n#{@body}"
|
|
35
|
+
end
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
def failure_message_when_negated
|
|
39
|
+
"expected not to find an element with #{@attr} but found one"
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
def description
|
|
43
|
+
if @expected
|
|
44
|
+
"have Stimulus value \"#{@name}\" with \"#{@expected}\" for controller \"#{@controller}\""
|
|
45
|
+
else
|
|
46
|
+
"have Stimulus value \"#{@name}\" for controller \"#{@controller}\""
|
|
47
|
+
end
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
private
|
|
51
|
+
|
|
52
|
+
def extract_body(subject)
|
|
53
|
+
subject.respond_to?(:body) ? subject.body : subject.to_s
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
def document
|
|
57
|
+
Nokogiri::HTML5.fragment(@body)
|
|
58
|
+
end
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
def have_stimulus_value(controller, name, expected = nil)
|
|
62
|
+
HaveStimulusValue.new(controller, name, expected)
|
|
63
|
+
end
|
|
64
|
+
end
|
|
65
|
+
end
|
|
@@ -5,6 +5,8 @@ require "nokogiri"
|
|
|
5
5
|
require_relative "matchers/have_stimulus_controller"
|
|
6
6
|
require_relative "matchers/have_stimulus_action"
|
|
7
7
|
require_relative "matchers/have_stimulus_target"
|
|
8
|
+
require_relative "matchers/have_stimulus_value"
|
|
9
|
+
require_relative "matchers/have_stimulus_class"
|
|
8
10
|
|
|
9
11
|
module StimulusSpec
|
|
10
12
|
module Matchers
|
data/sig/stimulus_spec.rbs
CHANGED
|
@@ -16,6 +16,8 @@ module StimulusSpec
|
|
|
16
16
|
def have_stimulus_controller: (String name) -> HaveStimulusController
|
|
17
17
|
def have_stimulus_action: (String descriptor) -> HaveStimulusAction
|
|
18
18
|
def have_stimulus_target: (String controller, String target) -> HaveStimulusTarget
|
|
19
|
+
def have_stimulus_value: (String controller, String name, ?String? expected) -> HaveStimulusValue
|
|
20
|
+
def have_stimulus_class: (String controller, String name, ?String? expected) -> HaveStimulusClass
|
|
19
21
|
|
|
20
22
|
class HaveStimulusController
|
|
21
23
|
def initialize: (String name) -> void
|
|
@@ -43,5 +45,23 @@ module StimulusSpec
|
|
|
43
45
|
def failure_message_when_negated: () -> String
|
|
44
46
|
def description: () -> String
|
|
45
47
|
end
|
|
48
|
+
|
|
49
|
+
class HaveStimulusValue
|
|
50
|
+
def initialize: (String controller, String name, ?String? expected) -> void
|
|
51
|
+
def matches?: (untyped subject) -> bool
|
|
52
|
+
def does_not_match?: (untyped subject) -> bool
|
|
53
|
+
def failure_message: () -> String
|
|
54
|
+
def failure_message_when_negated: () -> String
|
|
55
|
+
def description: () -> String
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
class HaveStimulusClass
|
|
59
|
+
def initialize: (String controller, String name, ?String? expected) -> void
|
|
60
|
+
def matches?: (untyped subject) -> bool
|
|
61
|
+
def does_not_match?: (untyped subject) -> bool
|
|
62
|
+
def failure_message: () -> String
|
|
63
|
+
def failure_message_when_negated: () -> String
|
|
64
|
+
def description: () -> String
|
|
65
|
+
end
|
|
46
66
|
end
|
|
47
67
|
end
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: stimulus_spec
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.
|
|
4
|
+
version: 0.3.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Chuck Smith
|
|
@@ -47,8 +47,10 @@ files:
|
|
|
47
47
|
- lib/stimulus_spec/configuration.rb
|
|
48
48
|
- lib/stimulus_spec/matchers.rb
|
|
49
49
|
- lib/stimulus_spec/matchers/have_stimulus_action.rb
|
|
50
|
+
- lib/stimulus_spec/matchers/have_stimulus_class.rb
|
|
50
51
|
- lib/stimulus_spec/matchers/have_stimulus_controller.rb
|
|
51
52
|
- lib/stimulus_spec/matchers/have_stimulus_target.rb
|
|
53
|
+
- lib/stimulus_spec/matchers/have_stimulus_value.rb
|
|
52
54
|
- lib/stimulus_spec/version.rb
|
|
53
55
|
- sig/stimulus_spec.rbs
|
|
54
56
|
homepage: https://github.com/eclectic-coding/stimulus_spec
|