ruby-trello-czuger 2.0.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/README.md +182 -0
- data/lib/trello.rb +163 -0
- data/lib/trello/action.rb +68 -0
- data/lib/trello/association.rb +14 -0
- data/lib/trello/association_proxy.rb +42 -0
- data/lib/trello/attachment.rb +40 -0
- data/lib/trello/authorization.rb +187 -0
- data/lib/trello/basic_data.rb +132 -0
- data/lib/trello/board.rb +211 -0
- data/lib/trello/card.rb +467 -0
- data/lib/trello/checklist.rb +143 -0
- data/lib/trello/client.rb +120 -0
- data/lib/trello/comment.rb +62 -0
- data/lib/trello/configuration.rb +68 -0
- data/lib/trello/core_ext/array.rb +6 -0
- data/lib/trello/core_ext/hash.rb +6 -0
- data/lib/trello/core_ext/string.rb +6 -0
- data/lib/trello/cover_image.rb +8 -0
- data/lib/trello/has_actions.rb +9 -0
- data/lib/trello/item.rb +37 -0
- data/lib/trello/item_state.rb +30 -0
- data/lib/trello/json_utils.rb +64 -0
- data/lib/trello/label.rb +108 -0
- data/lib/trello/label_name.rb +31 -0
- data/lib/trello/list.rb +114 -0
- data/lib/trello/member.rb +112 -0
- data/lib/trello/multi_association.rb +12 -0
- data/lib/trello/net.rb +39 -0
- data/lib/trello/notification.rb +61 -0
- data/lib/trello/organization.rb +68 -0
- data/lib/trello/plugin_datum.rb +34 -0
- data/lib/trello/token.rb +37 -0
- data/lib/trello/webhook.rb +103 -0
- data/spec/action_spec.rb +149 -0
- data/spec/array_spec.rb +13 -0
- data/spec/association_spec.rb +26 -0
- data/spec/basic_auth_policy_spec.rb +51 -0
- data/spec/board_spec.rb +442 -0
- data/spec/card_spec.rb +822 -0
- data/spec/checklist_spec.rb +296 -0
- data/spec/client_spec.rb +257 -0
- data/spec/configuration_spec.rb +95 -0
- data/spec/hash_spec.rb +15 -0
- data/spec/integration/how_to_authorize_spec.rb +53 -0
- data/spec/integration/how_to_use_boards_spec.rb +48 -0
- data/spec/integration/integration_test.rb +40 -0
- data/spec/item_spec.rb +75 -0
- data/spec/json_utils_spec.rb +73 -0
- data/spec/label_spec.rb +205 -0
- data/spec/list_spec.rb +253 -0
- data/spec/member_spec.rb +159 -0
- data/spec/notification_spec.rb +143 -0
- data/spec/oauth_policy_spec.rb +160 -0
- data/spec/organization_spec.rb +71 -0
- data/spec/spec_helper.rb +435 -0
- data/spec/string_spec.rb +55 -0
- data/spec/token_spec.rb +89 -0
- data/spec/trello_spec.rb +134 -0
- data/spec/webhook_spec.rb +130 -0
- metadata +200 -0
data/spec/card_spec.rb
ADDED
@@ -0,0 +1,822 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
module Trello
|
4
|
+
describe Card do
|
5
|
+
include Helpers
|
6
|
+
|
7
|
+
let(:card) { client.find(:card, 'abcdef123456789123456789') }
|
8
|
+
let(:client) { Client.new }
|
9
|
+
|
10
|
+
before do
|
11
|
+
allow(client)
|
12
|
+
.to receive(:get)
|
13
|
+
.with("/cards/abcdef123456789123456789", {})
|
14
|
+
.and_return JSON.generate(cards_details.first)
|
15
|
+
end
|
16
|
+
|
17
|
+
context "finding" do
|
18
|
+
let(:client) { Trello.client }
|
19
|
+
|
20
|
+
before do
|
21
|
+
allow(client)
|
22
|
+
.to receive(:find)
|
23
|
+
end
|
24
|
+
|
25
|
+
it "delegates to Trello.client#find" do
|
26
|
+
expect(client)
|
27
|
+
.to receive(:find)
|
28
|
+
.with(:card, 'abcdef123456789123456789', {})
|
29
|
+
|
30
|
+
Card.find('abcdef123456789123456789')
|
31
|
+
end
|
32
|
+
|
33
|
+
it "is equivalent to client#find" do
|
34
|
+
expect(Card.find('abcdef123456789123456789')).to eq(card)
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
context "creating" do
|
39
|
+
let(:client) { Trello.client }
|
40
|
+
|
41
|
+
it "creates a new record" do
|
42
|
+
cards_details.each do |card_details|
|
43
|
+
card = Card.new(card_details)
|
44
|
+
expect(card).to be_valid
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
it 'properly initializes all fields from response-like formatted hash' do
|
49
|
+
card_details = cards_details.first
|
50
|
+
card = Card.new(card_details)
|
51
|
+
expect(card.id).to eq(card_details['id'])
|
52
|
+
expect(card.short_id).to eq(card_details['idShort'])
|
53
|
+
expect(card.name).to eq(card_details['name'])
|
54
|
+
expect(card.desc).to eq(card_details['desc'])
|
55
|
+
expect(card.closed).to eq(card_details['closed'])
|
56
|
+
expect(card.list_id).to eq(card_details['idList'])
|
57
|
+
expect(card.board_id).to eq(card_details['idBoard'])
|
58
|
+
expect(card.cover_image_id).to eq(card_details['idAttachmentCover'])
|
59
|
+
expect(card.member_ids).to eq(card_details['idMembers'])
|
60
|
+
expect(card.labels).to eq(card_details['labels'].map { |lbl| Trello::Label.new(lbl) })
|
61
|
+
expect(card.card_labels).to eq(card_details['idLabels'])
|
62
|
+
expect(card.url).to eq(card_details['url'])
|
63
|
+
expect(card.short_url).to eq(card_details['shortUrl'])
|
64
|
+
expect(card.pos).to eq(card_details['pos'])
|
65
|
+
expect(card.last_activity_date).to eq(card_details['dateLastActivity'])
|
66
|
+
end
|
67
|
+
|
68
|
+
it 'properly initializes all fields from options-like formatted hash' do
|
69
|
+
card_details = cards_details[1]
|
70
|
+
card = Card.new(card_details)
|
71
|
+
expect(card.name).to eq(card_details[:name])
|
72
|
+
expect(card.list_id).to eq(card_details[:list_id])
|
73
|
+
expect(card.desc).to eq(card_details[:desc])
|
74
|
+
expect(card.member_ids).to eq(card_details[:member_ids])
|
75
|
+
expect(card.card_labels).to eq(card_details[:card_labels])
|
76
|
+
expect(card.due).to eq(card_details[:due])
|
77
|
+
expect(card.pos).to eq(card_details[:pos])
|
78
|
+
expect(card.source_card_id).to eq(card_details[:source_card_id])
|
79
|
+
expect(card.source_card_properties).to eq(card_details[:source_card_properties])
|
80
|
+
end
|
81
|
+
|
82
|
+
it 'must not be valid if not given a name' do
|
83
|
+
card = Card.new('idList' => lists_details.first['id'])
|
84
|
+
expect(card).to_not be_valid
|
85
|
+
end
|
86
|
+
|
87
|
+
it 'must not be valid if not given a list id' do
|
88
|
+
card = Card.new('name' => lists_details.first['name'])
|
89
|
+
expect(card).to_not be_valid
|
90
|
+
end
|
91
|
+
|
92
|
+
it 'creates a new record and saves it on Trello', refactor: true do
|
93
|
+
payload = {
|
94
|
+
name: 'Test Card',
|
95
|
+
desc: nil,
|
96
|
+
card_labels: "abcdef123456789123456789"
|
97
|
+
}
|
98
|
+
|
99
|
+
result = JSON.generate(cards_details.first.merge(payload.merge(idList: lists_details.first['id'])))
|
100
|
+
|
101
|
+
expected_payload = {name: "Test Card", desc: nil, idList: "abcdef123456789123456789",
|
102
|
+
idMembers: nil, idLabels: "abcdef123456789123456789", pos: nil, due: nil, dueComplete: false, idCardSource: nil, keepFromSource: 'all'}
|
103
|
+
|
104
|
+
expect(client)
|
105
|
+
.to receive(:post)
|
106
|
+
.with("/cards", expected_payload)
|
107
|
+
.and_return result
|
108
|
+
|
109
|
+
card = Card.create(cards_details.first.merge(payload.merge(list_id: lists_details.first['id'])))
|
110
|
+
|
111
|
+
expect(card).to be_a Card
|
112
|
+
end
|
113
|
+
|
114
|
+
it 'creates a duplicate card with all source properties and saves it on Trello', refactor: true do
|
115
|
+
payload = {
|
116
|
+
source_card_id: cards_details.first['id']
|
117
|
+
}
|
118
|
+
|
119
|
+
result = JSON.generate(cards_details.first.merge(payload.merge(idList: lists_details.first['id'])))
|
120
|
+
|
121
|
+
expected_payload = {name: nil, desc: nil, idList: "abcdef123456789123456789",
|
122
|
+
idMembers: nil, idLabels: nil, pos: nil, due: nil, dueComplete: false, idCardSource: cards_details.first['id'], keepFromSource: 'all'}
|
123
|
+
|
124
|
+
expect(client)
|
125
|
+
.to receive(:post)
|
126
|
+
.with("/cards", expected_payload)
|
127
|
+
.and_return result
|
128
|
+
|
129
|
+
card = Card.create(cards_details.first.merge(payload.merge(list_id: lists_details.first['id'])))
|
130
|
+
|
131
|
+
expect(card).to be_a Card
|
132
|
+
end
|
133
|
+
|
134
|
+
it 'creates a duplicate card with source due date and checklists and saves it on Trello', refactor: true do
|
135
|
+
payload = {
|
136
|
+
source_card_id: cards_details.first['id'],
|
137
|
+
source_card_properties: ['due', 'checklists']
|
138
|
+
}
|
139
|
+
|
140
|
+
result = JSON.generate(cards_details.first.merge(payload.merge(idList: lists_details.first['id'])))
|
141
|
+
|
142
|
+
expected_payload = {name: nil, desc: nil, idList: "abcdef123456789123456789",
|
143
|
+
idMembers: nil, idLabels: nil, pos: nil, due: nil, dueComplete: false, idCardSource: cards_details.first['id'], keepFromSource: ['due', 'checklists']}
|
144
|
+
|
145
|
+
expect(client)
|
146
|
+
.to receive(:post)
|
147
|
+
.with("/cards", expected_payload)
|
148
|
+
.and_return result
|
149
|
+
|
150
|
+
card = Card.create(cards_details.first.merge(payload.merge(list_id: lists_details.first['id'])))
|
151
|
+
|
152
|
+
expect(card).to be_a Card
|
153
|
+
end
|
154
|
+
|
155
|
+
it 'creates a duplicate card with no source properties and saves it on Trello', refactor: true do
|
156
|
+
payload = {
|
157
|
+
source_card_id: cards_details.first['id'],
|
158
|
+
source_card_properties: nil
|
159
|
+
}
|
160
|
+
|
161
|
+
result = JSON.generate(cards_details.first.merge(payload.merge(idList: lists_details.first['id'])))
|
162
|
+
|
163
|
+
expected_payload = {name: nil, desc: nil, idList: "abcdef123456789123456789",
|
164
|
+
idMembers: nil, idLabels: nil, pos: nil, due: nil, dueComplete: false, idCardSource: cards_details.first['id'], keepFromSource: nil}
|
165
|
+
|
166
|
+
expect(client)
|
167
|
+
.to receive(:post)
|
168
|
+
.with("/cards", expected_payload)
|
169
|
+
.and_return result
|
170
|
+
|
171
|
+
card = Card.create(cards_details.first.merge(payload.merge(list_id: lists_details.first['id'])))
|
172
|
+
|
173
|
+
expect(card).to be_a Card
|
174
|
+
end
|
175
|
+
|
176
|
+
end
|
177
|
+
|
178
|
+
context "updating" do
|
179
|
+
it "updating name does a put on the correct resource with the correct value" do
|
180
|
+
expected_new_name = "xxx"
|
181
|
+
|
182
|
+
payload = {
|
183
|
+
name: expected_new_name,
|
184
|
+
}
|
185
|
+
|
186
|
+
expect(client)
|
187
|
+
.to receive(:put)
|
188
|
+
.once
|
189
|
+
.with("/cards/abcdef123456789123456789", payload)
|
190
|
+
|
191
|
+
card.name = expected_new_name
|
192
|
+
card.save
|
193
|
+
end
|
194
|
+
|
195
|
+
it "updating desc does a put on the correct resource with the correct value" do
|
196
|
+
expected_new_desc = "xxx"
|
197
|
+
|
198
|
+
payload = {
|
199
|
+
desc: expected_new_desc,
|
200
|
+
}
|
201
|
+
|
202
|
+
expect(client).to receive(:put).once.with("/cards/abcdef123456789123456789", payload)
|
203
|
+
|
204
|
+
card.desc = expected_new_desc
|
205
|
+
card.save
|
206
|
+
end
|
207
|
+
end
|
208
|
+
|
209
|
+
context "deleting" do
|
210
|
+
it "deletes the card" do
|
211
|
+
expect(client)
|
212
|
+
.to receive(:delete)
|
213
|
+
.with("/cards/#{card.id}")
|
214
|
+
|
215
|
+
card.delete
|
216
|
+
end
|
217
|
+
end
|
218
|
+
|
219
|
+
context "fields" do
|
220
|
+
it "gets its id" do
|
221
|
+
expect(card.id).to_not be_nil
|
222
|
+
end
|
223
|
+
|
224
|
+
it "gets its short id" do
|
225
|
+
expect(card.short_id).to_not be_nil
|
226
|
+
end
|
227
|
+
|
228
|
+
it "gets its name" do
|
229
|
+
expect(card.name).to_not be_nil
|
230
|
+
end
|
231
|
+
|
232
|
+
it "gets its description" do
|
233
|
+
expect(card.desc).to_not be_nil
|
234
|
+
end
|
235
|
+
|
236
|
+
it "knows if it is open or closed" do
|
237
|
+
expect(card.closed).to_not be_nil
|
238
|
+
end
|
239
|
+
|
240
|
+
it "gets its url" do
|
241
|
+
expect(card.url).to_not be_nil
|
242
|
+
end
|
243
|
+
|
244
|
+
it "gets its short url" do
|
245
|
+
expect(card.short_url).to_not be_nil
|
246
|
+
end
|
247
|
+
|
248
|
+
it "gets its last active date" do
|
249
|
+
expect(card.last_activity_date).to_not be_nil
|
250
|
+
end
|
251
|
+
|
252
|
+
it "gets its cover image id" do
|
253
|
+
expect(card.cover_image_id).to_not be_nil
|
254
|
+
end
|
255
|
+
|
256
|
+
it "gets its pos" do
|
257
|
+
expect(card.pos).to_not be_nil
|
258
|
+
end
|
259
|
+
|
260
|
+
|
261
|
+
it 'gets its creation time' do
|
262
|
+
expect(card.created_at).to be_kind_of Time
|
263
|
+
end
|
264
|
+
|
265
|
+
it 'get its correct creation time' do
|
266
|
+
expect(card.created_at).to eq(Time.parse('2061-05-04 04:40:18 +0200'))
|
267
|
+
end
|
268
|
+
|
269
|
+
end
|
270
|
+
|
271
|
+
context "actions" do
|
272
|
+
let(:filter) { :all }
|
273
|
+
|
274
|
+
before do
|
275
|
+
allow(client)
|
276
|
+
.to receive(:get)
|
277
|
+
.with("/cards/abcdef123456789123456789/actions", { filter: filter })
|
278
|
+
.and_return actions_payload
|
279
|
+
end
|
280
|
+
|
281
|
+
it "asks for all actions by default" do
|
282
|
+
expect(card.actions.count).to be > 0
|
283
|
+
end
|
284
|
+
|
285
|
+
context 'when overriding a filter' do
|
286
|
+
let(:filter) { :updateCard }
|
287
|
+
|
288
|
+
it "allows the filter" do
|
289
|
+
expect(card.actions(filter: filter).count).to be > 0
|
290
|
+
end
|
291
|
+
end
|
292
|
+
end
|
293
|
+
|
294
|
+
context "boards" do
|
295
|
+
before do
|
296
|
+
allow(client)
|
297
|
+
.to receive(:get)
|
298
|
+
.with("/boards/abcdef123456789123456789", {})
|
299
|
+
.and_return JSON.generate(boards_details.first)
|
300
|
+
end
|
301
|
+
|
302
|
+
it "has a board" do
|
303
|
+
expect(card.board).to_not be_nil
|
304
|
+
end
|
305
|
+
end
|
306
|
+
|
307
|
+
context "cover image" do
|
308
|
+
before do
|
309
|
+
allow(client)
|
310
|
+
.to receive(:get)
|
311
|
+
.with("/attachments/abcdef123456789123456789", {})
|
312
|
+
.and_return JSON.generate(attachments_details.first)
|
313
|
+
end
|
314
|
+
|
315
|
+
it "has a cover image" do
|
316
|
+
expect(card.cover_image).to_not be_nil
|
317
|
+
end
|
318
|
+
end
|
319
|
+
|
320
|
+
context "checklists" do
|
321
|
+
before do
|
322
|
+
allow(client)
|
323
|
+
.to receive(:get)
|
324
|
+
.with("/cards/abcdef123456789123456789/checklists", { filter: :all})
|
325
|
+
.and_return checklists_payload
|
326
|
+
end
|
327
|
+
|
328
|
+
it "has a list of checklists" do
|
329
|
+
expect(card.checklists.count).to be > 0
|
330
|
+
end
|
331
|
+
|
332
|
+
it "creates a new checklist for the card" do
|
333
|
+
expect(client)
|
334
|
+
.to receive(:post)
|
335
|
+
.with("/cards/abcdef123456789123456789/checklists", name: "new checklist")
|
336
|
+
|
337
|
+
card.create_new_checklist("new checklist")
|
338
|
+
end
|
339
|
+
end
|
340
|
+
|
341
|
+
context "list" do
|
342
|
+
before do
|
343
|
+
allow(client)
|
344
|
+
.to receive(:get)
|
345
|
+
.with("/lists/abcdef123456789123456789", {})
|
346
|
+
.and_return JSON.generate(lists_details.first)
|
347
|
+
end
|
348
|
+
it 'has a list' do
|
349
|
+
expect(card.list).to_not be_nil
|
350
|
+
end
|
351
|
+
|
352
|
+
it 'can be moved to another list' do
|
353
|
+
other_list = double(id: '987654321987654321fedcba')
|
354
|
+
payload = {value: other_list.id}
|
355
|
+
|
356
|
+
expect(client)
|
357
|
+
.to receive(:put)
|
358
|
+
.with("/cards/abcdef123456789123456789/idList", payload)
|
359
|
+
|
360
|
+
card.move_to_list(other_list)
|
361
|
+
end
|
362
|
+
|
363
|
+
it 'should not be moved if new list is identical to old list' do
|
364
|
+
other_list = double(id: 'abcdef123456789123456789')
|
365
|
+
expect(client).to_not receive(:put)
|
366
|
+
card.move_to_list(other_list)
|
367
|
+
end
|
368
|
+
|
369
|
+
it "should accept a string for moving a card to list" do
|
370
|
+
payload = { value: "12345678"}
|
371
|
+
|
372
|
+
expect(client)
|
373
|
+
.to receive(:put)
|
374
|
+
.with("/cards/abcdef123456789123456789/idList", payload)
|
375
|
+
|
376
|
+
card.move_to_list("12345678")
|
377
|
+
end
|
378
|
+
|
379
|
+
it 'can be moved to another board' do
|
380
|
+
other_board = double(id: '987654321987654321fedcba')
|
381
|
+
payload = {value: other_board.id}
|
382
|
+
|
383
|
+
expect(client)
|
384
|
+
.to receive(:put)
|
385
|
+
.with("/cards/abcdef123456789123456789/idBoard", payload)
|
386
|
+
|
387
|
+
card.move_to_board(other_board)
|
388
|
+
end
|
389
|
+
|
390
|
+
it 'can be moved to a list on another board' do
|
391
|
+
other_board = double(id: '987654321987654321fedcba')
|
392
|
+
other_list = double(id: '987654321987654321aalist')
|
393
|
+
payload = {value: other_board.id, idList: other_list.id}
|
394
|
+
|
395
|
+
expect(client)
|
396
|
+
.to receive(:put)
|
397
|
+
.with("/cards/abcdef123456789123456789/idBoard", payload)
|
398
|
+
|
399
|
+
card.move_to_board(other_board, other_list)
|
400
|
+
end
|
401
|
+
|
402
|
+
it 'can be moved to a list on the same board' do
|
403
|
+
current_board = double(id: 'abcdef123456789123456789')
|
404
|
+
other_list = double(
|
405
|
+
id: '987654321987654321fedcba',
|
406
|
+
board_id: 'abcdef123456789123456789'
|
407
|
+
)
|
408
|
+
allow(List).to receive(:find).with('987654321987654321fedcba').
|
409
|
+
and_return(other_list)
|
410
|
+
allow(card).to receive(:board).and_return(current_board)
|
411
|
+
payload = {value: other_list.id}
|
412
|
+
|
413
|
+
expect(client)
|
414
|
+
.to receive(:put)
|
415
|
+
.with('/cards/abcdef123456789123456789/idList', payload)
|
416
|
+
|
417
|
+
card.move_to_list_on_any_board(other_list.id)
|
418
|
+
end
|
419
|
+
|
420
|
+
it 'can be moved to a list on another board' do
|
421
|
+
current_board = double(id: 'abcdef123456789123456789')
|
422
|
+
other_board = double(id: '987654321987654321fedcba')
|
423
|
+
other_list = double(
|
424
|
+
id: '987654321987654321aalist',
|
425
|
+
board_id: '987654321987654321fedcba'
|
426
|
+
)
|
427
|
+
allow(List).to receive(:find).with('987654321987654321aalist').
|
428
|
+
and_return(other_list)
|
429
|
+
allow(card).to receive(:board).and_return(current_board)
|
430
|
+
allow(Board).to receive(:find).with('987654321987654321fedcba').
|
431
|
+
and_return(other_board)
|
432
|
+
payload = { value: other_board.id, idList: other_list.id }
|
433
|
+
|
434
|
+
expect(client)
|
435
|
+
.to receive(:put)
|
436
|
+
.with('/cards/abcdef123456789123456789/idBoard', payload)
|
437
|
+
|
438
|
+
card.move_to_list_on_any_board(other_list.id)
|
439
|
+
end
|
440
|
+
|
441
|
+
it 'should not be moved if new board is identical with old board', focus: true do
|
442
|
+
other_board = double(id: 'abcdef123456789123456789')
|
443
|
+
expect(client).to_not receive(:put)
|
444
|
+
card.move_to_board(other_board)
|
445
|
+
end
|
446
|
+
end
|
447
|
+
|
448
|
+
context "members" do
|
449
|
+
before do
|
450
|
+
allow(client)
|
451
|
+
.to receive(:get)
|
452
|
+
.with("/boards/abcdef123456789123456789", {})
|
453
|
+
.and_return JSON.generate(boards_details.first)
|
454
|
+
|
455
|
+
allow(client)
|
456
|
+
.to receive(:get)
|
457
|
+
.with("/members/abcdef123456789123456789")
|
458
|
+
.and_return user_payload
|
459
|
+
end
|
460
|
+
|
461
|
+
it "has a list of members" do
|
462
|
+
expect(card.board).to_not be_nil
|
463
|
+
expect(card.members).to_not be_nil
|
464
|
+
end
|
465
|
+
|
466
|
+
it "allows a member to be added to a card" do
|
467
|
+
new_member = double(id: '4ee7df3ce582acdec80000b2')
|
468
|
+
payload = {
|
469
|
+
value: new_member.id
|
470
|
+
}
|
471
|
+
|
472
|
+
expect(client)
|
473
|
+
.to receive(:post)
|
474
|
+
.with("/cards/abcdef123456789123456789/members", payload)
|
475
|
+
|
476
|
+
card.add_member(new_member)
|
477
|
+
end
|
478
|
+
|
479
|
+
it "allows a member to be removed from a card" do
|
480
|
+
existing_member = double(id: '4ee7df3ce582acdec80000b2')
|
481
|
+
|
482
|
+
expect(client)
|
483
|
+
.to receive(:delete)
|
484
|
+
.with("/cards/abcdef123456789123456789/members/#{existing_member.id}")
|
485
|
+
|
486
|
+
card.remove_member(existing_member)
|
487
|
+
end
|
488
|
+
end
|
489
|
+
|
490
|
+
context "add/remove votes" do
|
491
|
+
let(:authenticated_member) { double(id: '4ee7df3ce582acdec80000b2') }
|
492
|
+
|
493
|
+
before do
|
494
|
+
allow(card)
|
495
|
+
.to receive(:me)
|
496
|
+
.and_return(authenticated_member)
|
497
|
+
end
|
498
|
+
|
499
|
+
it 'upvotes a card with the currently authenticated member' do
|
500
|
+
expect(client)
|
501
|
+
.to receive(:post)
|
502
|
+
.with("/cards/abcdef123456789123456789/membersVoted", {
|
503
|
+
value: authenticated_member.id
|
504
|
+
})
|
505
|
+
|
506
|
+
card.upvote
|
507
|
+
end
|
508
|
+
|
509
|
+
it 'returns the card even if the user has already upvoted' do
|
510
|
+
expect(client)
|
511
|
+
.to receive(:post)
|
512
|
+
.with("/cards/abcdef123456789123456789/membersVoted", {
|
513
|
+
value: authenticated_member.id
|
514
|
+
})
|
515
|
+
.and_raise(Trello::Error, 'member has already voted')
|
516
|
+
expect(card.upvote).to be_kind_of Trello::Card
|
517
|
+
end
|
518
|
+
|
519
|
+
it 'removes an upvote from a card' do
|
520
|
+
expect(client)
|
521
|
+
.to receive(:delete)
|
522
|
+
.with("/cards/abcdef123456789123456789/membersVoted/#{authenticated_member.id}")
|
523
|
+
|
524
|
+
card.remove_upvote
|
525
|
+
end
|
526
|
+
|
527
|
+
it 'returns card after remove_upvote even if the user has not previously upvoted it' do
|
528
|
+
expect(client)
|
529
|
+
.to receive(:delete)
|
530
|
+
.with("/cards/abcdef123456789123456789/membersVoted/#{authenticated_member.id}")
|
531
|
+
.and_raise(Trello::Error, 'member has not voted on the card')
|
532
|
+
|
533
|
+
card.remove_upvote
|
534
|
+
end
|
535
|
+
|
536
|
+
end
|
537
|
+
|
538
|
+
context "return all voters" do
|
539
|
+
it 'returns members that have voted for the card' do
|
540
|
+
no_voters = JSON.generate([])
|
541
|
+
expect(client)
|
542
|
+
.to receive(:get)
|
543
|
+
.with("/cards/#{card.id}/membersVoted")
|
544
|
+
.and_return(no_voters)
|
545
|
+
|
546
|
+
card.voters
|
547
|
+
|
548
|
+
|
549
|
+
voters = JSON.generate([user_details])
|
550
|
+
expect(client)
|
551
|
+
.to receive(:get)
|
552
|
+
.with("/cards/#{card.id}/membersVoted")
|
553
|
+
.and_return(voters)
|
554
|
+
|
555
|
+
expect(card.voters.first).to be_kind_of Trello::Member
|
556
|
+
end
|
557
|
+
end
|
558
|
+
|
559
|
+
|
560
|
+
context "comments" do
|
561
|
+
it "posts a comment" do
|
562
|
+
expect(client)
|
563
|
+
.to receive(:post)
|
564
|
+
.with("/cards/abcdef123456789123456789/actions/comments", { text: 'testing' })
|
565
|
+
.and_return JSON.generate(boards_details.first)
|
566
|
+
|
567
|
+
card.add_comment "testing"
|
568
|
+
end
|
569
|
+
end
|
570
|
+
|
571
|
+
context "labels" do
|
572
|
+
before do
|
573
|
+
allow(client)
|
574
|
+
.to receive(:post)
|
575
|
+
.with("/cards/ebcdef123456789123456789/labels", { value: 'green' })
|
576
|
+
.and_return "not important"
|
577
|
+
|
578
|
+
allow(client)
|
579
|
+
.to receive(:delete)
|
580
|
+
.with("/cards/ebcdef123456789123456789/labels/green")
|
581
|
+
.and_return "not important"
|
582
|
+
end
|
583
|
+
|
584
|
+
it "includes card label objects list" do
|
585
|
+
labels = card.labels
|
586
|
+
expect(labels.size).to eq(4)
|
587
|
+
expect(labels[0].color).to eq('yellow')
|
588
|
+
expect(labels[0].id).to eq('abcdef123456789123456789')
|
589
|
+
expect(labels[0].board_id).to eq('abcdef123456789123456789')
|
590
|
+
expect(labels[0].name).to eq('iOS')
|
591
|
+
expect(labels[0].uses).to eq(3)
|
592
|
+
expect(labels[1].color).to eq('purple')
|
593
|
+
expect(labels[1].id).to eq('bbcdef123456789123456789')
|
594
|
+
expect(labels[1].board_id).to eq('abcdef123456789123456789')
|
595
|
+
expect(labels[1].name).to eq('Issue or bug')
|
596
|
+
expect(labels[1].uses).to eq(1)
|
597
|
+
end
|
598
|
+
|
599
|
+
it "includes label ids list" do
|
600
|
+
label_ids = card.card_labels
|
601
|
+
expect(label_ids.size).to eq(4)
|
602
|
+
expect(label_ids[0]).to eq('abcdef123456789123456789')
|
603
|
+
expect(label_ids[1]).to eq('bbcdef123456789123456789')
|
604
|
+
expect(label_ids[2]).to eq('cbcdef123456789123456789')
|
605
|
+
expect(label_ids[3]).to eq('dbcdef123456789123456789')
|
606
|
+
end
|
607
|
+
|
608
|
+
it "can remove a label" do
|
609
|
+
expect(client).to receive(:delete).once.with("/cards/abcdef123456789123456789/idLabels/abcdef123456789123456789")
|
610
|
+
label = Label.new(label_details.first)
|
611
|
+
card.remove_label(label)
|
612
|
+
end
|
613
|
+
|
614
|
+
it "can add a label" do
|
615
|
+
expect(client).to receive(:post).once.with("/cards/abcdef123456789123456789/idLabels", {:value => "abcdef123456789123456789"})
|
616
|
+
label = Label.new(label_details.first)
|
617
|
+
card.add_label label
|
618
|
+
end
|
619
|
+
|
620
|
+
it "throws an error when trying to add a invalid label" do
|
621
|
+
allow(client).to receive(:post).with("/cards/abcdef123456789123456789/idLabels", { value: 'abcdef123456789123456789' }).
|
622
|
+
and_return "not important"
|
623
|
+
label = Label.new(label_details.first)
|
624
|
+
label.name = nil
|
625
|
+
card.add_label(label)
|
626
|
+
expect(card.errors.full_messages.to_sentence).to eq("Label is not valid.")
|
627
|
+
end
|
628
|
+
|
629
|
+
it "throws an error when trying to remove a invalid label" do
|
630
|
+
allow(client).to receive(:delete).with("/cards/abcdef123456789123456789/idLabels/abcdef123456789123456789").
|
631
|
+
and_return "not important"
|
632
|
+
label = Label.new(label_details.first)
|
633
|
+
label.name = nil
|
634
|
+
card.remove_label(label)
|
635
|
+
expect(card.errors.full_messages.to_sentence).to eq("Label is not valid.")
|
636
|
+
end
|
637
|
+
end
|
638
|
+
|
639
|
+
context "plugins" do
|
640
|
+
it "can list the existing plugins with correct fields" do
|
641
|
+
allow(client)
|
642
|
+
.to receive(:get)
|
643
|
+
.with("/boards/abcdef123456789123456789", {})
|
644
|
+
.and_return JSON.generate(boards_details.first)
|
645
|
+
|
646
|
+
allow(client)
|
647
|
+
.to receive(:get)
|
648
|
+
.with("/cards/abcdef123456789123456789/pluginData", {})
|
649
|
+
.and_return plugin_data_payload
|
650
|
+
|
651
|
+
expect(card.board).to_not be_nil
|
652
|
+
expect(card.plugin_data).to_not be_nil
|
653
|
+
|
654
|
+
first_plugin = card.plugin_data.first
|
655
|
+
expect(first_plugin.id).to eq plugin_data_details[0]["id"]
|
656
|
+
expect(first_plugin.idPlugin).to eq plugin_data_details[0]["idPlugin"]
|
657
|
+
expect(first_plugin.scope).to eq plugin_data_details[0]["scope"]
|
658
|
+
expect(first_plugin.idModel).to eq plugin_data_details[0]["idModel"]
|
659
|
+
expect(first_plugin.value).to eq JSON.parse plugin_data_details[0]["value"]
|
660
|
+
expect(first_plugin.access).to eq plugin_data_details[0]["access"]
|
661
|
+
|
662
|
+
second_plugin = card.plugin_data[1]
|
663
|
+
expect(second_plugin.id).to eq plugin_data_details[1]["id"]
|
664
|
+
expect(second_plugin.idPlugin).to eq plugin_data_details[1]["idPlugin"]
|
665
|
+
expect(second_plugin.scope).to eq plugin_data_details[1]["scope"]
|
666
|
+
expect(second_plugin.idModel).to eq plugin_data_details[1]["idModel"]
|
667
|
+
expect(second_plugin.value).to eq JSON.parse plugin_data_details[1]["value"]
|
668
|
+
expect(second_plugin.access).to eq plugin_data_details[1]["access"]
|
669
|
+
|
670
|
+
end
|
671
|
+
end
|
672
|
+
|
673
|
+
context "attachments" do
|
674
|
+
it "can add an attachment" do
|
675
|
+
f = File.new('spec/list_spec.rb', 'r')
|
676
|
+
allow(client)
|
677
|
+
.to receive(:get)
|
678
|
+
.with("/cards/abcdef123456789123456789/attachments")
|
679
|
+
.and_return attachments_payload
|
680
|
+
|
681
|
+
allow(client)
|
682
|
+
.to receive(:post)
|
683
|
+
.with("/cards/abcdef123456789123456789/attachments", { file: f, name: '' })
|
684
|
+
.and_return "not important"
|
685
|
+
|
686
|
+
card.add_attachment(f)
|
687
|
+
|
688
|
+
expect(card.errors).to be_empty
|
689
|
+
end
|
690
|
+
|
691
|
+
it "can list the existing attachments with correct fields" do
|
692
|
+
allow(client)
|
693
|
+
.to receive(:get)
|
694
|
+
.with("/boards/abcdef123456789123456789", {})
|
695
|
+
.and_return JSON.generate(boards_details.first)
|
696
|
+
|
697
|
+
allow(client)
|
698
|
+
.to receive(:get)
|
699
|
+
.with("/cards/abcdef123456789123456789/attachments")
|
700
|
+
.and_return attachments_payload
|
701
|
+
|
702
|
+
expect(card.board).to_not be_nil
|
703
|
+
expect(card.attachments).to_not be_nil
|
704
|
+
|
705
|
+
first_attachment = card.attachments.first
|
706
|
+
expect(first_attachment.id).to eq attachments_details[0]["id"]
|
707
|
+
expect(first_attachment.name).to eq attachments_details[0]["name"]
|
708
|
+
expect(first_attachment.url).to eq attachments_details[0]["url"]
|
709
|
+
expect(first_attachment.bytes).to eq attachments_details[0]["bytes"]
|
710
|
+
expect(first_attachment.member_id).to eq attachments_details[0]["idMember"]
|
711
|
+
expect(first_attachment.date).to eq Time.parse(attachments_details[0]["date"])
|
712
|
+
expect(first_attachment.is_upload).to eq attachments_details[0]["isUpload"]
|
713
|
+
expect(first_attachment.mime_type).to eq attachments_details[0]["mimeType"]
|
714
|
+
expect(first_attachment.previews).to eq attachments_details[0]["previews"]
|
715
|
+
|
716
|
+
second_attachment = card.attachments[1]
|
717
|
+
expect(second_attachment.previews).to eq nil
|
718
|
+
end
|
719
|
+
|
720
|
+
it "can remove an attachment" do
|
721
|
+
allow(client)
|
722
|
+
.to receive(:delete)
|
723
|
+
.with("/cards/abcdef123456789123456789/attachments/abcdef123456789123456789")
|
724
|
+
.and_return "not important"
|
725
|
+
|
726
|
+
allow(client)
|
727
|
+
.to receive(:get)
|
728
|
+
.with("/cards/abcdef123456789123456789/attachments")
|
729
|
+
.and_return attachments_payload
|
730
|
+
|
731
|
+
card.remove_attachment(card.attachments.first)
|
732
|
+
expect(card.errors).to be_empty
|
733
|
+
end
|
734
|
+
end
|
735
|
+
|
736
|
+
describe "#closed?" do
|
737
|
+
it "returns the closed attribute" do
|
738
|
+
expect(card).to_not be_closed
|
739
|
+
end
|
740
|
+
end
|
741
|
+
|
742
|
+
describe "#close" do
|
743
|
+
it "updates the close attribute to true" do
|
744
|
+
card.close
|
745
|
+
expect(card).to be_closed
|
746
|
+
end
|
747
|
+
end
|
748
|
+
|
749
|
+
describe "#close!" do
|
750
|
+
it "updates the close attribute to true and saves the list" do
|
751
|
+
payload = { closed: true }
|
752
|
+
|
753
|
+
expect(client)
|
754
|
+
.to receive(:put)
|
755
|
+
.once
|
756
|
+
.with("/cards/abcdef123456789123456789", payload)
|
757
|
+
|
758
|
+
card.close!
|
759
|
+
end
|
760
|
+
end
|
761
|
+
|
762
|
+
describe "can mark due_complete" do
|
763
|
+
it "updates the due completed attribute to true" do
|
764
|
+
due_date = Time.now
|
765
|
+
|
766
|
+
payload = {
|
767
|
+
due: due_date,
|
768
|
+
}
|
769
|
+
|
770
|
+
expect(client)
|
771
|
+
.to receive(:put)
|
772
|
+
.once
|
773
|
+
.with("/cards/abcdef123456789123456789", payload)
|
774
|
+
|
775
|
+
card.due = due_date
|
776
|
+
card.save
|
777
|
+
|
778
|
+
expect(card.due).to_not be_nil
|
779
|
+
|
780
|
+
payload = {
|
781
|
+
dueComplete: true
|
782
|
+
}
|
783
|
+
|
784
|
+
expect(client)
|
785
|
+
.to receive(:put)
|
786
|
+
.once
|
787
|
+
.with("/cards/abcdef123456789123456789", payload)
|
788
|
+
|
789
|
+
card.due_complete = true
|
790
|
+
card.save
|
791
|
+
|
792
|
+
expect(card.due_complete).to be true
|
793
|
+
end
|
794
|
+
end
|
795
|
+
|
796
|
+
describe "#update_fields" do
|
797
|
+
it "does not set any fields when the fields argument is empty" do
|
798
|
+
expected = cards_details.first
|
799
|
+
|
800
|
+
card = Card.new(expected)
|
801
|
+
card.client = client
|
802
|
+
|
803
|
+
card.update_fields({})
|
804
|
+
|
805
|
+
expected.each do |key, value|
|
806
|
+
if card.respond_to?(key) && key != 'labels'
|
807
|
+
expect(card.send(key)).to eq value
|
808
|
+
end
|
809
|
+
|
810
|
+
expect(card.labels).to eq expected['labels'].map { |lbl| Trello::Label.new(lbl) }
|
811
|
+
expect(card.short_id).to eq expected['idShort']
|
812
|
+
expect(card.short_url).to eq expected['shortUrl']
|
813
|
+
expect(card.board_id).to eq expected['idBoard']
|
814
|
+
expect(card.member_ids).to eq expected['idMembers']
|
815
|
+
expect(card.cover_image_id).to eq expected['idAttachmentCover']
|
816
|
+
expect(card.list_id).to eq expected['idList']
|
817
|
+
expect(card.card_labels).to eq expected['idLabels']
|
818
|
+
end
|
819
|
+
end
|
820
|
+
end
|
821
|
+
end
|
822
|
+
end
|