watch_tower 0.0.0.1 → 0.0.1.beta1

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.
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