rspec-grape-entity 0.1.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 +7 -0
- data/.github/workflows/rspec.yml +26 -0
- data/.github/workflows/rubocop.yml +30 -0
- data/.gitignore +17 -0
- data/.rspec +3 -0
- data/.rubocop.yml +13 -0
- data/Gemfile +13 -0
- data/LICENSE +21 -0
- data/LICENSE.txt +21 -0
- data/README.md +695 -0
- data/Rakefile +12 -0
- data/bin/console +15 -0
- data/bin/setup +8 -0
- data/lib/rspec-grape-entity.rb +3 -0
- data/lib/rspec_grape_entity/describe_exposure.rb +125 -0
- data/lib/rspec_grape_entity/dsl.rb +16 -0
- data/lib/rspec_grape_entity/its_exposure.rb +72 -0
- data/lib/rspec_grape_entity/matchers/be_a_exposure_type_matcher.rb +33 -0
- data/lib/rspec_grape_entity/matchers/be_merged_matcher.rb +21 -0
- data/lib/rspec_grape_entity/matchers/be_safe_matcher.rb +21 -0
- data/lib/rspec_grape_entity/matchers/be_using_class_matcher.rb +21 -0
- data/lib/rspec_grape_entity/matchers/have_conditions_met_matcher.rb +36 -0
- data/lib/rspec_grape_entity/matchers/have_formatting_matcher.rb +39 -0
- data/lib/rspec_grape_entity/matchers/have_key_matcher.rb +21 -0
- data/lib/rspec_grape_entity/matchers/have_root_matcher.rb +33 -0
- data/lib/rspec_grape_entity/matchers/include_documentation_matcher.rb +26 -0
- data/lib/rspec_grape_entity/matchers/matcher_helpers.rb +24 -0
- data/lib/rspec_grape_entity/matchers/override_exposure_matcher.rb +21 -0
- data/lib/rspec_grape_entity/version.rb +5 -0
- data/lib/rspec_grape_entity.rb +28 -0
- data/rspec-grape-entity.gemspec +37 -0
- data/spec/describe_exposure_spec.rb +29 -0
- data/spec/entities/test_entity_spec.rb +150 -0
- data/spec/its_exposure_spec.rb +16 -0
- data/spec/matchers/be_a_exposure_type_matcher_spec.rb +106 -0
- data/spec/matchers/be_merged_matcher_spec.rb +19 -0
- data/spec/matchers/be_safe_matcher_spec.rb +19 -0
- data/spec/matchers/be_using_class_matcher_spec.rb +15 -0
- data/spec/matchers/have_conditions_met_matcher_spec.rb +40 -0
- data/spec/matchers/have_formatting_matcher_spec.rb +25 -0
- data/spec/matchers/have_key_matcher_spec.rb +23 -0
- data/spec/matchers/have_root_matcher_spec.rb +47 -0
- data/spec/matchers/include_documentation_matcher_spec.rb +31 -0
- data/spec/matchers/matcher_helpers_spec.rb +76 -0
- data/spec/matchers/override_exposure_matcher_spec.rb +19 -0
- data/spec/spec_helper.rb +21 -0
- data/spec/support/test_entity.rb +23 -0
- data/spec/support/user_entity.rb +12 -0
- metadata +181 -0
data/README.md
ADDED
@@ -0,0 +1,695 @@
|
|
1
|
+

|
2
|
+

|
3
|
+
|
4
|
+
|
5
|
+
# Table of Contents
|
6
|
+
|
7
|
+
- [Rspec::Grape::Entity](#rspecgrapeentity)
|
8
|
+
- [Introduction](#introduction)
|
9
|
+
- [Installation](#installation)
|
10
|
+
- [Include Rspec::Grape::Entity](#include-rspecgrapeentity)
|
11
|
+
- [Usage](#usage)
|
12
|
+
- [Example](#example)
|
13
|
+
- [Entity Matchers](#entity-matchers)
|
14
|
+
- [`root`](#have_rootplural)
|
15
|
+
- [Exposure Matchers](#exposure-matchers)
|
16
|
+
- [`be_a_block_exposure`](#be_a_block_exposure)
|
17
|
+
- [`be_a_delegator_exposure`](#be_a_delegator_exposure)
|
18
|
+
- [`be_a_formatter_exposure`](#be_a_formatter_exposure)
|
19
|
+
- [`be_a_formatter_block_exposure`](#be_a_formatter_block_exposure)
|
20
|
+
- [`be_a_nesting_exposure`](#be_a_nesting_exposure)
|
21
|
+
- [`be_a_represent_exposure`](#be_a_represent_exposure)
|
22
|
+
- [`be_merged`](#be_merged)
|
23
|
+
- [`be_safe`](#be_safe)
|
24
|
+
- [`be_using_class`](#be_using_classentity)
|
25
|
+
- [`have_conditions_met`](#have_conditions_metobject)
|
26
|
+
- [`have_formatting`](#have_formattingformatter)
|
27
|
+
- [`have_key`](#have_keykey)
|
28
|
+
- [`include_documentation`](#include_documentationdocs)
|
29
|
+
- [`override_exposure`](#override_exposure)
|
30
|
+
- [Development](#development)
|
31
|
+
- [Report Issues](#report-issues)
|
32
|
+
- [License](#license)
|
33
|
+
|
34
|
+
# Rspec::Grape::Entity
|
35
|
+
|
36
|
+
## Introduction
|
37
|
+
|
38
|
+
This gem, inspired by [rspec-its](https://github.com/rspec/rspec-its), provides the `its_exposure` and `describe_exposure`
|
39
|
+
short-hand to specify the expected configuration of a given [GrapeEntity](https://github.com/ruby-grape/grape-entity)
|
40
|
+
exposure.
|
41
|
+
|
42
|
+
## Installation
|
43
|
+
|
44
|
+
Add this line to your application's Gemfile:
|
45
|
+
|
46
|
+
```ruby
|
47
|
+
gem 'rspec-grape-entity', group: :test
|
48
|
+
```
|
49
|
+
|
50
|
+
And then execute:
|
51
|
+
|
52
|
+
$ bundle install
|
53
|
+
|
54
|
+
Or install it yourself as:
|
55
|
+
|
56
|
+
$ gem install rspec-grape-entity
|
57
|
+
|
58
|
+
## Include `RSpec::Grape::Entity`
|
59
|
+
|
60
|
+
Include the `RSpec::Grape::Entity` matchers automatically by defining the described `type` as `:grape_entity` or by
|
61
|
+
including the `RSpec::Grape::Entity::DSL` directly.
|
62
|
+
|
63
|
+
```ruby
|
64
|
+
# Automatic
|
65
|
+
describe MyEntity, type: :grape_entity do
|
66
|
+
# ... tests
|
67
|
+
end
|
68
|
+
|
69
|
+
# Manually
|
70
|
+
describe MyEntity do
|
71
|
+
include RSpec::Grape::Entity::DSL
|
72
|
+
|
73
|
+
# ... tests
|
74
|
+
end
|
75
|
+
```
|
76
|
+
|
77
|
+
## Usage
|
78
|
+
|
79
|
+
Use the `describe_exposure` or `its_exposure` methods to generate a nested example group that specifies the expected
|
80
|
+
exposure of an attribute of the entity using `should`, `should_not` or `is_expected`.
|
81
|
+
|
82
|
+
`describe_exposure` and `its_exposure` accepts a symbol or string.
|
83
|
+
|
84
|
+
```ruby
|
85
|
+
describe_exposure :id do
|
86
|
+
#...
|
87
|
+
end
|
88
|
+
|
89
|
+
describe_exposure 'id' do
|
90
|
+
#...
|
91
|
+
end
|
92
|
+
|
93
|
+
its_exposure(:id) { is_expected.not_to be_nil }
|
94
|
+
its_exposure('id') { is_expected.not_to be_nil }
|
95
|
+
```
|
96
|
+
|
97
|
+
You can use a string with dots to specify a nested exposure attribute.
|
98
|
+
|
99
|
+
```ruby
|
100
|
+
describe_exposure 'status.value' do
|
101
|
+
#...
|
102
|
+
end
|
103
|
+
|
104
|
+
describe_exposure 'status.changed_at' do
|
105
|
+
#...
|
106
|
+
end
|
107
|
+
|
108
|
+
its_exposure('status.value') { is_expected.not_to be_nil }
|
109
|
+
its_exposure('status.changed_at') { is_expected.not_to be_nil }
|
110
|
+
```
|
111
|
+
|
112
|
+
## Example
|
113
|
+
|
114
|
+
```ruby
|
115
|
+
class TestEntity < Grape::Entity
|
116
|
+
root "test_items", "test_item"
|
117
|
+
|
118
|
+
format_with :iso_timestamp, &:iso8601
|
119
|
+
|
120
|
+
expose :id, documentation: { type: Integer, desc: "The record id" }
|
121
|
+
expose :record_status, as: :status, if: :all
|
122
|
+
expose :user, safe: true, using: UserEntity, if: { type: :admin }
|
123
|
+
expose :custom_data, merge: true do |_, _|
|
124
|
+
{
|
125
|
+
foo: :bar
|
126
|
+
}
|
127
|
+
end
|
128
|
+
expose :permissions, override: true do
|
129
|
+
expose :read
|
130
|
+
expose :update
|
131
|
+
expose :destroy
|
132
|
+
end
|
133
|
+
expose :created_at, format_with: ->(date) { date.iso8601 }, if: ->(instance, _) { instance.has_date }
|
134
|
+
expose :updated_at, format_with: :iso_timestamp
|
135
|
+
end
|
136
|
+
|
137
|
+
RSpec.describe TestEntity, type: :grape_entity do
|
138
|
+
let(:object) do
|
139
|
+
OpenStruct.new id: 1,
|
140
|
+
record_status: "active",
|
141
|
+
user: OpenStruct.new,
|
142
|
+
read: true,
|
143
|
+
update: false,
|
144
|
+
destroy: false,
|
145
|
+
created_at: Time.utc(2022, 1, 1, 15, 0, 0),
|
146
|
+
updated_at: Time.now,
|
147
|
+
has_date: true
|
148
|
+
end
|
149
|
+
|
150
|
+
it { expect(described_class).to have_root("test_items").with_singular("test_item") }
|
151
|
+
|
152
|
+
context "when using its_exposure" do
|
153
|
+
let(:object_without_date) { OpenStruct.new has_date: false }
|
154
|
+
|
155
|
+
its_exposure(:id) { is_expected.to be_a_delegator_exposure }
|
156
|
+
its_exposure(:id) { is_expected.to include_documentation type: Integer, desc: "The record id" }
|
157
|
+
its_exposure(:id) { is_expected.not_to be_safe }
|
158
|
+
its_exposure(:id) { is_expected.not_to be_merged }
|
159
|
+
its_exposure(:id) { is_expected.not_to override_exposure }
|
160
|
+
its_exposure(:record_status) { is_expected.to be_a_delegator_exposure }
|
161
|
+
its_exposure(:record_status) { is_expected.to have_key :status }
|
162
|
+
its_exposure(:record_status) { is_expected.to have_conditions_met(object).with_options(all: :something) }
|
163
|
+
its_exposure(:record_status) { is_expected.to_not have_conditions_met object }
|
164
|
+
its_exposure(:record_status) { is_expected.not_to be_safe }
|
165
|
+
its_exposure(:record_status) { is_expected.not_to be_merged }
|
166
|
+
its_exposure(:record_status) { is_expected.not_to override_exposure }
|
167
|
+
its_exposure(:user) { is_expected.to be_a_represent_exposure }
|
168
|
+
its_exposure(:user) { is_expected.to be_using_class UserEntity }
|
169
|
+
its_exposure(:user) { is_expected.to have_conditions_met(object).with_options(type: :admin) }
|
170
|
+
its_exposure(:user) { is_expected.not_to have_conditions_met(object).with_options(type: :user) }
|
171
|
+
its_exposure(:user) { is_expected.not_to have_conditions_met object }
|
172
|
+
its_exposure(:custom_data) { is_expected.to be_a_block_exposure }
|
173
|
+
its_exposure(:custom_data) { is_expected.not_to be_safe }
|
174
|
+
its_exposure(:custom_data) { is_expected.to be_merged }
|
175
|
+
its_exposure(:custom_data) { is_expected.not_to override_exposure }
|
176
|
+
its_exposure(:permissions) { is_expected.to be_a_nesting_exposure }
|
177
|
+
its_exposure(:permissions) { is_expected.not_to be_safe }
|
178
|
+
its_exposure(:permissions) { is_expected.not_to be_merged }
|
179
|
+
its_exposure(:permissions) { is_expected.to override_exposure }
|
180
|
+
its_exposure("permissions.read") { is_expected.to be_a_delegator_exposure }
|
181
|
+
its_exposure("permissions.read") { is_expected.not_to be_safe }
|
182
|
+
its_exposure("permissions.read") { is_expected.not_to be_merged }
|
183
|
+
its_exposure("permissions.read") { is_expected.not_to override_exposure }
|
184
|
+
its_exposure("permissions.update") { is_expected.to be_a_delegator_exposure }
|
185
|
+
its_exposure("permissions.update") { is_expected.not_to be_safe }
|
186
|
+
its_exposure("permissions.update") { is_expected.not_to be_merged }
|
187
|
+
its_exposure("permissions.update") { is_expected.not_to override_exposure }
|
188
|
+
its_exposure("permissions.destroy") { is_expected.to be_a_delegator_exposure }
|
189
|
+
its_exposure("permissions.destroy") { is_expected.not_to be_safe }
|
190
|
+
its_exposure("permissions.destroy") { is_expected.not_to be_merged }
|
191
|
+
its_exposure("permissions.destroy") { is_expected.not_to override_exposure }
|
192
|
+
its_exposure("created_at") { is_expected.to be_a_formatter_block_exposure }
|
193
|
+
its_exposure("created_at") { is_expected.to have_formatting("2022-01-01T15:00:00Z").with_object(object) }
|
194
|
+
its_exposure("created_at") { is_expected.not_to be_safe }
|
195
|
+
its_exposure("created_at") { is_expected.not_to be_merged }
|
196
|
+
its_exposure("created_at") { is_expected.not_to override_exposure }
|
197
|
+
its_exposure("created_at") { is_expected.to have_conditions_met object }
|
198
|
+
its_exposure("created_at") { is_expected.to_not have_conditions_met object_without_date }
|
199
|
+
end
|
200
|
+
|
201
|
+
context "when using describe_exposure" do
|
202
|
+
shared_examples "has permissions" do |permission|
|
203
|
+
describe_exposure "permissions.#{permission}" do
|
204
|
+
it { is_expected.to be_a_delegator_exposure }
|
205
|
+
it { is_expected.not_to be_safe }
|
206
|
+
it { is_expected.not_to be_merged }
|
207
|
+
it { is_expected.not_to override_exposure }
|
208
|
+
end
|
209
|
+
end
|
210
|
+
|
211
|
+
describe_exposure :id do
|
212
|
+
it { is_expected.to be_a_delegator_exposure }
|
213
|
+
it { is_expected.to include_documentation type: Integer, desc: "The record id" }
|
214
|
+
it { is_expected.not_to be_safe }
|
215
|
+
it { is_expected.not_to be_merged }
|
216
|
+
it { is_expected.not_to override_exposure }
|
217
|
+
end
|
218
|
+
|
219
|
+
describe_exposure :record_status do
|
220
|
+
it { is_expected.to be_a_delegator_exposure }
|
221
|
+
it { is_expected.to have_key :status }
|
222
|
+
it { is_expected.to have_conditions_met(object).with_options(all: :something) }
|
223
|
+
it { is_expected.to_not have_conditions_met object }
|
224
|
+
it { is_expected.not_to be_safe }
|
225
|
+
it { is_expected.not_to be_merged }
|
226
|
+
it { is_expected.not_to override_exposure }
|
227
|
+
end
|
228
|
+
|
229
|
+
describe_exposure :user do
|
230
|
+
it { is_expected.to be_a_represent_exposure }
|
231
|
+
it { is_expected.to be_using_class UserEntity }
|
232
|
+
|
233
|
+
context "when type is an admin" do
|
234
|
+
it { is_expected.to have_conditions_met(object).with_options(type: :admin) }
|
235
|
+
end
|
236
|
+
|
237
|
+
context "when type is not an admin" do
|
238
|
+
it { is_expected.not_to have_conditions_met(object).with_options(type: :user) }
|
239
|
+
end
|
240
|
+
|
241
|
+
context "when no type is declared" do
|
242
|
+
it { is_expected.not_to have_conditions_met object }
|
243
|
+
end
|
244
|
+
end
|
245
|
+
|
246
|
+
describe_exposure :custom_data do
|
247
|
+
it { is_expected.to be_a_block_exposure }
|
248
|
+
it { is_expected.not_to be_safe }
|
249
|
+
it { is_expected.to be_merged }
|
250
|
+
it { is_expected.not_to override_exposure }
|
251
|
+
end
|
252
|
+
|
253
|
+
describe_exposure :permissions do
|
254
|
+
it { is_expected.to be_a_nesting_exposure }
|
255
|
+
it { is_expected.not_to be_safe }
|
256
|
+
it { is_expected.not_to be_merged }
|
257
|
+
it { is_expected.to override_exposure }
|
258
|
+
end
|
259
|
+
|
260
|
+
it_behaves_like "has permissions", "read"
|
261
|
+
it_behaves_like "has permissions", "update"
|
262
|
+
it_behaves_like "has permissions", "destroy"
|
263
|
+
|
264
|
+
describe_exposure :created_at do
|
265
|
+
it { is_expected.to be_a_formatter_block_exposure }
|
266
|
+
it { is_expected.to have_formatting("2022-01-01T15:00:00Z").with_object(object) }
|
267
|
+
it { is_expected.not_to be_safe }
|
268
|
+
it { is_expected.not_to be_merged }
|
269
|
+
it { is_expected.not_to override_exposure }
|
270
|
+
|
271
|
+
context "when has date" do
|
272
|
+
it { is_expected.to have_conditions_met object }
|
273
|
+
end
|
274
|
+
|
275
|
+
context "when does not have date" do
|
276
|
+
let(:object) { OpenStruct.new has_date: false }
|
277
|
+
|
278
|
+
it { is_expected.not_to have_conditions_met object }
|
279
|
+
end
|
280
|
+
end
|
281
|
+
end
|
282
|
+
end
|
283
|
+
```
|
284
|
+
|
285
|
+
## Entity Matchers
|
286
|
+
|
287
|
+
### `have_root(plural)`
|
288
|
+
|
289
|
+
Test that only the plural definition is set.
|
290
|
+
|
291
|
+
```ruby
|
292
|
+
class Entity < Grape::Entity
|
293
|
+
root "items"
|
294
|
+
end
|
295
|
+
|
296
|
+
RSpec.describe Entity, type: :grape_entity do
|
297
|
+
it { expect(described_class).to have_root "items" }
|
298
|
+
end
|
299
|
+
```
|
300
|
+
|
301
|
+
Chain `singular(singular)` to specify the expected singular definition.
|
302
|
+
|
303
|
+
```ruby
|
304
|
+
class Entity < Grape::Entity
|
305
|
+
root "items", "item"
|
306
|
+
end
|
307
|
+
|
308
|
+
RSpec.describe Entity, type: :grape_entity do
|
309
|
+
it { expect(described_class).to have_root("items").singular("item") }
|
310
|
+
end
|
311
|
+
```
|
312
|
+
|
313
|
+
## Exposure Matchers
|
314
|
+
|
315
|
+
Use `should`, `should_not`, `will`, `will_not`, `is_expected` to specify the expected value of the exposed attribute.
|
316
|
+
|
317
|
+
### `be_a_block_exposure`
|
318
|
+
|
319
|
+
Test that the exposed attribute is a block exposure.
|
320
|
+
|
321
|
+
```ruby
|
322
|
+
class Entity < Grape::Entity
|
323
|
+
expose :block do |item, options|
|
324
|
+
#...something
|
325
|
+
end
|
326
|
+
end
|
327
|
+
|
328
|
+
RSpec.describe Entity, type: :grape_entity do
|
329
|
+
describe_exposure :block do
|
330
|
+
it { is_expected.to be_a_block_exposure }
|
331
|
+
end
|
332
|
+
|
333
|
+
# Or
|
334
|
+
|
335
|
+
its_exposure(:block) { is_expected.to be_a_block_exposure }
|
336
|
+
end
|
337
|
+
```
|
338
|
+
|
339
|
+
### `be_a_delegator_exposure`
|
340
|
+
|
341
|
+
Test that the exposed attribute is a delegator exposure.
|
342
|
+
|
343
|
+
```ruby
|
344
|
+
class Entity < Grape::Entity
|
345
|
+
expose :id
|
346
|
+
end
|
347
|
+
|
348
|
+
RSpec.describe Entity, type: :grape_entity do
|
349
|
+
describe_exposure :id do
|
350
|
+
it { is_expected.to be_a_delegator_exposure }
|
351
|
+
end
|
352
|
+
|
353
|
+
# Or
|
354
|
+
|
355
|
+
its_exposure(:id) { is_expected.to be_a_delegator_exposure }
|
356
|
+
end
|
357
|
+
```
|
358
|
+
|
359
|
+
### `be_a_formatter_exposure`
|
360
|
+
|
361
|
+
Test that the exposed attribute is a formatter exposure using a symbol.
|
362
|
+
|
363
|
+
```ruby
|
364
|
+
class Entity < Grape::Entity
|
365
|
+
expose :created_at, format_with: :iso_timestamp
|
366
|
+
end
|
367
|
+
|
368
|
+
RSpec.describe Entity, type: :grape_entity do
|
369
|
+
describe_exposure :created_at do
|
370
|
+
it { is_expected.to be_a_formatter_exposure }
|
371
|
+
end
|
372
|
+
|
373
|
+
# Or
|
374
|
+
|
375
|
+
its_exposure(:created_at) { is_expected.to be_a_formatter_exposure }
|
376
|
+
end
|
377
|
+
```
|
378
|
+
|
379
|
+
### `be_a_formatter_block_exposure`
|
380
|
+
|
381
|
+
Test that the exposed attribute is a formatter exposure using a proc.
|
382
|
+
|
383
|
+
```ruby
|
384
|
+
class Entity < Grape::Entity
|
385
|
+
expose :created_at, format_with: ->(date) { date.iso8601 }
|
386
|
+
end
|
387
|
+
|
388
|
+
RSpec.describe Entity, type: :grape_entity do
|
389
|
+
describe_exposure :created_at do
|
390
|
+
it { is_expected.to be_a_formatter_block_exposure }
|
391
|
+
end
|
392
|
+
|
393
|
+
# Or
|
394
|
+
|
395
|
+
its_exposure(:created_at) { is_expected.to be_a_formatter_block_exposure }
|
396
|
+
end
|
397
|
+
```
|
398
|
+
|
399
|
+
### `be_a_nesting_exposure`
|
400
|
+
|
401
|
+
Test that the exposed attribute is a nesting exposure.
|
402
|
+
|
403
|
+
```ruby
|
404
|
+
class Entity < Grape::Entity
|
405
|
+
expose :status do
|
406
|
+
expose :status, as: :value
|
407
|
+
expose :changed_at, as: :status_changed_at
|
408
|
+
end
|
409
|
+
end
|
410
|
+
|
411
|
+
RSpec.describe Entity, type: :grape_entity do
|
412
|
+
describe_exposure :status do
|
413
|
+
it { is_expected.to be_a_nesting_exposure }
|
414
|
+
end
|
415
|
+
|
416
|
+
# Or
|
417
|
+
|
418
|
+
its_exposure(:status) { is_expected.to be_a_nesting_exposure }
|
419
|
+
end
|
420
|
+
```
|
421
|
+
|
422
|
+
### `be_a_represent_exposure`
|
423
|
+
|
424
|
+
Test that the exposed attribute is a represented exposure.
|
425
|
+
|
426
|
+
```ruby
|
427
|
+
class Entity < Grape::Entity
|
428
|
+
expose :user, using: UserEntity
|
429
|
+
end
|
430
|
+
|
431
|
+
RSpec.describe Entity, type: :grape_entity do
|
432
|
+
describe_exposure :user do
|
433
|
+
it { is_expected.to be_a_represent_exposure }
|
434
|
+
end
|
435
|
+
|
436
|
+
# Or
|
437
|
+
|
438
|
+
its_exposure(:user) { is_expected.to be_a_represent_exposure }
|
439
|
+
end
|
440
|
+
```
|
441
|
+
|
442
|
+
### `be_merged`
|
443
|
+
|
444
|
+
Test that the exposed attribute is merged into the parent.
|
445
|
+
|
446
|
+
```ruby
|
447
|
+
class Entity < Grape::Entity
|
448
|
+
expose :user, using: UserEntity, merge: true
|
449
|
+
end
|
450
|
+
|
451
|
+
RSpec.describe Entity, type: :grape_entity do
|
452
|
+
describe_exposure :user do
|
453
|
+
it { is_expected.to be_merged }
|
454
|
+
end
|
455
|
+
|
456
|
+
# Or
|
457
|
+
|
458
|
+
its_exposure(:user) { is_expected.to be_merged }
|
459
|
+
end
|
460
|
+
```
|
461
|
+
|
462
|
+
### `be_safe`
|
463
|
+
|
464
|
+
Test that the exposed attribute is safe.
|
465
|
+
|
466
|
+
```ruby
|
467
|
+
class Entity < Grape::Entity
|
468
|
+
expose :user, using: UserEntity, safe: true
|
469
|
+
end
|
470
|
+
|
471
|
+
RSpec.describe Entity, type: :grape_entity do
|
472
|
+
describe_exposure :user do
|
473
|
+
it { is_expected.to be_safe }
|
474
|
+
end
|
475
|
+
|
476
|
+
# Or
|
477
|
+
|
478
|
+
its_exposure(:user) { is_expected.to be_safe }
|
479
|
+
end
|
480
|
+
```
|
481
|
+
|
482
|
+
### `be_using_class(entity)`
|
483
|
+
|
484
|
+
Test that the exposed attribute uses an entity presenter.
|
485
|
+
|
486
|
+
```ruby
|
487
|
+
class Entity < Grape::Entity
|
488
|
+
expose :user, using: UserEntity
|
489
|
+
end
|
490
|
+
|
491
|
+
RSpec.describe Entity, type: :grape_entity do
|
492
|
+
describe_exposure :user do
|
493
|
+
it { is_expected.to be_using_class(UserEntity) }
|
494
|
+
end
|
495
|
+
|
496
|
+
# Or
|
497
|
+
|
498
|
+
its_exposure(:user) { is_expected.to be_using_class(UserEntity) }
|
499
|
+
end
|
500
|
+
```
|
501
|
+
|
502
|
+
### `have_conditions_met(object)`
|
503
|
+
|
504
|
+
Test that the exposed attribute conditions are met with a given object.
|
505
|
+
|
506
|
+
```ruby
|
507
|
+
class Entity < Grape::Entity
|
508
|
+
expose :protected, if: ->(instance) { instance.is_a?(Admin) }
|
509
|
+
end
|
510
|
+
|
511
|
+
RSpec.describe Entity, type: :grape_entity do
|
512
|
+
let(:admin) { Admin.new }
|
513
|
+
let(:user) { User.new }
|
514
|
+
|
515
|
+
describe_exposure :protected do
|
516
|
+
it { is_expected.to have_conditions_met(admin) }
|
517
|
+
it { is_expected.not_to have_conditions_met(user) }
|
518
|
+
end
|
519
|
+
|
520
|
+
# Or
|
521
|
+
|
522
|
+
its_exposure(:protected) { is_expected.to have_conditions_met(admin) }
|
523
|
+
its_exposure(:protected) { is_expected.not_to have_conditions_met(user) }
|
524
|
+
end
|
525
|
+
```
|
526
|
+
|
527
|
+
Chain with `with_options(options)` to pass an options hash to the attribute exposure's conditions.
|
528
|
+
|
529
|
+
```ruby
|
530
|
+
class Entity < Grape::Entity
|
531
|
+
expose :status, if: :all
|
532
|
+
expose :secret, if: { type: :admin }
|
533
|
+
end
|
534
|
+
|
535
|
+
RSpec.describe Entity, type: :grape_entity do
|
536
|
+
let(:admin) { Admin.new }
|
537
|
+
|
538
|
+
describe_exposure :status do
|
539
|
+
it { is_expected.to have_conditions_met(admin).with_options(all: true) }
|
540
|
+
it { is_expected.not_to have_conditions_met(admin) }
|
541
|
+
end
|
542
|
+
|
543
|
+
describe_exposure :secret do
|
544
|
+
it { is_expected.to have_conditions_met(admin).with_options(type: :admin) }
|
545
|
+
it { is_expected.not_to have_conditions_met(admin).with_options(type: :user) }
|
546
|
+
it { is_expected.not_to have_conditions_met(admin) }
|
547
|
+
end
|
548
|
+
|
549
|
+
# Or
|
550
|
+
|
551
|
+
its_exposure(:status) { is_expected.to have_conditions_met(admin).with_options(all: true) }
|
552
|
+
its_exposure(:status) { is_expected.not_to have_conditions_met(admin) }
|
553
|
+
its_exposure(:secret) { is_expected.to have_conditions_met(admin).with_options(type: :admin) }
|
554
|
+
its_exposure(:secret) { is_expected.not_to have_conditions_met(admin).with_options(type: :user) }
|
555
|
+
its_exposure(:secret) { is_expected.not_to have_conditions_met(admin) }
|
556
|
+
end
|
557
|
+
```
|
558
|
+
|
559
|
+
### `have_formatting(formatter)`
|
560
|
+
|
561
|
+
Test that the exposed attribute uses a given formatter.
|
562
|
+
|
563
|
+
```ruby
|
564
|
+
class Entity < Grape::Entity
|
565
|
+
expose :created_at, format_with: :iso_timestamp
|
566
|
+
end
|
567
|
+
|
568
|
+
RSpec.describe Entity, type: :grape_entity do
|
569
|
+
describe_exposure :created_at do
|
570
|
+
it { is_expected.to have_formatting :iso_timestamp }
|
571
|
+
end
|
572
|
+
|
573
|
+
# Or
|
574
|
+
|
575
|
+
its_exposure(:created_at) { is_expected.to have_formatting :iso_timestamp }
|
576
|
+
end
|
577
|
+
```
|
578
|
+
|
579
|
+
Chain with `with_object(object)` when the formatter option is a Proc.
|
580
|
+
|
581
|
+
```ruby
|
582
|
+
class Entity < Grape::Entity
|
583
|
+
expose :created_at, format_with: ->(date) { |date| date.iso8601 }
|
584
|
+
end
|
585
|
+
|
586
|
+
RSpec.describe Entity, type: :grape_entity do
|
587
|
+
let(:date) { Time.utc 2022, 1, 22, 17, 0, 0 }
|
588
|
+
|
589
|
+
describe_exposure :created_at do
|
590
|
+
it { is_expected.to have_formatting("2022-01-22T17:00:00Z").with_object(date) }
|
591
|
+
end
|
592
|
+
|
593
|
+
# Or
|
594
|
+
|
595
|
+
its_exposure(:created_at) { is_expected.to have_formatting("2022-01-22T17:00:00Z").with_object(date) }
|
596
|
+
end
|
597
|
+
```
|
598
|
+
|
599
|
+
### `have_key(key)`
|
600
|
+
|
601
|
+
Test that the exposed attribute uses an alias.
|
602
|
+
|
603
|
+
```ruby
|
604
|
+
class Entity < Grape::Entity
|
605
|
+
expose :to_param, as: :id
|
606
|
+
end
|
607
|
+
|
608
|
+
RSpec.describe Entity, type: :grape_entity do
|
609
|
+
describe_exposure :to_param do
|
610
|
+
it { is_expected.to have_key :id }
|
611
|
+
end
|
612
|
+
|
613
|
+
# Or
|
614
|
+
|
615
|
+
its_exposure(:to_param) { is_expected.to have_key :id }
|
616
|
+
end
|
617
|
+
```
|
618
|
+
|
619
|
+
### `include_documentation(*docs)`
|
620
|
+
|
621
|
+
Test that the exposed attribute has the included documentation.
|
622
|
+
|
623
|
+
```ruby
|
624
|
+
class Entity < Grape::Entity
|
625
|
+
expose :id, documentation: { type: Integer, desc: "Entity id" }
|
626
|
+
end
|
627
|
+
|
628
|
+
RSpec.describe Entity, type: :grape_entity do
|
629
|
+
describe_exposure :id do
|
630
|
+
it { is_expected.to have_documentation :type, :desc }
|
631
|
+
it { is_expected.to have_documentation :type }
|
632
|
+
it { is_expected.to have_documentation :desc }
|
633
|
+
it { is_expected.to have_documentation type: Integer }
|
634
|
+
it { is_expected.to have_documentation desc: "Entity id" }
|
635
|
+
it { is_expected.to have_documentation type: Integer, desc: "Entity id" }
|
636
|
+
end
|
637
|
+
|
638
|
+
# Or
|
639
|
+
|
640
|
+
its_exposure(:id) { is_expected.to have_documentation :type, :desc }
|
641
|
+
its_exposure(:id) { is_expected.to have_documentation :type }
|
642
|
+
its_exposure(:id) { is_expected.to have_documentation :desc }
|
643
|
+
its_exposure(:id) { is_expected.to have_documentation type: Integer }
|
644
|
+
its_exposure(:id) { is_expected.to have_documentation desc: "Entity id" }
|
645
|
+
its_exposure(:id) { is_expected.to have_documentation type: Integer, desc: "Entity id" }
|
646
|
+
end
|
647
|
+
```
|
648
|
+
|
649
|
+
### `override_exposure`
|
650
|
+
|
651
|
+
Test that the exposed attribute uses an alias.
|
652
|
+
|
653
|
+
```ruby
|
654
|
+
class BaseEntity < Grape::Entity
|
655
|
+
expose :id
|
656
|
+
end
|
657
|
+
|
658
|
+
class Entity < BaseEntity
|
659
|
+
expose :id, override: true do |instance, options|
|
660
|
+
#...
|
661
|
+
end
|
662
|
+
end
|
663
|
+
|
664
|
+
RSpec.describe Entity, type: :grape_entity do
|
665
|
+
describe_exposure :id do
|
666
|
+
it { is_expected.to override_exposure }
|
667
|
+
end
|
668
|
+
|
669
|
+
# Or
|
670
|
+
|
671
|
+
its_exposure(:to_param) { is_expected.to override_exposure }
|
672
|
+
end
|
673
|
+
```
|
674
|
+
|
675
|
+
## Development
|
676
|
+
|
677
|
+
After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake spec` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
|
678
|
+
|
679
|
+
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).
|
680
|
+
|
681
|
+
## Report Issues
|
682
|
+
|
683
|
+
Bug reports and pull requests are welcome on GitHub at https://github.com/jefawks3/rspec-grape-entity.
|
684
|
+
|
685
|
+
## Contributing
|
686
|
+
|
687
|
+
1. Fork it
|
688
|
+
2. Create your feature branch (`git checkout -b my-new-feature`)
|
689
|
+
3. Commit your changes (`git commit -am 'Add some feature'`)
|
690
|
+
4. Push to the branch (`git push origin my-new-feature`)
|
691
|
+
5. Create new Pull Request
|
692
|
+
|
693
|
+
## License
|
694
|
+
|
695
|
+
The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
|
data/Rakefile
ADDED
data/bin/console
ADDED
@@ -0,0 +1,15 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
4
|
+
require "bundler/setup"
|
5
|
+
require "rspec-grape-entity"
|
6
|
+
|
7
|
+
# You can add fixtures and/or initialization code here to make experimenting
|
8
|
+
# with your gem easier. You can also use a different console, if you like.
|
9
|
+
|
10
|
+
# (If you use this, don't forget to add pry to your Gemfile!)
|
11
|
+
# require "pry"
|
12
|
+
# Pry.start
|
13
|
+
|
14
|
+
require "irb"
|
15
|
+
IRB.start(__FILE__)
|