turbo_rspec 1.1.0 → 1.2.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 -0
- data/README.md +33 -1
- data/ROADMAP.md +0 -11
- data/lib/turbo_rspec/configuration.rb +6 -0
- data/lib/turbo_rspec/matchers/have_turbo_frame.rb +24 -2
- data/lib/turbo_rspec/matchers/have_turbo_stream.rb +54 -3
- data/lib/turbo_rspec/version.rb +1 -1
- data/lib/turbo_rspec.rb +21 -0
- data/sig/turbo_rspec.rbs +157 -2
- metadata +1 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 7341e9dfd6be8235a2269e94ef9923e470aa5da675ccd52f8d507f42c3950398
|
|
4
|
+
data.tar.gz: 65f4f2bb0aa32c4032d4ba2daad8c9442e20d056b3a455b0937eaeaf380f924a
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: b433e046578d5f3914a95cd443ed32020bff526bddf3bf79eea34ef7bc52a7ce06823ac25714f309842c357d97e0e70edd208b75a85c8e0a118e7d8b86b36c9f
|
|
7
|
+
data.tar.gz: 44d25746b61d50d932f74f92b2f6a07c50c70a6d320c452f302693b507617da38facf8fca1d0ac61f66cf01a49187e8288a20c6182b27cbe4ab91db42fe28ec5
|
data/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,16 @@
|
|
|
1
1
|
## [Unreleased]
|
|
2
2
|
|
|
3
|
+
## [1.2.0] - 2026-06-01
|
|
4
|
+
|
|
5
|
+
### Added
|
|
6
|
+
|
|
7
|
+
- `have_turbo_stream.with_attributes(hash)` — assert arbitrary HTML attributes on a stream element; accepts string or symbol keys
|
|
8
|
+
- `have_turbo_frame.with_attributes(hash)` — same for frame elements in request specs
|
|
9
|
+
- `have_turbo_stream.children_only` — assert the `children-only` attribute on Turbo 8 morph streams
|
|
10
|
+
- `TurboRspec.register_action(:name)` — declare custom Turbo stream actions; `with_action` now raises `ArgumentError` for unrecognised actions with a hint to register them
|
|
11
|
+
- `TurboRspec::BUILTIN_ACTIONS` — public constant listing all built-in action names
|
|
12
|
+
- `TurboRspec.known_actions` — returns built-in + registered custom actions
|
|
13
|
+
|
|
3
14
|
## [1.1.0] - 2026-06-01
|
|
4
15
|
|
|
5
16
|
### Added
|
data/README.md
CHANGED
|
@@ -99,13 +99,33 @@ expect(response).to have_turbo_stream
|
|
|
99
99
|
.targeting("messages")
|
|
100
100
|
.with_content("Hello")
|
|
101
101
|
|
|
102
|
+
# With arbitrary attributes
|
|
103
|
+
expect(response).to have_turbo_stream.with_attributes("data-controller" => "messages")
|
|
104
|
+
|
|
105
|
+
# Turbo 8 morph with children-only
|
|
106
|
+
expect(response).to have_turbo_stream.with_action(:morph).children_only
|
|
107
|
+
|
|
108
|
+
# Count qualifiers — assert how many matching streams appear
|
|
109
|
+
expect(response).to have_turbo_stream.with_action(:append).once
|
|
110
|
+
expect(response).to have_turbo_stream.with_action(:append).twice
|
|
111
|
+
expect(response).to have_turbo_stream.with_action(:append).exactly(3).times
|
|
112
|
+
expect(response).to have_turbo_stream.with_action(:append).at_least(2).times
|
|
113
|
+
expect(response).to have_turbo_stream.with_action(:append).at_most(1).times
|
|
114
|
+
|
|
102
115
|
# Negation
|
|
103
116
|
expect(response).not_to have_turbo_stream.with_action(:replace)
|
|
104
117
|
```
|
|
105
118
|
|
|
106
119
|
#### Actions
|
|
107
120
|
|
|
108
|
-
Turbo
|
|
121
|
+
Turbo's built-in stream actions: `append`, `prepend`, `replace`, `update`, `remove`, `before`, `after`, `refresh`, `morph`.
|
|
122
|
+
|
|
123
|
+
`with_action` raises `ArgumentError` for unrecognised names. Register custom actions in your test setup:
|
|
124
|
+
|
|
125
|
+
```ruby
|
|
126
|
+
# spec/support/turbo_rspec.rb
|
|
127
|
+
TurboRspec.register_action(:sparkle, :highlight)
|
|
128
|
+
```
|
|
109
129
|
|
|
110
130
|
### `have_turbo_streams`
|
|
111
131
|
|
|
@@ -189,6 +209,18 @@ expect(page).to have_turbo_frame("messages").with_content("Hello")
|
|
|
189
209
|
# Loaded (frame finished loading)
|
|
190
210
|
expect(page).to have_turbo_frame("messages").loaded
|
|
191
211
|
|
|
212
|
+
# Lazy frame — assert loading="lazy" attribute
|
|
213
|
+
expect(page).to have_turbo_frame("messages").lazy
|
|
214
|
+
|
|
215
|
+
# With src — assert the src attribute on a lazy-loaded frame
|
|
216
|
+
expect(page).to have_turbo_frame("messages").with_src("/messages/new")
|
|
217
|
+
|
|
218
|
+
# With arbitrary attributes (request specs only — use Capybara selectors in system specs)
|
|
219
|
+
expect(response).to have_turbo_frame.with_attributes("data-controller" => "chat")
|
|
220
|
+
|
|
221
|
+
# Chained
|
|
222
|
+
expect(page).to have_turbo_frame("messages").lazy.with_src("/messages/new")
|
|
223
|
+
|
|
192
224
|
# Negation
|
|
193
225
|
expect(page).not_to have_turbo_frame("notifications")
|
|
194
226
|
```
|
data/ROADMAP.md
CHANGED
|
@@ -4,17 +4,6 @@ RSpec matchers for [Turbo](https://github.com/hotwired/turbo-rails): Turbo Strea
|
|
|
4
4
|
|
|
5
5
|
---
|
|
6
6
|
|
|
7
|
-
## 1.1 — Matcher parity
|
|
8
|
-
|
|
9
|
-
- **Count qualifiers on `have_turbo_stream`** — `.once`, `.twice`, `.exactly(n).times` for request specs, matching the parity already present on `have_broadcasted_turbo_stream_to`. A single response with two `append` streams is a real case that currently requires two separate expectations.
|
|
10
|
-
- **Capybara `have_turbo_frame` additions** — `.with_src(url)` (assert the `src` attribute of a lazy frame) and `.lazy` (assert `loading="lazy"` is set). These cover the lazy-loading pattern that's very common with Turbo Frames.
|
|
11
|
-
|
|
12
|
-
## 1.2 — Turbo 8 / morphing
|
|
13
|
-
|
|
14
|
-
- **`with_attributes(hash)` chain** — generic attribute assertion for both `have_turbo_stream` and `have_turbo_frame`. Covers non-standard attributes (e.g., `data-*` passed through custom actions) without needing new named chains per attribute.
|
|
15
|
-
- **Morph-aware assertions** — `morph` is a supported action but Turbo 8's `<turbo-stream action="morph">` has distinct semantics (`[children-only]` attribute). A `.children_only` chain on `have_turbo_stream` would make morph specs readable without raw attribute checks.
|
|
16
|
-
- **Custom Turbo action support** — Turbo 8 allows registering custom stream actions. A `TurboRspec.register_action(:sparkle)` API would let teams assert their own actions without them being treated as unknown values.
|
|
17
|
-
|
|
18
7
|
## 1.3 — Stimulus companion
|
|
19
8
|
|
|
20
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.
|
|
@@ -13,8 +13,14 @@ module TurboRspec
|
|
|
13
13
|
# @return [Boolean]
|
|
14
14
|
attr_accessor :auto_include
|
|
15
15
|
|
|
16
|
+
# @!attribute [rw] custom_actions
|
|
17
|
+
# Additional stream action names registered via +TurboRspec.register_action+.
|
|
18
|
+
# @return [Array<String>]
|
|
19
|
+
attr_accessor :custom_actions
|
|
20
|
+
|
|
16
21
|
def initialize
|
|
17
22
|
@auto_include = true
|
|
23
|
+
@custom_actions = []
|
|
18
24
|
end
|
|
19
25
|
end
|
|
20
26
|
end
|
|
@@ -21,6 +21,7 @@ module TurboRspec
|
|
|
21
21
|
@id = nil
|
|
22
22
|
@content = nil
|
|
23
23
|
@partial = nil
|
|
24
|
+
@attributes = {}
|
|
24
25
|
end
|
|
25
26
|
|
|
26
27
|
# Constrains the match to frames with the given id attribute.
|
|
@@ -47,6 +48,14 @@ module TurboRspec
|
|
|
47
48
|
self
|
|
48
49
|
end
|
|
49
50
|
|
|
51
|
+
# Constrains the match to frames that have all of the given HTML attributes.
|
|
52
|
+
# @param attrs [Hash]
|
|
53
|
+
# @return [self]
|
|
54
|
+
def with_attributes(attrs)
|
|
55
|
+
@attributes = attrs.transform_keys(&:to_s).transform_values(&:to_s)
|
|
56
|
+
self
|
|
57
|
+
end
|
|
58
|
+
|
|
50
59
|
# @param response_or_body [#body, String]
|
|
51
60
|
# @return [Boolean]
|
|
52
61
|
def matches?(response_or_body)
|
|
@@ -92,7 +101,8 @@ module TurboRspec
|
|
|
92
101
|
def frame_matches?(frame)
|
|
93
102
|
matches_id?(frame) &&
|
|
94
103
|
matches_content?(frame) &&
|
|
95
|
-
matches_partial?(frame)
|
|
104
|
+
matches_partial?(frame) &&
|
|
105
|
+
matches_attributes?(frame)
|
|
96
106
|
end
|
|
97
107
|
|
|
98
108
|
def matches_id?(frame)
|
|
@@ -109,11 +119,17 @@ module TurboRspec
|
|
|
109
119
|
frame.to_html.include?(@partial)
|
|
110
120
|
end
|
|
111
121
|
|
|
122
|
+
def matches_attributes?(frame)
|
|
123
|
+
return true if @attributes.empty?
|
|
124
|
+
@attributes.all? { |k, v| frame[k] == v }
|
|
125
|
+
end
|
|
126
|
+
|
|
112
127
|
def constraint_description
|
|
113
128
|
parts = []
|
|
114
129
|
parts << " with id #{@id.inspect}" if @id
|
|
115
130
|
parts << " with content #{@content.inspect}" if @content
|
|
116
131
|
parts << " rendering #{@partial.inspect}" if @partial
|
|
132
|
+
parts << " with attributes #{@attributes.inspect}" if @attributes.any?
|
|
117
133
|
parts.join
|
|
118
134
|
end
|
|
119
135
|
|
|
@@ -144,11 +160,13 @@ module TurboRspec
|
|
|
144
160
|
count += 1 if !@id.nil? && matches_id?(frame)
|
|
145
161
|
count += 1 if !@content.nil? && matches_content?(frame)
|
|
146
162
|
count += 1 if !@partial.nil? && matches_partial?(frame)
|
|
163
|
+
count += 1 if @attributes.any? && matches_attributes?(frame)
|
|
147
164
|
count
|
|
148
165
|
end
|
|
149
166
|
|
|
150
167
|
def constraint_count
|
|
151
|
-
[@id, @content, @partial].count { |c| !c.nil? }
|
|
168
|
+
[@id, @content, @partial].count { |c| !c.nil? } +
|
|
169
|
+
(@attributes.any? ? 1 : 0)
|
|
152
170
|
end
|
|
153
171
|
|
|
154
172
|
def constraint_diff(frame)
|
|
@@ -156,6 +174,10 @@ module TurboRspec
|
|
|
156
174
|
lines << " #{matches_id?(frame) ? "✓" : "✗"} id: expected #{@id.inspect}, got #{frame["id"].inspect}" if @id
|
|
157
175
|
lines << " #{matches_content?(frame) ? "✓" : "✗"} content: expected to include #{@content.inspect}, got #{frame.text.strip.slice(0, 50).inspect}" if @content
|
|
158
176
|
lines << " #{matches_partial?(frame) ? "✓" : "✗"} rendering: expected to include #{@partial.inspect}" if @partial
|
|
177
|
+
@attributes.each do |k, v|
|
|
178
|
+
actual = frame[k]
|
|
179
|
+
lines << " #{(actual == v) ? "✓" : "✗"} attr[#{k}]: expected #{v.inspect}, got #{actual.inspect}"
|
|
180
|
+
end
|
|
159
181
|
lines
|
|
160
182
|
end
|
|
161
183
|
end
|
|
@@ -28,6 +28,8 @@ module TurboRspec
|
|
|
28
28
|
@target_all = nil
|
|
29
29
|
@content = nil
|
|
30
30
|
@partial = nil
|
|
31
|
+
@attributes = {}
|
|
32
|
+
@children_only = false
|
|
31
33
|
@expected_count = nil
|
|
32
34
|
@count_type = :at_least
|
|
33
35
|
@matching_count = 0
|
|
@@ -35,9 +37,15 @@ module TurboRspec
|
|
|
35
37
|
|
|
36
38
|
# Constrains the match to streams with the given action.
|
|
37
39
|
# @param action [Symbol, String] e.g. +:append+, +:replace+, +:remove+, +:refresh+, +:morph+
|
|
40
|
+
# @raise [ArgumentError] if the action is not a known built-in or registered custom action
|
|
38
41
|
# @return [self]
|
|
39
42
|
def with_action(action)
|
|
40
|
-
|
|
43
|
+
action_str = action.to_s
|
|
44
|
+
unless TurboRspec.known_actions.include?(action_str)
|
|
45
|
+
raise ArgumentError, "Unknown Turbo stream action #{action_str.inspect}. " \
|
|
46
|
+
"Register custom actions with TurboRspec.register_action(:#{action_str})."
|
|
47
|
+
end
|
|
48
|
+
@action = action_str
|
|
41
49
|
self
|
|
42
50
|
end
|
|
43
51
|
|
|
@@ -73,6 +81,26 @@ module TurboRspec
|
|
|
73
81
|
self
|
|
74
82
|
end
|
|
75
83
|
|
|
84
|
+
# Constrains the match to streams that have all of the given HTML attributes.
|
|
85
|
+
# Keys and values are compared as strings.
|
|
86
|
+
# @param attrs [Hash]
|
|
87
|
+
# @return [self]
|
|
88
|
+
# @example
|
|
89
|
+
# expect(response).to have_turbo_stream.with_attributes("data-controller" => "messages")
|
|
90
|
+
def with_attributes(attrs)
|
|
91
|
+
@attributes = attrs.transform_keys(&:to_s).transform_values(&:to_s)
|
|
92
|
+
self
|
|
93
|
+
end
|
|
94
|
+
|
|
95
|
+
# Constrains the match to morph streams with the +children-only+ attribute set.
|
|
96
|
+
# @return [self]
|
|
97
|
+
# @example
|
|
98
|
+
# expect(response).to have_turbo_stream.with_action(:morph).children_only
|
|
99
|
+
def children_only
|
|
100
|
+
@children_only = true
|
|
101
|
+
self
|
|
102
|
+
end
|
|
103
|
+
|
|
76
104
|
# Asserts exactly one matching stream.
|
|
77
105
|
# @return [self]
|
|
78
106
|
def once
|
|
@@ -167,7 +195,9 @@ module TurboRspec
|
|
|
167
195
|
matches_target?(stream) &&
|
|
168
196
|
matches_target_all?(stream) &&
|
|
169
197
|
matches_content?(stream) &&
|
|
170
|
-
matches_partial?(stream)
|
|
198
|
+
matches_partial?(stream) &&
|
|
199
|
+
matches_attributes?(stream) &&
|
|
200
|
+
matches_children_only?(stream)
|
|
171
201
|
end
|
|
172
202
|
|
|
173
203
|
def matches_action?(stream)
|
|
@@ -192,6 +222,16 @@ module TurboRspec
|
|
|
192
222
|
stream.to_html.include?(@partial)
|
|
193
223
|
end
|
|
194
224
|
|
|
225
|
+
def matches_attributes?(stream)
|
|
226
|
+
return true if @attributes.empty?
|
|
227
|
+
@attributes.all? { |k, v| stream[k] == v }
|
|
228
|
+
end
|
|
229
|
+
|
|
230
|
+
def matches_children_only?(stream)
|
|
231
|
+
return true unless @children_only
|
|
232
|
+
!stream["children-only"].nil?
|
|
233
|
+
end
|
|
234
|
+
|
|
195
235
|
def count_matches?(n)
|
|
196
236
|
if @expected_count.nil?
|
|
197
237
|
n >= 1
|
|
@@ -213,6 +253,8 @@ module TurboRspec
|
|
|
213
253
|
parts << " targeting all #{@target_all.inspect}" if @target_all
|
|
214
254
|
parts << " with content #{@content.inspect}" if @content
|
|
215
255
|
parts << " rendering #{@partial.inspect}" if @partial
|
|
256
|
+
parts << " with attributes #{@attributes.inspect}" if @attributes.any?
|
|
257
|
+
parts << " children only" if @children_only
|
|
216
258
|
parts.join
|
|
217
259
|
end
|
|
218
260
|
|
|
@@ -261,11 +303,15 @@ module TurboRspec
|
|
|
261
303
|
count += 1 if !@target_all.nil? && matches_target_all?(stream)
|
|
262
304
|
count += 1 if !@content.nil? && matches_content?(stream)
|
|
263
305
|
count += 1 if !@partial.nil? && matches_partial?(stream)
|
|
306
|
+
count += 1 if @attributes.any? && matches_attributes?(stream)
|
|
307
|
+
count += 1 if @children_only && matches_children_only?(stream)
|
|
264
308
|
count
|
|
265
309
|
end
|
|
266
310
|
|
|
267
311
|
def constraint_count
|
|
268
|
-
[@action, @target, @target_all, @content, @partial].count { |c| !c.nil? }
|
|
312
|
+
[@action, @target, @target_all, @content, @partial].count { |c| !c.nil? } +
|
|
313
|
+
(@attributes.any? ? 1 : 0) +
|
|
314
|
+
(@children_only ? 1 : 0)
|
|
269
315
|
end
|
|
270
316
|
|
|
271
317
|
def constraint_diff(stream)
|
|
@@ -275,6 +321,11 @@ module TurboRspec
|
|
|
275
321
|
lines << " #{matches_target_all?(stream) ? "✓" : "✗"} targets: expected #{@target_all.inspect}, got #{stream["targets"].inspect}" if @target_all
|
|
276
322
|
lines << " #{matches_content?(stream) ? "✓" : "✗"} content: expected to include #{@content.inspect}, got #{stream.text.strip.slice(0, 50).inspect}" if @content
|
|
277
323
|
lines << " #{matches_partial?(stream) ? "✓" : "✗"} rendering: expected to include #{@partial.inspect}" if @partial
|
|
324
|
+
@attributes.each do |k, v|
|
|
325
|
+
actual = stream[k]
|
|
326
|
+
lines << " #{(actual == v) ? "✓" : "✗"} attr[#{k}]: expected #{v.inspect}, got #{actual.inspect}"
|
|
327
|
+
end
|
|
328
|
+
lines << " #{matches_children_only?(stream) ? "✓" : "✗"} children-only: expected attribute to be present" if @children_only
|
|
278
329
|
lines
|
|
279
330
|
end
|
|
280
331
|
end
|
data/lib/turbo_rspec/version.rb
CHANGED
data/lib/turbo_rspec.rb
CHANGED
|
@@ -19,6 +19,9 @@ module TurboRspec
|
|
|
19
19
|
# Base error class for TurboRspec.
|
|
20
20
|
class Error < StandardError; end
|
|
21
21
|
|
|
22
|
+
# Built-in Turbo stream action names.
|
|
23
|
+
BUILTIN_ACTIONS = %w[append prepend replace update remove before after refresh morph].freeze
|
|
24
|
+
|
|
22
25
|
class << self
|
|
23
26
|
# Yields the configuration object for customization.
|
|
24
27
|
#
|
|
@@ -46,6 +49,24 @@ module TurboRspec
|
|
|
46
49
|
@configuration = Configuration.new
|
|
47
50
|
end
|
|
48
51
|
|
|
52
|
+
# Registers one or more custom Turbo stream action names so that
|
|
53
|
+
# +have_turbo_stream.with_action(:name)+ does not raise +ArgumentError+.
|
|
54
|
+
#
|
|
55
|
+
# @param actions [Array<Symbol, String>]
|
|
56
|
+
# @return [void]
|
|
57
|
+
# @example
|
|
58
|
+
# TurboRspec.register_action(:sparkle, :highlight)
|
|
59
|
+
def register_action(*actions)
|
|
60
|
+
configuration.custom_actions |= actions.map(&:to_s)
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
# Returns all known stream action names (built-in + registered custom).
|
|
64
|
+
#
|
|
65
|
+
# @return [Array<String>]
|
|
66
|
+
def known_actions
|
|
67
|
+
BUILTIN_ACTIONS + configuration.custom_actions
|
|
68
|
+
end
|
|
69
|
+
|
|
49
70
|
# Installs RSpec integration — includes matchers and helpers into the
|
|
50
71
|
# appropriate example groups. Called automatically when RSpec is present.
|
|
51
72
|
#
|
data/sig/turbo_rspec.rbs
CHANGED
|
@@ -1,4 +1,159 @@
|
|
|
1
1
|
module TurboRspec
|
|
2
2
|
VERSION: String
|
|
3
|
-
|
|
4
|
-
|
|
3
|
+
|
|
4
|
+
# All built-in Turbo stream action names.
|
|
5
|
+
BUILTIN_ACTIONS: Array[String]
|
|
6
|
+
|
|
7
|
+
def self.configure: () { (Configuration) -> void } -> void
|
|
8
|
+
def self.configuration: () -> Configuration
|
|
9
|
+
def self.reset_configuration!: () -> void
|
|
10
|
+
def self.register_action: (*(Symbol | String) actions) -> void
|
|
11
|
+
def self.known_actions: () -> Array[String]
|
|
12
|
+
def self.install_rspec_integration: (untyped config) -> void
|
|
13
|
+
|
|
14
|
+
class Error < StandardError
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
class Configuration
|
|
18
|
+
attr_accessor auto_include: bool
|
|
19
|
+
attr_accessor custom_actions: Array[String]
|
|
20
|
+
|
|
21
|
+
def initialize: () -> void
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
module Matchers
|
|
25
|
+
def have_turbo_stream: () -> HaveTurboStream
|
|
26
|
+
def assert_no_turbo_stream: () -> HaveTurboStream
|
|
27
|
+
def have_turbo_streams: (*HaveTurboStream matchers) -> HaveTurboStreams
|
|
28
|
+
def have_turbo_frame: () -> HaveTurboFrame
|
|
29
|
+
def have_broadcasted_turbo_stream_to: (String | untyped stream_or_object) -> HaveBroadcastedTurboStreamTo
|
|
30
|
+
def broadcast_turbo_stream_to: (String | untyped stream_or_object) -> HaveBroadcastedTurboStreamTo
|
|
31
|
+
|
|
32
|
+
class HaveTurboStream
|
|
33
|
+
def initialize: () -> void
|
|
34
|
+
|
|
35
|
+
# Constraint chain
|
|
36
|
+
def with_action: (Symbol | String action) -> self
|
|
37
|
+
def targeting: (String dom_id) -> self
|
|
38
|
+
def targeting_all: (String selector) -> self
|
|
39
|
+
def with_content: (String text) -> self
|
|
40
|
+
def rendering: (String partial) -> self
|
|
41
|
+
def with_attributes: (Hash[String | Symbol, untyped] attrs) -> self
|
|
42
|
+
def children_only: () -> self
|
|
43
|
+
|
|
44
|
+
# Count qualifiers
|
|
45
|
+
def once: () -> self
|
|
46
|
+
def twice: () -> self
|
|
47
|
+
def exactly: (Integer n) -> self
|
|
48
|
+
def at_least: (Integer n) -> self
|
|
49
|
+
def at_most: (Integer n) -> self
|
|
50
|
+
def times: () -> self
|
|
51
|
+
|
|
52
|
+
# RSpec matcher interface
|
|
53
|
+
def matches?: (untyped response_or_body) -> bool
|
|
54
|
+
def does_not_match?: (untyped response_or_body) -> bool
|
|
55
|
+
def failure_message: () -> String
|
|
56
|
+
def failure_message_when_negated: () -> String
|
|
57
|
+
def description: () -> String
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
class HaveTurboFrame
|
|
61
|
+
def initialize: () -> void
|
|
62
|
+
|
|
63
|
+
# Constraint chain
|
|
64
|
+
def with_id: (String id) -> self
|
|
65
|
+
def with_content: (String text) -> self
|
|
66
|
+
def rendering: (String partial) -> self
|
|
67
|
+
def with_attributes: (Hash[String | Symbol, untyped] attrs) -> self
|
|
68
|
+
|
|
69
|
+
# RSpec matcher interface
|
|
70
|
+
def matches?: (untyped response_or_body) -> bool
|
|
71
|
+
def does_not_match?: (untyped response_or_body) -> bool
|
|
72
|
+
def failure_message: () -> String
|
|
73
|
+
def failure_message_when_negated: () -> String
|
|
74
|
+
def description: () -> String
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
class HaveBroadcastedTurboStreamTo
|
|
78
|
+
def initialize: (String | untyped stream_or_object) -> void
|
|
79
|
+
|
|
80
|
+
# Constraint chain (mirrors HaveTurboStream)
|
|
81
|
+
def with_action: (Symbol | String action) -> self
|
|
82
|
+
def targeting: (String dom_id) -> self
|
|
83
|
+
def targeting_all: (String selector) -> self
|
|
84
|
+
def with_content: (String text) -> self
|
|
85
|
+
def rendering: (String partial) -> self
|
|
86
|
+
|
|
87
|
+
# Count qualifiers
|
|
88
|
+
def once: () -> self
|
|
89
|
+
def twice: () -> self
|
|
90
|
+
def exactly: (Integer n) -> self
|
|
91
|
+
def at_least: (Integer n) -> self
|
|
92
|
+
def at_most: (Integer n) -> self
|
|
93
|
+
def times: () -> self
|
|
94
|
+
|
|
95
|
+
# RSpec block-expectation matcher interface
|
|
96
|
+
def supports_block_expectations?: () -> bool
|
|
97
|
+
def matches?: (untyped block) -> bool
|
|
98
|
+
def does_not_match?: (untyped block) -> bool
|
|
99
|
+
def failure_message: () -> String
|
|
100
|
+
def failure_message_when_negated: () -> String
|
|
101
|
+
def description: () -> String
|
|
102
|
+
end
|
|
103
|
+
|
|
104
|
+
class HaveTurboStreams
|
|
105
|
+
def initialize: (Array[HaveTurboStream] expected_streams) -> void
|
|
106
|
+
def matches?: (untyped response_or_body) -> bool
|
|
107
|
+
def does_not_match?: (untyped response_or_body) -> bool
|
|
108
|
+
def failure_message: () -> String
|
|
109
|
+
def failure_message_when_negated: () -> String
|
|
110
|
+
def description: () -> String
|
|
111
|
+
end
|
|
112
|
+
end
|
|
113
|
+
|
|
114
|
+
module Helpers
|
|
115
|
+
def turbo_stream_html: (action: Symbol | String, ?target: String?, ?targets: String?, ?content: String?) -> String
|
|
116
|
+
def turbo_frame_html: (id: String, ?content: String?) -> String
|
|
117
|
+
end
|
|
118
|
+
|
|
119
|
+
module Assertions
|
|
120
|
+
def assert_turbo_stream: (untyped response_or_body, ?action: (Symbol | String)?, ?target: String?, ?targets: String?, ?content: String?, ?partial: String?, ?message: String?) -> void
|
|
121
|
+
def refute_turbo_stream: (untyped response_or_body, ?action: (Symbol | String)?, ?target: String?, ?targets: String?, ?content: String?, ?partial: String?, ?message: String?) -> void
|
|
122
|
+
def assert_turbo_frame: (untyped response_or_body, ?id: String?, ?content: String?, ?partial: String?, ?message: String?) -> void
|
|
123
|
+
def refute_turbo_frame: (untyped response_or_body, ?id: String?, ?content: String?, ?partial: String?, ?message: String?) -> void
|
|
124
|
+
end
|
|
125
|
+
|
|
126
|
+
module Capybara
|
|
127
|
+
module Matchers
|
|
128
|
+
def have_turbo_frame: (String id) -> HaveTurboFrame
|
|
129
|
+
def have_turbo_stream_tag: (?String? signed_stream_name) -> HaveTurboStreamTag
|
|
130
|
+
def within_turbo_frame: (String id) { () -> void } -> void
|
|
131
|
+
|
|
132
|
+
class HaveTurboFrame
|
|
133
|
+
def initialize: (String id) -> void
|
|
134
|
+
|
|
135
|
+
# Constraint chain
|
|
136
|
+
def with_content: (String text) -> self
|
|
137
|
+
def loaded: () -> self
|
|
138
|
+
def with_src: (String url) -> self
|
|
139
|
+
def lazy: () -> self
|
|
140
|
+
|
|
141
|
+
# Capybara matcher interface
|
|
142
|
+
def matches?: (untyped page_or_node) -> bool
|
|
143
|
+
def does_not_match?: (untyped page_or_node) -> bool
|
|
144
|
+
def failure_message: () -> String
|
|
145
|
+
def failure_message_when_negated: () -> String
|
|
146
|
+
def description: () -> String
|
|
147
|
+
end
|
|
148
|
+
|
|
149
|
+
class HaveTurboStreamTag
|
|
150
|
+
def initialize: (?signed_stream_name: String?) -> void
|
|
151
|
+
def matches?: (untyped page_or_node) -> bool
|
|
152
|
+
def does_not_match?: (untyped page_or_node) -> bool
|
|
153
|
+
def failure_message: () -> String
|
|
154
|
+
def failure_message_when_negated: () -> String
|
|
155
|
+
def description: () -> String
|
|
156
|
+
end
|
|
157
|
+
end
|
|
158
|
+
end
|
|
159
|
+
end
|