gitdocs 0.5.0.pre6 → 0.5.0.pre7

Sign up to get free protection for your applications and to get access to all the features.
Files changed (61) hide show
  1. checksums.yaml +8 -8
  2. data/.gitignore +1 -0
  3. data/.haml-lint.yml +3 -0
  4. data/.jslint.yml +84 -0
  5. data/.rubocop.yml +13 -0
  6. data/CHANGELOG +11 -0
  7. data/README.md +6 -2
  8. data/Rakefile +22 -3
  9. data/gitdocs.gemspec +36 -29
  10. data/lib/gitdocs.rb +5 -2
  11. data/lib/gitdocs/cli.rb +31 -8
  12. data/lib/gitdocs/configuration.rb +95 -49
  13. data/lib/gitdocs/manager.rb +36 -28
  14. data/lib/gitdocs/migration/001_create_shares.rb +2 -0
  15. data/lib/gitdocs/migration/002_add_remote_branch.rb +2 -0
  16. data/lib/gitdocs/migration/003_create_configs.rb +2 -0
  17. data/lib/gitdocs/migration/004_add_index_for_path.rb +4 -0
  18. data/lib/gitdocs/migration/005_add_start_web_frontend.rb +2 -0
  19. data/lib/gitdocs/migration/006_add_web_port_to_config.rb +2 -0
  20. data/lib/gitdocs/migration/007_add_sync_type.rb +11 -0
  21. data/lib/gitdocs/notifier.rb +89 -6
  22. data/lib/gitdocs/public/img/file.png +0 -0
  23. data/lib/gitdocs/public/img/folder.png +0 -0
  24. data/lib/gitdocs/public/js/app.js +26 -11
  25. data/lib/gitdocs/public/js/edit.js +3 -3
  26. data/lib/gitdocs/public/js/settings.js +8 -5
  27. data/lib/gitdocs/public/js/util.js +21 -20
  28. data/lib/gitdocs/rendering.rb +14 -9
  29. data/lib/gitdocs/repository.rb +180 -216
  30. data/lib/gitdocs/repository/path.rb +166 -0
  31. data/lib/gitdocs/runner.rb +22 -65
  32. data/lib/gitdocs/search.rb +35 -0
  33. data/lib/gitdocs/server.rb +123 -86
  34. data/lib/gitdocs/version.rb +1 -1
  35. data/lib/gitdocs/views/_header.haml +6 -6
  36. data/lib/gitdocs/views/app.haml +17 -17
  37. data/lib/gitdocs/views/dir.haml +10 -10
  38. data/lib/gitdocs/views/edit.haml +8 -9
  39. data/lib/gitdocs/views/file.haml +1 -1
  40. data/lib/gitdocs/views/home.haml +4 -4
  41. data/lib/gitdocs/views/revisions.haml +6 -6
  42. data/lib/gitdocs/views/search.haml +6 -6
  43. data/lib/gitdocs/views/settings.haml +23 -16
  44. data/test/.rubocop.yml +13 -0
  45. data/test/integration/browse_test.rb +149 -0
  46. data/test/integration/full_sync_test.rb +3 -11
  47. data/test/integration/share_management_test.rb +59 -10
  48. data/test/integration/status_test.rb +2 -0
  49. data/test/integration/test_helper.rb +40 -7
  50. data/test/unit/configuration_test.rb +82 -0
  51. data/test/unit/notifier_test.rb +165 -0
  52. data/test/unit/repository_path_test.rb +368 -0
  53. data/test/{repository_test.rb → unit/repository_test.rb} +426 -245
  54. data/test/unit/runner_test.rb +122 -0
  55. data/test/unit/search_test.rb +52 -0
  56. data/test/{test_helper.rb → unit/test_helper.rb} +5 -0
  57. metadata +138 -41
  58. data/lib/gitdocs/docfile.rb +0 -23
  59. data/test/configuration_test.rb +0 -41
  60. data/test/notifier_test.rb +0 -68
  61. data/test/runner_test.rb +0 -123
@@ -1,19 +1,11 @@
1
+ # -*- encoding : utf-8 -*-
2
+
1
3
  require File.expand_path('../test_helper', __FILE__)
2
4
 
3
5
  describe 'fully synchronizing repositories' do
4
6
  before do
5
7
  git_clone_and_gitdocs_add(git_init_remote, 'clone1', 'clone2', 'clone3')
6
-
7
- # TODO: apply this configuration through the CLI in future
8
- configuration = Gitdocs::Configuration.new
9
- configuration.shares.each do |share|
10
- share.update_attributes(polling_interval: 0.1, notification: false)
11
- end
12
-
13
- start_cmd = 'gitdocs start --debug --pid=gitdocs.pid --port 7777'
14
- run(start_cmd, 15)
15
- assert_success(true)
16
- assert_partial_output('Started gitdocs', output_from(start_cmd))
8
+ start_daemon
17
9
  end
18
10
 
19
11
  it 'should sync new files' do
@@ -1,3 +1,5 @@
1
+ # -*- encoding : utf-8 -*-
2
+
1
3
  require File.expand_path('../test_helper', __FILE__)
2
4
 
3
5
  describe 'Manage which shares are being watched' do
@@ -14,24 +16,71 @@ describe 'Manage which shares are being watched' do
14
16
  cmd = "gitdocs create local #{abs_remote_path} --pid=gitdocs.pid"
15
17
  run_simple(cmd, true, 15)
16
18
  assert_success(true)
17
- assert_partial_output("Added path local to doc list", output_from(cmd))
19
+ assert_partial_output('Added path local to doc list', output_from(cmd))
18
20
  end
19
21
 
20
- it 'should remove a share' do
22
+ it 'should update a share through the UI' do
21
23
  git_init_local
22
24
  gitdocs_add
25
+ start_daemon
26
+ visit('http://localhost:7777/')
27
+ click_link('Settings')
23
28
 
24
- cmd = 'gitdocs rm local --pid=gitdocs.pid'
25
- run_simple(cmd, true, 15)
26
- assert_success(true)
27
- assert_partial_output("Removed path local from doc list", output_from(cmd))
28
-
29
- gitdocs_status
30
- assert_gitdocs_status_not_contain(abs_current_dir('local'))
29
+ within('#settings') do
30
+ within('#share-0') do
31
+ fill_in('share[0][polling_interval]', with: '0.2')
32
+ select('Fetch only', from: 'share[0][sync_type]')
33
+ end
34
+ click_button('Save')
35
+ end
36
+
37
+ # Allow the asynchronous portion of the update finish before checking
38
+ # the result.
39
+ sleep(1)
40
+
41
+ click_link('Settings')
42
+ within('#settings') do
43
+ within('#share-0') do
44
+ page.must_have_field('share[0][polling_interval]', with: '0.2')
45
+ page.must_have_field('share[0][sync_type]', with: 'fetch')
46
+ end
47
+ end
48
+ end
49
+
50
+ describe 'remove a share' do
51
+ before do
52
+ git_init_local
53
+ gitdocs_add
54
+ end
55
+
56
+ it 'through CLI' do
57
+ cmd = 'gitdocs rm local --pid=gitdocs.pid'
58
+ run_simple(cmd, true, 15)
59
+ assert_success(true)
60
+ assert_partial_output('Removed path local from doc list', output_from(cmd))
61
+
62
+ gitdocs_status
63
+ assert_gitdocs_status_not_contain(abs_current_dir('local'))
64
+ end
65
+
66
+ it 'through UI' do
67
+ start_daemon
68
+ visit('http://localhost:7777/')
69
+ click_link('Settings')
70
+
71
+ within('#settings') do
72
+ within('#share-0') { click_link('Delete') }
73
+
74
+ page.must_have_css('.share', count: 0)
75
+ end
76
+ end
31
77
  end
32
78
 
33
79
  it 'should clear all existing shares' do
34
- ['local1', 'local2', 'local3'].each { |x| git_init_local(x) ; gitdocs_add(x) }
80
+ %w(local1 local2 local3).each do |path|
81
+ git_init_local(path)
82
+ gitdocs_add(path)
83
+ end
35
84
 
36
85
  cmd = 'gitdocs clear --pid=gitdocs.pid'
37
86
  run_simple(cmd, true, 15)
@@ -1,3 +1,5 @@
1
+ # -*- encoding : utf-8 -*-
2
+
1
3
  require File.expand_path('../test_helper', __FILE__)
2
4
 
3
5
  describe 'CLI with display daemon and share status' do
@@ -1,3 +1,5 @@
1
+ # -*- encoding : utf-8 -*-
2
+
1
3
  require 'rubygems'
2
4
  require 'minitest/autorun'
3
5
  $LOAD_PATH.unshift File.expand_path('../../lib')
@@ -5,6 +7,18 @@ require 'gitdocs'
5
7
  require 'aruba'
6
8
  require 'aruba/api'
7
9
  require 'timeout'
10
+ require 'capybara'
11
+ require 'capybara_minitest_spec'
12
+ require 'capybara/poltergeist'
13
+
14
+ Capybara.app_host = 'http://localhost:7777/'
15
+ Capybara.default_driver = :poltergeist
16
+ Capybara.run_server = false
17
+ Capybara.default_wait_time = 20
18
+
19
+ Capybara.register_driver :poltergeist do |app|
20
+ Capybara::Poltergeist::Driver.new(app, timeout: 20)
21
+ end
8
22
 
9
23
  module MiniTest::Aruba
10
24
  class ArubaApiWrapper
@@ -43,6 +57,8 @@ end
43
57
 
44
58
  module Helper
45
59
  include MiniTest::Aruba
60
+ include Capybara::DSL
61
+ include Capybara::RSpecMatchers
46
62
 
47
63
  def before_setup
48
64
  super
@@ -50,24 +66,41 @@ module Helper
50
66
  ENV['TEST'] = nil
51
67
  end
52
68
 
69
+ def teardown
70
+ Capybara.reset_sessions!
71
+ Capybara.use_default_driver
72
+ end
73
+
53
74
  def after_teardown
54
75
  super
55
76
 
56
77
  terminate_processes!
57
78
  prep_for_fs_check do
58
- next unless File.exists?('gitdocs.pid')
79
+ next unless File.exist?('gitdocs.pid')
59
80
 
60
81
  pid = IO.read('gitdocs.pid').to_i
61
82
  Process.kill('KILL', pid)
62
83
  begin
63
84
  Process.wait(pid)
64
- rescue SystemCallError
85
+ rescue SystemCallError # rubocop:disable Lint/HandleExceptions
65
86
  # This means that the process is already gone.
66
87
  # Nothing to do.
67
88
  end
68
89
  end
69
90
  end
70
91
 
92
+ def start_daemon
93
+ configuration = Gitdocs::Configuration.new
94
+ configuration.shares.each do |share|
95
+ share.update_attributes(polling_interval: 0.1, notification: false)
96
+ end
97
+
98
+ start_cmd = 'gitdocs start --debug --pid=gitdocs.pid --port 7777'
99
+ run(start_cmd, 15)
100
+ assert_success(true)
101
+ assert_partial_output('Started gitdocs', output_from(start_cmd))
102
+ end
103
+
71
104
  # @overload abs_current_dir
72
105
  # @return [String] absolute path for current_dir
73
106
  # @overload abs_current_dir(relative_path)
@@ -94,7 +127,7 @@ module Helper
94
127
 
95
128
  def wait_for_clean_workdir(path)
96
129
  dirty = true
97
- Timeout.timeout(10) do
130
+ Timeout.timeout(20) do
98
131
  while dirty
99
132
  begin
100
133
  sleep(0.1)
@@ -116,7 +149,7 @@ module Helper
116
149
  def wait_for_exact_file_content(file, exact_content)
117
150
  in_current_dir do
118
151
  begin
119
- Timeout.timeout(10) do
152
+ Timeout.timeout(20) do
120
153
  sleep(0.1) until File.exist?(file) && IO.read(file) == exact_content
121
154
  end
122
155
  rescue Timeout::Error
@@ -135,7 +168,7 @@ module Helper
135
168
  def wait_for_directory(path)
136
169
  in_current_dir do
137
170
  begin
138
- Timeout.timeout(10) { sleep(0.1) until Dir.exist?(path) }
171
+ Timeout.timeout(20) { sleep(0.1) until Dir.exist?(path) }
139
172
  rescue Timeout::Error
140
173
  nil
141
174
  end
@@ -147,7 +180,7 @@ module Helper
147
180
  def wait_for_conflict_markers(path)
148
181
  in_current_dir do
149
182
  begin
150
- Timeout.timeout(10) { sleep(0.1) if File.exist?(path) }
183
+ Timeout.timeout(20) { sleep(0.1) if File.exist?(path) }
151
184
  rescue Timeout::Error
152
185
  nil
153
186
  ensure
@@ -155,7 +188,7 @@ module Helper
155
188
  end
156
189
 
157
190
  begin
158
- Timeout.timeout(10) { sleep(0.1) if Dir.glob("#{path} (*)").empty? }
191
+ Timeout.timeout(20) { sleep(0.1) if Dir.glob("#{path} (*)").empty? }
159
192
  rescue Timeout::Error
160
193
  nil
161
194
  ensure
@@ -0,0 +1,82 @@
1
+ # -*- encoding : utf-8 -*-
2
+
3
+ require File.expand_path('../test_helper', __FILE__)
4
+
5
+ describe 'gitdocs configuration' do
6
+ before do
7
+ ENV['TEST'] = 'true'
8
+ ShellTools.capture { @config = Gitdocs::Configuration.new('/tmp/gitdocs') }
9
+ end
10
+
11
+ it 'has sensible default config root' do
12
+ assert_equal '/tmp/gitdocs', @config.config_root
13
+ end
14
+
15
+ it 'can retrieve empty shares' do
16
+ assert_equal [], @config.shares
17
+ end
18
+
19
+ it 'can have a path added' do
20
+ @config.add_path('/my/../my/path') # normalized test
21
+ assert_equal '/my/path', @config.shares.first.path
22
+ assert_equal 15.0, @config.shares.first.polling_interval
23
+ end
24
+
25
+ it 'can have a path removed' do
26
+ @config.add_path('/my/path')
27
+ @config.add_path('/my/path/2')
28
+ @config.remove_path('/my/../my/path/2') # normalized test
29
+ assert_equal ['/my/path'], @config.shares.map(&:path)
30
+ end
31
+
32
+ it 'can have a share removed by id' do
33
+ @config.add_path('/my/path')
34
+ @config.add_path('/my/path/2')
35
+
36
+ # Delete an index which exists
37
+ @config.remove_by_id(2).must_equal(true) # normalized test
38
+ assert_equal ['/my/path'], @config.shares.map(&:path)
39
+
40
+ # Try to delete an index which does not exist
41
+ @config.remove_by_id(5).must_equal(false) # normalized test
42
+ assert_equal ['/my/path'], @config.shares.map(&:path)
43
+ end
44
+
45
+ it 'can clear paths' do
46
+ @config.add_path('/my/path')
47
+ @config.add_path('/my/path/2')
48
+ @config.clear
49
+ assert_equal [], @config.shares.map(&:path)
50
+ end
51
+
52
+ describe '#update_all' do
53
+ before do
54
+ @config.add_path('/my/path')
55
+ @config.add_path('/my/path/2')
56
+ @config.add_path('/my/path/3')
57
+ @config.add_path('/my/path/4')
58
+ @config.add_path('/my/path/5')
59
+ @config.update_all(
60
+ 'config' => { 'start_web_frontend' => false, 'web_frontend_port' => 9999 },
61
+ 'share' => {
62
+ '0' => { 'path' => '/my/path', 'polling_interval' => 42 },
63
+ '1' => { 'path' => '/my/path/2', 'polling_interval' => 66 },
64
+ '2' => { 'path' => '/my/path/3a', 'polling_interval' => 77 },
65
+ '3' => { 'path' => '', 'polling_interval' => 88 },
66
+ '4' => { 'polling_interval' => 99 }
67
+ }
68
+ )
69
+ end
70
+ it { @config.shares.size.must_equal(5) }
71
+ it { @config.shares[0].path.must_equal('/my/path') }
72
+ it { @config.shares[0].polling_interval.must_equal(42) }
73
+ it { @config.shares[1].path.must_equal('/my/path/2') }
74
+ it { @config.shares[1].polling_interval.must_equal(66) }
75
+ it { @config.shares[2].path.must_equal('/my/path/3a') }
76
+ it { @config.shares[2].polling_interval.must_equal(77) }
77
+ it { @config.shares[3].path.must_equal('/my/path/4') }
78
+ it { @config.shares[3].polling_interval.must_equal(15) }
79
+ it { @config.shares[4].path.must_equal('/my/path/5') }
80
+ it { @config.shares[4].polling_interval.must_equal(15) }
81
+ end
82
+ end
@@ -0,0 +1,165 @@
1
+ # -*- encoding : utf-8 -*-
2
+ require File.expand_path('../test_helper', __FILE__)
3
+
4
+ describe Gitdocs::Notifier do
5
+ let(:notifier) { Gitdocs::Notifier.new(show_notifications) }
6
+
7
+ describe '#error' do
8
+ subject { Gitdocs::Notifier.error(:title, :message) }
9
+ before do
10
+ Gitdocs::Notifier.expects(:new).with(true).returns(notifier = mock)
11
+ notifier.expects(:error).with(:title, :message)
12
+ end
13
+ it { subject }
14
+ end
15
+
16
+ describe '#info' do
17
+ subject { notifier.info('title', 'message') }
18
+
19
+ describe 'without notifications' do
20
+ let(:show_notifications) { false }
21
+ before { notifier.expects(:puts).with('title: message') }
22
+ it { subject }
23
+ end
24
+
25
+ describe 'with notifications' do
26
+ let(:show_notifications) { true }
27
+ before do
28
+ Guard::Notifier.expects(:turn_on)
29
+ Guard::Notifier.expects(:notify)
30
+ .with(
31
+ 'message',
32
+ title: 'title',
33
+ image: File.expand_path('../../../lib/img/icon.png', __FILE__)
34
+ )
35
+ end
36
+ it { subject }
37
+ end
38
+ end
39
+
40
+ describe '#warn' do
41
+ subject { notifier.warn('title', 'message') }
42
+
43
+ describe 'without notifications' do
44
+ let(:show_notifications) { false }
45
+ before { Kernel.expects(:warn).with('title: message') }
46
+ it { subject }
47
+ end
48
+
49
+ describe 'with notifications' do
50
+ let(:show_notifications) { true }
51
+ before do
52
+ Guard::Notifier.expects(:turn_on)
53
+ Guard::Notifier.expects(:notify).with('message', title: 'title')
54
+ end
55
+ it { subject }
56
+ end
57
+ end
58
+
59
+ describe '#error' do
60
+ subject { notifier.error('title', 'message') }
61
+
62
+ describe 'without notifications' do
63
+ let(:show_notifications) { false }
64
+ before { Kernel.expects(:warn).with('title: message') }
65
+ it { subject }
66
+ end
67
+
68
+ describe 'with notifications' do
69
+ let(:show_notifications) { true }
70
+ before do
71
+ Guard::Notifier.expects(:turn_on)
72
+ Guard::Notifier.expects(:notify).with('message', title: 'title', image: :failure)
73
+ end
74
+ it { subject }
75
+ end
76
+ end
77
+
78
+ describe '#merge_notification' do
79
+ subject { notifier.merge_notification(result, 'root_path') }
80
+
81
+ let(:show_notifications) { false }
82
+
83
+ describe 'with no changes' do
84
+ before do
85
+ # Ensure that the notification methods are not called.
86
+ notifier.stubs(:warn).raises
87
+ notifier.stubs(:info).raises
88
+ notifier.stubs(:error).raises
89
+ end
90
+ describe('with nil') { let(:result) { nil } ; it { subject } }
91
+ describe('with no_remote') { let(:result) { :no_remote } ; it { subject } }
92
+ describe('with no changes') { let(:result) { {} } ; it { subject } }
93
+ end
94
+
95
+ describe 'with changes' do
96
+ let(:result) { { 'Alice' => 1, 'Bob' => 3 } }
97
+ before do
98
+ notifier.expects(:info).with(
99
+ 'Updated with 4 changes',
100
+ "In root_path:\n* Alice (1 change)\n* Bob (3 changes)"
101
+ )
102
+ end
103
+ it { subject }
104
+ end
105
+
106
+ describe 'with conflicts' do
107
+ let(:result) { ['file'] }
108
+ before do
109
+ notifier.expects(:warn).with(
110
+ 'There were some conflicts',
111
+ '* file'
112
+ )
113
+ end
114
+ it { subject }
115
+ end
116
+
117
+ describe 'with anything else' do
118
+ let(:result) { 'error' }
119
+ before do
120
+ notifier.expects(:error).with(
121
+ 'There was a problem synchronizing this gitdoc',
122
+ "A problem occurred in root_path:\nerror"
123
+ )
124
+ end
125
+ it { subject }
126
+ end
127
+ end
128
+
129
+ describe '#push_notification' do
130
+ subject { notifier.push_notification(result, 'root_path') }
131
+
132
+ let(:show_notifications) { false }
133
+
134
+ describe('with nil') { let(:result) { nil } ; it { subject } }
135
+ describe('with no_remote') { let(:result) { :no_remote } ; it { subject } }
136
+ describe('with nothing') { let(:result) { :nothing } ; it { subject } }
137
+
138
+ describe 'with changes' do
139
+ let(:result) { { 'Alice' => 1, 'Bob' => 3 } }
140
+ before do
141
+ notifier.expects(:info)
142
+ .with('Pushed 4 changes', 'root_path has been pushed')
143
+ end
144
+ it { subject }
145
+ end
146
+
147
+ describe 'with conflict' do
148
+ let(:result) { :conflict }
149
+ before do
150
+ notifier.expects(:warn)
151
+ .with('There was a conflict in root_path, retrying', '')
152
+ end
153
+ it { subject }
154
+ end
155
+
156
+ describe 'with anything else' do
157
+ let(:result) { 'error' }
158
+ before do
159
+ notifier.expects(:error)
160
+ .with('BAD Could not push changes in root_path', 'error')
161
+ end
162
+ it { subject }
163
+ end
164
+ end
165
+ end