freshtrack 0.4.2 → 0.5.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.
- 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
|