watch_tower 0.0.0.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (115) hide show
  1. data/.gitignore +10 -0
  2. data/.todo +33 -0
  3. data/.travis.yml +14 -0
  4. data/Gemfile +43 -0
  5. data/Guardfile +25 -0
  6. data/MIT-LICENSE +20 -0
  7. data/README.md +38 -0
  8. data/Rakefile +8 -0
  9. data/TODO +17 -0
  10. data/bin/watchtower +10 -0
  11. data/ci/adapters/jruby-mysql.yml +8 -0
  12. data/ci/adapters/jruby-postgresql.yml +6 -0
  13. data/ci/adapters/jruby-sqlite.yml +6 -0
  14. data/ci/adapters/ruby-mysql.yml +8 -0
  15. data/ci/adapters/ruby-postgresql.yml +6 -0
  16. data/ci/adapters/ruby-sqlite.yml +6 -0
  17. data/ci/travis.rb +102 -0
  18. data/lib/watch_tower.rb +60 -0
  19. data/lib/watch_tower/appscript.rb +22 -0
  20. data/lib/watch_tower/cli.rb +15 -0
  21. data/lib/watch_tower/cli/.gitkeep +0 -0
  22. data/lib/watch_tower/cli/install.rb +63 -0
  23. data/lib/watch_tower/cli/open.rb +24 -0
  24. data/lib/watch_tower/cli/start.rb +140 -0
  25. data/lib/watch_tower/config.rb +38 -0
  26. data/lib/watch_tower/core_ext.rb +4 -0
  27. data/lib/watch_tower/core_ext/.gitkeep +0 -0
  28. data/lib/watch_tower/editor.rb +17 -0
  29. data/lib/watch_tower/editor/.gitkeep +0 -0
  30. data/lib/watch_tower/editor/base_appscript.rb +34 -0
  31. data/lib/watch_tower/editor/base_ps.rb +6 -0
  32. data/lib/watch_tower/editor/textmate.rb +17 -0
  33. data/lib/watch_tower/editor/xcode.rb +22 -0
  34. data/lib/watch_tower/errors.rb +25 -0
  35. data/lib/watch_tower/eye.rb +79 -0
  36. data/lib/watch_tower/project.rb +14 -0
  37. data/lib/watch_tower/project/.gitkeep +0 -0
  38. data/lib/watch_tower/project/any_based.rb +22 -0
  39. data/lib/watch_tower/project/git_based.rb +86 -0
  40. data/lib/watch_tower/project/init.rb +38 -0
  41. data/lib/watch_tower/project/path_based.rb +144 -0
  42. data/lib/watch_tower/server.rb +62 -0
  43. data/lib/watch_tower/server/.gitkeep +0 -0
  44. data/lib/watch_tower/server/app.rb +37 -0
  45. data/lib/watch_tower/server/assets/images/WatchTower.jpg +0 -0
  46. data/lib/watch_tower/server/assets/images/percentage.png +0 -0
  47. data/lib/watch_tower/server/assets/javascripts/application.js +3 -0
  48. data/lib/watch_tower/server/assets/javascripts/percentage.coffee +8 -0
  49. data/lib/watch_tower/server/assets/stylesheets/application.css +7 -0
  50. data/lib/watch_tower/server/assets/stylesheets/global.sass +71 -0
  51. data/lib/watch_tower/server/assets/stylesheets/project.sass +59 -0
  52. data/lib/watch_tower/server/configurations.rb +10 -0
  53. data/lib/watch_tower/server/configurations/asset.rb +43 -0
  54. data/lib/watch_tower/server/database.rb +105 -0
  55. data/lib/watch_tower/server/db/migrate/001_create_projects.rb +15 -0
  56. data/lib/watch_tower/server/db/migrate/002_create_files.rb +16 -0
  57. data/lib/watch_tower/server/db/migrate/003_create_time_entries.rb +12 -0
  58. data/lib/watch_tower/server/db/migrate/004_create_durations.rb +14 -0
  59. data/lib/watch_tower/server/db/migrate/005_add_hash_to_time_entries.rb +6 -0
  60. data/lib/watch_tower/server/db/migrate/006_add_hash_to_files.rb +6 -0
  61. data/lib/watch_tower/server/decorator.rb +21 -0
  62. data/lib/watch_tower/server/decorator/application_decorator.rb +91 -0
  63. data/lib/watch_tower/server/decorator/file_decorator.rb +38 -0
  64. data/lib/watch_tower/server/decorator/project_decorator.rb +51 -0
  65. data/lib/watch_tower/server/helpers.rb +13 -0
  66. data/lib/watch_tower/server/helpers/asset.rb +29 -0
  67. data/lib/watch_tower/server/helpers/improved_partials.rb +41 -0
  68. data/lib/watch_tower/server/models/duration.rb +11 -0
  69. data/lib/watch_tower/server/models/file.rb +31 -0
  70. data/lib/watch_tower/server/models/project.rb +17 -0
  71. data/lib/watch_tower/server/models/time_entry.rb +64 -0
  72. data/lib/watch_tower/server/public/assets/WatchTower-4d6de11e1bd34165ad91ac46fb711bf3.jpg +0 -0
  73. data/lib/watch_tower/server/public/assets/application-7829b53b5ece1a16d22dc3d00f329023.css +107 -0
  74. data/lib/watch_tower/server/public/assets/application-e0e6b7731aade460f680331e65cf0682.js +9359 -0
  75. data/lib/watch_tower/server/public/assets/percentage-d8589e21a5fc85d32a445f531ff8ab95.png +0 -0
  76. data/lib/watch_tower/server/vendor/assets/javascripts/jquery-ui.js +11729 -0
  77. data/lib/watch_tower/server/vendor/assets/javascripts/jquery.js +8981 -0
  78. data/lib/watch_tower/server/vendor/assets/javascripts/jquery_ujs.js +363 -0
  79. data/lib/watch_tower/server/views/.gitkeep +0 -0
  80. data/lib/watch_tower/server/views/_file.haml +9 -0
  81. data/lib/watch_tower/server/views/_project.haml +13 -0
  82. data/lib/watch_tower/server/views/index.haml +7 -0
  83. data/lib/watch_tower/server/views/layout.haml +32 -0
  84. data/lib/watch_tower/server/views/project.haml +12 -0
  85. data/lib/watch_tower/templates/config.yml +146 -0
  86. data/lib/watch_tower/templates/watchtower.plist +23 -0
  87. data/lib/watch_tower/version.rb +8 -0
  88. data/spec/factories.rb +45 -0
  89. data/spec/spec_helper.rb +26 -0
  90. data/spec/support/active_record.rb +44 -0
  91. data/spec/support/factory_girl.rb +6 -0
  92. data/spec/support/launchy.rb +3 -0
  93. data/spec/support/sinatra.rb +10 -0
  94. data/spec/support/timecop.rb +7 -0
  95. data/spec/watch_tower/appscript_spec.rb +6 -0
  96. data/spec/watch_tower/cli/install_spec.rb +16 -0
  97. data/spec/watch_tower/cli/open_spec.rb +14 -0
  98. data/spec/watch_tower/cli/start_spec.rb +17 -0
  99. data/spec/watch_tower/cli_spec.rb +15 -0
  100. data/spec/watch_tower/config_spec.rb +25 -0
  101. data/spec/watch_tower/editor/textmate_spec.rb +43 -0
  102. data/spec/watch_tower/editor/xcode_spec.rb +43 -0
  103. data/spec/watch_tower/editor_spec.rb +19 -0
  104. data/spec/watch_tower/eye_spec.rb +130 -0
  105. data/spec/watch_tower/project/git_based_spec.rb +131 -0
  106. data/spec/watch_tower/project/path_based_spec.rb +111 -0
  107. data/spec/watch_tower/project_spec.rb +82 -0
  108. data/spec/watch_tower/server/app_spec.rb +186 -0
  109. data/spec/watch_tower/server/decorator/project_decorator_spec.rb +60 -0
  110. data/spec/watch_tower/server/models/file_spec.rb +284 -0
  111. data/spec/watch_tower/server/models/project_spec.rb +165 -0
  112. data/spec/watch_tower/server/models/time_entry_spec.rb +37 -0
  113. data/spec/watch_tower/server_spec.rb +4 -0
  114. data/watch_tower.gemspec +80 -0
  115. metadata +450 -0
@@ -0,0 +1,131 @@
1
+ require 'spec_helper'
2
+
3
+ class Project
4
+ describe GitBased do
5
+ before(:all) do
6
+ # Arguments
7
+ @code = '/home/user/Code'
8
+ @file_path = '/home/user/Code/OpenSource/watch_tower/lib/watch_tower/server/models/time_entries.rb'
9
+
10
+ # Expected results
11
+ @project_path = '/home/user/Code/OpenSource/watch_tower'
12
+ @project_git_folder_path = @project_path + '/.git'
13
+ @project_name = 'watch_tower'
14
+ end
15
+
16
+ before(:each) do
17
+ # Project.stubs(:expand_path).returns
18
+ end
19
+
20
+ describe "#project_git_folder_path" do
21
+ it "should respond to :project_git_folder_path" do
22
+ -> { subject.send(:project_git_folder_path) }.should_not raise_error NoMethodError
23
+ end
24
+
25
+ it "should return a path if exists" do
26
+ @file_path.split('/').each_index do |i|
27
+ path = @file_path.split('/')[0..i].join('/') + '/.git'
28
+ if @project_git_folder_path == path
29
+ File.stubs(:exists?).with(path).returns(true)
30
+ else
31
+ File.stubs(:exists?).with(path).returns(false)
32
+ end
33
+ end
34
+
35
+ subject.send(:project_git_folder_path, @file_path).should == @project_git_folder_path
36
+ end
37
+
38
+ it "should return nil if path does not exist" do
39
+ file_path = @file_path.gsub(%r{#{@code}}, '/some/other/path')
40
+ file_path.split('/').each_index do |i|
41
+ path = file_path.split('/')[0..i].join('/')
42
+ File.stubs(:exists?).with(path + '/.git').returns(false)
43
+ end
44
+
45
+ subject.send(:project_git_folder_path, file_path).should be_nil
46
+ end
47
+
48
+ it "should cache it" do
49
+ File.expects(:exists?).never
50
+
51
+ subject.send(:project_git_folder_path, @file_path).should == @project_git_folder_path
52
+ end
53
+ end
54
+
55
+
56
+ describe "#active_for_path?" do
57
+ it { should respond_to(:active_for_path?) }
58
+
59
+ it "should be able to determine if a path is git-ized" do
60
+ GitBased.expects(:project_git_folder_path).returns(@project_path)
61
+
62
+ subject.active_for_path?(@file_path).should be_true
63
+ end
64
+
65
+ it "should be able to determine if a path is not git-ized" do
66
+ GitBased.expects(:project_git_folder_path).returns(nil)
67
+
68
+ subject.active_for_path?(@file_path).should be_false
69
+ end
70
+ end
71
+
72
+ describe "#working_directory" do
73
+ it { should respond_to(:working_directory) }
74
+
75
+ it "should return the working directory of a path" do
76
+ GitBased.stubs(:project_git_folder_path).returns(@project_git_folder_path)
77
+
78
+ subject.working_directory(@file_path).should == @project_path
79
+ end
80
+
81
+ it "should cache it" do
82
+ GitBased.expects(:project_git_folder_path).never
83
+
84
+ subject.working_directory(@file_path).should == @project_path
85
+ end
86
+ end
87
+
88
+ describe "#project_name" do
89
+ it { should respond_to(:project_name) }
90
+
91
+ it "should return the working directory of a path" do
92
+ GitBased.stubs(:project_git_folder_path).returns(@project_git_folder_path)
93
+
94
+ subject.project_name(@file_path).should == @project_name
95
+ end
96
+
97
+ it "should cache it" do
98
+ GitBased.expects(:project_git_folder_path).never
99
+
100
+ subject.project_name(@file_path).should == @project_name
101
+ end
102
+ end
103
+
104
+ describe "#head" do
105
+ before(:each) do
106
+ GitBased.stubs(:active_for_path?).returns(true)
107
+ GitBased.stubs(:working_directory).returns(@project_path)
108
+ end
109
+
110
+ it { should respond_to :head }
111
+
112
+ it "should create a Git::Base object" do
113
+ commit = mock
114
+ git_base = mock
115
+ git_base.stubs(:log).returns([commit])
116
+ ::Git.expects(:open).with(@project_path).returns(git_base).once
117
+
118
+ subject.head(@project_path)
119
+ end
120
+
121
+ it "should return the head revision" do
122
+ commit = mock
123
+ git_base = mock
124
+ git_base.stubs(:log).returns([commit])
125
+ ::Git.stubs(:open).with(@project_path).returns(git_base)
126
+
127
+ subject.head(@project_path).should == commit
128
+ end
129
+ end
130
+ end
131
+ end
@@ -0,0 +1,111 @@
1
+ require 'spec_helper'
2
+
3
+ class Project
4
+ describe PathBased do
5
+ before(:all) do
6
+ # Arguments
7
+ @code = '/home/user/Code'
8
+ @file_path = '/home/user/Code/OpenSource/watch_tower/lib/watch_tower/server/models/time_entries.rb'
9
+ @nested_project_layers = 2
10
+ @args = [@code, @file_path, @nested_project_layers]
11
+ @options = {
12
+ code: @code,
13
+ nested_project_layers: @nested_project_layers
14
+ }
15
+
16
+ # Expected results
17
+ @project_path = '/home/user/Code/OpenSource/watch_tower'
18
+ @project_name = 'watch_tower'
19
+ @project_path_parts = ['/home/user/Code', 'OpenSource', 'watch_tower']
20
+ end
21
+
22
+ before(:each) do
23
+ PathBased.stubs(:code_path).returns(@code)
24
+ PathBased.stubs(:code_path).returns(@code)
25
+ end
26
+
27
+ describe "#project_path_part" do
28
+ it "should respond to #project_path_part" do
29
+ -> { subject.send :project_path_part, *@args }.should_not raise_error NoMethodError
30
+ end
31
+
32
+ it "should be able to return the project name from the path Given the correct path" do
33
+ subject.send(:project_path_part, *@args).should == @project_path_parts
34
+ end
35
+
36
+ it "should raises PathNotUnderCodePath if the path is not nested under code" do
37
+ file_path = @file_path.gsub(%r{#{@code}}, '/some/other/path')
38
+ -> { subject.send(:project_path_part, @code, file_path, @nested_project_layers) }.should raise_error PathNotUnderCodePath
39
+ end
40
+
41
+ it "should cache the path it" do
42
+ file_path = @file_path
43
+ file_path.expects(:scan).never
44
+
45
+ subject.send(:project_path_part, @code, file_path, @nested_project_layers).should == @project_path_parts
46
+ end
47
+ end
48
+
49
+ describe "#project_name_from_nested_path" do
50
+ it "should respond to #project_name_from_nested_path" do
51
+ -> { subject.send :project_name_from_nested_path, *@args }.should_not raise_error NoMethodError
52
+ end
53
+
54
+ it "should be able to return the project path parts from the path Given the correct path" do
55
+ subject.send(:project_name_from_nested_path, *@args).should == @project_name
56
+ end
57
+
58
+ it "should raises PathNotUnderCodePath if the path is not nested under code" do
59
+ file_path = @file_path.gsub(%r{#{@code}}, '/some/other/path')
60
+ -> { subject.send(:project_name_from_nested_path, @code, file_path, @nested_project_layers) }.should raise_error PathNotUnderCodePath
61
+ end
62
+ end
63
+
64
+ describe "#project_path_from_nested_path" do
65
+ it "should respond to #project_path_from_nested_path" do
66
+ -> { subject.send :project_path_from_nested_path, *@args }.should_not raise_error NoMethodError
67
+ end
68
+
69
+ it "should be able to return the project name from the path Given the correct path" do
70
+ subject.send(:project_path_from_nested_path, *@args).should == @project_path
71
+ end
72
+
73
+ it "should raises PathNotUnderCodePath if the path is not nested under code" do
74
+ file_path = @file_path.gsub(%r{#{@code}}, '/some/other/path')
75
+ -> { subject.send(:project_path_from_nested_path, @code, file_path, @nested_project_layers) }.should raise_error PathNotUnderCodePath
76
+ end
77
+ end
78
+
79
+ describe "#working_directory" do
80
+ it { should respond_to :working_directory }
81
+
82
+ it "should return the project path from nested path of the given path" do
83
+ PathBased.expects(:project_path_from_nested_path).with(*@args).returns(@project_path).once
84
+
85
+ subject.working_directory(@file_path, @options).should == @project_path
86
+ end
87
+
88
+ it "should cache the path it" do
89
+ PathBased.expects(:project_path_from_nested_path).with(*@args).returns(@project_path).never
90
+
91
+ subject.working_directory(@file_path, @options).should == @project_path
92
+ end
93
+ end
94
+
95
+ describe "#project_name" do
96
+ it { should respond_to :project_name }
97
+
98
+ it "should return the project path from nested path of the given path" do
99
+ PathBased.expects(:project_name_from_nested_path).with(*@args).returns(@project_name).once
100
+
101
+ subject.project_name(@file_path, @options).should == @project_name
102
+ end
103
+
104
+ it "should cache the path it" do
105
+ PathBased.expects(:project_name_from_nested_path).with(*@args).returns(@project_name).never
106
+
107
+ subject.project_name(@file_path, @options).should == @project_name
108
+ end
109
+ end
110
+ end
111
+ end
@@ -0,0 +1,82 @@
1
+ require 'spec_helper'
2
+
3
+ describe Project do
4
+ describe "Class Methods" do
5
+ subject { Project }
6
+
7
+ before(:all) do
8
+ # Arguments
9
+ @code = '/home/user/Code'
10
+ @file_path = '/home/user/Code/OpenSource/watch_tower/lib/watch_tower/server/models/time_entries.rb'
11
+
12
+ # Expected results
13
+ @project_path = '/home/user/Code/OpenSource/watch_tower'
14
+ @project_name = 'watch_tower'
15
+ end
16
+
17
+ before(:each) do
18
+ ::File.stubs(:exists?).returns(true)
19
+ end
20
+
21
+ describe "#new_from_path" do
22
+
23
+ it { should respond_to :new_from_path }
24
+
25
+ describe "path based" do
26
+ before(:each) do
27
+ Project::GitBased.expects(:active_for_path?).returns(false).once
28
+ Project::PathBased.expects(:working_directory).returns(@project_path).once
29
+ Project::PathBased.expects(:project_name).returns(@project_name).once
30
+ end
31
+
32
+ it "should create a project based off of git" do
33
+ p = subject.new_from_path(@file_path)
34
+ p.should be_instance_of subject
35
+ end
36
+
37
+ it "should return the name of the project" do
38
+ p = subject.new_from_path(@file_path)
39
+ p.name.should == @project_name
40
+ end
41
+
42
+ it "should return the path of the project" do
43
+ p = subject.new_from_path(@file_path)
44
+ p.path.should == @project_path
45
+ end
46
+ end
47
+
48
+ describe "git based" do
49
+ before(:each) do
50
+ Project::GitBased.expects(:active_for_path?).returns(true).once
51
+ Project::GitBased.expects(:working_directory).returns(@project_path).once
52
+ Project::GitBased.expects(:project_name).returns(@project_name).once
53
+ end
54
+
55
+ it "should create a project based off of git" do
56
+ p = subject.new_from_path(@file_path)
57
+ p.should be_instance_of subject
58
+ end
59
+
60
+ it "should return the name of the project" do
61
+ p = subject.new_from_path(@file_path)
62
+ p.name.should == @project_name
63
+ end
64
+
65
+ it "should return the path of the project" do
66
+ p = subject.new_from_path(@file_path)
67
+ p.path.should == @project_path
68
+ end
69
+ end
70
+ end
71
+ end
72
+
73
+ describe "Instance Methods" do
74
+ subject { Project.new @project_name, @project_path }
75
+
76
+ it { should respond_to :name }
77
+ it { should respond_to :path }
78
+
79
+ its(:name) { should == @project_name }
80
+ its(:path) { should == @project_path }
81
+ end
82
+ end
@@ -0,0 +1,186 @@
1
+ require 'spec_helper'
2
+
3
+ module Server
4
+ describe App do
5
+ before(:each) do
6
+ @projects = {
7
+ empty: {files: [], time_entries:[] },
8
+ not_empty: {files: [], time_entries: []}
9
+ }
10
+
11
+ @projects[:not_empty][:project] = FactoryGirl.create(:project)
12
+ 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)
17
+ end
18
+ @projects[:not_empty][:duration] = FactoryGirl.create(:duration, file: @projects[:not_empty][:files].first)
19
+
20
+ @projects[:empty][:project] = FactoryGirl.create(:project)
21
+ 2.times do
22
+ @projects[:empty][:files] << FactoryGirl.create(:file, project: @projects[:empty][:project])
23
+ end
24
+ @projects[:empty][:time_entries] << FactoryGirl.create(:time_entry, file: @projects[:empty][:files].first)
25
+ @projects[:empty][:duration] = FactoryGirl.create(:duration, file: @projects[:empty][:files].first)
26
+ end
27
+
28
+ describe "#index" do
29
+ before(:each) do
30
+ visit '/'
31
+ end
32
+
33
+ it "should render the layout" do
34
+ page.should have_selector :xpath, '//html/head/title'
35
+ end
36
+
37
+ it "should have the correct title" do
38
+ within :xpath, '//html/head/title' do
39
+ page.should have_content "Watch Tower - Projects"
40
+ end
41
+ end
42
+
43
+ it "should have a projects section" do
44
+ within :xpath, '//html/body' do
45
+ page.should have_selector 'section#projects'
46
+ end
47
+ end
48
+
49
+ it "should display each project as an article" do
50
+ within 'section#projects' do
51
+ page.should have_selector '.project'
52
+ end
53
+ end
54
+
55
+ it "should display have a section for the project's name" do
56
+ within 'section#projects' do
57
+ page.should have_selector '.project > .name'
58
+ end
59
+ end
60
+
61
+ it "should display the name of each project" do
62
+ within 'section#projects .project > .name' do
63
+ page.should have_content @projects[:not_empty][:project].name.camelcase
64
+ end
65
+ end
66
+
67
+ it "should not show the project with an empty time entries" do
68
+ within 'section#projects' do
69
+ page.should_not have_content @projects[:empty][:project].name.camelcase
70
+ end
71
+ end
72
+
73
+ it "should display the elapsed time of each project" do
74
+ within 'section#projects' do
75
+ page.should have_selector '.project > .elapsed'
76
+ end
77
+ end
78
+
79
+ it "should display an image under each project's name to categorize percentage" do
80
+ within 'section#projects' do
81
+ page.should have_selector '.project > .percentage_img_container > .percentage > img'
82
+ end
83
+ end
84
+
85
+ it "should have link on each project's name" do
86
+ within 'section#projects .project .name' do
87
+ page.should have_selector('a')
88
+ end
89
+ end
90
+
91
+ it "should link to the project's page" do
92
+ within 'section#projects .project .name' do
93
+ page.should have_selector('a', href: "/project/#{@projects[:not_empty][:project].id}")
94
+ end
95
+ end
96
+ end
97
+
98
+ describe "#project" do
99
+ before(:each) do
100
+ visit "/project/#{@projects[:not_empty][:project].id}"
101
+ end
102
+
103
+ it "should render the layout" do
104
+ page.should have_selector :xpath, '//html/head/title'
105
+ end
106
+
107
+ it "should have the correct title" do
108
+ within :xpath, '//html/head/title' do
109
+ page.should have_content "Watch Tower - Project - #{@projects[:not_empty][:project].name.camelcase}"
110
+ end
111
+ end
112
+
113
+ it "should have a projects section" do
114
+ within :xpath, '//html/body' do
115
+ page.should have_selector 'article#project'
116
+ end
117
+ end
118
+
119
+ it "should have the an h1 for the project's name" do
120
+ within 'article#project' do
121
+ page.should have_selector 'header > h1.project_name'
122
+ end
123
+ end
124
+
125
+ it "should have the project's name in an h1" do
126
+ within 'article#project header > h1.project_name' do
127
+ page.should have_content @projects[:not_empty][:project].name.camelcase
128
+ end
129
+ end
130
+
131
+ it "should have the an h2 for the project's path" do
132
+ within 'article#project' do
133
+ page.should have_selector 'header > h2.project_path'
134
+ end
135
+ end
136
+
137
+ it "should have the project's path in an h2" do
138
+ within 'article#project header > h2.project_path' do
139
+ page.should have_content @projects[:not_empty][:project].path
140
+ end
141
+ end
142
+
143
+ it "should have a section for the files" do
144
+ within 'article#project' do
145
+ page.should have_selector 'section#files'
146
+ end
147
+ end
148
+
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'
152
+ end
153
+ end
154
+
155
+ 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
158
+ end
159
+ end
160
+
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'
164
+ end
165
+ end
166
+
167
+ it "should display the file's elaped time" do
168
+ within 'article#project section#files article.file div.elapsed' do
169
+ page.should have_content @projects[:not_empty][:files].first.elapsed_time.to_s
170
+ end
171
+ end
172
+
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
+ it "should not display files having 0 seconds" do
180
+ within 'article#project section#files' do
181
+ page.should_not have_content @projects[:not_empty][:files].last.path
182
+ end
183
+ end
184
+ end
185
+ end
186
+ end