watch_tower 0.0.0.1 → 0.0.1.beta1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (106) hide show
  1. data/.todo +3 -0
  2. data/.travis.yml +4 -0
  3. data/Gemfile +0 -4
  4. data/README.md +48 -2
  5. data/TODO +3 -0
  6. data/ci/travis.rb +4 -1
  7. data/lib/watch_tower/appscript.rb +65 -7
  8. data/lib/watch_tower/cli/install.rb +27 -3
  9. data/lib/watch_tower/cli/open.rb +2 -0
  10. data/lib/watch_tower/cli/start.rb +3 -1
  11. data/lib/watch_tower/cli.rb +2 -0
  12. data/lib/watch_tower/config.rb +2 -0
  13. data/lib/watch_tower/core_ext.rb +2 -0
  14. data/lib/watch_tower/editor/base_appscript.rb +28 -1
  15. data/lib/watch_tower/editor/base_ps.rb +25 -0
  16. data/lib/watch_tower/editor/textmate.rb +2 -0
  17. data/lib/watch_tower/editor/xcode.rb +2 -0
  18. data/lib/watch_tower/editor.rb +2 -0
  19. data/lib/watch_tower/errors.rb +2 -0
  20. data/lib/watch_tower/eye.rb +13 -2
  21. data/lib/watch_tower/file_tree.rb +78 -0
  22. data/lib/watch_tower/project/any_based.rb +2 -0
  23. data/lib/watch_tower/project/git_based.rb +2 -0
  24. data/lib/watch_tower/project/init.rb +2 -0
  25. data/lib/watch_tower/project/path_based.rb +2 -0
  26. data/lib/watch_tower/project.rb +2 -0
  27. data/lib/watch_tower/server/app.rb +37 -7
  28. data/lib/watch_tower/server/assets/javascripts/application.js +4 -0
  29. data/lib/watch_tower/server/assets/javascripts/datepicker.coffee +21 -0
  30. data/lib/watch_tower/server/assets/javascripts/file_tree.coffee +30 -0
  31. data/lib/watch_tower/server/assets/javascripts/percentage.coffee +15 -5
  32. data/lib/watch_tower/server/assets/stylesheets/application.css +1 -0
  33. data/lib/watch_tower/server/assets/stylesheets/date.sass +17 -0
  34. data/lib/watch_tower/server/assets/stylesheets/file_tree.sass +42 -0
  35. data/lib/watch_tower/server/assets/stylesheets/global.sass +7 -4
  36. data/lib/watch_tower/server/configurations/asset.rb +2 -0
  37. data/lib/watch_tower/server/configurations.rb +2 -0
  38. data/lib/watch_tower/server/database.rb +2 -0
  39. data/lib/watch_tower/server/db/migrate/001_create_projects.rb +2 -0
  40. data/lib/watch_tower/server/db/migrate/002_create_files.rb +2 -0
  41. data/lib/watch_tower/server/db/migrate/003_create_time_entries.rb +2 -0
  42. data/lib/watch_tower/server/db/migrate/004_create_durations.rb +2 -0
  43. data/lib/watch_tower/server/db/migrate/005_add_hash_to_time_entries.rb +2 -0
  44. data/lib/watch_tower/server/db/migrate/006_add_hash_to_files.rb +2 -0
  45. data/lib/watch_tower/server/db/migrate/007_add_editor_to_times_entries.rb +8 -0
  46. data/lib/watch_tower/server/db/migrate/008_rename_editor_to_editor_name_in_times_entries.rb +5 -0
  47. data/lib/watch_tower/server/db/migrate/009_remove_editor_index_from_time_entries.rb +7 -0
  48. data/lib/watch_tower/server/db/migrate/010_add_editor_version_to_times_entries.rb +7 -0
  49. data/lib/watch_tower/server/helpers/asset.rb +2 -0
  50. data/lib/watch_tower/server/helpers/improved_partials.rb +2 -0
  51. data/lib/watch_tower/server/helpers/presenters.rb +33 -0
  52. data/lib/watch_tower/server/helpers.rb +3 -0
  53. data/lib/watch_tower/server/models/duration.rb +9 -0
  54. data/lib/watch_tower/server/models/file.rb +17 -0
  55. data/lib/watch_tower/server/models/project.rb +30 -0
  56. data/lib/watch_tower/server/models/time_entry.rb +5 -0
  57. data/lib/watch_tower/server/presenters/application_presenter.rb +165 -0
  58. data/lib/watch_tower/server/presenters/file_presenter.rb +10 -0
  59. data/lib/watch_tower/server/presenters/project_presenter.rb +20 -0
  60. data/lib/watch_tower/server/presenters.rb +13 -0
  61. data/lib/watch_tower/server/public/assets/{WatchTower-4d6de11e1bd34165ad91ac46fb711bf3.jpg → WatchTower-58eff0713efffbc6054defddc879e0b1.jpg} +0 -0
  62. data/lib/watch_tower/server/public/assets/application-4e6971066e06aa53b0c8e52c764044d1.css +389 -0
  63. data/lib/watch_tower/server/public/assets/application-6a1be75d4fd6a545faceb638e47a2486.js +23778 -0
  64. data/lib/watch_tower/server/public/assets/calendar-379834cd6e6321a940b662ace47f3032.gif +0 -0
  65. data/lib/watch_tower/server/public/assets/calendar-blue-d6aa74feef7ee4287532761db99a6c0a.gif +0 -0
  66. data/lib/watch_tower/server/public/assets/calendar-green-3752fe2996091379c8d321f759039385.gif +0 -0
  67. data/lib/watch_tower/server/public/assets/jquery.datepick-9c8dfe3a4d40bcafc7b182e194c13836.css +227 -0
  68. data/lib/watch_tower/server/public/assets/{percentage-d8589e21a5fc85d32a445f531ff8ab95.png → percentage-d0176e99520c95e93eee63738ef5d487.png} +0 -0
  69. data/lib/watch_tower/server/vendor/assets/images/calendar-blue.gif +0 -0
  70. data/lib/watch_tower/server/vendor/assets/images/calendar-green.gif +0 -0
  71. data/lib/watch_tower/server/vendor/assets/images/calendar.gif +0 -0
  72. data/lib/watch_tower/server/vendor/assets/javascripts/jquery-datepick-ext.js +266 -0
  73. data/lib/watch_tower/server/vendor/assets/javascripts/jquery-datepick-validation.js +232 -0
  74. data/lib/watch_tower/server/vendor/assets/javascripts/jquery-datepick.js +2092 -0
  75. data/lib/watch_tower/server/vendor/assets/stylesheets/jquery.datepick.css +226 -0
  76. data/lib/watch_tower/server/views/_project.haml +10 -13
  77. data/lib/watch_tower/server/views/index.haml +9 -6
  78. data/lib/watch_tower/server/views/layout.haml +7 -4
  79. data/lib/watch_tower/server/views/project.haml +9 -11
  80. data/lib/watch_tower/server.rb +8 -1
  81. data/lib/watch_tower/templates/{watchtower.plist → watchtower.plist.erb} +5 -6
  82. data/lib/watch_tower/version.rb +12 -3
  83. data/lib/watch_tower.rb +20 -1
  84. data/spec/factories.rb +2 -0
  85. data/spec/watch_tower/appscript_spec.rb +36 -2
  86. data/spec/watch_tower/editor/base_appscript_spec.rb +83 -0
  87. data/spec/watch_tower/editor/textmate_spec.rb +37 -0
  88. data/spec/watch_tower/editor/xcode_spec.rb +6 -0
  89. data/spec/watch_tower/eye_spec.rb +21 -0
  90. data/spec/watch_tower/file_tree_spec.rb +156 -0
  91. data/spec/watch_tower/server/app_spec.rb +64 -20
  92. data/spec/watch_tower/server/models/file_spec.rb +93 -31
  93. data/spec/watch_tower/server/models/project_spec.rb +147 -18
  94. data/spec/watch_tower/server/models/time_entry_spec.rb +10 -0
  95. data/spec/watch_tower/server/{decorator/project_decorator_spec.rb → presenters/application_presenter_spec.rb} +24 -13
  96. data/spec/watch_tower/server/presenters/file_presenter_spec.rb +8 -0
  97. data/spec/watch_tower/server/presenters/project_presenter_spec.rb +130 -0
  98. data/watch_tower.gemspec +10 -4
  99. metadata +114 -74
  100. data/lib/watch_tower/server/decorator/application_decorator.rb +0 -91
  101. data/lib/watch_tower/server/decorator/file_decorator.rb +0 -38
  102. data/lib/watch_tower/server/decorator/project_decorator.rb +0 -51
  103. data/lib/watch_tower/server/decorator.rb +0 -21
  104. data/lib/watch_tower/server/public/assets/application-7829b53b5ece1a16d22dc3d00f329023.css +0 -107
  105. data/lib/watch_tower/server/public/assets/application-e0e6b7731aade460f680331e65cf0682.js +0 -9359
  106. data/lib/watch_tower/server/views/_file.haml +0 -9
@@ -0,0 +1,156 @@
1
+ require 'spec_helper'
2
+
3
+ describe FileTree do
4
+ before(:each) do
5
+ @base_path = "/project"
6
+ @simple_files = [
7
+ { path: "#{@base_path}/file1.rb", elapsed_time: 3600 },
8
+ { path: "#{@base_path}/file2.rb", elapsed_time: 1800 },
9
+ ]
10
+ @files = @simple_files.dup
11
+ @files << { path: "#{@base_path}/folder/file_under_folder1.rb", elapsed_time: 1800 }
12
+ @files << { path: "#{@base_path}/folder/file_under_folder2.rb", elapsed_time: 900 }
13
+
14
+ @simple_files_elapsed_times = {
15
+ @base_path => 5400
16
+ }
17
+
18
+ @files_elapsed_times = {
19
+ @base_path => 8100,
20
+ "#{@base_path}/folder" => 2700
21
+ }
22
+ end
23
+
24
+ describe "#remove_base_path_from_files" do
25
+ subject { FileTree.new(@base_path, @files) }
26
+
27
+ it { should respond_to :remove_base_path_from_files }
28
+
29
+ it "should remove the base_path from all the files in the @files array" do
30
+ files = subject.send(:remove_base_path_from_files, @base_path, @simple_files)
31
+
32
+ files.each do |f|
33
+ f[:path].should_not match %r(#{@base_path}/)
34
+ end
35
+ end
36
+ end
37
+
38
+ describe "#process" do
39
+ subject { FileTree.new(@base_path, @files) }
40
+
41
+ it { should respond_to :process }
42
+
43
+ it "should be called on initialize" do
44
+ FileTree.any_instance.expects(:process).once
45
+
46
+ FileTree.new(@base_path, @files)
47
+ end
48
+
49
+ it "should call parse_files" do
50
+ FileTree.any_instance.expects(:parse_files).once
51
+ FileTree.any_instance.stubs(:parse_folders)
52
+
53
+ FileTree.new(@base_path, @files)
54
+ end
55
+
56
+ it "should call parse_folders" do
57
+ FileTree.any_instance.stubs(:parse_files)
58
+ FileTree.any_instance.expects(:parse_folders).once
59
+
60
+ FileTree.new(@base_path, @files)
61
+ end
62
+ end
63
+
64
+ describe "simple files without folders" do
65
+ subject { FileTree.new(@base_path, @simple_files) }
66
+
67
+ describe "#files" do
68
+ it { should respond_to :files }
69
+
70
+ it "should return an array" do
71
+ subject.files.should be_instance_of Array
72
+ end
73
+
74
+ it "should include all the files with their elapsed times" do
75
+ subject.files.each do |f|
76
+ f.should be_instance_of Hash
77
+ f[:path].should =~ /^file(1|2)\.rb$/
78
+ f[:elapsed_time].to_s.should =~ /^(3600|1800)$/
79
+ end
80
+ end
81
+ end
82
+
83
+ describe "#elapsed_time" do
84
+ it { should respond_to :elapsed_time }
85
+
86
+ it "should return elapsed_time" do
87
+ subject.elapsed_time.should == @simple_files_elapsed_times[@base_path]
88
+ end
89
+ end
90
+
91
+ describe "#nested_tree" do
92
+ it { should respond_to :nested_tree }
93
+
94
+ it "should have an empty nested_tree" do
95
+ subject.nested_tree.should be_empty
96
+ end
97
+
98
+ end
99
+ end
100
+
101
+ describe "files with folders" do
102
+ subject { FileTree.new(@base_path, @files) }
103
+
104
+ describe "#files" do
105
+ it { should respond_to :files }
106
+
107
+ it "should return an array" do
108
+ subject.files.should be_instance_of Array
109
+ end
110
+
111
+ it "should include all the files with their elapsed times" do
112
+ subject.files.each do |f|
113
+ f.should be_instance_of Hash
114
+ f[:path].should =~ /^file(1|2)\.rb$/
115
+ f[:elapsed_time].to_s.should =~ /^(3600|1800)$/
116
+ end
117
+ end
118
+ end
119
+
120
+ describe "#elapsed_time" do
121
+ it { should respond_to :elapsed_time }
122
+
123
+ it "should return elapsed_time" do
124
+ subject.elapsed_time.should == @files_elapsed_times[@base_path]
125
+ end
126
+ end
127
+
128
+ describe "#nested_tree" do
129
+ it { should respond_to :nested_tree }
130
+
131
+ it "should not have an empty nested_tree" do
132
+ subject.nested_tree.should_not be_empty
133
+ end
134
+
135
+ it "should be a Hash" do
136
+ subject.nested_tree.should be_instance_of Hash
137
+ end
138
+
139
+ it "should return a FileTree for each element of the hash" do
140
+ subject.nested_tree['folder'].should be_instance_of FileTree
141
+ end
142
+
143
+ it "should include all the files under the folder" do
144
+ subject.nested_tree['folder'].files.each do |f|
145
+ f.should be_instance_of Hash
146
+ f[:path].should =~ /^file_under_folder(1|2)\.rb$/
147
+ f[:elapsed_time].to_s.should =~ /^(1800|900)$/
148
+ end
149
+ end
150
+
151
+ it "should return the elapsed time of the folder" do
152
+ subject.nested_tree['folder'].elapsed_time.should == @files_elapsed_times["#{@base_path}/folder"]
153
+ end
154
+ end
155
+ end
156
+ end
@@ -9,13 +9,18 @@ module Server
9
9
  }
10
10
 
11
11
  @projects[:not_empty][:project] = FactoryGirl.create(:project)
12
+ Timecop.freeze(Time.now)
12
13
  2.times do
13
- @projects[:not_empty][:files] << FactoryGirl.create(:file, project: @projects[:not_empty][:project])
14
- end
15
- 5.times do
16
- @projects[:not_empty][:time_entries] << FactoryGirl.create(:time_entry, file: @projects[:not_empty][:files].first)
14
+ 2.times do
15
+ @projects[:not_empty][:files] << FactoryGirl.create(:file, project: @projects[:not_empty][:project])
16
+ end
17
+ 5.times do
18
+ @projects[:not_empty][:time_entries] << FactoryGirl.create(:time_entry, file: @projects[:not_empty][:files].first)
19
+ end
20
+ @projects[:not_empty][:duration] = FactoryGirl.create(:duration, file: @projects[:not_empty][:files].first)
21
+ Timecop.freeze(Time.now + 2.days)
17
22
  end
18
- @projects[:not_empty][:duration] = FactoryGirl.create(:duration, file: @projects[:not_empty][:files].first)
23
+ Timecop.return
19
24
 
20
25
  @projects[:empty][:project] = FactoryGirl.create(:project)
21
26
  2.times do
@@ -25,6 +30,21 @@ module Server
25
30
  @projects[:empty][:duration] = FactoryGirl.create(:duration, file: @projects[:empty][:files].first)
26
31
  end
27
32
 
33
+ after(:each) do
34
+ # Reset the from/to date
35
+ visit "/?from_date=&to_date="
36
+ end
37
+
38
+ describe "#layout" do
39
+ it "should show a datepicker" do
40
+ visit '/'
41
+ within '#main' do
42
+ page.should have_selector 'aside#date'
43
+ page.should have_selector 'aside#date input'
44
+ end
45
+ end
46
+ end
47
+
28
48
  describe "#index" do
29
49
  before(:each) do
30
50
  visit '/'
@@ -93,6 +113,24 @@ module Server
93
113
  page.should have_selector('a', href: "/project/#{@projects[:not_empty][:project].id}")
94
114
  end
95
115
  end
116
+
117
+ it "should display No projects available for the selected date range if there are no projects" do
118
+ Project.delete_all
119
+ visit '/'
120
+ page.should have_content "No projects available for the selected date range."
121
+ end
122
+
123
+ it "should display No projects available for the selected date range if projects exist but date range is way off" do
124
+ params = ['from_date=10/01/2001', 'to_date=10/10/2001']
125
+ visit "/?#{params.join('&')}"
126
+ page.should have_content "No projects available for the selected date range."
127
+ end
128
+
129
+ it "should display the project with elapsed time with the date range" do
130
+ params = [(Time.now + 1.day).strftime('%m/%d/%Y'), (Time.now + 3.days).strftime('%m/%d/%Y')]
131
+ visit "/?#{params.join('&')}"
132
+ page.should have_content '8 seconds'
133
+ end
96
134
  end
97
135
 
98
136
  describe "#project" do
@@ -146,41 +184,47 @@ module Server
146
184
  end
147
185
  end
148
186
 
149
- it "should have a div for the file's path" do
150
- within 'article#project section#files article.file' do
151
- page.should have_selector 'div.path'
187
+ it "should have a span for the file's path" do
188
+ within 'article#project section#files li.file' do
189
+ page.should have_selector 'span.path'
152
190
  end
153
191
  end
154
192
 
155
193
  it "should display the file's path" do
156
- within 'article#project section#files article.file div.path' do
157
- page.should have_content @projects[:not_empty][:files].first.path
194
+ within 'article#project section#files li.file span.path' do
195
+ page.should have_content ::File.basename(@projects[:not_empty][:files].first.path)
158
196
  end
159
197
  end
160
198
 
161
- it "should have a div for the file's elapsed time" do
162
- within 'article#project section#files article.file' do
163
- page.should have_selector 'div.elapsed'
199
+ it "should have a span for the file's elapsed time" do
200
+ within 'article#project section#files li.file' do
201
+ page.should have_selector 'span.elapsed_time'
164
202
  end
165
203
  end
166
204
 
167
205
  it "should display the file's elaped time" do
168
- within 'article#project section#files article.file div.elapsed' do
206
+ within 'article#project section#files li.file span.elapsed_time' do
169
207
  page.should have_content @projects[:not_empty][:files].first.elapsed_time.to_s
170
208
  end
171
209
  end
172
210
 
173
- it "should display an image under each file's name to categorize percentage" do
174
- within 'article#project section#files article.file' do
175
- page.should have_selector '.percentage_img_container > .percentage > img'
176
- end
177
- end
178
-
179
211
  it "should not display files having 0 seconds" do
180
212
  within 'article#project section#files' do
181
213
  page.should_not have_content @projects[:not_empty][:files].last.path
182
214
  end
183
215
  end
216
+
217
+ it "should display No files available for the selected date range if there are no files" do
218
+ @projects[:not_empty][:project].files.delete_all
219
+ visit "/project/#{@projects[:not_empty][:project].id}"
220
+ page.should have_content "No files available for the selected date range."
221
+ end
222
+
223
+ it "should display No files available for the selected date range if projects exist but date range is way off" do
224
+ params = ['from_date=10/01/2001', 'to_date=10/10/2001']
225
+ visit "/project/#{@projects[:not_empty][:project].id}?#{params.join('&')}"
226
+ page.should have_content "No files available for the selected date range."
227
+ end
184
228
  end
185
229
  end
186
230
  end
@@ -229,44 +229,25 @@ module Server
229
229
  File.worked_on.should_not include(@files.last)
230
230
  end
231
231
  end
232
- end
233
-
234
- describe "Methods" do
235
- before(:each) do
236
- @project = FactoryGirl.create :project
237
- @files = []
238
- 2.times do
239
- @files << FactoryGirl.create(:file, project: @project)
240
- 2.times do
241
- FactoryGirl.create :time_entry, file: @files.last
242
- end
243
- end
244
- end
245
232
 
246
- describe "one project" do
247
- describe "#sum_elapsed_time" do
248
- it "should return the sum of all elapsed times of all the files for the same project" do
249
- @project.files.sum_elapsed_time.should == @files.inject(0) {|s, f| s += f.elapsed_time }
250
- end
233
+ describe "Methods" do
234
+ it "should respond_to sum_elapsed_time" do
235
+ File.should respond_to(:sum_elapsed_time)
251
236
  end
252
237
 
253
- describe "#percent" do
254
- it "should return 50" do
255
- @project.files.first.percent.should == 50
256
- end
257
- end
258
- end
238
+ it { should respond_to :percent }
259
239
 
260
- describe "two projects" do
261
- before(:each) do
262
- @other_files = []
263
- 2.times do
264
- @other_files << FactoryGirl.create(:file)
240
+ describe "one project" do
241
+ before(:each) do
242
+ @project = FactoryGirl.create :project
243
+ @files = []
265
244
  2.times do
266
- FactoryGirl.create :time_entry, file: @other_files.last
245
+ @files << FactoryGirl.create(:file, project: @project)
246
+ 2.times do
247
+ FactoryGirl.create :time_entry, file: @files.last
248
+ end
267
249
  end
268
250
  end
269
-
270
251
  describe "#sum_elapsed_time" do
271
252
  it "should return the sum of all elapsed times of all the files for the same project" do
272
253
  @project.files.sum_elapsed_time.should == @files.inject(0) {|s, f| s += f.elapsed_time }
@@ -279,6 +260,87 @@ module Server
279
260
  end
280
261
  end
281
262
  end
263
+
264
+ describe "two projects" do
265
+ before(:each) do
266
+ @project = FactoryGirl.create :project
267
+ @files = []
268
+ 2.times do
269
+ @files << FactoryGirl.create(:file, project: @project)
270
+ 2.times do
271
+ FactoryGirl.create :time_entry, file: @files.last
272
+ end
273
+ end
274
+
275
+ @other_files = []
276
+ 2.times do
277
+ @other_files << FactoryGirl.create(:file)
278
+ 2.times do
279
+ FactoryGirl.create :time_entry, file: @other_files.last
280
+ end
281
+ end
282
+
283
+ describe "#sum_elapsed_time" do
284
+ it "should return the sum of all elapsed times of all the files for the same project" do
285
+ @project.files.sum_elapsed_time.should == @files.inject(0) {|s, f| s += f.elapsed_time }
286
+ end
287
+ end
288
+
289
+ describe "#percent" do
290
+ it "should return 50" do
291
+ @project.files.first.percent.should == 50
292
+ end
293
+ end
294
+ end
295
+ end
296
+
297
+ describe "#date_range" do
298
+ before(:each) do
299
+ @tw_files = []
300
+ @lw_files = []
301
+ @project = FactoryGirl.create(:project)
302
+ # Freeze the time to this week
303
+ Timecop.freeze(Time.now)
304
+ # Create a bunch of files
305
+ 2.times do
306
+ @tw_files << FactoryGirl.create(:file, project: @project)
307
+ 2.times do |n|
308
+ FactoryGirl.create(:time_entry, file: @tw_files.last)
309
+ end
310
+ end
311
+ # Freeze the time to last week
312
+ Timecop.freeze(Time.now - 7.days)
313
+ # Create a bunch of files
314
+ 2.times do
315
+ @lw_files << FactoryGirl.create(:file, project: @project)
316
+ 2.times do |n|
317
+ FactoryGirl.create(:time_entry, file: @lw_files.last)
318
+ end
319
+ end
320
+ # Return the frozen time to now
321
+ Timecop.freeze(Time.now + 7.days)
322
+ end
323
+
324
+ subject { File }
325
+
326
+ it { should respond_to :date_range }
327
+
328
+ it "should return an active relation object" do
329
+ from = (Time.now - 6.days).strftime '%m/%d/%Y'
330
+ to = Time.now.strftime '%m/%d/%Y'
331
+
332
+ q = subject.date_range from, to
333
+ q.should be_instance_of ActiveRecord::Relation
334
+ end
335
+
336
+ it "should not return project's of last week" do
337
+ from = (Time.now - 6.days).strftime '%m/%d/%Y'
338
+ to = Time.now.strftime '%m/%d/%Y'
339
+
340
+ q = subject.date_range from, to
341
+ @lw_files.each {|p| q.should_not include(p)}
342
+ end
343
+ end
282
344
  end
283
345
  end
284
346
  end
@@ -8,10 +8,6 @@ module Server
8
8
  it { should respond_to :files_count }
9
9
  end
10
10
 
11
- describe "Methods" do
12
- it { should respond_to :elapsed_time }
13
- end
14
-
15
11
  describe "Validations" do
16
12
  it { should_not be_valid }
17
13
 
@@ -46,20 +42,23 @@ module Server
46
42
  @project.reload.files_count.should == 10
47
43
  end
48
44
 
49
- it "should have time_entries through files" # do
50
- # file1 = FactoryGirl.create :file, project: @project
51
- # file2 = FactoryGirl.create :file, project: @project
52
- #
53
- # 10.times do
54
- # FactoryGirl.create :time_entry, file: file1
55
- # end
56
- #
57
- # 10.times do
58
- # FactoryGirl.create :time_entry, file: file2
59
- # end
60
- #
61
- # @project.reload.time_entries.size.should == 20
62
- # end
45
+ it "should have many time entries through files" do
46
+ f = FactoryGirl.create :file, project: @project
47
+ t = FactoryGirl.create :time_entry, file: f
48
+
49
+ @project.reload.time_entries.should include(t)
50
+ end
51
+
52
+ it "should have many durations through files" do
53
+ f = FactoryGirl.create :file, project: @project
54
+
55
+ 2.times do
56
+ FactoryGirl.create :time_entry, file: f
57
+ end
58
+
59
+ @project.reload.durations.should_not be_empty
60
+ @project.reload.durations.should include(Duration.first)
61
+ end
63
62
  end
64
63
 
65
64
  describe "TimeEntries count" do
@@ -161,5 +160,135 @@ module Server
161
160
  Project.worked_on.should_not include(@projects.last)
162
161
  end
163
162
  end
163
+
164
+ describe "Methods" do
165
+ describe "#elapsed_time" do
166
+ before(:each) do
167
+ @projects = []
168
+ 2.times do
169
+ @projects << FactoryGirl.create(:project)
170
+ end
171
+
172
+ @projects.each do |p|
173
+ 2.times do
174
+ f = FactoryGirl.create(:file, project: p)
175
+ 2.times do
176
+ FactoryGirl.create :time_entry, file: f
177
+ end
178
+ end
179
+ end
180
+ end
181
+
182
+ it { should respond_to :elapsed_time }
183
+
184
+ it "should have the right elapsed_time" do
185
+ @projects.first.elapsed_time.should == 4
186
+ end
187
+ end
188
+
189
+
190
+ describe "#sum_elapsed_time" do
191
+ before(:each) do
192
+ @projects = []
193
+ 2.times do
194
+ @projects << FactoryGirl.create(:project)
195
+ end
196
+
197
+ @projects.each do |p|
198
+ 2.times do
199
+ f = FactoryGirl.create(:file, project: p)
200
+ 2.times do
201
+ FactoryGirl.create :time_entry, file: f
202
+ end
203
+ end
204
+ end
205
+ end
206
+
207
+ it "should respond_to sum_elapsed_time" do
208
+ Project.should respond_to(:sum_elapsed_time)
209
+ end
210
+
211
+ it "should return the sum of all elapsed times of all projects" do
212
+ Project.sum_elapsed_time.should == @projects.inject(0) {|s, p| s += p.elapsed_time }
213
+ end
214
+ end
215
+
216
+ describe "#date_range" do
217
+ before(:each) do
218
+ @tw_projects = []
219
+ @lw_projects = []
220
+ # Freeze the time to this week
221
+ Timecop.freeze(Time.now)
222
+ # Create a bunch of projects
223
+ 2.times do
224
+ @tw_projects << FactoryGirl.create(:project)
225
+ 2.times do
226
+ f = FactoryGirl.create(:file, project: @tw_projects.last)
227
+ 2.times do |n|
228
+ FactoryGirl.create(:time_entry, file: f)
229
+ end
230
+ end
231
+ end
232
+ # Freeze the time to last week
233
+ Timecop.freeze(Time.now - 7.days)
234
+ # Create a bunch of projects
235
+ 2.times do
236
+ @lw_projects << FactoryGirl.create(:project)
237
+ 2.times do
238
+ f = FactoryGirl.create(:file, project: @lw_projects.last)
239
+ 2.times do
240
+ FactoryGirl.create(:time_entry, file: f)
241
+ end
242
+ end
243
+ end
244
+ # Return the frozen time to now
245
+ Timecop.freeze(Time.now + 7.days)
246
+ end
247
+
248
+ subject { Project }
249
+
250
+ it { should respond_to :date_range }
251
+
252
+ it "should return an active relation object" do
253
+ from = (Time.now - 6.days).strftime '%m/%d/%Y'
254
+ to = Time.now.strftime '%m/%d/%Y'
255
+
256
+ q = subject.date_range from, to
257
+ q.should be_instance_of ActiveRecord::Relation
258
+ end
259
+
260
+ it "should not return project's of last week" do
261
+ from = (Time.now - 6.days).strftime '%m/%d/%Y'
262
+ to = Time.now.strftime '%m/%d/%Y'
263
+
264
+ q = subject.date_range from, to
265
+ @lw_projects.each {|p| q.should_not include(p)}
266
+ end
267
+ end
268
+
269
+ describe "#percent" do
270
+ before(:each) do
271
+ @projects = []
272
+ 2.times do
273
+ @projects << FactoryGirl.create(:project)
274
+ end
275
+
276
+ @projects.each do |p|
277
+ 2.times do
278
+ f = FactoryGirl.create(:file, project: p)
279
+ 2.times do
280
+ FactoryGirl.create :time_entry, file: f
281
+ end
282
+ end
283
+ end
284
+ end
285
+
286
+ it { should respond_to :percent }
287
+
288
+ it "should return 50" do
289
+ @projects.first.percent.should == 50
290
+ end
291
+ end
292
+ end
164
293
  end
165
294
  end
@@ -19,6 +19,16 @@ module Server
19
19
  m.should_not be_valid
20
20
  end
21
21
 
22
+ it "should require an editor_name" do
23
+ m = FactoryGirl.build :time_entry, editor_name: nil
24
+ m.should_not be_valid
25
+ end
26
+
27
+ it "should require an editor_version" do
28
+ m = FactoryGirl.build :time_entry, editor_version: nil
29
+ m.should_not be_valid
30
+ end
31
+
22
32
  it "should be valid if attributes requirements are met" do
23
33
  t = FactoryGirl.build :time_entry
24
34
  t.should be_valid
@@ -1,21 +1,18 @@
1
1
  require 'spec_helper'
2
2
 
3
3
  module Server
4
- module Decorator
5
- describe ProjectDecorator do
6
- before(:each) do
7
- @project = FactoryGirl.create :project
8
- end
4
+ module Presenters
5
+ describe ApplicationPresenter do
9
6
 
10
- subject { ProjectDecorator.find(@project.id) }
7
+ describe "Instance Methods" do
8
+ before(:each) do
9
+ @model = mock
10
+ @model.stubs(:id).returns(1)
11
+ @model.stubs(:elapsed_time).returns(1234)
12
+ end
11
13
 
12
- it "should return a formatted elapsed time" do
13
- time = 1.day + 2.hours + 3.minutes + 34.seconds
14
- subject.stubs(:elapsed_time).returns(time)
15
- subject.elapsed.should == '1 day, 2 hours, 3 minutes and 34 seconds'
16
- end
14
+ subject { ApplicationPresenter.new(@model, nil) }
17
15
 
18
- describe "Application Decorator" do
19
16
  describe "#humanize_time" do
20
17
  it "should return 1 second" do
21
18
  subject.send(:humanize_time, 1).should == '1 second'
@@ -55,6 +52,20 @@ module Server
55
52
  end
56
53
  end
57
54
  end
55
+
56
+ describe "Class Methods" do
57
+ subject { ApplicationPresenter }
58
+
59
+ describe "#presents" do
60
+ it { should respond_to :presents }
61
+
62
+ it "should create a method the same name as the argument passed to presents" do
63
+ Class.new(subject) do
64
+ presents :foo
65
+ end.new(@project, nil).should respond_to :foo
66
+ end
67
+ end
68
+ end
58
69
  end
59
70
  end
60
- end
71
+ end
@@ -0,0 +1,8 @@
1
+ require 'spec_helper'
2
+
3
+ module Server
4
+ module Presenters
5
+ describe FilePresenter do
6
+ end
7
+ end
8
+ end