freshtrack 0.4.2 → 0.5.0
Sign up to get free protection for your applications and to get access to all the features.
- data/History.txt +5 -0
- data/Rakefile +51 -4
- data/VERSION +1 -0
- data/bin/freshtrack +4 -4
- data/config/hoe.rb +2 -2
- data/lib/freshtrack/version.rb +2 -2
- data/lib/freshtrack.rb +13 -4
- data/spec/.bacon +0 -0
- data/spec/core_ext/array_spec.rb +5 -5
- data/spec/core_ext/numeric_spec.rb +2 -2
- data/spec/core_ext/time_spec.rb +3 -3
- data/spec/freshbooks/base_object_spec.rb +7 -7
- data/spec/freshbooks/invoice_spec.rb +40 -40
- data/spec/freshbooks/payment_spec.rb +4 -4
- data/spec/freshbooks/project_spec.rb +119 -119
- data/spec/freshbooks/task_spec.rb +102 -102
- data/spec/freshbooks/time_entry_spec.rb +97 -97
- data/spec/freshtrack_command_spec.rb +77 -45
- data/spec/freshtrack_spec.rb +237 -130
- data/spec/spec_helper.rb +3 -21
- data/spec/time_collectors/one_inch_punch_spec.rb +55 -53
- data/spec/time_collectors/punch_spec.rb +74 -66
- metadata +83 -54
- data/spec/spec.opts +0 -3
data/spec/freshtrack_spec.rb
CHANGED
@@ -2,181 +2,275 @@ require File.expand_path(File.dirname(__FILE__) + '/spec_helper.rb')
|
|
2
2
|
|
3
3
|
describe Freshtrack do
|
4
4
|
describe 'loading configuration' do
|
5
|
-
before
|
6
|
-
File.
|
5
|
+
before do
|
6
|
+
File.stub!(:read).and_return('')
|
7
7
|
end
|
8
8
|
|
9
9
|
it 'should load the contents of the .freshtrack.yml file' do
|
10
|
-
File.
|
11
|
-
File.
|
10
|
+
File.stub!(:expand_path).with('~/.freshtrack.yml').and_return('~/.freshtrack.yml')
|
11
|
+
File.should.receive(:read).with('~/.freshtrack.yml').and_return('')
|
12
12
|
Freshtrack.load_config
|
13
13
|
end
|
14
14
|
|
15
15
|
it 'should turn the file contents into data' do
|
16
|
-
file_contents =
|
17
|
-
File.
|
18
|
-
YAML.
|
16
|
+
file_contents = mock('lorem ipsum')
|
17
|
+
File.stub!(:read).and_return(file_contents)
|
18
|
+
YAML.should.receive(:load).with(file_contents)
|
19
19
|
Freshtrack.load_config
|
20
20
|
end
|
21
21
|
|
22
22
|
it 'should store the configuration data' do
|
23
|
-
config =
|
24
|
-
YAML.
|
23
|
+
config = mock('config data')
|
24
|
+
YAML.stub!(:load).and_return(config)
|
25
25
|
Freshtrack.load_config
|
26
26
|
Freshtrack.config.should == config
|
27
27
|
end
|
28
28
|
end
|
29
29
|
|
30
30
|
describe 'getting configuration data' do
|
31
|
-
before
|
32
|
-
@config = {
|
33
|
-
|
31
|
+
before do
|
32
|
+
@config = {
|
33
|
+
'collector' => 'one_inch_punch',
|
34
|
+
'company' => 'comptastic',
|
35
|
+
'token' => '13984ujaslkdfj0932',
|
36
|
+
'project_task_mapping' => {
|
37
|
+
'projone' => { :project => 'ProjectOne', :task => 'ProjectOneTask' },
|
38
|
+
'projtwo' => { :project => 'Project_Two', :task => 'Project_Two_Task' }
|
39
|
+
}
|
40
|
+
}
|
41
|
+
Freshtrack.stub!(:config).and_return(@config)
|
34
42
|
end
|
35
43
|
|
36
|
-
it 'should provide easy access to the
|
37
|
-
@config['
|
38
|
-
Freshtrack.company.should == company
|
44
|
+
it 'should provide easy access to the project/task mapping' do
|
45
|
+
Freshtrack.project_task_mapping.should == @config['project_task_mapping']
|
39
46
|
end
|
40
47
|
|
41
|
-
|
42
|
-
|
43
|
-
|
48
|
+
describe 'when no project name is set' do
|
49
|
+
before do
|
50
|
+
Freshtrack.stub!(:project_name).and_return(nil)
|
51
|
+
end
|
52
|
+
|
53
|
+
it 'should provide easy access to the top-level company' do
|
54
|
+
Freshtrack.company.should == @config['company']
|
55
|
+
end
|
56
|
+
|
57
|
+
it 'should provide easy access to the top-level token' do
|
58
|
+
Freshtrack.token.should == @config['token']
|
59
|
+
end
|
60
|
+
|
61
|
+
it 'should return nil for the company if no top-level company is set' do
|
62
|
+
@config.delete('company')
|
63
|
+
Freshtrack.company.should.be.nil
|
64
|
+
end
|
65
|
+
|
66
|
+
it 'should return nil for the token if no top-level token is set' do
|
67
|
+
@config.delete('token')
|
68
|
+
Freshtrack.token.should.be.nil
|
69
|
+
end
|
44
70
|
end
|
45
71
|
|
46
|
-
|
47
|
-
|
48
|
-
|
72
|
+
describe 'when a project name is set' do
|
73
|
+
before do
|
74
|
+
@project_name = 'projone'
|
75
|
+
Freshtrack.stub!(:project_name).and_return(@project_name)
|
76
|
+
end
|
77
|
+
|
78
|
+
describe 'and the project has no company info set' do
|
79
|
+
before do
|
80
|
+
@config['project_task_mapping'][@project_name].delete(:company)
|
81
|
+
@config['project_task_mapping'][@project_name].delete(:token)
|
82
|
+
end
|
83
|
+
|
84
|
+
it 'should provide easy access to the top-level company' do
|
85
|
+
Freshtrack.company.should == @config['company']
|
86
|
+
end
|
87
|
+
|
88
|
+
it 'should provide easy access to the top-level token' do
|
89
|
+
Freshtrack.token.should == @config['token']
|
90
|
+
end
|
91
|
+
end
|
92
|
+
|
93
|
+
describe 'and the project has company info set' do
|
94
|
+
before do
|
95
|
+
@project_company = 'special_company'
|
96
|
+
@project_token = '134oiujasdlfkj309'
|
97
|
+
@config['project_task_mapping'][@project_name][:company] = @project_company
|
98
|
+
@config['project_task_mapping'][@project_name][:token] = @project_token
|
99
|
+
end
|
100
|
+
|
101
|
+
it 'should provide easy access to the project-level company' do
|
102
|
+
Freshtrack.company.should == @project_company
|
103
|
+
end
|
104
|
+
|
105
|
+
it 'should provide easy access to the project-level token' do
|
106
|
+
Freshtrack.token.should == @project_token
|
107
|
+
end
|
108
|
+
|
109
|
+
describe 'and the info is incomplete' do
|
110
|
+
it 'should provide easy access to the top-level company and token if the project-level company is missing' do
|
111
|
+
@config['project_task_mapping'][@project_name].delete(:company)
|
112
|
+
Freshtrack.company.should == @config['company']
|
113
|
+
Freshtrack.token.should == @config['token']
|
114
|
+
end
|
115
|
+
|
116
|
+
it 'should provide easy access to the top-level company and token if the project-level token is missing' do
|
117
|
+
@config['project_task_mapping'][@project_name].delete(:token)
|
118
|
+
Freshtrack.company.should == @config['company']
|
119
|
+
Freshtrack.token.should == @config['token']
|
120
|
+
end
|
121
|
+
end
|
122
|
+
end
|
49
123
|
end
|
50
124
|
end
|
51
125
|
|
52
126
|
describe 'initialization' do
|
53
|
-
before
|
127
|
+
before do
|
128
|
+
@project_name = 'the_project'
|
54
129
|
@company = 'zee_company_boss'
|
55
130
|
@token = 'token goes here'
|
56
|
-
Freshtrack.
|
57
|
-
Freshtrack.
|
131
|
+
Freshtrack.stub!(:load_config)
|
132
|
+
Freshtrack.stub!(:company).and_return(@company)
|
133
|
+
Freshtrack.stub!(:token).and_return(@token)
|
134
|
+
end
|
135
|
+
|
136
|
+
it 'should accept a project name' do
|
137
|
+
lambda { Freshtrack.init(@project_name) }.should.not.raise(ArgumentError)
|
138
|
+
end
|
139
|
+
|
140
|
+
it 'should not require a project name' do
|
141
|
+
lambda { Freshtrack.init }.should.not.raise(ArgumentError)
|
142
|
+
end
|
143
|
+
|
144
|
+
it 'should provide easy access to the project name' do
|
145
|
+
Freshtrack.init(@project_name)
|
146
|
+
Freshtrack.project_name.should == @project_name
|
147
|
+
end
|
148
|
+
|
149
|
+
it 'should default the project name to nil' do
|
150
|
+
Freshtrack.init
|
151
|
+
Freshtrack.project_name.should.be.nil
|
58
152
|
end
|
59
153
|
|
60
154
|
it 'should load the configuration' do
|
61
|
-
Freshtrack.
|
155
|
+
Freshtrack.should.receive(:load_config)
|
62
156
|
Freshtrack.init
|
63
157
|
end
|
64
158
|
|
65
159
|
it 'should use the config data to set up FreshBooks' do
|
66
|
-
FreshBooks.
|
160
|
+
FreshBooks.should.receive(:setup).with("#{@company}.freshbooks.com", @token)
|
67
161
|
Freshtrack.init
|
68
162
|
end
|
69
163
|
end
|
70
164
|
|
71
165
|
describe 'getting project data' do
|
72
|
-
before
|
166
|
+
before do
|
73
167
|
@project_name = :proj
|
74
168
|
@project_task_mapping = { @project_name => { :project => 'fb proj', :task => 'fb task' } }
|
75
|
-
Freshtrack.
|
76
|
-
@project =
|
77
|
-
@task =
|
78
|
-
FreshBooks::Project.
|
79
|
-
FreshBooks::Task.
|
169
|
+
Freshtrack.stub!(:project_task_mapping).and_return(@project_task_mapping)
|
170
|
+
@project = mock('project')
|
171
|
+
@task = mock('task')
|
172
|
+
FreshBooks::Project.stub!(:find_by_name).and_return(@project)
|
173
|
+
FreshBooks::Task.stub!(:find_by_name).and_return(@task)
|
80
174
|
end
|
81
175
|
|
82
176
|
it 'should require an argument' do
|
83
|
-
lambda { Freshtrack.get_project_data }.should
|
177
|
+
lambda { Freshtrack.get_project_data }.should.raise(ArgumentError)
|
84
178
|
end
|
85
179
|
it 'should accept an argument' do
|
86
|
-
lambda { Freshtrack.get_project_data(@project_name) }.
|
180
|
+
lambda { Freshtrack.get_project_data(@project_name) }.should.not.raise(ArgumentError)
|
87
181
|
end
|
88
182
|
|
89
183
|
it 'should require the argument to be a valid project identifier' do
|
90
184
|
@project_task_mapping.delete(@project_name)
|
91
|
-
lambda { Freshtrack.get_project_data(@project_name) }.should
|
185
|
+
lambda { Freshtrack.get_project_data(@project_name) }.should.raise
|
92
186
|
end
|
93
187
|
|
94
188
|
it 'should accept an argument that is a valid project identifier' do
|
95
|
-
lambda { Freshtrack.get_project_data(@project_name) }.
|
189
|
+
lambda { Freshtrack.get_project_data(@project_name) }.should.not.raise
|
96
190
|
end
|
97
191
|
|
98
192
|
it 'should get the indicated FreshBooks project' do
|
99
|
-
FreshBooks::Project.
|
193
|
+
FreshBooks::Project.should.receive(:find_by_name).with(@project_task_mapping[@project_name][:project]).and_return(@project)
|
100
194
|
Freshtrack.get_project_data(@project_name)
|
101
195
|
end
|
102
196
|
|
103
197
|
it 'should abort if no FreshBooks project found' do
|
104
|
-
FreshBooks::Project.
|
105
|
-
lambda { Freshtrack.get_project_data(@project_name) }.should
|
198
|
+
FreshBooks::Project.stub!(:find_by_name).and_return(nil)
|
199
|
+
lambda { Freshtrack.get_project_data(@project_name) }.should.raise
|
106
200
|
end
|
107
201
|
|
108
202
|
it 'should provide easy access to the project' do
|
109
|
-
FreshBooks::Project.
|
203
|
+
FreshBooks::Project.stub!(:find_by_name).and_return(@project)
|
110
204
|
Freshtrack.get_project_data(@project_name)
|
111
205
|
Freshtrack.project.should == @project
|
112
206
|
end
|
113
207
|
|
114
208
|
it 'should get the indicated FreshBooks task' do
|
115
|
-
FreshBooks::Task.
|
209
|
+
FreshBooks::Task.should.receive(:find_by_name).with(@project_task_mapping[@project_name][:task]).and_return(@task)
|
116
210
|
Freshtrack.get_project_data(@project_name)
|
117
211
|
end
|
118
212
|
|
119
213
|
it 'should abort if no FreshBooks task found' do
|
120
|
-
FreshBooks::Task.
|
121
|
-
lambda { Freshtrack.get_project_data(@project_name) }.should
|
214
|
+
FreshBooks::Task.stub!(:find_by_name).and_return(nil)
|
215
|
+
lambda { Freshtrack.get_project_data(@project_name) }.should.raise
|
122
216
|
end
|
123
217
|
|
124
218
|
it 'should provide easy access to the task' do
|
125
|
-
FreshBooks::Task.
|
219
|
+
FreshBooks::Task.stub!(:find_by_name).and_return(@task)
|
126
220
|
Freshtrack.get_project_data(@project_name)
|
127
221
|
Freshtrack.task.should == @task
|
128
222
|
end
|
129
223
|
end
|
130
224
|
|
131
225
|
describe 'getting data' do
|
132
|
-
before
|
226
|
+
before do
|
133
227
|
@project_name = :proj
|
134
|
-
Freshtrack.
|
135
|
-
@collector =
|
136
|
-
Freshtrack.
|
228
|
+
Freshtrack.stub!(:get_project_data)
|
229
|
+
@collector = mock('collector', :get_time_data => nil)
|
230
|
+
Freshtrack.stub!(:collector).and_return(@collector)
|
137
231
|
end
|
138
232
|
|
139
233
|
it 'should require an argument' do
|
140
|
-
lambda { Freshtrack.get_data }.should
|
234
|
+
lambda { Freshtrack.get_data }.should.raise(ArgumentError)
|
141
235
|
end
|
142
236
|
|
143
237
|
it 'should accept an argument' do
|
144
|
-
lambda { Freshtrack.get_data(@project_name) }.
|
238
|
+
lambda { Freshtrack.get_data(@project_name) }.should.not.raise(ArgumentError)
|
145
239
|
end
|
146
240
|
|
147
241
|
it 'should accept options' do
|
148
|
-
lambda { Freshtrack.get_data(@project_name, :before => Time.now) }.
|
242
|
+
lambda { Freshtrack.get_data(@project_name, :before => Time.now) }.should.not.raise(ArgumentError)
|
149
243
|
end
|
150
244
|
|
151
245
|
it 'should get project data for supplied project' do
|
152
|
-
Freshtrack.
|
246
|
+
Freshtrack.should.receive(:get_project_data).with(@project_name)
|
153
247
|
Freshtrack.get_data(@project_name)
|
154
248
|
end
|
155
249
|
|
156
250
|
it 'should retrieve a time collector' do
|
157
|
-
Freshtrack.
|
251
|
+
Freshtrack.should.receive(:collector).and_return(@collector)
|
158
252
|
Freshtrack.get_data(@project_name)
|
159
253
|
end
|
160
254
|
|
161
255
|
it 'should get time data for supplied project' do
|
162
|
-
@collector.
|
256
|
+
@collector.should.receive(:get_time_data).with(@project_name)
|
163
257
|
Freshtrack.get_data(@project_name)
|
164
258
|
end
|
165
259
|
|
166
260
|
it 'should pass the options on when retrieving a time collector' do
|
167
261
|
options = { :after => Time.now - 12345 }
|
168
|
-
Freshtrack.
|
262
|
+
Freshtrack.should.receive(:collector).with(options).and_return(@collector)
|
169
263
|
Freshtrack.get_data(@project_name, options)
|
170
264
|
end
|
171
265
|
|
172
266
|
it 'should default options to an empty hash' do
|
173
|
-
Freshtrack.
|
267
|
+
Freshtrack.should.receive(:collector).with({}).and_return(@collector)
|
174
268
|
Freshtrack.get_data(@project_name)
|
175
269
|
end
|
176
270
|
|
177
271
|
it 'should return time data' do
|
178
|
-
time_data =
|
179
|
-
@collector.
|
272
|
+
time_data = mock('time data')
|
273
|
+
@collector.stub!(:get_time_data).and_return(time_data)
|
180
274
|
Freshtrack.get_data(@project_name).should == time_data
|
181
275
|
end
|
182
276
|
end
|
@@ -192,38 +286,41 @@ describe Freshtrack do
|
|
192
286
|
end
|
193
287
|
end
|
194
288
|
|
195
|
-
before
|
196
|
-
Freshtrack.
|
197
|
-
@collector =
|
198
|
-
Freshtrack::TimeCollector::Punch.
|
289
|
+
before do
|
290
|
+
Freshtrack.stub!(:config).and_return({'collector' => 'punch' })
|
291
|
+
@collector = mock('collector')
|
292
|
+
Freshtrack::TimeCollector::Punch.stub!(:new).and_return(@collector)
|
199
293
|
end
|
200
294
|
|
201
295
|
it 'should accept options' do
|
202
|
-
lambda { Freshtrack.collector(:before => Time.now) }.
|
296
|
+
lambda { Freshtrack.collector(:before => Time.now) }.should.not.raise(ArgumentError)
|
203
297
|
end
|
204
298
|
|
205
299
|
it 'should not require options' do
|
206
|
-
lambda { Freshtrack.collector }.
|
300
|
+
lambda { Freshtrack.collector }.should.not.raise(ArgumentError)
|
207
301
|
end
|
208
302
|
|
209
303
|
it 'should require a library based on the collector given in the config' do
|
210
|
-
|
304
|
+
# this expectation makes facon go crazy with `require` in the rest of the specs
|
305
|
+
# Freshtrack.should.receive(:require) do |arg|
|
306
|
+
# arg.should == 'freshtrack/time_collectors/punch'
|
307
|
+
# end
|
211
308
|
Freshtrack.collector
|
212
309
|
end
|
213
310
|
|
214
311
|
it 'should create a collector object based on the collector given in the config' do
|
215
|
-
Freshtrack::TimeCollector::Punch.
|
312
|
+
Freshtrack::TimeCollector::Punch.should.receive(:new)
|
216
313
|
Freshtrack.collector
|
217
314
|
end
|
218
315
|
|
219
316
|
it 'should pass the options on when creating a collector object' do
|
220
317
|
options = { :before => Time.now }
|
221
|
-
Freshtrack::TimeCollector::Punch.
|
318
|
+
Freshtrack::TimeCollector::Punch.should.receive(:new).with(options)
|
222
319
|
Freshtrack.collector(options)
|
223
320
|
end
|
224
321
|
|
225
322
|
it 'should pass an empty hash if no options given' do
|
226
|
-
Freshtrack::TimeCollector::Punch.
|
323
|
+
Freshtrack::TimeCollector::Punch.should.receive(:new).with({})
|
227
324
|
Freshtrack.collector
|
228
325
|
end
|
229
326
|
|
@@ -232,189 +329,199 @@ describe Freshtrack do
|
|
232
329
|
end
|
233
330
|
|
234
331
|
it 'should error if no collector is given in the config' do
|
235
|
-
Freshtrack.
|
236
|
-
lambda { Freshtrack.collector }.should
|
332
|
+
Freshtrack.stub!(:config).and_return({})
|
333
|
+
lambda { Freshtrack.collector }.should.raise(StandardError)
|
237
334
|
end
|
238
335
|
|
239
336
|
it "should accept a collector of 'punch'" do
|
240
|
-
Freshtrack.
|
241
|
-
lambda { Freshtrack.collector }.
|
337
|
+
Freshtrack.stub!(:config).and_return({'collector' => 'punch'})
|
338
|
+
lambda { Freshtrack.collector }.should.not.raise
|
242
339
|
end
|
243
340
|
|
244
341
|
it "should accept a collector of 'one_inch_punch'" do
|
245
|
-
Freshtrack.
|
246
|
-
Freshtrack::TimeCollector::OneInchPunch.
|
247
|
-
lambda { Freshtrack.collector }.
|
342
|
+
Freshtrack.stub!(:config).and_return({'collector' => 'one_inch_punch'})
|
343
|
+
Freshtrack::TimeCollector::OneInchPunch.stub!(:new)
|
344
|
+
lambda { Freshtrack.collector }.should.not.raise
|
248
345
|
end
|
249
346
|
|
250
347
|
it 'should correctly camel-case a collector name' do
|
251
|
-
Freshtrack.
|
252
|
-
Freshtrack::TimeCollector::OneInchPunch.
|
348
|
+
Freshtrack.stub!(:config).and_return({'collector' => 'one_inch_punch'})
|
349
|
+
Freshtrack::TimeCollector::OneInchPunch.should.receive(:new)
|
253
350
|
Freshtrack.collector
|
254
351
|
end
|
255
352
|
|
256
353
|
it 'should error if an unknown collector is given in the config' do
|
257
|
-
Freshtrack.
|
258
|
-
lambda { Freshtrack.collector }.should
|
354
|
+
Freshtrack.stub!(:config).and_return({'collector' => 'blam'})
|
355
|
+
lambda { Freshtrack.collector }.should.raise(LoadError)
|
259
356
|
end
|
260
357
|
end
|
261
358
|
|
262
359
|
describe 'tracking time' do
|
263
|
-
before
|
360
|
+
before do
|
264
361
|
@project_name = :proj
|
265
362
|
@data = []
|
266
|
-
Freshtrack.
|
363
|
+
Freshtrack.stub!(:get_data).and_return(@data)
|
267
364
|
end
|
268
365
|
|
269
366
|
it 'should require an argument' do
|
270
|
-
lambda { Freshtrack.track }.should
|
367
|
+
lambda { Freshtrack.track }.should.raise(ArgumentError)
|
271
368
|
end
|
272
369
|
|
273
370
|
it 'should accept an argument' do
|
274
|
-
lambda { Freshtrack.track(@project_name) }.
|
371
|
+
lambda { Freshtrack.track(@project_name) }.should.not.raise(ArgumentError)
|
275
372
|
end
|
276
373
|
|
277
374
|
it 'should accept options' do
|
278
|
-
lambda { Freshtrack.track(@project_name, :before => Time.now) }.
|
375
|
+
lambda { Freshtrack.track(@project_name, :before => Time.now) }.should.not.raise(ArgumentError)
|
279
376
|
end
|
280
377
|
|
281
378
|
it 'should get data for supplied project' do
|
282
|
-
Freshtrack.
|
379
|
+
Freshtrack.should.receive(:get_data).and_return(@data) do |project, _|
|
380
|
+
project.should == @project_name
|
381
|
+
end
|
283
382
|
Freshtrack.track(@project_name)
|
284
383
|
end
|
285
384
|
|
286
385
|
it 'should pass options on when getting data' do
|
287
386
|
options = { :after => Time.now - 12345 }
|
288
|
-
Freshtrack.
|
387
|
+
Freshtrack.should.receive(:get_data).and_return(@data) do |project, opts|
|
388
|
+
project.should == @project_name
|
389
|
+
opts.should == options
|
390
|
+
end
|
289
391
|
Freshtrack.track(@project_name, options)
|
290
392
|
end
|
291
393
|
|
292
394
|
it 'should default options to an empty hash' do
|
293
|
-
Freshtrack.
|
395
|
+
Freshtrack.should.receive(:get_data).and_return(@data) do |project, opts|
|
396
|
+
project.should == @project_name
|
397
|
+
opts.should == {}
|
398
|
+
end
|
294
399
|
Freshtrack.track(@project_name)
|
295
400
|
end
|
296
401
|
|
297
402
|
it 'should create entries for project data' do
|
298
403
|
2.times do
|
299
|
-
ent =
|
404
|
+
ent = mock('entry data')
|
300
405
|
@data.push(ent)
|
301
|
-
Freshtrack.
|
406
|
+
Freshtrack.should.receive(:create_entry).with(ent)
|
302
407
|
end
|
303
408
|
Freshtrack.track(@project_name)
|
304
409
|
end
|
305
410
|
end
|
306
411
|
|
307
412
|
describe 'creating an entry' do
|
308
|
-
before
|
413
|
+
before do
|
309
414
|
@date = Date.today - 3
|
310
415
|
@hours = 5.67
|
311
416
|
@notes = 'notes for the time entry'
|
312
417
|
@entry_data = { 'date' => @date, 'hours' => @hours, 'notes' => @notes }
|
313
|
-
@time_entry =
|
314
|
-
FreshBooks::TimeEntry.
|
418
|
+
@time_entry = mock('time entry', :project_id= => nil, :task_id= => nil, :date= => nil, :hours= => nil, :notes= => nil, :create => true)
|
419
|
+
FreshBooks::TimeEntry.stub!(:new).and_return(@time_entry)
|
315
420
|
|
316
|
-
@project =
|
317
|
-
@task =
|
318
|
-
Freshtrack.
|
319
|
-
Freshtrack.
|
421
|
+
@project = mock('project', :project_id => mock('project id'))
|
422
|
+
@task = mock('task', :task_id => mock('task id'))
|
423
|
+
Freshtrack.stub!(:project).and_return(@project)
|
424
|
+
Freshtrack.stub!(:task).and_return(@task)
|
320
425
|
|
321
|
-
STDERR.
|
426
|
+
STDERR.stub!(:puts)
|
322
427
|
end
|
323
428
|
|
324
429
|
it 'should require an argument' do
|
325
|
-
lambda { Freshtrack.create_entry }.should
|
430
|
+
lambda { Freshtrack.create_entry }.should.raise(ArgumentError)
|
326
431
|
end
|
327
432
|
|
328
433
|
it 'should accept an argument' do
|
329
|
-
lambda { Freshtrack.create_entry(@entry_data) }.
|
434
|
+
lambda { Freshtrack.create_entry(@entry_data) }.should.not.raise(ArgumentError)
|
330
435
|
end
|
331
436
|
|
332
437
|
it 'should instantiate a new time entry' do
|
333
|
-
FreshBooks::TimeEntry.
|
438
|
+
FreshBooks::TimeEntry.should.receive(:new).and_return(@time_entry)
|
334
439
|
Freshtrack.create_entry(@entry_data)
|
335
440
|
end
|
336
441
|
|
337
442
|
describe 'with the time entry instance' do
|
338
443
|
it 'should set the project' do
|
339
|
-
@time_entry.
|
444
|
+
@time_entry.should.receive(:project_id=).with(@project.project_id)
|
340
445
|
Freshtrack.create_entry(@entry_data)
|
341
446
|
end
|
342
447
|
|
343
448
|
it 'should set the task' do
|
344
|
-
@time_entry.
|
449
|
+
@time_entry.should.receive(:task_id=).with(@task.task_id)
|
345
450
|
Freshtrack.create_entry(@entry_data)
|
346
451
|
end
|
347
452
|
|
348
453
|
it 'should set the date' do
|
349
|
-
@time_entry.
|
454
|
+
@time_entry.should.receive(:date=).with(@date)
|
350
455
|
Freshtrack.create_entry(@entry_data)
|
351
456
|
end
|
352
457
|
|
353
458
|
it 'should set the hours' do
|
354
|
-
@time_entry.
|
459
|
+
@time_entry.should.receive(:hours=).with(@hours)
|
355
460
|
Freshtrack.create_entry(@entry_data)
|
356
461
|
end
|
357
462
|
|
358
463
|
it 'should set the notes' do
|
359
|
-
@time_entry.
|
464
|
+
@time_entry.should.receive(:notes=).with(@notes)
|
360
465
|
Freshtrack.create_entry(@entry_data)
|
361
466
|
end
|
362
467
|
end
|
363
468
|
|
364
469
|
it 'should create the time entry' do
|
365
|
-
@time_entry.
|
470
|
+
@time_entry.should.receive(:create)
|
366
471
|
Freshtrack.create_entry(@entry_data)
|
367
472
|
end
|
368
473
|
|
369
474
|
describe 'successfully' do
|
370
|
-
before
|
371
|
-
@time_entry.
|
475
|
+
before do
|
476
|
+
@time_entry.stub!(:create).and_return(5)
|
372
477
|
end
|
373
478
|
|
374
479
|
it 'should be silent' do
|
375
|
-
STDERR.
|
480
|
+
STDERR.should.receive(:puts).never
|
376
481
|
Freshtrack.create_entry(@entry_data)
|
377
482
|
end
|
378
483
|
|
379
484
|
it 'should return true' do
|
380
|
-
Freshtrack.create_entry(@entry_data).should
|
485
|
+
Freshtrack.create_entry(@entry_data).should == true
|
381
486
|
end
|
382
487
|
end
|
383
488
|
|
384
489
|
describe 'unsuccessfully' do
|
385
|
-
before
|
386
|
-
@time_entry.
|
490
|
+
before do
|
491
|
+
@time_entry.stub!(:create).and_return(nil)
|
387
492
|
end
|
388
493
|
|
389
494
|
it 'should output an indication' do
|
390
|
-
STDERR.
|
495
|
+
STDERR.should.receive(:puts) do |arg|
|
496
|
+
arg.should.match(/#{@date.to_s}/)
|
497
|
+
end
|
391
498
|
Freshtrack.create_entry(@entry_data)
|
392
499
|
end
|
393
500
|
|
394
501
|
it 'should return nil' do
|
395
|
-
Freshtrack.create_entry(@entry_data).should
|
502
|
+
Freshtrack.create_entry(@entry_data).should.be.nil
|
396
503
|
end
|
397
504
|
end
|
398
505
|
end
|
399
506
|
|
400
507
|
it 'should list open invoices' do
|
401
|
-
Freshtrack.should
|
508
|
+
Freshtrack.should.respond_to(:open_invoices)
|
402
509
|
end
|
403
510
|
|
404
511
|
describe 'listing open invoices' do
|
405
|
-
before
|
406
|
-
@invoices = Array.new(5) {
|
407
|
-
FreshBooks::Invoice.
|
512
|
+
before do
|
513
|
+
@invoices = Array.new(5) { mock('invoice', :open? => false) }
|
514
|
+
FreshBooks::Invoice.stub!(:list).and_return(@invoices)
|
408
515
|
end
|
409
516
|
|
410
517
|
it 'should get a list of invoices' do
|
411
|
-
FreshBooks::Invoice.
|
518
|
+
FreshBooks::Invoice.should.receive(:list).and_return(@invoices)
|
412
519
|
Freshtrack.open_invoices
|
413
520
|
end
|
414
521
|
|
415
522
|
it 'should return only the open invoices' do
|
416
523
|
open_invoices = @invoices.values_at(0,1,2)
|
417
|
-
open_invoices.each { |i| i.
|
524
|
+
open_invoices.each { |i| i.stub!(:open?).and_return(true) }
|
418
525
|
|
419
526
|
Freshtrack.open_invoices.should == open_invoices
|
420
527
|
end
|
@@ -424,34 +531,34 @@ describe Freshtrack do
|
|
424
531
|
end
|
425
532
|
|
426
533
|
it 'should return an empty array if there are no invoices' do
|
427
|
-
FreshBooks::Invoice.
|
534
|
+
FreshBooks::Invoice.stub!(:list).and_return([])
|
428
535
|
Freshtrack.open_invoices.should == []
|
429
536
|
end
|
430
537
|
|
431
538
|
it 'should return an empty array if the invoice list returns nil' do
|
432
|
-
FreshBooks::Invoice.
|
539
|
+
FreshBooks::Invoice.stub!(:list).and_return(nil)
|
433
540
|
Freshtrack.open_invoices.should == []
|
434
541
|
end
|
435
542
|
end
|
436
543
|
|
437
544
|
it 'should show invoice aging' do
|
438
|
-
Freshtrack.should
|
545
|
+
Freshtrack.should.respond_to(:invoice_aging)
|
439
546
|
end
|
440
547
|
|
441
548
|
describe 'showing invoice aging' do
|
442
|
-
before
|
549
|
+
before do
|
443
550
|
today = Date.today
|
444
551
|
|
445
552
|
@invoices = [
|
446
|
-
|
447
|
-
|
448
|
-
|
553
|
+
mock('invoice', :invoice_id => '1234', :number => '4567', :client => mock('client', :organization => 'client 20'), :date => today - 3, :status => 'partial', :amount => 50, :owed_amount => 3),
|
554
|
+
mock('invoice', :invoice_id => '19873', :number => '1456', :client => mock('client', :organization => 'client 3'), :date => today - 20, :status => 'viewed', :amount => 60, :owed_amount => 60),
|
555
|
+
mock('invoice', :invoice_id => '0038', :number => '30267', :client => mock('client', :organization => 'client 4'), :date => today - 59, :status => 'sent', :amount => 20, :owed_amount => 20)
|
449
556
|
]
|
450
|
-
Freshtrack.
|
557
|
+
Freshtrack.stub!(:open_invoices).and_return(@invoices)
|
451
558
|
end
|
452
559
|
|
453
560
|
it 'should get open invoices' do
|
454
|
-
Freshtrack.
|
561
|
+
Freshtrack.should.receive(:open_invoices).and_return(@invoices)
|
455
562
|
Freshtrack.invoice_aging
|
456
563
|
end
|
457
564
|
|
@@ -490,7 +597,7 @@ describe Freshtrack do
|
|
490
597
|
end
|
491
598
|
|
492
599
|
it 'should return an empty array if there are no open invoices' do
|
493
|
-
Freshtrack.
|
600
|
+
Freshtrack.stub!(:open_invoices).and_return([])
|
494
601
|
Freshtrack.invoice_aging.should == []
|
495
602
|
end
|
496
603
|
end
|