zenspec 0.1.0 → 0.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 +6 -0
- data/README.md +119 -466
- data/lib/zenspec/version.rb +1 -1
- metadata +3 -3
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 7737ee41815ffc6e727f44826dda5052addaa5925860cd1421665ffed41e85e0
|
|
4
|
+
data.tar.gz: 37c0da188f214be5efe29ac58496a24b2831f2e076d4fb247a1bbbaaec57fa59
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: cb5d249f606858f4f5835c7889cc6aeacc157ed30e79bac209758c42e5b8abb076e84afdb978ec1c0cbc537d4f1e6c6ba672d53fbcf2f87ae5faa8d3428cbe6b
|
|
7
|
+
data.tar.gz: 4796a8d28823742fa70c5417918d310f0ebfaed055a26cd7a8938919b35e2d5cc2de5d18b22622af4d46296d41ea861850b66bf28b76fb955243703f1f1f64e3
|
data/CHANGELOG.md
CHANGED
data/README.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# Zenspec
|
|
2
2
|
|
|
3
|
-
A comprehensive RSpec matcher library for testing GraphQL APIs, Interactor service objects, and Rails applications.
|
|
3
|
+
A comprehensive RSpec matcher library for testing GraphQL APIs, Interactor service objects, and Rails applications.
|
|
4
4
|
|
|
5
5
|
## Installation
|
|
6
6
|
|
|
@@ -16,23 +16,9 @@ And then execute:
|
|
|
16
16
|
bundle install
|
|
17
17
|
```
|
|
18
18
|
|
|
19
|
-
|
|
19
|
+
## Quick Start
|
|
20
20
|
|
|
21
|
-
|
|
22
|
-
gem install zenspec
|
|
23
|
-
```
|
|
24
|
-
|
|
25
|
-
## Features
|
|
26
|
-
|
|
27
|
-
- **GraphQL Matchers** - Test GraphQL queries, mutations, types, and schemas
|
|
28
|
-
- **Interactor Matchers** - Test service objects with clean syntax
|
|
29
|
-
- **GraphQL Helpers** - Execute queries and mutations with ease
|
|
30
|
-
- **Progress Loader** - Docker-style progress bars for terminal output
|
|
31
|
-
- **Shoulda Matchers** - Automatically configured for Rails models and controllers
|
|
32
|
-
- **Rails Integration** - Automatically loads in Rails applications
|
|
33
|
-
- **Non-Rails Support** - Works in any Ruby project
|
|
34
|
-
|
|
35
|
-
## Usage
|
|
21
|
+
### 1. Add to your spec helper
|
|
36
22
|
|
|
37
23
|
```ruby
|
|
38
24
|
# In your spec/rails_helper.rb or spec/spec_helper.rb
|
|
@@ -41,179 +27,24 @@ require "zenspec"
|
|
|
41
27
|
# Everything is automatically configured!
|
|
42
28
|
```
|
|
43
29
|
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
## Progress Loader
|
|
47
|
-
|
|
48
|
-
Docker-style progress bars for displaying terminal progress.
|
|
49
|
-
|
|
50
|
-
### RSpec Formatters
|
|
51
|
-
|
|
52
|
-
Zenspec includes two custom RSpec formatters for displaying test progress:
|
|
53
|
-
|
|
54
|
-
#### Progress Bar Formatter (Recommended)
|
|
55
|
-
|
|
56
|
-
Clean, colorful output with status icons:
|
|
57
|
-
|
|
58
|
-
```bash
|
|
59
|
-
# Use the progress bar formatter
|
|
60
|
-
rspec --require zenspec/formatters/progress_bar_formatter \
|
|
61
|
-
--format ProgressBarFormatter
|
|
62
|
-
|
|
63
|
-
# Or add to .rspec file:
|
|
64
|
-
# --require zenspec/formatters/progress_bar_formatter
|
|
65
|
-
# --format ProgressBarFormatter
|
|
66
|
-
```
|
|
67
|
-
|
|
68
|
-
Output example:
|
|
69
|
-
```
|
|
70
|
-
✔ user_spec.rb creates a new user successfully [10% 1/10]
|
|
71
|
-
✔ user_spec.rb validates email presence [20% 2/10]
|
|
72
|
-
⠿ user_spec.rb --> sends welcome email [30% 3/10]
|
|
73
|
-
✗ post_spec.rb publishes post with valid data [40% 4/10]
|
|
74
|
-
⊘ auth_spec.rb authenticates with OAuth [50% 5/10]
|
|
75
|
-
```
|
|
76
|
-
|
|
77
|
-
Format:
|
|
78
|
-
- **Running tests** show an arrow (-->) between filename and description
|
|
79
|
-
- **Completed tests** (passed/failed/pending) show only a space between filename and description
|
|
80
|
-
|
|
81
|
-
Colors:
|
|
82
|
-
- **Green** ✔ - Passed tests
|
|
83
|
-
- **Red** ✗ - Failed tests
|
|
84
|
-
- **Yellow** ⠿ - Currently running test (with arrow)
|
|
85
|
-
- **Cyan** ⊘ - Pending/skipped tests
|
|
86
|
-
|
|
87
|
-
#### Docker-Style Progress Formatter
|
|
88
|
-
|
|
89
|
-
Alternative formatter with Docker-style progress bars:
|
|
90
|
-
|
|
91
|
-
```bash
|
|
92
|
-
# Use the Docker-style formatter
|
|
93
|
-
rspec --require zenspec/formatters/progress_formatter \
|
|
94
|
-
--format Zenspec::Formatters::ProgressFormatter
|
|
95
|
-
```
|
|
96
|
-
|
|
97
|
-
Output example:
|
|
98
|
-
```
|
|
99
|
-
[=====================> ] 65% 13/20 spec/models/user_spec.rb:42
|
|
100
|
-
```
|
|
101
|
-
|
|
102
|
-
### Programmatic Usage
|
|
103
|
-
|
|
104
|
-
Use the progress loader in your code:
|
|
105
|
-
|
|
106
|
-
```ruby
|
|
107
|
-
# Basic usage
|
|
108
|
-
loader = Zenspec::ProgressLoader.new(total: 100, description: "Processing files")
|
|
109
|
-
|
|
110
|
-
100.times do |i|
|
|
111
|
-
# Do some work...
|
|
112
|
-
loader.update(i + 1, description: "Processing file #{i + 1}/100")
|
|
113
|
-
sleep(0.1)
|
|
114
|
-
end
|
|
30
|
+
### 2. (Optional) Add progress bar formatter to `.rspec`
|
|
115
31
|
|
|
116
|
-
loader.finish(description: "Processing complete!")
|
|
117
32
|
```
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
# Custom progress bar width (default is 40)
|
|
123
|
-
loader = Zenspec::ProgressLoader.new(
|
|
124
|
-
total: 50,
|
|
125
|
-
width: 60,
|
|
126
|
-
description: "Downloading layers"
|
|
127
|
-
)
|
|
128
|
-
|
|
129
|
-
loader.increment(description: "Layer 1/50")
|
|
130
|
-
loader.increment(description: "Layer 2/50")
|
|
131
|
-
# ...
|
|
132
|
-
loader.finish
|
|
33
|
+
--require zenspec/formatters/progress_bar_formatter
|
|
34
|
+
--format ProgressBarFormatter
|
|
35
|
+
--color
|
|
36
|
+
--require spec_helper
|
|
133
37
|
```
|
|
134
38
|
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
The progress loader automatically calculates and displays time information:
|
|
138
|
-
|
|
139
|
-
```ruby
|
|
140
|
-
loader = Zenspec::ProgressLoader.new(total: 1000)
|
|
141
|
-
|
|
142
|
-
loader.update(250)
|
|
143
|
-
# Output: [==========> ] 25% 250/1000 1s/4s
|
|
144
|
-
|
|
145
|
-
loader.update(500)
|
|
146
|
-
# Output: [====================> ] 50% 500/1000 2s/4s
|
|
147
|
-
```
|
|
39
|
+
That's it! You can now use all GraphQL and Interactor matchers in your tests.
|
|
148
40
|
|
|
149
41
|
---
|
|
150
42
|
|
|
151
|
-
##
|
|
152
|
-
|
|
153
|
-
### Basic Usage
|
|
154
|
-
|
|
155
|
-
```ruby
|
|
156
|
-
RSpec.describe CreateUser do
|
|
157
|
-
subject(:result) { described_class.call(params) }
|
|
43
|
+
## GraphQL Testing
|
|
158
44
|
|
|
159
|
-
|
|
160
|
-
let(:params) { { name: "John", email: "john@example.com" } }
|
|
45
|
+
### Response Matchers
|
|
161
46
|
|
|
162
|
-
|
|
163
|
-
it { is_expected.to succeed }
|
|
164
|
-
it { is_expected.to set_context(:user) }
|
|
165
|
-
it { is_expected.to succeed.with_data(kind_of(User)) }
|
|
166
|
-
end
|
|
167
|
-
|
|
168
|
-
context "with invalid params" do
|
|
169
|
-
let(:params) { { name: "", email: "invalid" } }
|
|
170
|
-
|
|
171
|
-
it { is_expected.to fail_interactor }
|
|
172
|
-
it { is_expected.to fail_interactor.with_error("validation_failed") }
|
|
173
|
-
it { is_expected.to have_error_code("validation_failed") }
|
|
174
|
-
end
|
|
175
|
-
end
|
|
176
|
-
```
|
|
177
|
-
|
|
178
|
-
### Available Matchers
|
|
179
|
-
|
|
180
|
-
#### `succeed`
|
|
181
|
-
|
|
182
|
-
```ruby
|
|
183
|
-
expect(result).to succeed
|
|
184
|
-
expect(result).to succeed.with_data("expected value")
|
|
185
|
-
expect(result).to succeed.with_context(:user_id, 123)
|
|
186
|
-
expect(CreateUser).to succeed # Works with classes too
|
|
187
|
-
```
|
|
188
|
-
|
|
189
|
-
#### `fail_interactor`
|
|
190
|
-
|
|
191
|
-
```ruby
|
|
192
|
-
expect(result).to fail_interactor
|
|
193
|
-
expect(result).to fail_interactor.with_error("invalid_input")
|
|
194
|
-
expect(result).to fail_interactor.with_errors("error1", "error2")
|
|
195
|
-
```
|
|
196
|
-
|
|
197
|
-
#### `have_error_code` / `have_error_codes`
|
|
198
|
-
|
|
199
|
-
```ruby
|
|
200
|
-
expect(result).to have_error_code("not_found")
|
|
201
|
-
expect(result).to have_error_codes("error1", "error2")
|
|
202
|
-
```
|
|
203
|
-
|
|
204
|
-
#### `set_context`
|
|
205
|
-
|
|
206
|
-
```ruby
|
|
207
|
-
expect(result).to set_context(:user)
|
|
208
|
-
expect(result).to set_context(:user_id, 123)
|
|
209
|
-
expect(result).to set_context(:data).to(expected_value)
|
|
210
|
-
```
|
|
211
|
-
|
|
212
|
-
---
|
|
213
|
-
|
|
214
|
-
## GraphQL Response Matchers
|
|
215
|
-
|
|
216
|
-
### Basic Usage
|
|
47
|
+
Test GraphQL query and mutation responses with clean, expressive matchers.
|
|
217
48
|
|
|
218
49
|
```ruby
|
|
219
50
|
RSpec.describe "User Queries" do
|
|
@@ -231,383 +62,209 @@ RSpec.describe "User Queries" do
|
|
|
231
62
|
GQL
|
|
232
63
|
end
|
|
233
64
|
|
|
234
|
-
#
|
|
65
|
+
# Basic checks
|
|
235
66
|
it { is_expected.to succeed_graphql }
|
|
236
67
|
it { is_expected.to have_graphql_data("user") }
|
|
237
68
|
it { is_expected.to have_graphql_data("user", "name").with_value("John") }
|
|
238
|
-
end
|
|
239
|
-
```
|
|
240
|
-
|
|
241
|
-
### Available Matchers
|
|
242
|
-
|
|
243
|
-
#### `have_graphql_data`
|
|
244
|
-
|
|
245
|
-
```ruby
|
|
246
|
-
# Basic checks
|
|
247
|
-
expect(result).to have_graphql_data
|
|
248
|
-
expect(result).to have_graphql_data("user")
|
|
249
|
-
expect(result).to have_graphql_data("user", "id")
|
|
250
|
-
|
|
251
|
-
# With specific value
|
|
252
|
-
expect(result).to have_graphql_data("user", "name").with_value("John Doe")
|
|
253
|
-
|
|
254
|
-
# Partial matching (subset)
|
|
255
|
-
expect(result).to have_graphql_data("user").matching(
|
|
256
|
-
id: "123",
|
|
257
|
-
name: "John"
|
|
258
|
-
# other fields can exist but aren't checked
|
|
259
|
-
)
|
|
260
|
-
|
|
261
|
-
# Inclusion (contains these values)
|
|
262
|
-
expect(result).to have_graphql_data("user").that_includes(name: "John")
|
|
263
|
-
|
|
264
|
-
# Presence checks
|
|
265
|
-
expect(result).to have_graphql_data("users").that_is_present
|
|
266
|
-
expect(result).to have_graphql_data("deletedAt").that_is_null
|
|
267
|
-
|
|
268
|
-
# Array count
|
|
269
|
-
expect(result).to have_graphql_data("users").with_count(5)
|
|
270
|
-
```
|
|
271
|
-
|
|
272
|
-
#### `have_graphql_errors`
|
|
273
|
-
|
|
274
|
-
```ruby
|
|
275
|
-
# Basic error check
|
|
276
|
-
expect(result).to have_graphql_errors
|
|
277
69
|
|
|
278
|
-
#
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
```
|
|
287
|
-
|
|
288
|
-
#### `succeed_graphql`
|
|
289
|
-
|
|
290
|
-
```ruby
|
|
291
|
-
expect(result).to succeed_graphql
|
|
292
|
-
```
|
|
293
|
-
|
|
294
|
-
#### `have_graphql_field` / `have_graphql_fields`
|
|
295
|
-
|
|
296
|
-
```ruby
|
|
297
|
-
data = result.dig("data", "user")
|
|
70
|
+
# Matching data
|
|
71
|
+
it "returns correct user data" do
|
|
72
|
+
expect(result).to have_graphql_data("user").matching(
|
|
73
|
+
id: user.id,
|
|
74
|
+
name: "John Doe",
|
|
75
|
+
email: "john@example.com"
|
|
76
|
+
)
|
|
77
|
+
end
|
|
298
78
|
|
|
299
|
-
|
|
300
|
-
|
|
79
|
+
# Array checks
|
|
80
|
+
it { is_expected.to have_graphql_data("users").with_count(5) }
|
|
81
|
+
it { is_expected.to have_graphql_data("users").that_is_present }
|
|
301
82
|
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
83
|
+
# Error handling
|
|
84
|
+
expect(result).to have_graphql_error.with_message("Not found")
|
|
85
|
+
expect(result).to have_graphql_error.with_extensions(code: "NOT_FOUND")
|
|
86
|
+
expect(result).to have_graphql_error.at_path(["user", "email"])
|
|
87
|
+
end
|
|
307
88
|
```
|
|
308
89
|
|
|
309
|
-
|
|
90
|
+
**Available matchers:**
|
|
91
|
+
- `succeed_graphql` - Verify successful query/mutation
|
|
92
|
+
- `have_graphql_data(path...)` - Check response data
|
|
93
|
+
- `have_graphql_error` / `have_graphql_errors` - Verify errors
|
|
94
|
+
- `have_graphql_field(name)` / `have_graphql_fields(hash)` - Check field presence
|
|
310
95
|
|
|
311
|
-
|
|
96
|
+
**Data matchers:**
|
|
97
|
+
- `.with_value(expected)` - Exact value match
|
|
98
|
+
- `.matching(hash)` - Partial hash match
|
|
99
|
+
- `.that_includes(hash)` - Includes these values
|
|
100
|
+
- `.that_is_present` / `.that_is_null` - Presence checks
|
|
101
|
+
- `.with_count(n)` - Array size
|
|
312
102
|
|
|
313
|
-
|
|
103
|
+
### Type Matchers
|
|
314
104
|
|
|
315
|
-
|
|
105
|
+
Test your GraphQL schema types, fields, and arguments.
|
|
316
106
|
|
|
317
107
|
```ruby
|
|
318
108
|
RSpec.describe UserType do
|
|
319
109
|
subject { described_class }
|
|
320
110
|
|
|
321
|
-
#
|
|
111
|
+
# Field testing
|
|
322
112
|
it { is_expected.to have_field(:id).of_type("ID!") }
|
|
323
113
|
it { is_expected.to have_field(:name).of_type("String!") }
|
|
324
114
|
it { is_expected.to have_field(:email).of_type("String") }
|
|
325
115
|
it { is_expected.to have_field(:posts).of_type("[Post!]!") }
|
|
326
116
|
|
|
327
|
-
#
|
|
117
|
+
# Fields with arguments
|
|
328
118
|
it do
|
|
329
119
|
is_expected.to have_field(:posts)
|
|
330
120
|
.with_argument(:limit, "Int")
|
|
331
121
|
.with_argument(:offset, "Int")
|
|
332
122
|
end
|
|
333
123
|
end
|
|
334
|
-
```
|
|
335
|
-
|
|
336
|
-
### Enum Testing
|
|
337
124
|
|
|
338
|
-
|
|
125
|
+
# Enum testing
|
|
339
126
|
RSpec.describe StatusEnum do
|
|
340
127
|
subject { described_class }
|
|
341
128
|
|
|
342
129
|
it { is_expected.to have_enum_values("ACTIVE", "INACTIVE", "PENDING") }
|
|
343
130
|
end
|
|
344
|
-
```
|
|
345
131
|
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
```ruby
|
|
132
|
+
# Schema queries and mutations
|
|
349
133
|
RSpec.describe AppSchema do
|
|
350
134
|
subject { described_class }
|
|
351
135
|
|
|
352
|
-
# Query fields
|
|
353
136
|
it { is_expected.to have_query(:user).with_argument(:id, "ID!") }
|
|
354
|
-
it { is_expected.to
|
|
355
|
-
|
|
356
|
-
# Mutation fields
|
|
357
|
-
it { is_expected.to have_mutation(:createUser).with_argument(:input, "CreateUserInput!") }
|
|
358
|
-
it { is_expected.to have_mutation(:deleteUser).of_type("Boolean!") }
|
|
359
|
-
end
|
|
360
|
-
```
|
|
361
|
-
|
|
362
|
-
### Field Argument Testing
|
|
363
|
-
|
|
364
|
-
```ruby
|
|
365
|
-
RSpec.describe "UserType posts field" do
|
|
366
|
-
subject(:field) { UserType.fields["posts"] }
|
|
367
|
-
|
|
368
|
-
it { is_expected.to have_argument(:limit).of_type("Int") }
|
|
369
|
-
it { is_expected.to have_argument(:status).of_type("Status!") }
|
|
137
|
+
it { is_expected.to have_mutation(:createUser).of_type("UserPayload!") }
|
|
370
138
|
end
|
|
371
139
|
```
|
|
372
140
|
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
141
|
+
**Available matchers:**
|
|
142
|
+
- `have_field(name)` - Check field exists
|
|
143
|
+
- `of_type(type)` - Verify field type
|
|
144
|
+
- `with_argument(name, type)` - Check field arguments
|
|
145
|
+
- `have_argument(name)` - Check argument on field
|
|
146
|
+
- `have_enum_values(*values)` - Verify enum values
|
|
147
|
+
- `have_query(name)` / `have_mutation(name)` - Schema-level checks
|
|
376
148
|
|
|
377
|
-
|
|
149
|
+
### GraphQL Helpers
|
|
378
150
|
|
|
379
|
-
|
|
151
|
+
Execute queries and mutations easily in your tests.
|
|
380
152
|
|
|
381
153
|
```ruby
|
|
382
|
-
#
|
|
154
|
+
# Execute query
|
|
383
155
|
result = graphql_execute(query, variables: { id: "123" }, context: { current_user: user })
|
|
384
156
|
|
|
385
|
-
#
|
|
386
|
-
result = graphql_execute(query, schema: MySchema)
|
|
387
|
-
```
|
|
388
|
-
|
|
389
|
-
### `graphql_execute_as`
|
|
390
|
-
|
|
391
|
-
```ruby
|
|
392
|
-
# Automatically adds user to context
|
|
157
|
+
# Execute as user (automatically adds to context)
|
|
393
158
|
result = graphql_execute_as(user, query, variables: { id: "123" })
|
|
394
159
|
|
|
395
|
-
#
|
|
396
|
-
result = graphql_execute_as(user, query, context_key: :admin)
|
|
397
|
-
```
|
|
398
|
-
|
|
399
|
-
### `graphql_mutate`
|
|
400
|
-
|
|
401
|
-
```ruby
|
|
402
|
-
# Execute mutations
|
|
160
|
+
# Execute mutation
|
|
403
161
|
result = graphql_mutate(mutation, input: { name: "John" }, context: { current_user: user })
|
|
404
|
-
```
|
|
405
|
-
|
|
406
|
-
### `graphql_mutate_as`
|
|
407
162
|
|
|
408
|
-
```ruby
|
|
409
163
|
# Execute mutation as user
|
|
410
164
|
result = graphql_mutate_as(user, mutation, input: { name: "John" })
|
|
411
165
|
```
|
|
412
166
|
|
|
413
167
|
---
|
|
414
168
|
|
|
415
|
-
##
|
|
416
|
-
|
|
417
|
-
Automatically includes and configures [Shoulda Matchers](https://github.com/thoughtbot/shoulda-matchers) for Rails projects.
|
|
418
|
-
|
|
419
|
-
### Model Validations
|
|
420
|
-
|
|
421
|
-
```ruby
|
|
422
|
-
RSpec.describe User do
|
|
423
|
-
subject { build(:user) }
|
|
424
|
-
|
|
425
|
-
# Validations
|
|
426
|
-
it { is_expected.to validate_presence_of(:email) }
|
|
427
|
-
it { is_expected.to validate_uniqueness_of(:email).case_insensitive }
|
|
428
|
-
it { is_expected.to validate_length_of(:password).is_at_least(8) }
|
|
429
|
-
|
|
430
|
-
# Associations
|
|
431
|
-
it { is_expected.to have_many(:posts).dependent(:destroy) }
|
|
432
|
-
it { is_expected.to belong_to(:organization) }
|
|
433
|
-
|
|
434
|
-
# Database columns
|
|
435
|
-
it { is_expected.to have_db_column(:email).of_type(:string) }
|
|
436
|
-
it { is_expected.to have_db_index(:email).unique }
|
|
437
|
-
end
|
|
438
|
-
```
|
|
439
|
-
|
|
440
|
-
### Controller Testing
|
|
441
|
-
|
|
442
|
-
```ruby
|
|
443
|
-
RSpec.describe UsersController do
|
|
444
|
-
describe "GET #index" do
|
|
445
|
-
it { is_expected.to respond_with(:success) }
|
|
446
|
-
it { is_expected.to render_template(:index) }
|
|
447
|
-
end
|
|
448
|
-
|
|
449
|
-
describe "POST #create" do
|
|
450
|
-
it { is_expected.to permit(:name, :email).for(:create) }
|
|
451
|
-
end
|
|
452
|
-
end
|
|
453
|
-
```
|
|
454
|
-
|
|
455
|
-
---
|
|
456
|
-
|
|
457
|
-
## Complete Examples
|
|
169
|
+
## Interactor Testing
|
|
458
170
|
|
|
459
|
-
|
|
171
|
+
Test your Interactor service objects with clean, expressive matchers.
|
|
460
172
|
|
|
461
173
|
```ruby
|
|
462
174
|
RSpec.describe CreateUser do
|
|
463
175
|
subject(:result) { described_class.call(params) }
|
|
464
176
|
|
|
465
|
-
|
|
466
|
-
let(:params) { { name: "John
|
|
177
|
+
context "with valid params" do
|
|
178
|
+
let(:params) { { name: "John", email: "john@example.com" } }
|
|
467
179
|
|
|
180
|
+
# Basic checks
|
|
468
181
|
it { is_expected.to succeed }
|
|
469
182
|
it { is_expected.to set_context(:user) }
|
|
470
183
|
it { is_expected.to succeed.with_context(:user, kind_of(User)) }
|
|
471
|
-
|
|
472
|
-
it "creates a user with correct attributes" do
|
|
473
|
-
expect(result).to succeed
|
|
474
|
-
expect(result.user.name).to eq("John Doe")
|
|
475
|
-
expect(result.user.email).to eq("john@example.com")
|
|
476
|
-
end
|
|
477
184
|
end
|
|
478
185
|
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
let(:params) { { name: "", email: "john@example.com" } }
|
|
482
|
-
|
|
483
|
-
it { is_expected.to fail_interactor }
|
|
484
|
-
it { is_expected.to fail_interactor.with_error("blank_name") }
|
|
485
|
-
it { is_expected.to have_error_code("blank_name") }
|
|
486
|
-
end
|
|
487
|
-
|
|
488
|
-
context "with duplicate email" do
|
|
489
|
-
before { create(:user, email: "john@example.com") }
|
|
490
|
-
|
|
491
|
-
let(:params) { { name: "John", email: "john@example.com" } }
|
|
186
|
+
context "with invalid params" do
|
|
187
|
+
let(:params) { { name: "", email: "invalid" } }
|
|
492
188
|
|
|
493
|
-
|
|
494
|
-
|
|
189
|
+
it { is_expected.to fail_interactor }
|
|
190
|
+
it { is_expected.to fail_interactor.with_error("validation_failed") }
|
|
191
|
+
it { is_expected.to have_error_code("validation_failed") }
|
|
495
192
|
end
|
|
496
193
|
end
|
|
497
194
|
```
|
|
498
195
|
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
let(:query) do
|
|
509
|
-
<<~GQL
|
|
510
|
-
query GetUser($id: ID!) {
|
|
511
|
-
user(id: $id) {
|
|
512
|
-
id
|
|
513
|
-
name
|
|
514
|
-
email
|
|
515
|
-
posts {
|
|
516
|
-
id
|
|
517
|
-
title
|
|
518
|
-
}
|
|
519
|
-
}
|
|
520
|
-
}
|
|
521
|
-
GQL
|
|
522
|
-
end
|
|
523
|
-
|
|
524
|
-
it { is_expected.to succeed_graphql }
|
|
525
|
-
it { is_expected.to have_graphql_data("user") }
|
|
196
|
+
**Available matchers:**
|
|
197
|
+
- `succeed` - Verify interactor succeeded
|
|
198
|
+
- `.with_context(key, value)` - Check context value
|
|
199
|
+
- `.with_data(value)` - Check context.data value
|
|
200
|
+
- `fail_interactor` - Verify interactor failed
|
|
201
|
+
- `.with_error(code)` - Check single error code
|
|
202
|
+
- `.with_errors(*codes)` - Check multiple error codes
|
|
203
|
+
- `set_context(key, value)` - Check context was set
|
|
204
|
+
- `have_error_code(code)` / `have_error_codes(*codes)` - Check error codes
|
|
526
205
|
|
|
527
|
-
|
|
528
|
-
expect(result).to have_graphql_data("user").matching(
|
|
529
|
-
id: user.id,
|
|
530
|
-
name: "John Doe",
|
|
531
|
-
email: "john@example.com"
|
|
532
|
-
)
|
|
533
|
-
end
|
|
206
|
+
---
|
|
534
207
|
|
|
535
|
-
|
|
208
|
+
## Shoulda Matchers
|
|
536
209
|
|
|
537
|
-
|
|
538
|
-
before { create_list(:post, 3, user: user) }
|
|
210
|
+
Automatically includes and configures [Shoulda Matchers](https://github.com/thoughtbot/shoulda-matchers) for Rails projects.
|
|
539
211
|
|
|
540
|
-
|
|
541
|
-
|
|
212
|
+
```ruby
|
|
213
|
+
RSpec.describe User do
|
|
214
|
+
subject { build(:user) }
|
|
542
215
|
|
|
543
|
-
|
|
544
|
-
|
|
216
|
+
# Validations
|
|
217
|
+
it { is_expected.to validate_presence_of(:email) }
|
|
218
|
+
it { is_expected.to validate_uniqueness_of(:email).case_insensitive }
|
|
545
219
|
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
end
|
|
220
|
+
# Associations
|
|
221
|
+
it { is_expected.to have_many(:posts).dependent(:destroy) }
|
|
222
|
+
it { is_expected.to belong_to(:organization) }
|
|
550
223
|
end
|
|
551
224
|
```
|
|
552
225
|
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
```ruby
|
|
556
|
-
RSpec.describe Types::UserType do
|
|
557
|
-
subject { described_class }
|
|
226
|
+
---
|
|
558
227
|
|
|
559
|
-
|
|
560
|
-
it { is_expected.to have_field(:id).of_type("ID!") }
|
|
561
|
-
it { is_expected.to have_field(:name).of_type("String!") }
|
|
562
|
-
it { is_expected.to have_field(:email).of_type("String") }
|
|
563
|
-
it { is_expected.to have_field(:createdAt).of_type("ISO8601DateTime!") }
|
|
228
|
+
## Progress Bar Formatter
|
|
564
229
|
|
|
565
|
-
|
|
566
|
-
it { is_expected.to have_field(:posts).of_type("[Post!]!") }
|
|
567
|
-
it { is_expected.to have_field(:organization).of_type("Organization") }
|
|
230
|
+
Clean, colorful test output with status icons.
|
|
568
231
|
|
|
569
|
-
|
|
570
|
-
describe "posts field" do
|
|
571
|
-
subject(:field) { described_class.fields["posts"] }
|
|
232
|
+
### Setup
|
|
572
233
|
|
|
573
|
-
|
|
574
|
-
it { is_expected.to have_argument(:offset).of_type("Int") }
|
|
575
|
-
it { is_expected.to have_argument(:status).of_type("PostStatus") }
|
|
576
|
-
end
|
|
577
|
-
end
|
|
234
|
+
Add to your `.rspec` file:
|
|
578
235
|
|
|
579
|
-
|
|
580
|
-
|
|
236
|
+
```
|
|
237
|
+
--require zenspec/formatters/progress_bar_formatter
|
|
238
|
+
--format ProgressBarFormatter
|
|
239
|
+
```
|
|
581
240
|
|
|
582
|
-
|
|
583
|
-
end
|
|
241
|
+
Or use via command line:
|
|
584
242
|
|
|
585
|
-
|
|
586
|
-
|
|
243
|
+
```bash
|
|
244
|
+
rspec --require zenspec/formatters/progress_bar_formatter --format ProgressBarFormatter
|
|
245
|
+
```
|
|
587
246
|
|
|
588
|
-
|
|
589
|
-
it { is_expected.to have_query(:user).with_argument(:id, "ID!") }
|
|
590
|
-
it { is_expected.to have_query(:users).of_type("[User!]!") }
|
|
591
|
-
it { is_expected.to have_query(:currentUser).of_type("User") }
|
|
592
|
-
end
|
|
247
|
+
### Output
|
|
593
248
|
|
|
594
|
-
describe "mutations" do
|
|
595
|
-
it { is_expected.to have_mutation(:createUser).with_argument(:input, "CreateUserInput!") }
|
|
596
|
-
it { is_expected.to have_mutation(:updateUser).with_argument(:input, "UpdateUserInput!") }
|
|
597
|
-
it { is_expected.to have_mutation(:deleteUser).of_type("Boolean!") }
|
|
598
|
-
end
|
|
599
|
-
end
|
|
600
249
|
```
|
|
250
|
+
✔ user_spec.rb [10% 1/10]
|
|
251
|
+
✔ post_spec.rb [20% 2/10]
|
|
252
|
+
⠿ auth_spec.rb --> authenticates with OAuth [30% 3/10]
|
|
253
|
+
```
|
|
254
|
+
|
|
255
|
+
**Icons:**
|
|
256
|
+
- ✔ Green - Passed
|
|
257
|
+
- ✗ Red - Failed
|
|
258
|
+
- ⠿ Yellow - Running (shows test description)
|
|
259
|
+
- ⊘ Cyan - Pending
|
|
601
260
|
|
|
602
261
|
---
|
|
603
262
|
|
|
604
263
|
## Configuration
|
|
605
264
|
|
|
606
|
-
Zenspec works out of the box with sensible defaults. For Rails applications, it automatically configures itself
|
|
265
|
+
Zenspec works out of the box with sensible defaults. For Rails applications, it automatically configures itself.
|
|
607
266
|
|
|
608
|
-
###
|
|
609
|
-
|
|
610
|
-
If you're not using Rails, the matchers are still automatically included when you require zenspec:
|
|
267
|
+
### Non-Rails Projects
|
|
611
268
|
|
|
612
269
|
```ruby
|
|
613
270
|
# spec/spec_helper.rb
|
|
@@ -620,18 +277,14 @@ require "zenspec"
|
|
|
620
277
|
|
|
621
278
|
## Development
|
|
622
279
|
|
|
623
|
-
After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake spec` to run the tests.
|
|
280
|
+
After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake spec` to run the tests.
|
|
624
281
|
|
|
625
|
-
To install this gem onto your local machine, run `bundle exec rake install`.
|
|
282
|
+
To install this gem onto your local machine, run `bundle exec rake install`.
|
|
626
283
|
|
|
627
284
|
## Contributing
|
|
628
285
|
|
|
629
|
-
Bug reports and pull requests are welcome on GitHub at https://github.com/zyxzen/zenspec.
|
|
286
|
+
Bug reports and pull requests are welcome on GitHub at https://github.com/zyxzen/zenspec.
|
|
630
287
|
|
|
631
288
|
## License
|
|
632
289
|
|
|
633
290
|
The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
|
|
634
|
-
|
|
635
|
-
## Code of Conduct
|
|
636
|
-
|
|
637
|
-
Everyone interacting in the Zenspec project's codebases, issue trackers, chat rooms and mailing lists is expected to follow the [code of conduct](https://github.com/zyxzen/zenspec/blob/main/CODE_OF_CONDUCT.md).
|
data/lib/zenspec/version.rb
CHANGED
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: zenspec
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.
|
|
4
|
+
version: 0.2.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Wilson Anciro
|
|
@@ -15,14 +15,14 @@ dependencies:
|
|
|
15
15
|
requirements:
|
|
16
16
|
- - "~>"
|
|
17
17
|
- !ruby/object:Gem::Version
|
|
18
|
-
version: '
|
|
18
|
+
version: '2.5'
|
|
19
19
|
type: :runtime
|
|
20
20
|
prerelease: false
|
|
21
21
|
version_requirements: !ruby/object:Gem::Requirement
|
|
22
22
|
requirements:
|
|
23
23
|
- - "~>"
|
|
24
24
|
- !ruby/object:Gem::Version
|
|
25
|
-
version: '
|
|
25
|
+
version: '2.5'
|
|
26
26
|
- !ruby/object:Gem::Dependency
|
|
27
27
|
name: interactor
|
|
28
28
|
requirement: !ruby/object:Gem::Requirement
|