https://github.com/moznion/rspec-sse-matchers
A collection of RSpec matchers for testing Server-Sent Events (SSE)
https://github.com/moznion/rspec-sse-matchers
rspec rspec-matchers server-sent-events sse
Last synced: about 1 month ago
JSON representation
A collection of RSpec matchers for testing Server-Sent Events (SSE)
- Host: GitHub
- URL: https://github.com/moznion/rspec-sse-matchers
- Owner: moznion
- License: mit
- Created: 2025-05-07T15:06:46.000Z (about 2 months ago)
- Default Branch: main
- Last Pushed: 2025-05-09T04:02:04.000Z (about 2 months ago)
- Last Synced: 2025-05-11T20:47:18.421Z (about 1 month ago)
- Topics: rspec, rspec-matchers, server-sent-events, sse
- Language: Ruby
- Homepage: https://rubygems.org/gems/rspec-sse-matchers
- Size: 52.7 KB
- Stars: 2
- Watchers: 1
- Forks: 0
- Open Issues: 1
-
Metadata Files:
- Readme: README.md
- Changelog: CHANGELOG.md
- License: LICENSE.txt
- Code of conduct: CODE_OF_CONDUCT.md
Awesome Lists containing this project
README
# RSpec SSE Matchers
A collection of [RSpec](https://rspec.info/) matchers for testing [Server-Sent Events (SSE)](https://developer.mozilla.org/en-US/docs/Web/API/Server-sent_events).
[](https://github.com/moznion/rspec-sse-matchers/actions/workflows/test.yml)
[](https://badge.fury.io/rb/rspec-sse-matchers)## Installation
Add this line to your application's Gemfile:
```ruby
gem 'rspec-sse-matchers'
```And then execute:
```bash
$ bundle install
```Or install it yourself as:
```bash
$ gem install rspec-sse-matchers
```## Synopsis
This gem provides a set of matchers to test SSE responses in your RSpec request specs:
```ruby
# In your controller
def index
response.headers['Cache-Control'] = 'no-store'
response.headers['Content-Type'] = 'text/event-stream'
sse = SSE.new(response.stream)sse.write({id: 1, message: 'Hello'}, event: 'message', id: 1)
sse.write({id: 2, message: 'World'}, event: 'update', id: 2)
sse.close
end# In your spec
RSpec.describe 'SSE endpoint', type: :request do
it 'sends the expected events' do
get '/events', headers: { 'Accept' => 'text/event-stream' }# Verify the response indicates a successful SSE connection
expect(response).to be_sse_successfully_opened# Verify the event types
expect(response).to be_sse_event_types(['message', 'update'])# Verify that the response is properly closed
expect(response).to be_sse_gracefully_closed
end
end
```### Available Matchers
#### Exact Order Matchers
These matchers check that the specified values appear in the exact order:
- `be_sse_events`: Check that the events exactly match the expected events
- `be_sse_event_types`: Check that the event types exactly match the expected types
- `be_sse_event_data`: Check that the event data exactly match the expected data
- `be_sse_event_ids`: Check that the event IDs exactly match the expected IDs
- `be_sse_reconnection_times`: Check that the reconnection times exactly match the expected timesAll event data matchers (`be_sse_event_data`, `contain_exactly_sse_event_data`, `have_sse_event_data`) and event matchers (`be_sse_events`, `contain_exactly_sse_events`, `have_sse_events`) accept a `json: true` option that will parse the JSON in event data for comparison.
#### Order-Independent Matchers
These matchers check that the specified values appear in any order:
- `contain_exactly_sse_events`: Check that the events match the expected events regardless of order
- `contain_exactly_sse_event_types`: Check that the event types match the expected types regardless of order
- `contain_exactly_sse_event_data`: Check that the event data match the expected data regardless of order
- `contain_exactly_sse_event_ids`: Check that the event IDs match the expected IDs regardless of order
- `contain_exactly_sse_reconnection_times`: Check that the reconnection times match the expected times regardless of order#### Inclusion Matchers
These matchers check that all the expected values are included:
- `have_sse_events`: Check that all the expected events are included
- `have_sse_event_types`: Check that all the expected event types are included
- `have_sse_event_data`: Check that all the expected event data are included
- `have_sse_event_ids`: Check that all the expected event IDs are included
- `have_sse_reconnection_times`: Check that all the expected reconnection times are included#### Miscellaneous Matchers
- `be_sse_successfully_opened`: Check that the response indicates the SSE connection has been opened successfully
- `be_sse_gracefully_closed`: Check that the response body ends with "\n\n" (indicating proper SSE close)### Argument Formats
All matchers can accept their arguments either as individual arguments or as an array:
```ruby
# These are equivalent:
expect(response).to have_sse_event_types('message', 'update', 'close')
expect(response).to have_sse_event_types(['message', 'update', 'close'])
```### JSON Parsing Option
Event data matchers and event matchers accept a `json: true` option that automatically parses JSON in event `data` for comparison:
```ruby
# Without JSON parsing (string comparison)
expect(response).to be_sse_event_data(['{"id":1,"name":"Alice"}', '{"id":2,"name":"Bob"}'])# With JSON parsing (object comparison)
expect(response).to be_sse_event_data([{"id" => 1, "name" => "Alice"}, {"id" => 2, "name" => "Bob"}], json: true)# This also works with event matchers
expect(response).to be_sse_events([
{type: 'message', data: {"id" => 1, "name" => "Alice"}, id: '1'},
{type: 'update', data: {"id" => 2, "name" => "Bob"}, id: '2'}
], json: true)
```When the `json: true` option is enabled, the matcher attempts to parse each event's `data` as JSON. If parsing fails (the data is not valid JSON), it raises an error.
## Examples
### Testing Event Types
```ruby
# Exact order
expect(response).to be_sse_event_types(['message', 'update', 'close'])# Any order
expect(response).to contain_exactly_sse_event_types(['update', 'message', 'close'])# Inclusion
expect(response).to have_sse_event_types(['message', 'update'])
```### Testing Event Data
```ruby
# Exact order
expect(response).to be_sse_event_data(['{"id":1}', '{"id":2}', '{"id":3}'])# Any order
expect(response).to contain_exactly_sse_event_data(['{"id":2}', '{"id":1}', '{"id":3}'])# Inclusion
expect(response).to have_sse_event_data(['{"id":1}', '{"id":2}'])# With JSON parsing
expect(response).to be_sse_event_data([{"id" => 1}, {"id" => 2}, {"id" => 3}], json: true)
```### Testing Event IDs
```ruby
# Exact order
expect(response).to be_sse_event_ids(['1', '2', '3'])# Any order
expect(response).to contain_exactly_sse_event_ids(['2', '1', '3'])# Inclusion
expect(response).to have_sse_event_ids(['1', '2'])
```### Testing Reconnection Times
```ruby
# Exact order
expect(response).to be_sse_reconnection_times([1000, 2000, 3000])# Any order
expect(response).to contain_exactly_sse_reconnection_times([2000, 1000, 3000])# Inclusion
expect(response).to have_sse_reconnection_times([1000, 2000])
```### Testing Complete Events
```ruby
events = [
{type: 'message', data: '{"id":1}', id: '1', retry: 1000},
{type: 'update', data: '{"id":2}', id: '2', retry: 2000}
]# Exact order
expect(response).to be_sse_events(events)# Any order
expect(response).to contain_exactly_sse_events(events)# Inclusion
expect(response).to have_sse_events([events.first])# With JSON parsing
json_events = [
{type: 'message', data: {"id" => 1}, id: '1', retry: 1000},
{type: 'update', data: {"id" => 2}, id: '2', retry: 2000}
]
expect(response).to be_sse_events(json_events, json: true)
```## Development
After checking out the repo, run `bin/setup` to install dependencies. Then, run `bundle exec rspec` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and the created tag, and push the `.gem` file to [rubygems.org](https://rubygems.org).
## Contributing
Bug reports and pull requests are welcome on GitHub at https://github.com/moznion/rspec-sse-matchers. This project is intended to be a safe, welcoming space for collaboration, and contributors are expected to adhere to the [code of conduct](https://github.com/moznion/rspec-sse-matchers/blob/main/CODE_OF_CONDUCT.md).
## License
The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
## Code of Conduct
Everyone interacting in the RSpec SSE Matchers project's codebases, issue trackers, chat rooms and mailing lists is expected to follow the [code of conduct](https://github.com/moznion/rspec-sse-matchers/blob/main/CODE_OF_CONDUCT.md).