westarete-tracker-tools 0.3.0

Sign up to get free protection for your applications and to get access to all the features.
data/spec/helper.rb ADDED
@@ -0,0 +1,17 @@
1
+ require 'rubygems'
2
+ require 'bundler'
3
+ begin
4
+ Bundler.setup(:default, :development)
5
+ rescue Bundler::BundlerError => e
6
+ $stderr.puts e.message
7
+ $stderr.puts "Run `bundle install` to install missing gems"
8
+ exit e.status_code
9
+ end
10
+
11
+ $LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
12
+ $LOAD_PATH.unshift(File.dirname(__FILE__))
13
+ require 'westarete-tracker-tools'
14
+
15
+ include WestAreteTrackerTools
16
+
17
+ require 'vcr_helper'
@@ -0,0 +1,132 @@
1
+ require 'helper'
2
+
3
+ describe Project do
4
+ use_vcr_cassette :record => :new_episodes, :match_requests_on => [:uri, :method]
5
+ before do
6
+ @project = Project.find_by_name('westarete-tracker-tools-test')
7
+ end
8
+ describe '.all' do
9
+ before do
10
+ @projects = Project.all
11
+ end
12
+ it 'returns all active projects' do
13
+ @projects.length.should == 15
14
+ @projects.first.should respond_to(:name)
15
+ @projects.collect(&:name).should == [
16
+ 'altoonabustest.com',
17
+ 'Background Questionnaires',
18
+ 'BellefonteDentistry.com',
19
+ 'Captcha',
20
+ 'centralinsgrp.com',
21
+ 'coolblue.com',
22
+ 'famfound.net',
23
+ 'ionmodern.com',
24
+ 'LifeViz',
25
+ 'oneononefit.com',
26
+ 'PAIG',
27
+ 'pasubway.com',
28
+ 'ReDi Index',
29
+ 'trailspace.com',
30
+ 'westarete-tracker-tools-test'
31
+ ]
32
+ end
33
+ end
34
+ describe '.find_by_name' do
35
+ context 'if the name exists' do
36
+ it 'returns the project' do
37
+ @project.name.should == 'westarete-tracker-tools-test'
38
+ end
39
+ end
40
+ context 'if the name does not exist' do
41
+ before do
42
+ @project = Project.find_by_name('not there')
43
+ end
44
+ it 'returns nil' do
45
+ @project.should be_nil
46
+ end
47
+ end
48
+ end
49
+ describe "#backlog" do
50
+ before do
51
+ @stories = @project.backlog
52
+ end
53
+ it 'returns all of the stories in the backlog, including the current iteration' do
54
+ @stories.length.should == 17
55
+ @stories.each { |s| s.should be_kind_of(Story) }
56
+ @stories.first.name.should == 'Newly accepted feature'
57
+ @stories.last.name.should == 'Release'
58
+ end
59
+ end
60
+ describe '#current' do
61
+ before do
62
+ @stories = @project.current
63
+ end
64
+ it 'returns all of the stories in the current iteration' do
65
+ @stories.collect(&:name).should == [
66
+ 'Newly accepted feature',
67
+ 'Rejected feature',
68
+ 'Delivered feature',
69
+ 'Finished feature',
70
+ 'Started feature',
71
+ ]
72
+ end
73
+ end
74
+ describe '#done' do
75
+ context 'when given no arguments' do
76
+ before do
77
+ @stories = @project.done
78
+ end
79
+ it 'returns all stories that have been completed in any iteration' do
80
+ @stories.collect(&:name).should == [
81
+ 'Old accepted feature',
82
+ 'Accepted feature',
83
+ 'Newly accepted feature',
84
+ ]
85
+ end
86
+ end
87
+ context 'when given a -2' do
88
+ before do
89
+ @stories = @project.done(-2)
90
+ end
91
+ it 'returns all stories that were completed two iterations ago' do
92
+ @stories.collect(&:name).should == ['Old accepted feature']
93
+ end
94
+ end
95
+ context 'when given a -1' do
96
+ before do
97
+ @stories = @project.done(-1)
98
+ end
99
+ it 'returns all stories that were completed in last iteration' do
100
+ @stories.collect(&:name).should == ['Accepted feature']
101
+ end
102
+ end
103
+ context 'when given a 0' do
104
+ before do
105
+ @stories = @project.done(0)
106
+ end
107
+ it 'returns all stories that have been completed during the current iteration' do
108
+ @stories.collect(&:name).should == ['Newly accepted feature']
109
+ end
110
+ end
111
+ context 'when given a 1' do
112
+ it 'raises an ArgumentError' do
113
+ lambda { @project.done(1) }.should raise_error(ArgumentError)
114
+ end
115
+ end
116
+ end
117
+ describe '#name' do
118
+ it "returns the project's name" do
119
+ @project.name.should == 'westarete-tracker-tools-test'
120
+ end
121
+ end
122
+ describe '#tracker_id' do
123
+ it "returns Pivotal Tracker's id for the project" do
124
+ @project.tracker_id.should == 213679
125
+ end
126
+ end
127
+ describe '#url' do
128
+ it "returns Pivotal Tracker's url for the project" do
129
+ @project.url.should == 'http://www.pivotaltracker.com/projects/213679'
130
+ end
131
+ end
132
+ end
@@ -0,0 +1,408 @@
1
+ require 'helper'
2
+
3
+ describe Story do
4
+ use_vcr_cassette :record => :new_episodes, :match_requests_on => [:uri, :method]
5
+ before do
6
+ @project = Project.find_by_name('westarete-tracker-tools-test')
7
+ @backlog = @project.backlog
8
+ @story = @backlog.detect { |s| s.name == 'Valid story' }
9
+ end
10
+ describe 'validations' do
11
+ context 'a bug' do
12
+ subject { @backlog.detect { |s| s.name == 'Bug' } }
13
+ it { should be_valid }
14
+ end
15
+ context 'a chore' do
16
+ subject { @backlog.detect { |s| s.name == 'Chore' } }
17
+ it { should be_valid }
18
+ end
19
+ context 'a feature' do
20
+ context 'that is valid' do
21
+ subject { @story }
22
+ it { should be_valid }
23
+ end
24
+ context 'that has no risk' do
25
+ before do
26
+ @story = @backlog.detect { |s| s.name == 'Story with no risk' }
27
+ end
28
+ it 'complains about the missing risk' do
29
+ @story.should_not be_valid
30
+ @story.errors[:risk].should include("can't be blank")
31
+ end
32
+ end
33
+ context 'that has no price' do
34
+ before do
35
+ @story = @backlog.detect { |s| s.name == 'Story with comments, but no price' }
36
+ end
37
+ it 'complains about the missing price' do
38
+ @story.should_not be_valid
39
+ @story.errors[:price].should include("can't be blank")
40
+ end
41
+ end
42
+ context 'that has no point estimate' do
43
+ before do
44
+ @story = @backlog.detect { |s| s.name == 'Story with no point estimate' }
45
+ end
46
+ it 'complains about the missing point estimate' do
47
+ @story.should_not be_valid
48
+ @story.errors[:points].should include("can't be blank")
49
+ end
50
+ end
51
+ context 'that has no description' do
52
+ before do
53
+ @story = @backlog.detect { |s| s.name == 'Story with no description' }
54
+ end
55
+ it 'complains about the missing description' do
56
+ @story.should_not be_valid
57
+ @story.errors[:description].should include("can't be blank")
58
+ end
59
+ end
60
+ context "that has a description that's too short" do
61
+ before do
62
+ @story = @backlog.detect { |s| s.name == 'Story with a description that is too short' }
63
+ end
64
+ it 'complains about the BS description' do
65
+ @story.should_not be_valid
66
+ @story.errors[:description].should include("is too short (minimum is 70 characters)")
67
+ end
68
+ end
69
+ end
70
+ end
71
+ context 'that is a feature' do
72
+ describe '#feature?' do
73
+ subject { @story.feature? }
74
+ it { should be_true }
75
+ end
76
+ describe '#bug?' do
77
+ subject { @story.bug? }
78
+ it { should be_false }
79
+ end
80
+ describe '#chore?' do
81
+ subject { @story.chore? }
82
+ it { should be_false }
83
+ end
84
+ describe '#release?' do
85
+ subject { @story.release? }
86
+ it { should be_false }
87
+ end
88
+ end
89
+ context 'that is a bug' do
90
+ before do
91
+ @story = @backlog.detect { |s| s.name == 'Bug' }
92
+ end
93
+ describe '#feature?' do
94
+ subject { @story.feature? }
95
+ it { should be_false }
96
+ end
97
+ describe '#bug?' do
98
+ subject { @story.bug? }
99
+ it { should be_true }
100
+ end
101
+ describe '#chore?' do
102
+ subject { @story.chore? }
103
+ it { should be_false }
104
+ end
105
+ describe '#release?' do
106
+ subject { @story.release? }
107
+ it { should be_false }
108
+ end
109
+ describe '#points' do
110
+ subject { @story.points }
111
+ it { should be_nil }
112
+ end
113
+ describe '#price' do
114
+ subject { @story.price }
115
+ it { should be_nil }
116
+ end
117
+ describe '#risk' do
118
+ subject { @story.risk }
119
+ it { should be_nil }
120
+ end
121
+ end
122
+ context 'that is a chore' do
123
+ before do
124
+ @story = @backlog.detect { |s| s.name == 'Chore' }
125
+ end
126
+ describe '#feature?' do
127
+ subject { @story.feature? }
128
+ it { should be_false }
129
+ end
130
+ describe '#bug?' do
131
+ subject { @story.bug? }
132
+ it { should be_false }
133
+ end
134
+ describe '#chore?' do
135
+ subject { @story.chore? }
136
+ it { should be_true }
137
+ end
138
+ describe '#release?' do
139
+ subject { @story.release? }
140
+ it { should be_false }
141
+ end
142
+ describe '#points' do
143
+ subject { @story.points }
144
+ it { should be_nil }
145
+ end
146
+ describe '#price' do
147
+ subject { @story.price }
148
+ it { should be_nil }
149
+ end
150
+ describe '#risk' do
151
+ subject { @story.risk }
152
+ it { should be_nil }
153
+ end
154
+ end
155
+ context 'that is a release' do
156
+ before do
157
+ @story = @backlog.detect { |s| s.name == 'Release' }
158
+ end
159
+ describe '#feature?' do
160
+ subject { @story.feature? }
161
+ it { should be_false }
162
+ end
163
+ describe '#bug?' do
164
+ subject { @story.bug? }
165
+ it { should be_false }
166
+ end
167
+ describe '#chore?' do
168
+ subject { @story.chore? }
169
+ it { should be_false }
170
+ end
171
+ describe '#release?' do
172
+ subject { @story.release? }
173
+ it { should be_true }
174
+ end
175
+ describe '#points' do
176
+ subject { @story.points }
177
+ it { should be_nil }
178
+ end
179
+ describe '#price' do
180
+ subject { @story.price }
181
+ it { should be_nil }
182
+ end
183
+ describe '#risk' do
184
+ subject { @story.risk }
185
+ it { should be_nil }
186
+ end
187
+ end
188
+ describe '#accepted_at' do
189
+ context 'for a story that is not started' do
190
+ subject { @story.accepted_at }
191
+ it { should be_nil }
192
+ end
193
+ context 'for a story that is in progress' do
194
+ subject do
195
+ @project.backlog.detect { |story| story.name == 'Started feature' }.accepted_at
196
+ end
197
+ it { should be_nil }
198
+ end
199
+ context 'for a story that is complete' do
200
+ before do
201
+ @story = @project.done.detect { |story| story.name == 'Accepted feature' }
202
+ end
203
+ it 'returns a date and time' do
204
+ @story.accepted_at.should be_kind_of(DateTime)
205
+ end
206
+ end
207
+ end
208
+ describe '#comments' do
209
+ context 'when there are comments' do
210
+ before do
211
+ @story = @project.backlog.detect { |s| s.name == 'Story with comments and two prices' }
212
+ end
213
+ it 'returns an array of note objects in chronological order' do
214
+ @story.comments.length.should == 2
215
+ @story.comments[0].noted_at.should < @story.comments[1].noted_at
216
+ end
217
+ end
218
+ context 'when there are no comments' do
219
+ before do
220
+ @story = @project.backlog.detect { |s| s.name == 'Story with no comments' }
221
+ end
222
+ it 'returns an empty array' do
223
+ @story.comments.should be_empty
224
+ end
225
+ end
226
+ end
227
+ describe '#complete?' do
228
+ context 'for a story that is not started' do
229
+ subject { @story.complete? }
230
+ it { should be_false }
231
+ end
232
+ context 'for a story that is in progress' do
233
+ subject do
234
+ @project.backlog.detect { |story| story.name == 'Started feature' }.complete?
235
+ end
236
+ it { should be_false }
237
+ end
238
+ context 'for a story that is complete' do
239
+ subject do
240
+ @project.done.detect { |story| story.name == 'Accepted feature' }.complete?
241
+ end
242
+ it { should be_true }
243
+ end
244
+ end
245
+ describe '#current_state' do
246
+ it 'returns a string that describes the current state' do
247
+ @story.current_state.should == 'unstarted'
248
+ end
249
+ end
250
+ describe '#description' do
251
+ it 'returns the description body' do
252
+ @story.description.should =~ /^As a /
253
+ end
254
+ end
255
+ describe '#finished?' do
256
+ context 'for a story that is not yet started' do
257
+ subject do
258
+ @project.backlog.detect { |story| story.name == 'Not yet started feature' }.finished?
259
+ end
260
+ it { should be_false }
261
+ end
262
+ context 'for a story that is started' do
263
+ subject do
264
+ @project.backlog.detect { |story| story.name == 'Started feature' }.finished?
265
+ end
266
+ it { should be_false }
267
+ end
268
+ context 'for a story that is finished' do
269
+ subject do
270
+ @project.backlog.detect { |story| story.name == 'Finished feature' }.finished?
271
+ end
272
+ it { should be_true }
273
+ end
274
+ context 'for a story that is delivered' do
275
+ subject do
276
+ @project.backlog.detect { |story| story.name == 'Delivered feature' }.finished?
277
+ end
278
+ it { should be_true }
279
+ end
280
+ context 'for a story that is rejected' do
281
+ subject do
282
+ @project.backlog.detect { |story| story.name == 'Rejected feature' }.finished?
283
+ end
284
+ it { should be_true }
285
+ end
286
+ context 'for a story that is accepted' do
287
+ subject do
288
+ @project.done.detect { |story| story.name == 'Accepted feature' }.finished?
289
+ end
290
+ it { should be_false }
291
+ end
292
+ end
293
+ describe '#incomplete?' do
294
+ context 'for a story that is not started' do
295
+ subject { @story.incomplete? }
296
+ it { should be_true }
297
+ end
298
+ context 'for a story that is in progress' do
299
+ subject do
300
+ @project.backlog.detect { |story| story.name == 'Started feature' }.incomplete?
301
+ end
302
+ it { should be_true }
303
+ end
304
+ context 'for a story that is complete' do
305
+ subject do
306
+ @project.done.detect { |story| story.name == 'Accepted feature' }.incomplete?
307
+ end
308
+ it { should be_false }
309
+ end
310
+ end
311
+ describe "#name" do
312
+ it "returns the story's name, without the risk rating" do
313
+ @story.name.should == 'Valid story'
314
+ end
315
+ end
316
+ describe '#owner' do
317
+ context 'when there is an owner' do
318
+ before do
319
+ @story = @project.backlog.detect { |story| story.name == 'Started feature' }
320
+ end
321
+ it "should return the owner's full name" do
322
+ @story.owner.should == 'Scott Woods'
323
+ end
324
+ end
325
+ context 'when there is no owner' do
326
+ subject do
327
+ @project.backlog.detect { |story| story.name == 'Not yet started feature' }.owner
328
+ end
329
+ it { should be_nil }
330
+ end
331
+ end
332
+ describe '#points' do
333
+ context 'if the story has been estimated' do
334
+ it 'returns the point estimate for the story' do
335
+ @story.points.should == 2
336
+ end
337
+ end
338
+ context 'if the story has not been estimated' do
339
+ before do
340
+ @story = @backlog.detect { |s| s.name == 'Story with no point estimate' }
341
+ end
342
+ it 'returns nil' do
343
+ @story.points.should be_nil
344
+ end
345
+ end
346
+ end
347
+ describe '#price' do
348
+ context 'when there are no comments' do
349
+ before do
350
+ @story = @backlog.detect { |s| s.name == 'Story with no comments' }
351
+ end
352
+ it 'returns nil' do
353
+ @story.price.should be_nil
354
+ end
355
+ end
356
+ context 'when there are comments, but none mention cost or price' do
357
+ before do
358
+ @story = @backlog.detect { |s| s.name == 'Story with comments, but no price' }
359
+ end
360
+ it 'returns nil' do
361
+ @story.price.should be_nil
362
+ end
363
+ end
364
+ context 'when there are multiple comments that mention price' do
365
+ before do
366
+ @story = @backlog.detect { |s| s.name == 'Story with comments and two prices' }
367
+ end
368
+ it 'uses the most recent comment by Scott Woods' do
369
+ @story.price.should == 100
370
+ end
371
+ end
372
+ end
373
+ describe "#requester" do
374
+ it "returns the requester's name" do
375
+ @story.requester.should == 'Scott Woods'
376
+ end
377
+ end
378
+ describe '#risk' do
379
+ context 'if a risk is present' do
380
+ it 'returns the risk rating for the story' do
381
+ @story.risk.should == 'R'
382
+ end
383
+ end
384
+ context 'if no risk is present' do
385
+ before do
386
+ @story = @backlog.detect { |s| s.name == 'Story with no risk' }
387
+ end
388
+ it 'returns nil' do
389
+ @story.risk.should be_nil
390
+ end
391
+ end
392
+ end
393
+ describe '#story_type' do
394
+ it 'returns a string of the story type' do
395
+ @story.story_type.should == 'feature'
396
+ end
397
+ end
398
+ describe '#tracker_id' do
399
+ it "returns Pivotal Tracker's id for the story" do
400
+ @story.tracker_id.should == 9072189
401
+ end
402
+ end
403
+ describe '#url' do
404
+ it 'returns the URL for the story' do
405
+ @story.url.should == 'http://www.pivotaltracker.com/story/show/9072189'
406
+ end
407
+ end
408
+ end