tessa 0.9.2 → 1.0.0.pre.rc1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (90) hide show
  1. checksums.yaml +5 -5
  2. data/.babelrc +8 -0
  3. data/.circleci/config.yml +42 -0
  4. data/.eslintrc +19 -0
  5. data/.gitignore +5 -0
  6. data/.ruby-version +1 -1
  7. data/Rakefile +8 -0
  8. data/app/assets/javascripts/.keep +0 -0
  9. data/app/assets/javascripts/tessa.esm.js +672 -0
  10. data/app/assets/javascripts/tessa.js +662 -0
  11. data/app/javascript/activestorage/file_checksum.js +53 -0
  12. data/app/javascript/tessa/index.js.coffee +183 -0
  13. data/config/routes.rb +3 -0
  14. data/lib/tessa/active_storage/asset_wrapper.rb +24 -0
  15. data/lib/tessa/engine.rb +5 -0
  16. data/lib/tessa/model/dynamic_extensions.rb +151 -0
  17. data/lib/tessa/model.rb +27 -36
  18. data/lib/tessa/rack_upload_proxy.rb +18 -10
  19. data/lib/tessa/version.rb +1 -1
  20. data/lib/tessa/view_helpers.rb +4 -2
  21. data/lib/tessa.rb +30 -0
  22. data/package.json +43 -0
  23. data/rollup.config.js +44 -0
  24. data/spec/dummy/.gitignore +27 -0
  25. data/spec/dummy/README.md +24 -0
  26. data/spec/dummy/Rakefile +6 -0
  27. data/spec/dummy/app/assets/config/manifest.js +2 -0
  28. data/spec/dummy/app/assets/images/.keep +0 -0
  29. data/spec/dummy/app/assets/stylesheets/application.css +15 -0
  30. data/spec/dummy/app/controllers/application_controller.rb +2 -0
  31. data/spec/dummy/app/controllers/concerns/.keep +0 -0
  32. data/spec/dummy/app/helpers/application_helper.rb +2 -0
  33. data/spec/dummy/app/jobs/application_job.rb +2 -0
  34. data/spec/dummy/app/models/concerns/.keep +0 -0
  35. data/spec/dummy/app/models/multiple_asset_model.rb +8 -0
  36. data/spec/dummy/app/models/single_asset_model.rb +5 -0
  37. data/spec/dummy/app/models/single_asset_model_form.rb +25 -0
  38. data/spec/dummy/app/views/layouts/application.html.erb +14 -0
  39. data/spec/dummy/bin/bundle +3 -0
  40. data/spec/dummy/bin/rails +4 -0
  41. data/spec/dummy/bin/rake +4 -0
  42. data/spec/dummy/bin/setup +28 -0
  43. data/spec/dummy/bin/update +28 -0
  44. data/spec/dummy/bin/yarn +11 -0
  45. data/spec/dummy/config/application.rb +33 -0
  46. data/spec/dummy/config/boot.rb +3 -0
  47. data/spec/dummy/config/credentials.yml.enc +1 -0
  48. data/spec/dummy/config/database.yml +25 -0
  49. data/spec/dummy/config/environment.rb +5 -0
  50. data/spec/dummy/config/environments/development.rb +47 -0
  51. data/spec/dummy/config/environments/production.rb +76 -0
  52. data/spec/dummy/config/environments/test.rb +43 -0
  53. data/spec/dummy/config/initializers/application_controller_renderer.rb +8 -0
  54. data/spec/dummy/config/initializers/assets.rb +14 -0
  55. data/spec/dummy/config/initializers/backtrace_silencers.rb +7 -0
  56. data/spec/dummy/config/initializers/content_security_policy.rb +25 -0
  57. data/spec/dummy/config/initializers/cookies_serializer.rb +5 -0
  58. data/spec/dummy/config/initializers/filter_parameter_logging.rb +4 -0
  59. data/spec/dummy/config/initializers/inflections.rb +16 -0
  60. data/spec/dummy/config/initializers/mime_types.rb +4 -0
  61. data/spec/dummy/config/initializers/wrap_parameters.rb +9 -0
  62. data/spec/dummy/config/locales/en.yml +33 -0
  63. data/spec/dummy/config/puma.rb +37 -0
  64. data/spec/dummy/config/routes.rb +3 -0
  65. data/spec/dummy/config/storage.yml +3 -0
  66. data/spec/dummy/config.ru +5 -0
  67. data/spec/dummy/db/migrate/20220606221557_create_active_storage_tables.active_storage.rb +27 -0
  68. data/spec/dummy/db/migrate/20220606221900_create_single_asset_models.rb +10 -0
  69. data/spec/dummy/db/migrate/20220607191519_create_multiple_asset_models.rb +10 -0
  70. data/spec/dummy/db/schema.rb +50 -0
  71. data/spec/dummy/lib/assets/.keep +0 -0
  72. data/spec/dummy/lib/tasks/.keep +0 -0
  73. data/spec/dummy/log/.keep +0 -0
  74. data/spec/dummy/package.json +5 -0
  75. data/spec/dummy/public/404.html +67 -0
  76. data/spec/dummy/public/422.html +67 -0
  77. data/spec/dummy/public/500.html +66 -0
  78. data/spec/dummy/public/apple-touch-icon-precomposed.png +0 -0
  79. data/spec/dummy/public/apple-touch-icon.png +0 -0
  80. data/spec/dummy/public/favicon.ico +0 -0
  81. data/spec/dummy/public/robots.txt +1 -0
  82. data/spec/dummy/tmp/.keep +0 -0
  83. data/spec/dummy/vendor/.keep +0 -0
  84. data/spec/rails_helper.rb +14 -0
  85. data/spec/tessa/model_spec.rb +195 -262
  86. data/spec/tessa/rack_upload_proxy_spec.rb +23 -81
  87. data/tessa.gemspec +7 -2
  88. data/yarn.lock +1661 -0
  89. metadata +179 -16
  90. data/circle.yml +0 -3
@@ -1,23 +1,20 @@
1
- require 'spec_helper'
1
+ require 'rails_helper'
2
2
 
3
3
  RSpec.describe Tessa::Model do
4
4
  subject(:described_module) { described_class }
5
- let(:model) { Class.new.tap { |c| c.send(:include, described_module) } }
5
+ let(:model) {
6
+ SingleAssetModel
7
+ }
6
8
 
7
9
  it { is_expected.to be_a(Module) }
8
10
 
9
11
  describe "::asset" do
10
12
  it "creates ModelField and sets it by name to @tessa_fields" do
11
- model.asset :new_field
12
- expect(model.tessa_fields[:new_field]).to be_a(Tessa::Model::Field)
13
+ expect(model.tessa_fields[:avatar]).to be_a(Tessa::Model::Field)
13
14
  end
14
15
 
15
16
  context "with a field named :avatar" do
16
17
  subject(:instance) { model.new }
17
- before do
18
- model.send :attr_accessor, :avatar_id
19
- model.asset :avatar
20
- end
21
18
 
22
19
  it "creates an #avatar method" do
23
20
  expect(instance).to respond_to(:avatar)
@@ -41,308 +38,225 @@ RSpec.describe Tessa::Model do
41
38
  end
42
39
 
43
40
  context "with customized field" do
44
- before do
45
- model.asset :custom_field, multiple: true, id_field: "another_place"
46
- end
41
+ let(:model) {
42
+ MultipleAssetModel
43
+ }
47
44
 
48
45
  it "sets all attributes on ModelField properly" do
49
- field = model.tessa_fields[:custom_field]
50
- expect(field.name).to eq("custom_field")
46
+ field = model.tessa_fields[:multiple_field]
47
+ expect(field.name).to eq("multiple_field")
51
48
  expect(field.multiple).to eq(true)
52
49
  expect(field.id_field).to eq("another_place")
53
50
  end
54
51
  end
55
52
 
56
53
  context "with inheritance hierarchy" do
57
- let(:submodel) { Class.new(model) }
58
- before do
59
- model.asset :field1
60
- submodel.asset :field2
61
- model.asset :field3
62
- end
54
+ let(:submodel) {
55
+ Class.new(model) do
56
+ asset :field2
57
+ end
58
+ }
63
59
 
64
60
  it "submodel has its own list of fields" do
65
- expect(submodel.tessa_fields.keys).to eq([:field1, :field2])
61
+ expect(submodel.tessa_fields.keys).to eq([:avatar, :field2])
66
62
  end
67
63
 
68
64
  it "does not alter parent class fields" do
69
- expect(model.tessa_fields.keys).to eq([:field1, :field3])
70
- end
71
- end
72
- end
73
-
74
- describe "#asset_setter" do
75
- let(:instance) { model.new }
76
-
77
- context "with change set present for a field" do
78
- let(:a) { Tessa::Asset.new(id: 1) }
79
- let(:b) { Tessa::Asset.new(id: 2) }
80
- let(:set) { instance.pending_tessa_change_sets[:field] }
81
- before do
82
- model.send :attr_accessor, :field_id
83
- model.asset :field
84
- instance.field = a
85
- end
86
-
87
- it "combines the changes with the new set" do
88
- instance.field = b
89
- instance.field = nil
90
- changes = set.changes.map { |change| [change.id, change.action.to_sym] }
91
- expect(changes).to eq([
92
- [1, :add],
93
- [2, :add],
94
- [1, :remove],
95
- [2, :remove],
96
- ])
97
- end
98
- end
99
-
100
- context "with a multiple typed field" do
101
- before do
102
- model.send(:attr_accessor, :file_ids)
103
- model.asset :file, multiple: true
104
- end
105
-
106
- context "when set with array of assets" do
107
- let(:assets) {
108
- [
109
- Tessa::Asset.new(id: 1),
110
- Tessa::Asset.new(id: 2),
111
- ]
112
- }
113
-
114
- it "sets field to list of ids from assets" do
115
- instance.file = assets
116
- expect(instance.file_ids).to eq([1, 2])
117
- end
118
-
119
- it "removes any ids that aren't in list" do
120
- instance.file_ids = [3]
121
- instance.file = assets
122
- expect(instance.file_ids).to eq([1, 2])
123
- end
124
-
125
- it "adds an AssetChangeSet to pending queue for this field" do
126
- instance.file = assets
127
- expect(instance.pending_tessa_change_sets).to be_a(Hash)
128
- expect(instance.pending_tessa_change_sets[:file]).to be_a(Tessa::AssetChangeSet)
129
- end
130
-
131
- describe "the added change set" do
132
- subject(:set) { instance.pending_tessa_change_sets[:file] }
133
-
134
- it "has an 'add' action for each new asset" do
135
- instance.file = assets
136
- ids = set.changes.select { |c| c.action == 'add' }.collect(&:id)
137
- expect(ids).to eq([1, 2])
138
- end
139
-
140
- it "has a 'remove' action for each missing asset" do
141
- instance.file_ids = [3]
142
- instance.file = assets
143
- ids = set.changes.select { |c| c.action == 'remove' }.collect(&:id)
144
- expect(ids).to eq([3])
145
- end
146
-
147
- it "adds each of the ids to the scoped_ids list" do
148
- instance.file_ids = [3]
149
- instance.file = assets
150
- expect(set.scoped_ids).to eq([1, 2, 3])
151
- end
152
- end
153
- end
154
-
155
- context "when set with an AssetChangeSet" do
156
- let(:set) { Tessa::AssetChangeSet.new }
157
- before do
158
- set.add(1)
159
- set.add(2)
160
- set.changes << Tessa::AssetChange.new(id: 0, action: "add")
161
- end
162
-
163
- it "sets field to list of ids from scoped_changes" do
164
- instance.file = set
165
- expect(instance.file_ids).to eq([1, 2])
166
- end
167
-
168
- it "leaves any ids that are not touched by the set" do
169
- instance.file_ids = [3]
170
- instance.file = set
171
- expect(instance.file_ids).to include(1, 2, 3)
172
- end
173
-
174
- it "removes any ids from field that are scoped as removals" do
175
- instance.file_ids = [3]
176
- set.remove(3)
177
- instance.file = set
178
- expect(instance.file_ids).to eq([1, 2])
179
- end
180
-
181
- it "adds the AssetChangeSet to pending queue for this field" do
182
- instance.file = set
183
- new_set = instance.pending_tessa_change_sets[:file]
184
- expect(new_set.changes).to eq(set.changes)
185
- expect(new_set.scoped_ids).to eq(set.scoped_ids)
186
- end
65
+ expect(model.tessa_fields.keys).to eq([:avatar])
187
66
  end
188
67
  end
189
68
 
190
- context "with a singular typed field" do
191
- before do
192
- model.send(:attr_accessor, :file_id)
193
- model.asset :file
194
- end
195
-
196
- context "when set with an Asset" do
197
- let(:asset) { Tessa::Asset.new(id: 1) }
198
-
199
- it "sets field to id from asset" do
200
- instance.file = asset
201
- expect(instance.file_id).to eq(1)
202
- end
203
-
204
- it "adds an AssetChangeSet to pending queue for this field" do
205
- instance.file = asset
206
- expect(instance.pending_tessa_change_sets[:file]).to be_a(Tessa::AssetChangeSet)
207
- end
208
-
209
- describe "the added change set" do
210
- let(:set) { instance.pending_tessa_change_sets[:file] }
211
-
212
- it "has an 'add' action for the new asset" do
213
- instance.file = asset
214
- expect(set.changes.select(&:add?).first.id).to eq(1)
215
- end
69
+ context "on a form object" do
70
+ let(:model) {
71
+ SingleAssetModelForm
72
+ }
73
+ subject(:instance) { model.new }
216
74
 
217
- it "has a 'remove' action for the previous asset" do
218
- instance.file_id = 2
219
- instance.file = asset
220
- expect(set.changes.select(&:remove?).first.id).to eq(2)
221
- end
222
- end
75
+ it "creates an #avatar method" do
76
+ expect(instance).to respond_to(:avatar)
223
77
  end
224
78
 
225
- context "when set with an AssetChangeSet" do
226
- let(:set) { Tessa::AssetChangeSet.new }
227
- before do
228
- set.changes << Tessa::AssetChange.new(id: 0, action: "add")
229
- set.add(1)
230
- end
231
-
232
- it "sets field to asset id of first 'add' action in scoped_changes" do
233
- instance.file = set
234
- expect(instance.file_id).to eq(1)
235
- end
236
-
237
- it "adds the AssetChangeSet to pending queue for this field" do
238
- instance.file = set
239
- new_set = instance.pending_tessa_change_sets[:file]
240
- expect(new_set.changes).to eq(set.changes)
241
- expect(new_set.scoped_ids).to eq(set.scoped_ids)
242
- end
243
-
244
- it 'keeps asset when set to existing value' do
245
- change_set = Tessa::AssetChangeSet.new(
246
- changes: { '999' => { 'action' => 'add' } },
247
- scoped_ids: [999]
248
- )
249
- instance.file = change_set
250
- expect(instance.file_id).to eq(999)
251
-
252
- change_set.scoped_ids = [4, 5, 6]
253
- instance.file = change_set
254
- removals = change_set.changes.select(&:remove?).map(&:id)
255
- expect(removals).to_not include(999)
256
- expect(instance.file_id).to eq(999)
257
-
258
- instance.file = change_set
259
- expect(instance.file_id).to eq(999)
260
- end
261
-
262
- context "with no remove in change set" do
263
- it "ensures there is a 'remove' action for previous value" do
264
- instance.file_id = 2
265
- instance.file = set
266
- expect(set.scoped_changes.select(&:remove?).map(&:id)).to eq([2])
267
- end
268
- end
79
+ it "creates an #avatar= method" do
80
+ expect(instance).to respond_to(:avatar=)
269
81
  end
270
82
  end
271
83
  end
272
84
 
273
85
  describe "#asset_getter" do
274
86
  let(:instance) { model.new }
275
- subject(:getter) { instance.file }
276
87
 
277
88
  context "with a multiple typed field" do
278
- before do
279
- model.send(:attr_accessor, :file_ids)
280
- model.asset :file, multiple: true
281
- end
89
+ let(:model) {
90
+ MultipleAssetModel
91
+ }
92
+ subject(:getter) { instance.multiple_field }
282
93
 
283
94
  it "calls find for each of the file_ids and returns result" do
284
- instance.file_ids = [1, 2, 3]
95
+ instance.another_place = [1, 2, 3]
285
96
  expect(Tessa::Asset).to receive(:find).with([1, 2, 3]).and_return([:a1, :a2, :a3])
286
97
  expect(getter).to eq([:a1, :a2, :a3])
287
98
  end
288
99
 
289
100
  it "caches the result" do
290
- instance.file_ids = [1]
101
+ instance.another_place = [1]
291
102
  expect(Tessa::Asset).to receive(:find).and_return(:val).once
292
- instance.file
293
- instance.file
103
+ instance.multiple_field
104
+ instance.multiple_field
294
105
  end
295
106
 
296
107
  context "with no values" do
297
108
  it "does not call find" do
298
- instance.file_ids = []
109
+ instance.another_place = []
299
110
  expect(Tessa::Asset).not_to receive(:find)
300
- expect(instance.file).to eq([])
111
+ expect(instance.multiple_field).to eq([])
301
112
  end
302
113
  end
303
114
  end
304
115
 
305
116
  context "with a singular typed field" do
306
- before do
307
- model.send(:attr_accessor, :file_id)
308
- model.asset :file
309
- end
117
+ let(:model) {
118
+ SingleAssetModel
119
+ }
120
+ subject(:getter) { instance.avatar }
310
121
 
311
122
  it "calls find for file_id and returns result" do
312
- instance.file_id = 1
123
+ instance.avatar_id = 1
313
124
  expect(Tessa::Asset).to receive(:find).with(1).and_return(:a1)
314
125
  expect(getter).to eq(:a1)
315
126
  end
316
127
 
317
128
  it "caches the result" do
318
- instance.file_id = 1
129
+ instance.avatar_id = 1
319
130
  expect(Tessa::Asset).to receive(:find).and_return(:val).once
320
- instance.file
321
- instance.file
131
+ instance.avatar
132
+ instance.avatar
133
+ end
134
+
135
+ it "wraps ActiveStorage uploads with AssetWrapper" do
136
+ file = Rack::Test::UploadedFile.new("README.md")
137
+ instance.avatar = file
138
+
139
+ asset = instance.avatar
140
+ expect(asset).to be_a(Tessa::ActiveStorage::AssetWrapper)
141
+ # This goes to the blobs URL, which then redirects to the backend service URL
142
+ expect(asset.public_url).to start_with('https://www.example.com/rails/active_storage/blobs/')
143
+ # This is a direct download to the service URL (in test mode that is "disk")
144
+ expect(asset.private_url).to start_with('https://www.example.com/rails/active_storage/disk/')
145
+ expect(asset.private_download_url).to start_with('https://www.example.com/rails/active_storage/disk/')
146
+ expect(asset.private_download_url).to include('&disposition=attachment')
322
147
  end
323
148
 
324
149
  context "with nil value" do
325
150
  it "does not call find" do
326
- instance.file_id = nil
151
+ instance.avatar_id = nil
327
152
  expect(Tessa::Asset).not_to receive(:find)
328
- expect(instance.file).to be_nil
153
+ expect(instance.avatar).to be_nil
329
154
  end
330
155
  end
331
156
  end
157
+
158
+ context "on a form object" do
159
+ let(:model) {
160
+ SingleAssetModelForm
161
+ }
162
+ subject(:getter) { instance.avatar }
163
+
164
+ it 'returns nil when empty' do
165
+ expect(getter).to be_nil
166
+ end
167
+
168
+ it 'returns assigned upload object' do
169
+ file = Rack::Test::UploadedFile.new("README.md")
170
+ instance.avatar = file
171
+ expect(getter).to eq(file)
172
+ end
173
+ end
174
+ end
175
+
176
+ describe "#asset_setter" do
177
+ let(:instance) { model.new }
178
+
179
+ context "with a singular typed field" do
180
+ let(:model) {
181
+ SingleAssetModel
182
+ }
183
+ subject(:getter) { instance.avatar }
184
+
185
+ it 'attaches uploaded file' do
186
+ file = Rack::Test::UploadedFile.new("README.md")
187
+ instance.avatar = file
188
+
189
+ expect(getter.name).to eq('avatar')
190
+ expect(getter.filename).to eq('README.md')
191
+ expect(getter.content_type).to eq('text/plain')
192
+ expect(getter.service_url)
193
+ .to start_with('https://www.example.com/rails/active_storage/disk/')
194
+ end
195
+
196
+ it 'sets the ID to be the ActiveStorage key' do
197
+ file = Rack::Test::UploadedFile.new("README.md")
198
+ instance.avatar = file
199
+
200
+ expect(instance.avatar_id).to eq(instance.avatar_attachment.key)
201
+ end
202
+
203
+ it 'sets the ID in the attributes' do
204
+ file = Rack::Test::UploadedFile.new("README.md")
205
+ instance.avatar = file
206
+
207
+ expect(instance.attributes['avatar_id']).to eq(instance.avatar_attachment.key)
208
+ end
209
+ end
210
+
211
+ context "with a multiple typed field" do
212
+ let(:model) {
213
+ MultipleAssetModel
214
+ }
215
+
216
+ it 'attaches uploaded files' do
217
+ file = Rack::Test::UploadedFile.new("README.md")
218
+ file2 = Rack::Test::UploadedFile.new("LICENSE.txt")
219
+ instance.multiple_field = [file, file2]
220
+
221
+ expect(instance.multiple_field[0].name).to eq('multiple_field')
222
+ expect(instance.multiple_field[0].filename).to eq('README.md')
223
+ expect(instance.multiple_field[0].content_type).to eq('text/plain')
224
+ expect(instance.multiple_field[0].service_url)
225
+ .to start_with('https://www.example.com/rails/active_storage/disk/')
226
+ expect(instance.multiple_field[1].name).to eq('multiple_field')
227
+ expect(instance.multiple_field[1].filename).to eq('LICENSE.txt')
228
+ expect(instance.multiple_field[1].content_type).to eq('text/plain')
229
+ expect(instance.multiple_field[1].service_url)
230
+ .to start_with('https://www.example.com/rails/active_storage/disk/')
231
+ end
232
+
233
+ it 'sets the ID to be the ActiveStorage key' do
234
+ file = Rack::Test::UploadedFile.new("README.md")
235
+ file2 = Rack::Test::UploadedFile.new("LICENSE.txt")
236
+ instance.multiple_field = [file, file2]
237
+
238
+ expect(instance.another_place).to eq(instance.multiple_field_attachments.map(&:key))
239
+ end
240
+
241
+ it 'sets the ID in the attributes' do
242
+ file = Rack::Test::UploadedFile.new("README.md")
243
+ file2 = Rack::Test::UploadedFile.new("LICENSE.txt")
244
+ instance.multiple_field = [file, file2]
245
+
246
+ expect(instance.attributes['another_place']).to eq(instance.multiple_field_attachments.map(&:key))
247
+ end
248
+ end
332
249
  end
333
250
 
334
251
  describe "#apply_tessa_change_sets" do
335
252
  let(:instance) { model.new }
336
- let(:sets) { Array.new(2) { instance_spy(Tessa::AssetChangeSet) } }
253
+ let(:sets) { [ instance_spy(Tessa::AssetChangeSet) ] }
337
254
 
338
255
  before do
339
- model.asset :field1
340
- model.asset :field2
341
256
  instance.instance_variable_set(
342
257
  :@pending_tessa_change_sets,
343
258
  {
344
- field1: sets[0],
345
- field2: sets[1],
259
+ avatar: sets[0],
346
260
  }
347
261
  )
348
262
  end
@@ -350,7 +264,6 @@ RSpec.describe Tessa::Model do
350
264
  it "iterates over all pending changesets calling apply" do
351
265
  instance.apply_tessa_change_sets
352
266
  expect(sets[0]).to have_received(:apply)
353
- expect(sets[1]).to have_received(:apply)
354
267
  end
355
268
 
356
269
  it "removes all changesets from list" do
@@ -375,10 +288,6 @@ RSpec.describe Tessa::Model do
375
288
  describe "#fetch_tessa_remote_assets" do
376
289
  subject(:result) { model.new.fetch_tessa_remote_assets(arg) }
377
290
 
378
- before do
379
- model.asset :avatar
380
- end
381
-
382
291
  context "argument is `nil`" do
383
292
  let(:arg) { nil }
384
293
 
@@ -449,25 +358,49 @@ RSpec.describe Tessa::Model do
449
358
 
450
359
  describe "#remove_all_tessa_assets" do
451
360
  let(:instance) { model.new }
452
- before do
453
- model.send :attr_accessor, :field1_id, :field2_ids
454
- model.asset :field1
455
- model.asset :field2, multiple: true
456
- instance.field1_id = 1
457
- instance.field2_ids = [2, 3]
361
+
362
+ context "with a single typed field" do
363
+ let(:model) {
364
+ SingleAssetModel
365
+ }
366
+
367
+ before do
368
+ instance.avatar_id = 1
369
+ end
370
+
371
+ it "adds pending change sets for each field removing all current assets" do
372
+ instance.remove_all_tessa_assets
373
+ changes = instance.pending_tessa_change_sets.values
374
+ .reduce(Tessa::AssetChangeSet.new, :+)
375
+ .changes
376
+ .map { |change| [change.id, change.action.to_sym] }
377
+ expect(changes).to eq([
378
+ [1, :remove]
379
+ ])
380
+ end
458
381
  end
459
382
 
460
- it "adds pending change sets for each field removing all current assets" do
461
- instance.remove_all_tessa_assets
462
- changes = instance.pending_tessa_change_sets.values
463
- .reduce(Tessa::AssetChangeSet.new, :+)
464
- .changes
465
- .map { |change| [change.id, change.action.to_sym] }
466
- expect(changes).to eq([
467
- [1, :remove],
468
- [2, :remove],
469
- [3, :remove],
470
- ])
383
+
384
+ context "with a multiple typed field" do
385
+ let(:model) {
386
+ MultipleAssetModel
387
+ }
388
+
389
+ before do
390
+ instance.another_place = [2, 3]
391
+ end
392
+
393
+ it "adds pending change sets for each field removing all current assets" do
394
+ instance.remove_all_tessa_assets
395
+ changes = instance.pending_tessa_change_sets.values
396
+ .reduce(Tessa::AssetChangeSet.new, :+)
397
+ .changes
398
+ .map { |change| [change.id, change.action.to_sym] }
399
+ expect(changes).to eq([
400
+ [2, :remove],
401
+ [3, :remove],
402
+ ])
403
+ end
471
404
  end
472
405
  end
473
406