turbo_rspec 1.5.0 → 1.6.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 +9 -0
- data/README.md +22 -11
- data/ROADMAP.md +0 -8
- data/lib/turbo_rspec/assertions.rb +35 -0
- data/lib/turbo_rspec/capybara/matchers/have_turbo_frame.rb +24 -0
- data/lib/turbo_rspec/matchers/have_stimulus_action.rb +71 -0
- data/lib/turbo_rspec/matchers/have_stimulus_controller.rb +58 -0
- data/lib/turbo_rspec/matchers/have_stimulus_target.rb +59 -0
- data/lib/turbo_rspec/matchers.rb +25 -0
- data/lib/turbo_rspec/version.rb +1 -1
- data/sig/turbo_rspec.rbs +34 -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: 3211f811cbd7919d35f8b918260ea97346668fb2ef3213f189d91c642a014b53
|
|
4
|
+
data.tar.gz: dda8641bcf4d6cb1a8ebc6de4ee5d7622f90bcec3a79b8180444b6e4bfcd2c38
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 352e59c5604c6362263a51d6b0eb9903b1b7c7b337bca65a05e01c15b42bae5e121def5d638ed66f8b73d06fda382460a85dce4d15ab7a3bff234fb26c310759
|
|
7
|
+
data.tar.gz: 1243fc05f3fc0256f3e028c66d505a0cbf8335108238c6fde0c0b4e6a0fc97f630bd2ae59c51cb37e64a0ce81265fb20b2d8c9be2e850ef7b29a3442c18f6991
|
data/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,14 @@
|
|
|
1
1
|
## [Unreleased]
|
|
2
2
|
|
|
3
|
+
## [1.6.0] - 2026-06-02
|
|
4
|
+
|
|
5
|
+
### Added
|
|
6
|
+
|
|
7
|
+
- `have_stimulus_controller`, `have_stimulus_action`, `have_stimulus_target` request-spec matchers — assert Stimulus `data-*` attributes in response HTML, auto-included in `type: :request` and `type: :controller` example groups
|
|
8
|
+
- `assert_broadcasted_turbo_stream_to` and `refute_broadcasted_turbo_stream_to` in `TurboRspec::Assertions` — Minitest broadcast parity with the RSpec `have_broadcasted_turbo_stream_to` matcher
|
|
9
|
+
- `have_turbo_frame` (Capybara) `.eager` chain — asserts `loading="eager"` on the frame (Turbo 8)
|
|
10
|
+
- `have_turbo_frame` (Capybara) `.strict` chain — asserts the `[strict]` boolean attribute on the frame (Turbo 8)
|
|
11
|
+
|
|
3
12
|
## [1.5.0] - 2026-06-02
|
|
4
13
|
|
|
5
14
|
### Added
|
data/README.md
CHANGED
|
@@ -8,10 +8,10 @@
|
|
|
8
8
|
|
|
9
9
|
Drop-in test matchers for [hotwired/turbo-rails](https://github.com/hotwired/turbo-rails) — replace every hand-rolled Turbo helper in your test suite with a single gem.
|
|
10
10
|
|
|
11
|
-
- **Request/controller specs** — `have_turbo_stream`, `have_turbo_frame`, `have_turbo_streams`
|
|
11
|
+
- **Request/controller specs** — `have_turbo_stream`, `have_turbo_frame`, `have_turbo_streams`, `have_stimulus_controller`, `have_stimulus_action`, `have_stimulus_target`
|
|
12
12
|
- **Broadcast specs** — `have_broadcasted_turbo_stream_to` with count qualifiers
|
|
13
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
|
-
- **Minitest** — `assert_turbo_stream`, `refute_turbo_stream`, `assert_turbo_frame`, `refute_turbo_frame`
|
|
14
|
+
- **Minitest** — `assert_turbo_stream`, `refute_turbo_stream`, `assert_turbo_frame`, `refute_turbo_frame`, `assert_broadcasted_turbo_stream_to`, `refute_broadcasted_turbo_stream_to`
|
|
15
15
|
- **Factory helpers** — `turbo_stream_html`, `turbo_frame_html`
|
|
16
16
|
- **Shared examples** — `it_behaves_like "a turbo stream response"`
|
|
17
17
|
- **Auto-included** — zero setup required when `turbo-rails` is in your bundle
|
|
@@ -220,6 +220,12 @@ expect(page).to have_turbo_frame("messages").loaded
|
|
|
220
220
|
# Lazy frame — assert loading="lazy" attribute
|
|
221
221
|
expect(page).to have_turbo_frame("messages").lazy
|
|
222
222
|
|
|
223
|
+
# Eager frame — assert loading="eager" attribute (Turbo 8)
|
|
224
|
+
expect(page).to have_turbo_frame("messages").eager
|
|
225
|
+
|
|
226
|
+
# Strict frame — assert [strict] attribute (Turbo 8)
|
|
227
|
+
expect(page).to have_turbo_frame("messages").strict
|
|
228
|
+
|
|
223
229
|
# With src — assert the src attribute on a lazy-loaded frame
|
|
224
230
|
expect(page).to have_turbo_frame("messages").with_src("/messages/new")
|
|
225
231
|
|
|
@@ -246,22 +252,22 @@ end
|
|
|
246
252
|
|
|
247
253
|
### Stimulus matchers
|
|
248
254
|
|
|
249
|
-
|
|
255
|
+
`have_stimulus_controller`, `have_stimulus_action`, and `have_stimulus_target` work in both **request specs** (parsing response HTML) and **system/feature specs** (Capybara).
|
|
250
256
|
|
|
251
257
|
```ruby
|
|
252
|
-
#
|
|
253
|
-
expect(
|
|
258
|
+
# Request specs — asserts Stimulus attributes in rendered HTML response
|
|
259
|
+
expect(response).to have_stimulus_controller("hello")
|
|
260
|
+
expect(response).to have_stimulus_action("click->hello#greet")
|
|
261
|
+
expect(response).to have_stimulus_action("hello#greet") # shorthand — matches any event
|
|
262
|
+
expect(response).to have_stimulus_target("hello", "name")
|
|
254
263
|
|
|
255
|
-
#
|
|
264
|
+
# System/feature specs — asserts on the live Capybara page
|
|
265
|
+
expect(page).to have_stimulus_controller("hello")
|
|
256
266
|
expect(page).to have_stimulus_action("click->hello#greet")
|
|
257
|
-
|
|
258
|
-
# Action — shorthand without event (matches any event prefix)
|
|
259
|
-
expect(page).to have_stimulus_action("hello#greet")
|
|
260
|
-
|
|
261
|
-
# Target — data-hello-target contains "name"
|
|
262
267
|
expect(page).to have_stimulus_target("hello", "name")
|
|
263
268
|
|
|
264
269
|
# Negation
|
|
270
|
+
expect(response).not_to have_stimulus_controller("missing")
|
|
265
271
|
expect(page).not_to have_stimulus_controller("missing")
|
|
266
272
|
```
|
|
267
273
|
|
|
@@ -430,6 +436,11 @@ refute_turbo_frame(response, id: "notifications")
|
|
|
430
436
|
|
|
431
437
|
# Custom failure message
|
|
432
438
|
assert_turbo_stream(response, action: :append, message: "expected append stream")
|
|
439
|
+
|
|
440
|
+
# Broadcast assertions (requires ActionCable test adapter)
|
|
441
|
+
assert_broadcasted_turbo_stream_to("notifications") { MyJob.perform_now }
|
|
442
|
+
assert_broadcasted_turbo_stream_to("notifications", action: :append, target: "messages") { MyJob.perform_now }
|
|
443
|
+
refute_broadcasted_turbo_stream_to("notifications") { MyJob.perform_now }
|
|
433
444
|
```
|
|
434
445
|
|
|
435
446
|
## Contributing
|
data/ROADMAP.md
CHANGED
|
@@ -2,14 +2,6 @@
|
|
|
2
2
|
|
|
3
3
|
RSpec matchers for [Turbo](https://github.com/hotwired/turbo-rails): Turbo Streams, Turbo Frames, and ActionCable broadcasts. The goal is to replace the hand-rolled helpers that every Rails/Turbo project accumulates.
|
|
4
4
|
|
|
5
|
-
## 1.6 — Minitest & Turbo 8 extensions
|
|
6
|
-
|
|
7
|
-
- **`assert_broadcasted_turbo_stream_to` / `refute_broadcasted_turbo_stream_to`** — `TurboRspec::Assertions` covers request-spec stream and frame assertions but has no broadcast counterpart. Teams using Minitest with ActionCable have no parity with the RSpec broadcast matcher.
|
|
8
|
-
- **`have_turbo_frame` Capybara `.eager` and `.strict` chains** — `.lazy` is already supported; Turbo 8 also added `loading="eager"` and a `[strict]` boolean attribute for strict frame loading. Both are missing from the Capybara matcher.
|
|
9
|
-
- **Stimulus request-spec matchers** — `have_stimulus_controller`, `have_stimulus_action`, and `have_stimulus_target` currently only work in Capybara system/feature specs. Teams writing request specs can't assert Stimulus attributes on rendered HTML responses without dropping to raw string matching.
|
|
10
|
-
|
|
11
|
-
---
|
|
12
|
-
|
|
13
5
|
## Guiding principles
|
|
14
6
|
|
|
15
7
|
- **Zero magic by default.** Auto-include only when it's unambiguous (Rails request specs). Everything else is opt-in.
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
+
require_relative "matchers/have_broadcasted_turbo_stream_to"
|
|
3
4
|
require_relative "matchers/have_turbo_frame"
|
|
4
5
|
require_relative "matchers/have_turbo_stream"
|
|
5
6
|
|
|
@@ -12,6 +13,30 @@ module TurboRspec
|
|
|
12
13
|
#
|
|
13
14
|
# No RSpec dependency required.
|
|
14
15
|
module Assertions
|
|
16
|
+
# Assert that a block broadcasts a +<turbo-stream>+ to the given stream.
|
|
17
|
+
# Requires ActionCable's test adapter.
|
|
18
|
+
#
|
|
19
|
+
# @param stream [String] the stream name
|
|
20
|
+
# @param action [Symbol, String, nil] optional action constraint
|
|
21
|
+
# @param target [String, nil] optional target constraint
|
|
22
|
+
# @param targets [String, nil] optional targets (CSS selector) constraint
|
|
23
|
+
# @param content [String, nil] optional content constraint
|
|
24
|
+
# @param partial [String, nil] optional partial path constraint
|
|
25
|
+
# @param message [String, nil] optional custom failure message
|
|
26
|
+
def assert_broadcasted_turbo_stream_to(stream, action: nil, target: nil, targets: nil, content: nil, partial: nil, message: nil, &block)
|
|
27
|
+
matcher = build_broadcast_matcher(stream, action: action, target: target, targets: targets, content: content, partial: partial)
|
|
28
|
+
assert matcher.matches?(block), message || matcher.failure_message
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
# Assert that a block does NOT broadcast a +<turbo-stream>+ to the given stream.
|
|
32
|
+
# Requires ActionCable's test adapter.
|
|
33
|
+
#
|
|
34
|
+
# @param stream [String] the stream name
|
|
35
|
+
def refute_broadcasted_turbo_stream_to(stream, action: nil, target: nil, targets: nil, content: nil, partial: nil, message: nil, &block)
|
|
36
|
+
matcher = build_broadcast_matcher(stream, action: action, target: target, targets: targets, content: content, partial: partial)
|
|
37
|
+
assert matcher.does_not_match?(block), message || matcher.failure_message_when_negated
|
|
38
|
+
end
|
|
39
|
+
|
|
15
40
|
def assert_turbo_stream(response_or_body, action: nil, target: nil, targets: nil, content: nil, partial: nil, message: nil)
|
|
16
41
|
matcher = build_stream_matcher(action: action, target: target, targets: targets, content: content, partial: partial)
|
|
17
42
|
assert matcher.matches?(response_or_body), message || matcher.failure_message
|
|
@@ -34,6 +59,16 @@ module TurboRspec
|
|
|
34
59
|
|
|
35
60
|
private
|
|
36
61
|
|
|
62
|
+
def build_broadcast_matcher(stream, action:, target:, targets:, content:, partial:)
|
|
63
|
+
matcher = Matchers::HaveBroadcastedTurboStreamTo.new(stream)
|
|
64
|
+
matcher.with_action(action) if action
|
|
65
|
+
matcher.targeting(target) if target
|
|
66
|
+
matcher.targeting_all(targets) if targets
|
|
67
|
+
matcher.with_content(content) if content
|
|
68
|
+
matcher.rendering(partial) if partial
|
|
69
|
+
matcher
|
|
70
|
+
end
|
|
71
|
+
|
|
37
72
|
def build_stream_matcher(action:, target:, targets:, content:, partial:)
|
|
38
73
|
matcher = Matchers::HaveTurboStream.new
|
|
39
74
|
matcher.with_action(action) if action
|
|
@@ -10,6 +10,8 @@ module TurboRspec
|
|
|
10
10
|
@loaded = false
|
|
11
11
|
@src = nil
|
|
12
12
|
@lazy = false
|
|
13
|
+
@eager = false
|
|
14
|
+
@strict = false
|
|
13
15
|
end
|
|
14
16
|
|
|
15
17
|
def with_content(text)
|
|
@@ -37,11 +39,27 @@ module TurboRspec
|
|
|
37
39
|
self
|
|
38
40
|
end
|
|
39
41
|
|
|
42
|
+
# Constrains the match to frames with +loading="eager"+.
|
|
43
|
+
# @return [self]
|
|
44
|
+
def eager
|
|
45
|
+
@eager = true
|
|
46
|
+
self
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
# Constrains the match to frames with the +[strict]+ attribute (Turbo 8).
|
|
50
|
+
# @return [self]
|
|
51
|
+
def strict
|
|
52
|
+
@strict = true
|
|
53
|
+
self
|
|
54
|
+
end
|
|
55
|
+
|
|
40
56
|
def matches?(page_or_node)
|
|
41
57
|
@node = find_frame(page_or_node)
|
|
42
58
|
return false unless @node
|
|
43
59
|
return false if @loaded && !@node[:complete]
|
|
44
60
|
return false if @lazy && @node[:loading] != "lazy"
|
|
61
|
+
return false if @eager && @node[:loading] != "eager"
|
|
62
|
+
return false if @strict && @node[:strict].nil?
|
|
45
63
|
return false if @src && @node[:src] != @src
|
|
46
64
|
return false if @content && !@node.has_content?(@content, wait: 0)
|
|
47
65
|
true
|
|
@@ -58,6 +76,10 @@ module TurboRspec
|
|
|
58
76
|
"expected turbo-frame##{@id} to be loaded (missing [complete] attribute)"
|
|
59
77
|
elsif @lazy && @node[:loading] != "lazy"
|
|
60
78
|
"expected turbo-frame##{@id} to be lazy (missing loading=\"lazy\" attribute)"
|
|
79
|
+
elsif @eager && @node[:loading] != "eager"
|
|
80
|
+
"expected turbo-frame##{@id} to be eager (missing loading=\"eager\" attribute)"
|
|
81
|
+
elsif @strict && @node[:strict].nil?
|
|
82
|
+
"expected turbo-frame##{@id} to be strict (missing [strict] attribute)"
|
|
61
83
|
elsif @src && @node[:src] != @src
|
|
62
84
|
"expected turbo-frame##{@id} to have src #{@src.inspect}, got #{@node[:src].inspect}"
|
|
63
85
|
else
|
|
@@ -85,6 +107,8 @@ module TurboRspec
|
|
|
85
107
|
parts = []
|
|
86
108
|
parts << " loaded" if @loaded
|
|
87
109
|
parts << " lazy" if @lazy
|
|
110
|
+
parts << " eager" if @eager
|
|
111
|
+
parts << " strict" if @strict
|
|
88
112
|
parts << " with src #{@src.inspect}" if @src
|
|
89
113
|
parts << " with content #{@content.inspect}" if @content
|
|
90
114
|
parts.join
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "nokogiri"
|
|
4
|
+
|
|
5
|
+
module TurboRspec
|
|
6
|
+
module Matchers
|
|
7
|
+
# RSpec matcher for asserting that a response body contains an element with
|
|
8
|
+
# the given Stimulus action descriptor (+data-action+ contains the descriptor
|
|
9
|
+
# as a space-separated token).
|
|
10
|
+
#
|
|
11
|
+
# Accepts a full descriptor (+click->hello#greet+) or a shorthand without
|
|
12
|
+
# an event (+hello#greet+), which matches any event prefix.
|
|
13
|
+
#
|
|
14
|
+
# @example Full descriptor
|
|
15
|
+
# expect(response).to have_stimulus_action("click->hello#greet")
|
|
16
|
+
#
|
|
17
|
+
# @example Shorthand (any event)
|
|
18
|
+
# expect(response).to have_stimulus_action("hello#greet")
|
|
19
|
+
class HaveStimulusAction
|
|
20
|
+
def initialize(action_descriptor)
|
|
21
|
+
@action_descriptor = action_descriptor.to_s
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
# @param response_or_body [#body, String]
|
|
25
|
+
# @return [Boolean]
|
|
26
|
+
def matches?(response_or_body)
|
|
27
|
+
@body = extract_body(response_or_body)
|
|
28
|
+
Nokogiri::HTML5.fragment(@body).css(selector).any?
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
# @param response_or_body [#body, String]
|
|
32
|
+
# @return [Boolean]
|
|
33
|
+
def does_not_match?(response_or_body)
|
|
34
|
+
!matches?(response_or_body)
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
# @return [String]
|
|
38
|
+
def failure_message
|
|
39
|
+
"expected response to have Stimulus action #{@action_descriptor.inspect}"
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
# @return [String]
|
|
43
|
+
def failure_message_when_negated
|
|
44
|
+
"expected response not to have Stimulus action #{@action_descriptor.inspect}"
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
# @return [String]
|
|
48
|
+
def description
|
|
49
|
+
"have Stimulus action #{@action_descriptor.inspect}"
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
private
|
|
53
|
+
|
|
54
|
+
def extract_body(response_or_body)
|
|
55
|
+
if response_or_body.respond_to?(:body)
|
|
56
|
+
response_or_body.body
|
|
57
|
+
else
|
|
58
|
+
response_or_body.to_s
|
|
59
|
+
end
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
def selector
|
|
63
|
+
if @action_descriptor.include?("->")
|
|
64
|
+
"[data-action~='#{@action_descriptor}']"
|
|
65
|
+
else
|
|
66
|
+
"[data-action*='#{@action_descriptor}']"
|
|
67
|
+
end
|
|
68
|
+
end
|
|
69
|
+
end
|
|
70
|
+
end
|
|
71
|
+
end
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "nokogiri"
|
|
4
|
+
|
|
5
|
+
module TurboRspec
|
|
6
|
+
module Matchers
|
|
7
|
+
# RSpec matcher for asserting that a response body contains an element with
|
|
8
|
+
# the given Stimulus controller (+data-controller+ contains the name as a
|
|
9
|
+
# space-separated token).
|
|
10
|
+
#
|
|
11
|
+
# @example
|
|
12
|
+
# expect(response).to have_stimulus_controller("hello")
|
|
13
|
+
# expect(response).not_to have_stimulus_controller("missing")
|
|
14
|
+
class HaveStimulusController
|
|
15
|
+
def initialize(controller_name)
|
|
16
|
+
@controller_name = controller_name.to_s
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
# @param response_or_body [#body, String]
|
|
20
|
+
# @return [Boolean]
|
|
21
|
+
def matches?(response_or_body)
|
|
22
|
+
@body = extract_body(response_or_body)
|
|
23
|
+
Nokogiri::HTML5.fragment(@body).css("[data-controller~='#{@controller_name}']").any?
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
# @param response_or_body [#body, String]
|
|
27
|
+
# @return [Boolean]
|
|
28
|
+
def does_not_match?(response_or_body)
|
|
29
|
+
!matches?(response_or_body)
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
# @return [String]
|
|
33
|
+
def failure_message
|
|
34
|
+
"expected response to have Stimulus controller #{@controller_name.inspect}"
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
# @return [String]
|
|
38
|
+
def failure_message_when_negated
|
|
39
|
+
"expected response not to have Stimulus controller #{@controller_name.inspect}"
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
# @return [String]
|
|
43
|
+
def description
|
|
44
|
+
"have Stimulus controller #{@controller_name.inspect}"
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
private
|
|
48
|
+
|
|
49
|
+
def extract_body(response_or_body)
|
|
50
|
+
if response_or_body.respond_to?(:body)
|
|
51
|
+
response_or_body.body
|
|
52
|
+
else
|
|
53
|
+
response_or_body.to_s
|
|
54
|
+
end
|
|
55
|
+
end
|
|
56
|
+
end
|
|
57
|
+
end
|
|
58
|
+
end
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "nokogiri"
|
|
4
|
+
|
|
5
|
+
module TurboRspec
|
|
6
|
+
module Matchers
|
|
7
|
+
# RSpec matcher for asserting that a response body contains an element with
|
|
8
|
+
# the given Stimulus target (+data-{controller}-target+ contains the target
|
|
9
|
+
# name as a space-separated token).
|
|
10
|
+
#
|
|
11
|
+
# @example
|
|
12
|
+
# expect(response).to have_stimulus_target("hello", "name")
|
|
13
|
+
# expect(response).not_to have_stimulus_target("hello", "missing")
|
|
14
|
+
class HaveStimulusTarget
|
|
15
|
+
def initialize(controller_name, target_name)
|
|
16
|
+
@controller_name = controller_name.to_s
|
|
17
|
+
@target_name = target_name.to_s
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
# @param response_or_body [#body, String]
|
|
21
|
+
# @return [Boolean]
|
|
22
|
+
def matches?(response_or_body)
|
|
23
|
+
@body = extract_body(response_or_body)
|
|
24
|
+
Nokogiri::HTML5.fragment(@body).css("[data-#{@controller_name}-target~='#{@target_name}']").any?
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
# @param response_or_body [#body, String]
|
|
28
|
+
# @return [Boolean]
|
|
29
|
+
def does_not_match?(response_or_body)
|
|
30
|
+
!matches?(response_or_body)
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
# @return [String]
|
|
34
|
+
def failure_message
|
|
35
|
+
"expected response to have Stimulus target #{@target_name.inspect} for controller #{@controller_name.inspect}"
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
# @return [String]
|
|
39
|
+
def failure_message_when_negated
|
|
40
|
+
"expected response not to have Stimulus target #{@target_name.inspect} for controller #{@controller_name.inspect}"
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
# @return [String]
|
|
44
|
+
def description
|
|
45
|
+
"have Stimulus target #{@target_name.inspect} for #{@controller_name.inspect}"
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
private
|
|
49
|
+
|
|
50
|
+
def extract_body(response_or_body)
|
|
51
|
+
if response_or_body.respond_to?(:body)
|
|
52
|
+
response_or_body.body
|
|
53
|
+
else
|
|
54
|
+
response_or_body.to_s
|
|
55
|
+
end
|
|
56
|
+
end
|
|
57
|
+
end
|
|
58
|
+
end
|
|
59
|
+
end
|
data/lib/turbo_rspec/matchers.rb
CHANGED
|
@@ -1,6 +1,9 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
3
|
require_relative "matchers/have_broadcasted_turbo_stream_to"
|
|
4
|
+
require_relative "matchers/have_stimulus_action"
|
|
5
|
+
require_relative "matchers/have_stimulus_controller"
|
|
6
|
+
require_relative "matchers/have_stimulus_target"
|
|
4
7
|
require_relative "matchers/have_turbo_frame"
|
|
5
8
|
require_relative "matchers/have_turbo_stream"
|
|
6
9
|
require_relative "matchers/have_turbo_streams"
|
|
@@ -48,6 +51,28 @@ module TurboRspec
|
|
|
48
51
|
HaveTurboStreams.new(matchers)
|
|
49
52
|
end
|
|
50
53
|
|
|
54
|
+
# Assert that a response body contains an element with the given Stimulus controller.
|
|
55
|
+
# @param controller_name [String]
|
|
56
|
+
# @return [HaveStimulusController]
|
|
57
|
+
def have_stimulus_controller(controller_name)
|
|
58
|
+
HaveStimulusController.new(controller_name)
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
# Assert that a response body contains an element with the given Stimulus action.
|
|
62
|
+
# @param action_descriptor [String]
|
|
63
|
+
# @return [HaveStimulusAction]
|
|
64
|
+
def have_stimulus_action(action_descriptor)
|
|
65
|
+
HaveStimulusAction.new(action_descriptor)
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
# Assert that a response body contains an element with the given Stimulus target.
|
|
69
|
+
# @param controller_name [String]
|
|
70
|
+
# @param target_name [String]
|
|
71
|
+
# @return [HaveStimulusTarget]
|
|
72
|
+
def have_stimulus_target(controller_name, target_name)
|
|
73
|
+
HaveStimulusTarget.new(controller_name, target_name)
|
|
74
|
+
end
|
|
75
|
+
|
|
51
76
|
# Assert that a response body matches a stored turbo stream snapshot.
|
|
52
77
|
# Creates the snapshot on the first run; diffs against it on subsequent runs.
|
|
53
78
|
# Set +UPDATE_TURBO_SNAPSHOTS=1+ to overwrite an existing snapshot.
|
data/lib/turbo_rspec/version.rb
CHANGED
data/sig/turbo_rspec.rbs
CHANGED
|
@@ -26,6 +26,9 @@ module TurboRspec
|
|
|
26
26
|
def have_turbo_stream: () -> HaveTurboStream
|
|
27
27
|
def assert_no_turbo_stream: () -> HaveTurboStream
|
|
28
28
|
def have_turbo_streams: (*HaveTurboStream matchers) -> HaveTurboStreams
|
|
29
|
+
def have_stimulus_controller: (String controller_name) -> HaveStimulusController
|
|
30
|
+
def have_stimulus_action: (String action_descriptor) -> HaveStimulusAction
|
|
31
|
+
def have_stimulus_target: (String controller_name, String target_name) -> HaveStimulusTarget
|
|
29
32
|
def match_turbo_stream_snapshot: (String name) -> MatchTurboStreamSnapshot
|
|
30
33
|
def have_turbo_frame: () -> HaveTurboFrame
|
|
31
34
|
def have_broadcasted_turbo_stream_to: (String | untyped stream_or_object) -> HaveBroadcastedTurboStreamTo
|
|
@@ -105,6 +108,33 @@ module TurboRspec
|
|
|
105
108
|
def description: () -> String
|
|
106
109
|
end
|
|
107
110
|
|
|
111
|
+
class HaveStimulusController
|
|
112
|
+
def initialize: (String controller_name) -> void
|
|
113
|
+
def matches?: (untyped response_or_body) -> bool
|
|
114
|
+
def does_not_match?: (untyped response_or_body) -> bool
|
|
115
|
+
def failure_message: () -> String
|
|
116
|
+
def failure_message_when_negated: () -> String
|
|
117
|
+
def description: () -> String
|
|
118
|
+
end
|
|
119
|
+
|
|
120
|
+
class HaveStimulusAction
|
|
121
|
+
def initialize: (String action_descriptor) -> void
|
|
122
|
+
def matches?: (untyped response_or_body) -> bool
|
|
123
|
+
def does_not_match?: (untyped response_or_body) -> bool
|
|
124
|
+
def failure_message: () -> String
|
|
125
|
+
def failure_message_when_negated: () -> String
|
|
126
|
+
def description: () -> String
|
|
127
|
+
end
|
|
128
|
+
|
|
129
|
+
class HaveStimulusTarget
|
|
130
|
+
def initialize: (String controller_name, String target_name) -> void
|
|
131
|
+
def matches?: (untyped response_or_body) -> bool
|
|
132
|
+
def does_not_match?: (untyped response_or_body) -> bool
|
|
133
|
+
def failure_message: () -> String
|
|
134
|
+
def failure_message_when_negated: () -> String
|
|
135
|
+
def description: () -> String
|
|
136
|
+
end
|
|
137
|
+
|
|
108
138
|
class MatchTurboStreamSnapshot
|
|
109
139
|
def initialize: (String name) -> void
|
|
110
140
|
def matches?: (untyped response_or_body) -> bool
|
|
@@ -130,6 +160,8 @@ module TurboRspec
|
|
|
130
160
|
end
|
|
131
161
|
|
|
132
162
|
module Assertions
|
|
163
|
+
def assert_broadcasted_turbo_stream_to: (String stream, ?action: (Symbol | String)?, ?target: String?, ?targets: String?, ?content: String?, ?partial: String?, ?message: String?) { () -> void } -> void
|
|
164
|
+
def refute_broadcasted_turbo_stream_to: (String stream, ?action: (Symbol | String)?, ?target: String?, ?targets: String?, ?content: String?, ?partial: String?, ?message: String?) { () -> void } -> void
|
|
133
165
|
def assert_turbo_stream: (untyped response_or_body, ?action: (Symbol | String)?, ?target: String?, ?targets: String?, ?content: String?, ?partial: String?, ?message: String?) -> void
|
|
134
166
|
def refute_turbo_stream: (untyped response_or_body, ?action: (Symbol | String)?, ?target: String?, ?targets: String?, ?content: String?, ?partial: String?, ?message: String?) -> void
|
|
135
167
|
def assert_turbo_frame: (untyped response_or_body, ?id: String?, ?content: String?, ?partial: String?, ?message: String?) -> void
|
|
@@ -153,6 +185,8 @@ module TurboRspec
|
|
|
153
185
|
def loaded: () -> self
|
|
154
186
|
def with_src: (String url) -> self
|
|
155
187
|
def lazy: () -> self
|
|
188
|
+
def eager: () -> self
|
|
189
|
+
def strict: () -> self
|
|
156
190
|
|
|
157
191
|
# Capybara matcher interface
|
|
158
192
|
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.6.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Chuck Smith
|
|
@@ -64,6 +64,9 @@ files:
|
|
|
64
64
|
- lib/turbo_rspec/helpers.rb
|
|
65
65
|
- lib/turbo_rspec/matchers.rb
|
|
66
66
|
- lib/turbo_rspec/matchers/have_broadcasted_turbo_stream_to.rb
|
|
67
|
+
- lib/turbo_rspec/matchers/have_stimulus_action.rb
|
|
68
|
+
- lib/turbo_rspec/matchers/have_stimulus_controller.rb
|
|
69
|
+
- lib/turbo_rspec/matchers/have_stimulus_target.rb
|
|
67
70
|
- lib/turbo_rspec/matchers/have_turbo_frame.rb
|
|
68
71
|
- lib/turbo_rspec/matchers/have_turbo_stream.rb
|
|
69
72
|
- lib/turbo_rspec/matchers/have_turbo_streams.rb
|