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.
- data/.todo +3 -0
- data/.travis.yml +4 -0
- data/Gemfile +0 -4
- data/README.md +48 -2
- data/TODO +3 -0
- data/ci/travis.rb +4 -1
- data/lib/watch_tower/appscript.rb +65 -7
- data/lib/watch_tower/cli/install.rb +27 -3
- data/lib/watch_tower/cli/open.rb +2 -0
- data/lib/watch_tower/cli/start.rb +3 -1
- data/lib/watch_tower/cli.rb +2 -0
- data/lib/watch_tower/config.rb +2 -0
- data/lib/watch_tower/core_ext.rb +2 -0
- data/lib/watch_tower/editor/base_appscript.rb +28 -1
- data/lib/watch_tower/editor/base_ps.rb +25 -0
- data/lib/watch_tower/editor/textmate.rb +2 -0
- data/lib/watch_tower/editor/xcode.rb +2 -0
- data/lib/watch_tower/editor.rb +2 -0
- data/lib/watch_tower/errors.rb +2 -0
- data/lib/watch_tower/eye.rb +13 -2
- data/lib/watch_tower/file_tree.rb +78 -0
- data/lib/watch_tower/project/any_based.rb +2 -0
- data/lib/watch_tower/project/git_based.rb +2 -0
- data/lib/watch_tower/project/init.rb +2 -0
- data/lib/watch_tower/project/path_based.rb +2 -0
- data/lib/watch_tower/project.rb +2 -0
- data/lib/watch_tower/server/app.rb +37 -7
- data/lib/watch_tower/server/assets/javascripts/application.js +4 -0
- data/lib/watch_tower/server/assets/javascripts/datepicker.coffee +21 -0
- data/lib/watch_tower/server/assets/javascripts/file_tree.coffee +30 -0
- data/lib/watch_tower/server/assets/javascripts/percentage.coffee +15 -5
- data/lib/watch_tower/server/assets/stylesheets/application.css +1 -0
- data/lib/watch_tower/server/assets/stylesheets/date.sass +17 -0
- data/lib/watch_tower/server/assets/stylesheets/file_tree.sass +42 -0
- data/lib/watch_tower/server/assets/stylesheets/global.sass +7 -4
- data/lib/watch_tower/server/configurations/asset.rb +2 -0
- data/lib/watch_tower/server/configurations.rb +2 -0
- data/lib/watch_tower/server/database.rb +2 -0
- data/lib/watch_tower/server/db/migrate/001_create_projects.rb +2 -0
- data/lib/watch_tower/server/db/migrate/002_create_files.rb +2 -0
- data/lib/watch_tower/server/db/migrate/003_create_time_entries.rb +2 -0
- data/lib/watch_tower/server/db/migrate/004_create_durations.rb +2 -0
- data/lib/watch_tower/server/db/migrate/005_add_hash_to_time_entries.rb +2 -0
- data/lib/watch_tower/server/db/migrate/006_add_hash_to_files.rb +2 -0
- data/lib/watch_tower/server/db/migrate/007_add_editor_to_times_entries.rb +8 -0
- data/lib/watch_tower/server/db/migrate/008_rename_editor_to_editor_name_in_times_entries.rb +5 -0
- data/lib/watch_tower/server/db/migrate/009_remove_editor_index_from_time_entries.rb +7 -0
- data/lib/watch_tower/server/db/migrate/010_add_editor_version_to_times_entries.rb +7 -0
- data/lib/watch_tower/server/helpers/asset.rb +2 -0
- data/lib/watch_tower/server/helpers/improved_partials.rb +2 -0
- data/lib/watch_tower/server/helpers/presenters.rb +33 -0
- data/lib/watch_tower/server/helpers.rb +3 -0
- data/lib/watch_tower/server/models/duration.rb +9 -0
- data/lib/watch_tower/server/models/file.rb +17 -0
- data/lib/watch_tower/server/models/project.rb +30 -0
- data/lib/watch_tower/server/models/time_entry.rb +5 -0
- data/lib/watch_tower/server/presenters/application_presenter.rb +165 -0
- data/lib/watch_tower/server/presenters/file_presenter.rb +10 -0
- data/lib/watch_tower/server/presenters/project_presenter.rb +20 -0
- data/lib/watch_tower/server/presenters.rb +13 -0
- data/lib/watch_tower/server/public/assets/{WatchTower-4d6de11e1bd34165ad91ac46fb711bf3.jpg → WatchTower-58eff0713efffbc6054defddc879e0b1.jpg} +0 -0
- data/lib/watch_tower/server/public/assets/application-4e6971066e06aa53b0c8e52c764044d1.css +389 -0
- data/lib/watch_tower/server/public/assets/application-6a1be75d4fd6a545faceb638e47a2486.js +23778 -0
- data/lib/watch_tower/server/public/assets/calendar-379834cd6e6321a940b662ace47f3032.gif +0 -0
- data/lib/watch_tower/server/public/assets/calendar-blue-d6aa74feef7ee4287532761db99a6c0a.gif +0 -0
- data/lib/watch_tower/server/public/assets/calendar-green-3752fe2996091379c8d321f759039385.gif +0 -0
- data/lib/watch_tower/server/public/assets/jquery.datepick-9c8dfe3a4d40bcafc7b182e194c13836.css +227 -0
- data/lib/watch_tower/server/public/assets/{percentage-d8589e21a5fc85d32a445f531ff8ab95.png → percentage-d0176e99520c95e93eee63738ef5d487.png} +0 -0
- data/lib/watch_tower/server/vendor/assets/images/calendar-blue.gif +0 -0
- data/lib/watch_tower/server/vendor/assets/images/calendar-green.gif +0 -0
- data/lib/watch_tower/server/vendor/assets/images/calendar.gif +0 -0
- data/lib/watch_tower/server/vendor/assets/javascripts/jquery-datepick-ext.js +266 -0
- data/lib/watch_tower/server/vendor/assets/javascripts/jquery-datepick-validation.js +232 -0
- data/lib/watch_tower/server/vendor/assets/javascripts/jquery-datepick.js +2092 -0
- data/lib/watch_tower/server/vendor/assets/stylesheets/jquery.datepick.css +226 -0
- data/lib/watch_tower/server/views/_project.haml +10 -13
- data/lib/watch_tower/server/views/index.haml +9 -6
- data/lib/watch_tower/server/views/layout.haml +7 -4
- data/lib/watch_tower/server/views/project.haml +9 -11
- data/lib/watch_tower/server.rb +8 -1
- data/lib/watch_tower/templates/{watchtower.plist → watchtower.plist.erb} +5 -6
- data/lib/watch_tower/version.rb +12 -3
- data/lib/watch_tower.rb +20 -1
- data/spec/factories.rb +2 -0
- data/spec/watch_tower/appscript_spec.rb +36 -2
- data/spec/watch_tower/editor/base_appscript_spec.rb +83 -0
- data/spec/watch_tower/editor/textmate_spec.rb +37 -0
- data/spec/watch_tower/editor/xcode_spec.rb +6 -0
- data/spec/watch_tower/eye_spec.rb +21 -0
- data/spec/watch_tower/file_tree_spec.rb +156 -0
- data/spec/watch_tower/server/app_spec.rb +64 -20
- data/spec/watch_tower/server/models/file_spec.rb +93 -31
- data/spec/watch_tower/server/models/project_spec.rb +147 -18
- data/spec/watch_tower/server/models/time_entry_spec.rb +10 -0
- data/spec/watch_tower/server/{decorator/project_decorator_spec.rb → presenters/application_presenter_spec.rb} +24 -13
- data/spec/watch_tower/server/presenters/file_presenter_spec.rb +8 -0
- data/spec/watch_tower/server/presenters/project_presenter_spec.rb +130 -0
- data/watch_tower.gemspec +10 -4
- metadata +114 -74
- data/lib/watch_tower/server/decorator/application_decorator.rb +0 -91
- data/lib/watch_tower/server/decorator/file_decorator.rb +0 -38
- data/lib/watch_tower/server/decorator/project_decorator.rb +0 -51
- data/lib/watch_tower/server/decorator.rb +0 -21
- data/lib/watch_tower/server/public/assets/application-7829b53b5ece1a16d22dc3d00f329023.css +0 -107
- data/lib/watch_tower/server/public/assets/application-e0e6b7731aade460f680331e65cf0682.js +0 -9359
- 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
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
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
|
-
|
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
|
150
|
-
within 'article#project section#files
|
151
|
-
page.should have_selector '
|
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
|
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
|
162
|
-
within 'article#project section#files
|
163
|
-
page.should have_selector '
|
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
|
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 "
|
247
|
-
|
248
|
-
|
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
|
-
|
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
|
-
|
261
|
-
|
262
|
-
|
263
|
-
|
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
|
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
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
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
|
5
|
-
describe
|
6
|
-
before(:each) do
|
7
|
-
@project = FactoryGirl.create :project
|
8
|
-
end
|
4
|
+
module Presenters
|
5
|
+
describe ApplicationPresenter do
|
9
6
|
|
10
|
-
|
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
|
-
|
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
|