turbo_rspec 1.2.0 → 1.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 +8 -0
- data/README.md +24 -1
- data/ROADMAP.md +0 -4
- data/lib/turbo_rspec/capybara/matchers/have_stimulus_action.rb +55 -0
- data/lib/turbo_rspec/capybara/matchers/have_stimulus_controller.rb +46 -0
- data/lib/turbo_rspec/capybara/matchers/have_stimulus_target.rb +47 -0
- data/lib/turbo_rspec/capybara/matchers.rb +15 -0
- data/lib/turbo_rspec/version.rb +1 -1
- data/sig/turbo_rspec.rbs +30 -0
- metadata +4 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: cdd85fcc9f87814d171569e835ad85de4767daf48642d5797f15ac02decf4641
|
|
4
|
+
data.tar.gz: a92c047884633fd146aa6806dc350edef72bd18747c39e3da618feb12223a29e
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 7ecdc1dc0c21c083a32270e7ab085e31e5fd4e6afde0193c9f58a967e7d79aaf543387e874d8cc2fd4f1afdcb0b56a2b2b53b6c6a230f3d80a41736b68c5c01e
|
|
7
|
+
data.tar.gz: b12927d25812f2991d889a7cedc37fe02cf4feb439adc76555dcfc8d6fd31151f9dd5cd80257ec82057d350c52e607cc2206e924bf3b159257453de9fad4ced5
|
data/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,13 @@
|
|
|
1
1
|
## [Unreleased]
|
|
2
2
|
|
|
3
|
+
## [1.3.0] - 2026-06-02
|
|
4
|
+
|
|
5
|
+
### Added
|
|
6
|
+
|
|
7
|
+
- `have_stimulus_controller(name)` — Capybara matcher asserting `data-controller` contains the given controller name
|
|
8
|
+
- `have_stimulus_action(descriptor)` — Capybara matcher asserting `data-action` contains the descriptor; accepts a full descriptor (`click->hello#greet`) or shorthand without event (`hello#greet`)
|
|
9
|
+
- `have_stimulus_target(controller, target)` — Capybara matcher asserting `data-{controller}-target` contains the target name
|
|
10
|
+
|
|
3
11
|
## [1.2.0] - 2026-06-01
|
|
4
12
|
|
|
5
13
|
### Added
|
data/README.md
CHANGED
|
@@ -10,7 +10,7 @@ Drop-in test matchers for [hotwired/turbo-rails](https://github.com/hotwired/tur
|
|
|
10
10
|
|
|
11
11
|
- **Request/controller specs** — `have_turbo_stream`, `have_turbo_frame`, `have_turbo_streams`
|
|
12
12
|
- **Broadcast specs** — `have_broadcasted_turbo_stream_to` with count qualifiers
|
|
13
|
-
- **System/feature specs** — Capybara matchers: `have_turbo_frame`, `have_turbo_stream_tag`, `within_turbo_frame`
|
|
13
|
+
- **System/feature specs** — Capybara matchers: `have_turbo_frame`, `have_turbo_stream_tag`, `within_turbo_frame`, `have_stimulus_controller`, `have_stimulus_action`, `have_stimulus_target`
|
|
14
14
|
- **Minitest** — `assert_turbo_stream`, `refute_turbo_stream`, `assert_turbo_frame`, `refute_turbo_frame`
|
|
15
15
|
- **Factory helpers** — `turbo_stream_html`, `turbo_frame_html`
|
|
16
16
|
- **Shared examples** — `it_behaves_like "a turbo stream response"`
|
|
@@ -236,6 +236,29 @@ within_turbo_frame("messages") do
|
|
|
236
236
|
end
|
|
237
237
|
```
|
|
238
238
|
|
|
239
|
+
### Stimulus matchers
|
|
240
|
+
|
|
241
|
+
Assert Stimulus controller, action, and target attributes on the page (Capybara).
|
|
242
|
+
|
|
243
|
+
```ruby
|
|
244
|
+
# Controller — data-controller contains "hello"
|
|
245
|
+
expect(page).to have_stimulus_controller("hello")
|
|
246
|
+
|
|
247
|
+
# Action — full descriptor
|
|
248
|
+
expect(page).to have_stimulus_action("click->hello#greet")
|
|
249
|
+
|
|
250
|
+
# Action — shorthand without event (matches any event prefix)
|
|
251
|
+
expect(page).to have_stimulus_action("hello#greet")
|
|
252
|
+
|
|
253
|
+
# Target — data-hello-target contains "name"
|
|
254
|
+
expect(page).to have_stimulus_target("hello", "name")
|
|
255
|
+
|
|
256
|
+
# Negation
|
|
257
|
+
expect(page).not_to have_stimulus_controller("missing")
|
|
258
|
+
```
|
|
259
|
+
|
|
260
|
+
All three matchers use space-separated token matching (`~=`), so they work correctly when multiple controllers, actions, or targets are declared on a single element.
|
|
261
|
+
|
|
239
262
|
### `have_turbo_stream_tag`
|
|
240
263
|
|
|
241
264
|
Assert that a `<turbo-stream-source>` subscription element is on the page.
|
data/ROADMAP.md
CHANGED
|
@@ -4,10 +4,6 @@ RSpec matchers for [Turbo](https://github.com/hotwired/turbo-rails): Turbo Strea
|
|
|
4
4
|
|
|
5
5
|
---
|
|
6
6
|
|
|
7
|
-
## 1.3 — Stimulus companion
|
|
8
|
-
|
|
9
|
-
- **Stimulus matchers** — `have_stimulus_controller`, `have_stimulus_action`, `have_stimulus_target` Capybara matchers. turbo-rails ships with Stimulus; teams using both already test Stimulus behavior manually. This is the natural next surface area for a complete Hotwire testing toolkit.
|
|
10
|
-
|
|
11
7
|
## 1.4 — Tooling
|
|
12
8
|
|
|
13
9
|
- **RuboCop cop** — flag request specs that assert `response.body` with a raw string match or `include "<turbo-stream"` instead of using the gem's matchers. Useful for migration and incremental adoption.
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module TurboRspec
|
|
4
|
+
module Capybara
|
|
5
|
+
module Matchers
|
|
6
|
+
# Capybara matcher asserting that an element with the given Stimulus
|
|
7
|
+
# action descriptor is present on the page (+data-action+ contains the
|
|
8
|
+
# descriptor as a space-separated token).
|
|
9
|
+
#
|
|
10
|
+
# Accepts either a full descriptor (+click->hello#greet+) or a shorthand
|
|
11
|
+
# without an event (+hello#greet+), which matches any event prefix.
|
|
12
|
+
#
|
|
13
|
+
# @example Full descriptor
|
|
14
|
+
# expect(page).to have_stimulus_action("click->hello#greet")
|
|
15
|
+
#
|
|
16
|
+
# @example Shorthand (any event)
|
|
17
|
+
# expect(page).to have_stimulus_action("hello#greet")
|
|
18
|
+
class HaveStimulusAction
|
|
19
|
+
def initialize(action_descriptor)
|
|
20
|
+
@action_descriptor = action_descriptor.to_s
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
def matches?(page_or_node)
|
|
24
|
+
page_or_node.has_css?(selector, wait: 0)
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
def does_not_match?(page_or_node)
|
|
28
|
+
page_or_node.has_no_css?(selector, wait: 0)
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
def failure_message
|
|
32
|
+
"expected page to have Stimulus action #{@action_descriptor.inspect}"
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
def failure_message_when_negated
|
|
36
|
+
"expected page not to have Stimulus action #{@action_descriptor.inspect}"
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
def description
|
|
40
|
+
"have Stimulus action #{@action_descriptor.inspect}"
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
private
|
|
44
|
+
|
|
45
|
+
def selector
|
|
46
|
+
if @action_descriptor.include?("->")
|
|
47
|
+
"[data-action~='#{@action_descriptor}']"
|
|
48
|
+
else
|
|
49
|
+
"[data-action*='#{@action_descriptor}']"
|
|
50
|
+
end
|
|
51
|
+
end
|
|
52
|
+
end
|
|
53
|
+
end
|
|
54
|
+
end
|
|
55
|
+
end
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module TurboRspec
|
|
4
|
+
module Capybara
|
|
5
|
+
module Matchers
|
|
6
|
+
# Capybara matcher asserting that an element with the given Stimulus
|
|
7
|
+
# controller is present on the page (+data-controller+ contains the name
|
|
8
|
+
# as a space-separated token).
|
|
9
|
+
#
|
|
10
|
+
# @example
|
|
11
|
+
# expect(page).to have_stimulus_controller("hello")
|
|
12
|
+
# expect(page).not_to have_stimulus_controller("missing")
|
|
13
|
+
class HaveStimulusController
|
|
14
|
+
def initialize(controller_name)
|
|
15
|
+
@controller_name = controller_name.to_s
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
def matches?(page_or_node)
|
|
19
|
+
page_or_node.has_css?(selector, wait: 0)
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
def does_not_match?(page_or_node)
|
|
23
|
+
page_or_node.has_no_css?(selector, wait: 0)
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
def failure_message
|
|
27
|
+
"expected page to have Stimulus controller #{@controller_name.inspect}"
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
def failure_message_when_negated
|
|
31
|
+
"expected page not to have Stimulus controller #{@controller_name.inspect}"
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
def description
|
|
35
|
+
"have Stimulus controller #{@controller_name.inspect}"
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
private
|
|
39
|
+
|
|
40
|
+
def selector
|
|
41
|
+
"[data-controller~='#{@controller_name}']"
|
|
42
|
+
end
|
|
43
|
+
end
|
|
44
|
+
end
|
|
45
|
+
end
|
|
46
|
+
end
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module TurboRspec
|
|
4
|
+
module Capybara
|
|
5
|
+
module Matchers
|
|
6
|
+
# Capybara matcher asserting that an element with the given Stimulus
|
|
7
|
+
# target is present on the page. Checks +data-{controller}-target+
|
|
8
|
+
# contains the target name as a space-separated token.
|
|
9
|
+
#
|
|
10
|
+
# @example
|
|
11
|
+
# expect(page).to have_stimulus_target("hello", "name")
|
|
12
|
+
# expect(page).not_to have_stimulus_target("hello", "missing")
|
|
13
|
+
class HaveStimulusTarget
|
|
14
|
+
def initialize(controller_name, target_name)
|
|
15
|
+
@controller_name = controller_name.to_s
|
|
16
|
+
@target_name = target_name.to_s
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
def matches?(page_or_node)
|
|
20
|
+
page_or_node.has_css?(selector, wait: 0)
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
def does_not_match?(page_or_node)
|
|
24
|
+
page_or_node.has_no_css?(selector, wait: 0)
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
def failure_message
|
|
28
|
+
"expected page to have Stimulus target #{@target_name.inspect} for controller #{@controller_name.inspect}"
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
def failure_message_when_negated
|
|
32
|
+
"expected page not to have Stimulus target #{@target_name.inspect} for controller #{@controller_name.inspect}"
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
def description
|
|
36
|
+
"have Stimulus target #{@target_name.inspect} for #{@controller_name.inspect}"
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
private
|
|
40
|
+
|
|
41
|
+
def selector
|
|
42
|
+
"[data-#{@controller_name}-target~='#{@target_name}']"
|
|
43
|
+
end
|
|
44
|
+
end
|
|
45
|
+
end
|
|
46
|
+
end
|
|
47
|
+
end
|
|
@@ -2,6 +2,9 @@
|
|
|
2
2
|
|
|
3
3
|
require_relative "matchers/have_turbo_frame"
|
|
4
4
|
require_relative "matchers/have_turbo_stream_tag"
|
|
5
|
+
require_relative "matchers/have_stimulus_controller"
|
|
6
|
+
require_relative "matchers/have_stimulus_action"
|
|
7
|
+
require_relative "matchers/have_stimulus_target"
|
|
5
8
|
|
|
6
9
|
module TurboRspec
|
|
7
10
|
module Capybara
|
|
@@ -14,6 +17,18 @@ module TurboRspec
|
|
|
14
17
|
HaveTurboStreamTag.new(signed_stream_name: signed_stream_name)
|
|
15
18
|
end
|
|
16
19
|
|
|
20
|
+
def have_stimulus_controller(controller_name)
|
|
21
|
+
HaveStimulusController.new(controller_name)
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
def have_stimulus_action(action_descriptor)
|
|
25
|
+
HaveStimulusAction.new(action_descriptor)
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
def have_stimulus_target(controller_name, target_name)
|
|
29
|
+
HaveStimulusTarget.new(controller_name, target_name)
|
|
30
|
+
end
|
|
31
|
+
|
|
17
32
|
# :nocov:
|
|
18
33
|
def within_turbo_frame(id, &block)
|
|
19
34
|
page.within("turbo-frame##{id}", &block)
|
data/lib/turbo_rspec/version.rb
CHANGED
data/sig/turbo_rspec.rbs
CHANGED
|
@@ -127,6 +127,9 @@ module TurboRspec
|
|
|
127
127
|
module Matchers
|
|
128
128
|
def have_turbo_frame: (String id) -> HaveTurboFrame
|
|
129
129
|
def have_turbo_stream_tag: (?String? signed_stream_name) -> HaveTurboStreamTag
|
|
130
|
+
def have_stimulus_controller: (String controller_name) -> HaveStimulusController
|
|
131
|
+
def have_stimulus_action: (String action_descriptor) -> HaveStimulusAction
|
|
132
|
+
def have_stimulus_target: (String controller_name, String target_name) -> HaveStimulusTarget
|
|
130
133
|
def within_turbo_frame: (String id) { () -> void } -> void
|
|
131
134
|
|
|
132
135
|
class HaveTurboFrame
|
|
@@ -146,6 +149,33 @@ module TurboRspec
|
|
|
146
149
|
def description: () -> String
|
|
147
150
|
end
|
|
148
151
|
|
|
152
|
+
class HaveStimulusController
|
|
153
|
+
def initialize: (String controller_name) -> void
|
|
154
|
+
def matches?: (untyped page_or_node) -> bool
|
|
155
|
+
def does_not_match?: (untyped page_or_node) -> bool
|
|
156
|
+
def failure_message: () -> String
|
|
157
|
+
def failure_message_when_negated: () -> String
|
|
158
|
+
def description: () -> String
|
|
159
|
+
end
|
|
160
|
+
|
|
161
|
+
class HaveStimulusAction
|
|
162
|
+
def initialize: (String action_descriptor) -> void
|
|
163
|
+
def matches?: (untyped page_or_node) -> bool
|
|
164
|
+
def does_not_match?: (untyped page_or_node) -> bool
|
|
165
|
+
def failure_message: () -> String
|
|
166
|
+
def failure_message_when_negated: () -> String
|
|
167
|
+
def description: () -> String
|
|
168
|
+
end
|
|
169
|
+
|
|
170
|
+
class HaveStimulusTarget
|
|
171
|
+
def initialize: (String controller_name, String target_name) -> void
|
|
172
|
+
def matches?: (untyped page_or_node) -> bool
|
|
173
|
+
def does_not_match?: (untyped page_or_node) -> bool
|
|
174
|
+
def failure_message: () -> String
|
|
175
|
+
def failure_message_when_negated: () -> String
|
|
176
|
+
def description: () -> String
|
|
177
|
+
end
|
|
178
|
+
|
|
149
179
|
class HaveTurboStreamTag
|
|
150
180
|
def initialize: (?signed_stream_name: String?) -> void
|
|
151
181
|
def matches?: (untyped page_or_node) -> bool
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: turbo_rspec
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 1.
|
|
4
|
+
version: 1.3.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Chuck Smith
|
|
@@ -54,6 +54,9 @@ files:
|
|
|
54
54
|
- lib/turbo_rspec.rb
|
|
55
55
|
- lib/turbo_rspec/assertions.rb
|
|
56
56
|
- lib/turbo_rspec/capybara/matchers.rb
|
|
57
|
+
- lib/turbo_rspec/capybara/matchers/have_stimulus_action.rb
|
|
58
|
+
- lib/turbo_rspec/capybara/matchers/have_stimulus_controller.rb
|
|
59
|
+
- lib/turbo_rspec/capybara/matchers/have_stimulus_target.rb
|
|
57
60
|
- lib/turbo_rspec/capybara/matchers/have_turbo_frame.rb
|
|
58
61
|
- lib/turbo_rspec/capybara/matchers/have_turbo_stream_tag.rb
|
|
59
62
|
- lib/turbo_rspec/configuration.rb
|