gitdocs 0.5.0.pre6 → 0.5.0.pre7

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