turbo_rspec 0.6.0 → 0.7.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 7e6568858c6d2ba4487a184d5161e3a254368447704c86437f7f5753003af0bb
4
- data.tar.gz: c3b05381ef257037eb0112e3bc925d4db1973780e16d6d56a9cd842db9f4d994
3
+ metadata.gz: 3049c79cdd7a1c8b6dc2447fb2034458f129b900343dcc759e5f8b1ae200189b
4
+ data.tar.gz: ca51420063475b7870a6d77e95d0e4b33f5d2b0e725c4f9b2419acdd4dbb4cd9
5
5
  SHA512:
6
- metadata.gz: 710e2a679f38e346a19031c81441b2ce56e0317b5b770275a1d69d1117fb7eb48af0551e1c5c6a62cd4651cdee9cc6976c32ec038472b3dd5beea2d5d978e662
7
- data.tar.gz: 557f56e9807599846b81204772ec47919daf2621ef090253971f1f9f9390bc5f22a63e438a9c4715dedbb939c44c4c517654852ac41fc87392bee88b4f8ff83d
6
+ metadata.gz: 3e096c87e0b36b9c291308e95a30f075c4c3761d25c9b2d6779647a5e6ace92ed7ff60b5e04cd31b837611dfe92098067a9cb7a4c302e8c12d26eba7bd0d6840
7
+ data.tar.gz: 82fcd0aaf837b61d78c079619d52ab99d79d99bc6d2eafec7cd1ce56f0a6558e3b69748928fadbc81fc0651b8910c4b0d44d45795f623c183b012ff423bf44e2
data/.yardopts ADDED
@@ -0,0 +1,6 @@
1
+ --markup markdown
2
+ --output-dir doc/
3
+ lib/**/*.rb
4
+ -
5
+ README.md
6
+ docs/*.md
data/CHANGELOG.md CHANGED
@@ -1,5 +1,13 @@
1
1
  ## [Unreleased]
2
2
 
3
+ ## [0.7.0] - 2026-05-28
4
+
5
+ ### Added
6
+
7
+ - Full YARD documentation on all public methods, classes, and modules
8
+ - `docs/migration_guide.md` — how to replace hand-rolled Turbo helpers with `turbo_rspec` matchers
9
+ - `docs/cookbook.md` — common patterns: request specs, lazy frames, broadcast job specs, multi-stream responses, Minitest, controller specs
10
+
3
11
  ## [0.6.0] - 2026-05-28
4
12
 
5
13
  ### Added
data/README.md CHANGED
@@ -8,6 +8,8 @@
8
8
 
9
9
  RSpec matchers for [Turbo](https://github.com/hotwired/turbo-rails) — assert Turbo Stream responses, Turbo Frame content, and ActionCable broadcasts without hand-rolling helpers in every project.
10
10
 
11
+ **Docs:** [API Reference](https://rubydoc.info/gems/turbo_rspec) · [Migration Guide](docs/migration_guide.md) · [Cookbook](docs/cookbook.md)
12
+
11
13
  ## Installation
12
14
 
13
15
  Add to your application's `Gemfile`:
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
- ## v0.7.0 — Documentation
8
-
9
- **Goal:** full docs before freezing the API.
10
-
11
- - Full YARD documentation on all public methods and classes
12
- - Migration guide: "replacing hand-rolled Turbo helpers in your test suite"
13
- - Cookbook: common patterns (lazy-loaded frames, job broadcast testing, multi-stream responses, controller specs)
14
- - Hosted on RubyDoc.info
15
-
16
- ---
17
-
18
7
  ## v1.0.0 — Stable API
19
8
 
20
9
  **Goal:** API freeze. Commit to semver stability. Make the gem the obvious default choice.
@@ -24,13 +13,13 @@ RSpec matchers for [Turbo](https://github.com/hotwired/turbo-rails): Turbo Strea
24
13
  - 100% branch coverage enforced in CI (`simplecov`)
25
14
  - Performance: benchmark matcher overhead to keep it negligible in large suites
26
15
  - `bin/release` script (mirrors solid_queue_web pattern): bump version, update CHANGELOG, tag, push; CI publishes via Trusted Publishing
16
+ - `turbo_rspec` generator (`rails generate turbo_rspec:install`) to scaffold `spec/support/turbo.rb`
27
17
 
28
18
  ---
29
19
 
30
20
  ## Post-1.0 ideas (not scheduled)
31
21
 
32
22
  - VS Code / RubyMine snippet pack for common patterns
33
- - `turbo_rspec` generator (`rails generate turbo_rspec:install`) to scaffold `spec/support/turbo.rb`
34
23
  - Playwright/Puppeteer bridge for headless assertions outside Capybara
35
24
  - Shared examples: `it_behaves_like "a turbo stream response"` for controller testing
36
25
 
data/docs/cookbook.md ADDED
@@ -0,0 +1,185 @@
1
+ # Cookbook: Common Turbo Testing Patterns
2
+
3
+ ## Request specs
4
+
5
+ ### Asserting a single stream
6
+
7
+ ```ruby
8
+ RSpec.describe "Messages", type: :request do
9
+ it "appends the new message" do
10
+ post messages_path, params: { message: { body: "Hello" } },
11
+ headers: { "Accept" => "text/vnd.turbo-stream.html" }
12
+
13
+ expect(response).to have_turbo_stream
14
+ .with_action(:append)
15
+ .targeting("messages")
16
+ .with_content("Hello")
17
+ end
18
+ end
19
+ ```
20
+
21
+ ### Asserting multiple streams in one expectation
22
+
23
+ ```ruby
24
+ it "updates the list and clears the form" do
25
+ post messages_path, params: { message: { body: "Hello" } }, as: :turbo_stream
26
+
27
+ expect(response).to have_turbo_streams(
28
+ have_turbo_stream.with_action(:append).targeting("messages"),
29
+ have_turbo_stream.with_action(:replace).targeting("message_form")
30
+ )
31
+ end
32
+ ```
33
+
34
+ ### Using shared examples
35
+
36
+ ```ruby
37
+ RSpec.describe "Messages", type: :request do
38
+ describe "POST /messages" do
39
+ before { post messages_path, params: { body: "Hello" }, as: :turbo_stream }
40
+
41
+ it_behaves_like "a turbo stream response", action: :append, target: "messages"
42
+ end
43
+ end
44
+ ```
45
+
46
+ ### Asserting a remove stream
47
+
48
+ ```ruby
49
+ it "removes the deleted message" do
50
+ delete message_path(message), as: :turbo_stream
51
+
52
+ expect(response).to have_turbo_stream
53
+ .with_action(:remove)
54
+ .targeting("message_#{message.id}")
55
+ end
56
+ ```
57
+
58
+ ### Asserting a Turbo Frame response
59
+
60
+ ```ruby
61
+ it "renders the edit form in the frame" do
62
+ get edit_message_path(message)
63
+
64
+ expect(response).to have_turbo_frame.with_id("message_#{message.id}")
65
+ end
66
+ ```
67
+
68
+ ## Lazy-loaded Turbo Frames
69
+
70
+ ```ruby
71
+ it "lazy-loads the message list frame" do
72
+ get messages_path
73
+
74
+ # Assert the frame tag is rendered in the page
75
+ expect(response.body).to include('turbo-frame id="messages"')
76
+ end
77
+
78
+ it "responds to the frame src request" do
79
+ get messages_path, headers: { "Turbo-Frame" => "messages" }
80
+
81
+ expect(response).to have_turbo_frame.with_id("messages")
82
+ end
83
+ ```
84
+
85
+ ## Broadcast matchers in job specs
86
+
87
+ ### Basic broadcast assertion
88
+
89
+ ```ruby
90
+ RSpec.describe NotifyUsersJob, type: :job do
91
+ it "broadcasts a stream to the user channel" do
92
+ expect { described_class.perform_now(user) }
93
+ .to have_broadcasted_turbo_stream_to("user_#{user.id}")
94
+ .with_action(:append)
95
+ .targeting("notifications")
96
+ end
97
+ end
98
+ ```
99
+
100
+ ### Count qualifiers
101
+
102
+ ```ruby
103
+ it "broadcasts exactly once per recipient" do
104
+ expect { described_class.perform_now(users) }
105
+ .to have_broadcasted_turbo_stream_to("notifications")
106
+ .exactly(users.count).times
107
+ end
108
+ ```
109
+
110
+ ### Broadcast to a model (requires turbo-rails)
111
+
112
+ ```ruby
113
+ it "broadcasts to the conversation channel" do
114
+ expect { described_class.perform_now }
115
+ .to have_broadcasted_turbo_stream_to(conversation)
116
+ .with_action(:append)
117
+ end
118
+ ```
119
+
120
+ ## Multi-stream responses
121
+
122
+ A single Turbo Stream response can contain multiple `<turbo-stream>` tags. All matchers handle this correctly — `have_turbo_stream` checks if *any* stream matches, while `have_turbo_streams` requires *all* listed streams to be present.
123
+
124
+ ```ruby
125
+ it "broadcasts multiple updates" do
126
+ post bulk_update_path, as: :turbo_stream
127
+
128
+ # passes if any one stream is :append
129
+ expect(response).to have_turbo_stream.with_action(:append)
130
+
131
+ # passes only if both streams are present
132
+ expect(response).to have_turbo_streams(
133
+ have_turbo_stream.with_action(:append).targeting("list"),
134
+ have_turbo_stream.with_action(:replace).targeting("count")
135
+ )
136
+ end
137
+ ```
138
+
139
+ ## Using factory helpers
140
+
141
+ ```ruby
142
+ RSpec.describe "Messages", type: :request do
143
+ # Build test HTML without hand-rolling strings
144
+ let(:stream_body) { turbo_stream_html(action: :append, target: "messages", content: "Hello") }
145
+
146
+ it "matches the expected stream" do
147
+ expect(stream_body).to have_turbo_stream.with_action(:append).with_content("Hello")
148
+ end
149
+ end
150
+ ```
151
+
152
+ ## Minitest integration
153
+
154
+ ```ruby
155
+ class MessagesControllerTest < ActionDispatch::IntegrationTest
156
+ include TurboRspec::Assertions
157
+
158
+ test "appends the new message" do
159
+ post messages_url, params: { message: { body: "Hello" } }, as: :turbo_stream
160
+
161
+ assert_turbo_stream(response, action: :append, target: "messages", content: "Hello")
162
+ end
163
+
164
+ test "does not render a replace stream" do
165
+ post messages_url, params: { message: { body: "Hello" } }, as: :turbo_stream
166
+
167
+ refute_turbo_stream(response, action: :replace)
168
+ end
169
+ end
170
+ ```
171
+
172
+ ## Controller specs
173
+
174
+ Matchers and helpers are also available in `type: :controller` specs:
175
+
176
+ ```ruby
177
+ RSpec.describe MessagesController, type: :controller do
178
+ it "responds with a turbo stream" do
179
+ post :create, params: { message: { body: "Hello" } },
180
+ format: :turbo_stream
181
+
182
+ expect(response).to have_turbo_stream.with_action(:append).targeting("messages")
183
+ end
184
+ end
185
+ ```
@@ -0,0 +1,98 @@
1
+ # Migration Guide: Replacing Hand-Rolled Turbo Helpers
2
+
3
+ If your test suite has accumulated custom helpers for asserting Turbo Stream responses, this guide shows how to replace them with `turbo_rspec` matchers.
4
+
5
+ ## Common hand-rolled patterns
6
+
7
+ ### Pattern 1: Parsing the response body manually
8
+
9
+ ```ruby
10
+ # Before
11
+ def assert_turbo_stream_append(target:)
12
+ doc = Nokogiri::HTML(response.body)
13
+ stream = doc.at_css("turbo-stream[action='append'][target='#{target}']")
14
+ assert stream, "Expected append stream targeting #{target}"
15
+ end
16
+
17
+ it "appends the message" do
18
+ post messages_path, params: { body: "Hello" }, as: :turbo_stream
19
+ assert_turbo_stream_append(target: "messages")
20
+ end
21
+ ```
22
+
23
+ ```ruby
24
+ # After — RSpec
25
+ expect(response).to have_turbo_stream.with_action(:append).targeting("messages")
26
+
27
+ # After — Minitest
28
+ assert_turbo_stream(response, action: :append, target: "messages")
29
+ ```
30
+
31
+ ### Pattern 2: String matching on the response body
32
+
33
+ ```ruby
34
+ # Before
35
+ def expect_turbo_stream(action:, target:)
36
+ expect(response.body).to include("action=\"#{action}\"")
37
+ expect(response.body).to include("target=\"#{target}\"")
38
+ end
39
+ ```
40
+
41
+ ```ruby
42
+ # After
43
+ expect(response).to have_turbo_stream.with_action(:append).targeting("messages")
44
+ ```
45
+
46
+ ### Pattern 3: Checking multiple streams
47
+
48
+ ```ruby
49
+ # Before
50
+ def assert_turbo_streams(*expected)
51
+ expected.each do |action:, target:|
52
+ assert response.body.include?("action=\"#{action}\"")
53
+ end
54
+ end
55
+ ```
56
+
57
+ ```ruby
58
+ # After
59
+ expect(response).to have_turbo_streams(
60
+ have_turbo_stream.with_action(:append).targeting("messages"),
61
+ have_turbo_stream.with_action(:replace).targeting("header")
62
+ )
63
+ ```
64
+
65
+ ### Pattern 4: Custom broadcast helpers in job specs
66
+
67
+ ```ruby
68
+ # Before
69
+ def expect_broadcast_to(stream, action:, target:)
70
+ messages = ActionCable.server.pubsub.broadcasts(stream)
71
+ assert messages.any? { |m| m.include?(action.to_s) && m.include?(target) }
72
+ end
73
+ ```
74
+
75
+ ```ruby
76
+ # After
77
+ expect { MyJob.perform_now }.to have_broadcasted_turbo_stream_to("stream")
78
+ .with_action(:append)
79
+ .targeting("messages")
80
+ ```
81
+
82
+ ## Setup
83
+
84
+ Remove any custom helpers from `spec/support/` or `test/test_helper.rb` and add to your `Gemfile`:
85
+
86
+ ```ruby
87
+ group :test do
88
+ gem "turbo_rspec"
89
+ end
90
+ ```
91
+
92
+ With `turbo-rails` in your bundle, matchers are automatically included in `type: :request` and `type: :controller` specs. For Minitest, include manually:
93
+
94
+ ```ruby
95
+ class ActionDispatch::IntegrationTest
96
+ include TurboRspec::Assertions
97
+ end
98
+ ```
@@ -1,7 +1,16 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module TurboRspec
4
+ # Holds global configuration for TurboRspec.
5
+ #
6
+ # @see TurboRspec.configure
4
7
  class Configuration
8
+ # @!attribute [rw] auto_include
9
+ # When +true+ (default), matchers are automatically included into
10
+ # +type: :request+ and +type: :controller+ example groups when
11
+ # +turbo-rails+ is present, and Capybara matchers into +type: :system+
12
+ # and +type: :feature+ when +capybara+ is also present.
13
+ # @return [Boolean]
5
14
  attr_accessor :auto_include
6
15
 
7
16
  def initialize
@@ -1,7 +1,20 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module TurboRspec
4
+ # Factory helpers for building Turbo HTML strings inline in tests.
5
+ # Auto-included into +type: :request+ and +type: :controller+ example groups.
4
6
  module Helpers
7
+ # Builds a +<turbo-stream>+ HTML string for use in test assertions.
8
+ #
9
+ # @param action [Symbol, String] the stream action (e.g. +:append+, +:replace+)
10
+ # @param target [String, nil] the +target+ DOM id attribute
11
+ # @param targets [String, nil] the +targets+ CSS selector attribute
12
+ # @param content [String, nil] optional content to place inside the template
13
+ # @return [String]
14
+ #
15
+ # @example
16
+ # turbo_stream_html(action: :append, target: "messages", content: "Hello")
17
+ # turbo_stream_html(action: :remove, targets: ".item")
5
18
  def turbo_stream_html(action:, target: nil, targets: nil, content: nil)
6
19
  attrs = "action=\"#{action}\""
7
20
  attrs += " target=\"#{target}\"" if target
@@ -10,6 +23,14 @@ module TurboRspec
10
23
  "<turbo-stream #{attrs}>#{inner}</turbo-stream>"
11
24
  end
12
25
 
26
+ # Builds a +<turbo-frame>+ HTML string for use in test assertions.
27
+ #
28
+ # @param id [String] the frame's +id+ attribute
29
+ # @param content [String, nil] optional content inside the frame
30
+ # @return [String]
31
+ #
32
+ # @example
33
+ # turbo_frame_html(id: "messages", content: "Hello")
13
34
  def turbo_frame_html(id:, content: nil)
14
35
  "<turbo-frame id=\"#{id}\">#{content}</turbo-frame>"
15
36
  end
@@ -4,6 +4,18 @@ require "nokogiri"
4
4
 
5
5
  module TurboRspec
6
6
  module Matchers
7
+ # RSpec matcher for asserting that a response body contains a
8
+ # +<turbo-frame>+ element. Use in request or controller specs.
9
+ #
10
+ # @example Basic usage
11
+ # expect(response).to have_turbo_frame
12
+ #
13
+ # @example With constraints
14
+ # expect(response).to have_turbo_frame
15
+ # .with_id("messages")
16
+ # .with_content("Hello")
17
+ #
18
+ # @see TurboRspec::Matchers#have_turbo_frame
7
19
  class HaveTurboFrame
8
20
  def initialize
9
21
  @id = nil
@@ -11,35 +23,50 @@ module TurboRspec
11
23
  @partial = nil
12
24
  end
13
25
 
26
+ # Constrains the match to frames with the given id attribute.
27
+ # @param id [String]
28
+ # @return [self]
14
29
  def with_id(id)
15
30
  @id = id.to_s
16
31
  self
17
32
  end
18
33
 
34
+ # Constrains the match to frames whose content includes the given text.
35
+ # @param text [String]
36
+ # @return [self]
19
37
  def with_content(text)
20
38
  @content = text.to_s
21
39
  self
22
40
  end
23
41
 
42
+ # Constrains the match to frames whose HTML includes the given partial path.
43
+ # @param partial [String]
44
+ # @return [self]
24
45
  def rendering(partial)
25
46
  @partial = partial.to_s
26
47
  self
27
48
  end
28
49
 
50
+ # @param response_or_body [#body, String]
51
+ # @return [Boolean]
29
52
  def matches?(response_or_body)
30
53
  @body = extract_body(response_or_body)
31
54
  @frames = parse_frames(@body)
32
55
  @frames.any? { |frame| frame_matches?(frame) }
33
56
  end
34
57
 
58
+ # @param response_or_body [#body, String]
59
+ # @return [Boolean]
35
60
  def does_not_match?(response_or_body)
36
61
  !matches?(response_or_body)
37
62
  end
38
63
 
64
+ # @return [String]
39
65
  def failure_message
40
66
  "expected response to contain a turbo frame#{constraint_description}\n#{found_frames_message}"
41
67
  end
42
68
 
69
+ # @return [String]
43
70
  def failure_message_when_negated
44
71
  "expected response not to contain a turbo frame#{constraint_description}"
45
72
  end
@@ -4,6 +4,23 @@ require "nokogiri"
4
4
 
5
5
  module TurboRspec
6
6
  module Matchers
7
+ # RSpec matcher for asserting that a response body contains a
8
+ # +<turbo-stream>+ element. Constraints are applied via a fluent chain;
9
+ # all specified constraints must match the *same* stream element.
10
+ #
11
+ # @example Basic usage
12
+ # expect(response).to have_turbo_stream
13
+ #
14
+ # @example Chained constraints
15
+ # expect(response).to have_turbo_stream
16
+ # .with_action(:append)
17
+ # .targeting("messages")
18
+ # .with_content("Hello")
19
+ #
20
+ # @example Negation
21
+ # expect(response).not_to have_turbo_stream.with_action(:replace)
22
+ #
23
+ # @see TurboRspec::Matchers#have_turbo_stream
7
24
  class HaveTurboStream
8
25
  def initialize
9
26
  @action = nil
@@ -13,49 +30,71 @@ module TurboRspec
13
30
  @partial = nil
14
31
  end
15
32
 
33
+ # Constrains the match to streams with the given action.
34
+ # @param action [Symbol, String] e.g. +:append+, +:replace+, +:remove+, +:refresh+, +:morph+
35
+ # @return [self]
16
36
  def with_action(action)
17
37
  @action = action.to_s
18
38
  self
19
39
  end
20
40
 
41
+ # Constrains the match to streams targeting a specific DOM id.
42
+ # @param dom_id [String]
43
+ # @return [self]
21
44
  def targeting(dom_id)
22
45
  @target = dom_id.to_s
23
46
  self
24
47
  end
25
48
 
49
+ # Constrains the match to streams targeting a CSS selector (the +targets+ attribute).
50
+ # @param selector [String] e.g. +".message-item"+
51
+ # @return [self]
26
52
  def targeting_all(selector)
27
53
  @target_all = selector.to_s
28
54
  self
29
55
  end
30
56
 
57
+ # Constrains the match to streams whose template content includes the given text.
58
+ # @param text [String]
59
+ # @return [self]
31
60
  def with_content(text)
32
61
  @content = text.to_s
33
62
  self
34
63
  end
35
64
 
65
+ # Constrains the match to streams whose rendered HTML includes the given partial path.
66
+ # @param partial [String] e.g. +"messages/_message"+
67
+ # @return [self]
36
68
  def rendering(partial)
37
69
  @partial = partial.to_s
38
70
  self
39
71
  end
40
72
 
73
+ # @param response_or_body [#body, String]
74
+ # @return [Boolean]
41
75
  def matches?(response_or_body)
42
76
  @body = extract_body(response_or_body)
43
77
  @streams = parse_streams(@body)
44
78
  @streams.any? { |stream| stream_matches?(stream) }
45
79
  end
46
80
 
81
+ # @param response_or_body [#body, String]
82
+ # @return [Boolean]
47
83
  def does_not_match?(response_or_body)
48
84
  !matches?(response_or_body)
49
85
  end
50
86
 
87
+ # @return [String]
51
88
  def failure_message
52
89
  "expected response to contain a turbo stream#{constraint_description}\n#{found_streams_message}"
53
90
  end
54
91
 
92
+ # @return [String]
55
93
  def failure_message_when_negated
56
94
  "expected response not to contain a turbo stream#{constraint_description}"
57
95
  end
58
96
 
97
+ # @return [String]
59
98
  def description
60
99
  "have turbo stream#{constraint_description}"
61
100
  end
@@ -6,23 +6,43 @@ require_relative "matchers/have_turbo_stream"
6
6
  require_relative "matchers/have_turbo_streams"
7
7
 
8
8
  module TurboRspec
9
+ # RSpec matchers for Turbo Stream and Turbo Frame assertions.
10
+ # Auto-included in +type: :request+ and +type: :controller+ example groups.
11
+ # Include explicitly for other contexts:
12
+ #
13
+ # RSpec.configure do |config|
14
+ # config.include TurboRspec::Matchers
15
+ # end
9
16
  module Matchers
17
+ # Assert that a block broadcasts a +<turbo-stream>+ to the given stream.
18
+ # @param stream_or_object [String, Object] stream name or streamable object
19
+ # @return [HaveBroadcastedTurboStreamTo]
10
20
  def have_broadcasted_turbo_stream_to(stream_or_object)
11
21
  HaveBroadcastedTurboStreamTo.new(stream_or_object)
12
22
  end
13
23
 
24
+ # @see #have_broadcasted_turbo_stream_to
14
25
  alias_method :broadcast_turbo_stream_to, :have_broadcasted_turbo_stream_to
15
26
 
27
+ # Assert that a response body contains a +<turbo-frame>+ element.
28
+ # @return [HaveTurboFrame]
16
29
  def have_turbo_frame
17
30
  HaveTurboFrame.new
18
31
  end
19
32
 
33
+ # Assert that a response body contains a +<turbo-stream>+ element.
34
+ # @return [HaveTurboStream]
20
35
  def have_turbo_stream
21
36
  HaveTurboStream.new
22
37
  end
23
38
 
39
+ # Alias of {#have_turbo_stream} for teams using minitest-style naming.
40
+ # @see #have_turbo_stream
24
41
  alias_method :assert_no_turbo_stream, :have_turbo_stream
25
42
 
43
+ # Assert that a response body contains *all* of the given turbo streams.
44
+ # @param matchers [Array<HaveTurboStream>] one or more {#have_turbo_stream} matchers
45
+ # @return [HaveTurboStreams]
26
46
  def have_turbo_streams(*matchers)
27
47
  HaveTurboStreams.new(matchers)
28
48
  end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module TurboRspec
4
- VERSION = "0.6.0"
4
+ VERSION = "0.7.0"
5
5
  end
data/lib/turbo_rspec.rb CHANGED
@@ -8,22 +8,49 @@ require_relative "turbo_rspec/assertions"
8
8
  require_relative "turbo_rspec/shared_examples"
9
9
  require_relative "turbo_rspec/capybara/matchers"
10
10
 
11
+ # TurboRspec provides RSpec matchers and Minitest assertions for
12
+ # {https://github.com/hotwired/turbo-rails turbo-rails}.
13
+ #
14
+ # @example Configure auto-include
15
+ # TurboRspec.configure do |config|
16
+ # config.auto_include = false
17
+ # end
11
18
  module TurboRspec
19
+ # Base error class for TurboRspec.
12
20
  class Error < StandardError; end
13
21
 
14
22
  class << self
23
+ # Yields the configuration object for customization.
24
+ #
25
+ # @yieldparam config [Configuration]
26
+ # @return [void]
27
+ # @example
28
+ # TurboRspec.configure do |config|
29
+ # config.auto_include = false
30
+ # end
15
31
  def configure
16
32
  yield configuration
17
33
  end
18
34
 
35
+ # Returns the global configuration instance.
36
+ #
37
+ # @return [Configuration]
19
38
  def configuration
20
39
  @configuration ||= Configuration.new
21
40
  end
22
41
 
42
+ # Resets configuration to defaults. Primarily for use in test suites.
43
+ #
44
+ # @return [void]
23
45
  def reset_configuration!
24
46
  @configuration = Configuration.new
25
47
  end
26
48
 
49
+ # Installs RSpec integration — includes matchers and helpers into the
50
+ # appropriate example groups. Called automatically when RSpec is present.
51
+ #
52
+ # @param config [RSpec::Core::Configuration]
53
+ # @return [void]
27
54
  def install_rspec_integration(config)
28
55
  return unless configuration.auto_include && Gem.loaded_specs.key?("turbo-rails")
29
56
  config.include Matchers, type: :request
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: 0.6.0
4
+ version: 0.7.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Chuck Smith
@@ -35,6 +35,7 @@ files:
35
35
  - ".github/workflows/ci.yml"
36
36
  - ".github/workflows/publish.yml"
37
37
  - ".rubocop.yml"
38
+ - ".yardopts"
38
39
  - CHANGELOG.md
39
40
  - CLAUDE.md
40
41
  - LICENSE.txt
@@ -42,6 +43,8 @@ files:
42
43
  - ROADMAP.md
43
44
  - Rakefile
44
45
  - codecov.yml
46
+ - docs/cookbook.md
47
+ - docs/migration_guide.md
45
48
  - lib/turbo_rspec.rb
46
49
  - lib/turbo_rspec/assertions.rb
47
50
  - lib/turbo_rspec/capybara/matchers.rb