homesick 1.0.0 → 1.1.0

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.
@@ -1,621 +0,0 @@
1
- # -*- encoding : utf-8 -*-
2
- require 'spec_helper'
3
- require 'capture-output'
4
-
5
- describe 'homesick' do
6
- let(:home) { create_construct }
7
- after { home.destroy! }
8
-
9
- let(:castles) { home.directory('.homesick/repos') }
10
-
11
- let(:homesick) { Homesick.new }
12
-
13
- before { homesick.stub(:repos_dir).and_return(castles) }
14
-
15
- describe 'clone' do
16
- context 'has a .homesickrc' do
17
- it 'should run the .homesickrc' do
18
- somewhere = create_construct
19
- local_repo = somewhere.directory('some_repo')
20
- local_repo.file('.homesickrc') do |file|
21
- file << "File.open(Dir.pwd + '/testing', 'w') { |f| f.print 'testing' }"
22
- end
23
-
24
- expect($stdout).to receive(:print)
25
- expect($stdin).to receive(:gets).and_return('y')
26
- expect_any_instance_of(Thor::Shell::Basic).to receive(:say_status).with('eval', kind_of(Pathname))
27
- homesick.clone local_repo
28
-
29
- castles.join('some_repo').join('testing').should exist
30
- end
31
- end
32
-
33
- context 'of a file' do
34
- it 'should symlink existing directories' do
35
- somewhere = create_construct
36
- local_repo = somewhere.directory('wtf')
37
-
38
- homesick.clone local_repo
39
-
40
- castles.join('wtf').readlink.should == local_repo
41
- end
42
-
43
- context 'when it exists in a repo directory' do
44
- before do
45
- existing_castle = given_castle('existing_castle')
46
- @existing_dir = existing_castle.parent
47
- end
48
-
49
- it 'should raise an error' do
50
- homesick.should_not_receive(:git_clone)
51
- expect { homesick.clone @existing_dir.to_s }.to raise_error(/already cloned/i)
52
- end
53
- end
54
- end
55
-
56
- it 'should clone git repo like file:///path/to.git' do
57
- bare_repo = File.join(create_construct.to_s, 'dotfiles.git')
58
- system "git init --bare #{bare_repo} >/dev/null 2>&1"
59
- # Capture stderr to suppress message about cloning an empty repo.
60
- Capture.stderr do
61
- homesick.clone "file://#{bare_repo}"
62
- end
63
- File.directory?(File.join(home.to_s, '.homesick/repos/dotfiles')).should be_true
64
- end
65
-
66
- it 'should clone git repo like git://host/path/to.git' do
67
- homesick.should_receive(:git_clone).with('git://github.com/technicalpickles/pickled-vim.git')
68
-
69
- homesick.clone 'git://github.com/technicalpickles/pickled-vim.git'
70
- end
71
-
72
- it 'should clone git repo like git@host:path/to.git' do
73
- homesick.should_receive(:git_clone).with('git@github.com:technicalpickles/pickled-vim.git')
74
-
75
- homesick.clone 'git@github.com:technicalpickles/pickled-vim.git'
76
- end
77
-
78
- it 'should clone git repo like http://host/path/to.git' do
79
- homesick.should_receive(:git_clone).with('http://github.com/technicalpickles/pickled-vim.git')
80
-
81
- homesick.clone 'http://github.com/technicalpickles/pickled-vim.git'
82
- end
83
-
84
- it 'should clone git repo like http://host/path/to' do
85
- homesick.should_receive(:git_clone).with('http://github.com/technicalpickles/pickled-vim')
86
-
87
- homesick.clone 'http://github.com/technicalpickles/pickled-vim'
88
- end
89
-
90
- it 'should clone git repo like host-alias:repos.git' do
91
- homesick.should_receive(:git_clone).with('gitolite:pickled-vim.git')
92
-
93
- homesick.clone 'gitolite:pickled-vim.git'
94
- end
95
-
96
- it 'should throw an exception when trying to clone a malformed uri like malformed' do
97
- homesick.should_not_receive(:git_clone)
98
- expect { homesick.clone 'malformed' }.to raise_error
99
- end
100
-
101
- it 'should clone a github repo' do
102
- homesick.should_receive(:git_clone).with('https://github.com/wfarr/dotfiles.git', :destination => Pathname.new('dotfiles'))
103
-
104
- homesick.clone 'wfarr/dotfiles'
105
- end
106
- end
107
-
108
- describe 'rc' do
109
- let(:castle) { given_castle('glencairn') }
110
-
111
- context 'when told to do so' do
112
- before do
113
- expect($stdout).to receive(:print)
114
- expect($stdin).to receive(:gets).and_return('y')
115
- end
116
-
117
- it 'executes the .homesickrc' do
118
- castle.file('.homesickrc') do |file|
119
- file << "File.open(Dir.pwd + '/testing', 'w') { |f| f.print 'testing' }"
120
- end
121
-
122
- expect_any_instance_of(Thor::Shell::Basic).to receive(:say_status).with('eval', kind_of(Pathname))
123
- homesick.rc castle
124
-
125
- castle.join('testing').should exist
126
- end
127
- end
128
-
129
- context 'when told not to do so' do
130
- before do
131
- expect($stdout).to receive(:print)
132
- expect($stdin).to receive(:gets).and_return('n')
133
- end
134
-
135
- it 'does not execute the .homesickrc' do
136
- castle.file('.homesickrc') do |file|
137
- file << "File.open(Dir.pwd + '/testing', 'w') { |f| f.print 'testing' }"
138
- end
139
-
140
- expect_any_instance_of(Thor::Shell::Basic).to receive(:say_status).with('eval skip', /not evaling.+/, :blue)
141
- homesick.rc castle
142
-
143
- castle.join('testing').should_not exist
144
- end
145
- end
146
- end
147
-
148
- describe 'symlink' do
149
- let(:castle) { given_castle('glencairn') }
150
-
151
- it 'links dotfiles from a castle to the home folder' do
152
- dotfile = castle.file('.some_dotfile')
153
-
154
- homesick.symlink('glencairn')
155
-
156
- home.join('.some_dotfile').readlink.should == dotfile
157
- end
158
-
159
- it 'links non-dotfiles from a castle to the home folder' do
160
- dotfile = castle.file('bin')
161
-
162
- homesick.symlink('glencairn')
163
-
164
- home.join('bin').readlink.should == dotfile
165
- end
166
-
167
- context 'when forced' do
168
- let(:homesick) { Homesick.new [], :force => true }
169
-
170
- it 'can override symlinks to directories' do
171
- somewhere_else = create_construct
172
- existing_dotdir_link = home.join('.vim')
173
- FileUtils.ln_s somewhere_else, existing_dotdir_link
174
-
175
- dotdir = castle.directory('.vim')
176
-
177
- homesick.symlink('glencairn')
178
-
179
- existing_dotdir_link.readlink.should == dotdir
180
- end
181
-
182
- it 'can override existing directory' do
183
- existing_dotdir = home.directory('.vim')
184
-
185
- dotdir = castle.directory('.vim')
186
-
187
- homesick.symlink('glencairn')
188
-
189
- existing_dotdir.readlink.should == dotdir
190
- end
191
- end
192
-
193
- context "with '.config' in .homesick_subdir" do
194
- let(:castle) { given_castle('glencairn', ['.config']) }
195
- it 'can symlink in sub directory' do
196
- dotdir = castle.directory('.config')
197
- dotfile = dotdir.file('.some_dotfile')
198
-
199
- homesick.symlink('glencairn')
200
-
201
- home_dotdir = home.join('.config')
202
- home_dotdir.symlink?.should be == false
203
- home_dotdir.join('.some_dotfile').readlink.should == dotfile
204
- end
205
- end
206
-
207
- context "with '.config/appA' in .homesick_subdir" do
208
- let(:castle) { given_castle('glencairn', ['.config/appA']) }
209
- it 'can symlink in nested sub directory' do
210
- dotdir = castle.directory('.config').directory('appA')
211
- dotfile = dotdir.file('.some_dotfile')
212
-
213
- homesick.symlink('glencairn')
214
-
215
- home_dotdir = home.join('.config').join('appA')
216
- home_dotdir.symlink?.should be == false
217
- home_dotdir.join('.some_dotfile').readlink.should == dotfile
218
- end
219
- end
220
-
221
- context "with '.config' and '.config/someapp' in .homesick_subdir" do
222
- let(:castle) { given_castle('glencairn', ['.config', '.config/someapp']) }
223
- it 'can symlink under both of .config and .config/someapp' do
224
- config_dir = castle.directory('.config')
225
- config_dotfile = config_dir.file('.some_dotfile')
226
- someapp_dir = config_dir.directory('someapp')
227
- someapp_dotfile = someapp_dir.file('.some_appfile')
228
-
229
- homesick.symlink('glencairn')
230
-
231
- home_config_dir = home.join('.config')
232
- home_someapp_dir = home_config_dir.join('someapp')
233
- home_config_dir.symlink?.should be == false
234
- home_config_dir.join('.some_dotfile').readlink.should be == config_dotfile
235
- home_someapp_dir.symlink?.should be == false
236
- home_someapp_dir.join('.some_appfile').readlink.should == someapp_dotfile
237
- end
238
- end
239
-
240
- context "when call with no castle name" do
241
- let(:castle) { given_castle('dotfiles') }
242
- it 'using default castle name: "dotfiles"' do
243
- dotfile = castle.file('.some_dotfile')
244
-
245
- homesick.symlink
246
-
247
- home.join('.some_dotfile').readlink.should == dotfile
248
- end
249
- end
250
- end
251
-
252
- describe 'unlink' do
253
- let(:castle) { given_castle('glencairn') }
254
-
255
- it 'unlinks dotfiles in the home folder' do
256
- castle.file('.some_dotfile')
257
-
258
- homesick.symlink('glencairn')
259
- homesick.unlink('glencairn')
260
-
261
- home.join('.some_dotfile').should_not exist
262
- end
263
-
264
- it 'unlinks non-dotfiles from the home folder' do
265
- castle.file('bin')
266
-
267
- homesick.symlink('glencairn')
268
- homesick.unlink('glencairn')
269
-
270
- home.join('bin').should_not exist
271
- end
272
-
273
- context "with '.config' in .homesick_subdir" do
274
- let(:castle) { given_castle('glencairn', ['.config']) }
275
-
276
- it 'can unlink sub directories' do
277
- castle.directory('.config').file('.some_dotfile')
278
-
279
- homesick.symlink('glencairn')
280
- homesick.unlink('glencairn')
281
-
282
- home_dotdir = home.join('.config')
283
- home_dotdir.should exist
284
- home_dotdir.join('.some_dotfile').should_not exist
285
- end
286
- end
287
-
288
- context "with '.config/appA' in .homesick_subdir" do
289
- let(:castle) { given_castle('glencairn', ['.config/appA']) }
290
-
291
- it 'can unsymlink in nested sub directory' do
292
- castle.directory('.config').directory('appA').file('.some_dotfile')
293
-
294
- homesick.symlink('glencairn')
295
- homesick.unlink('glencairn')
296
-
297
- home_dotdir = home.join('.config').join('appA')
298
- home_dotdir.should exist
299
- home_dotdir.join('.some_dotfile').should_not exist
300
- end
301
- end
302
-
303
- context "with '.config' and '.config/someapp' in .homesick_subdir" do
304
- let(:castle) { given_castle('glencairn', ['.config', '.config/someapp']) }
305
-
306
- it 'can unsymlink under both of .config and .config/someapp' do
307
- config_dir = castle.directory('.config')
308
- config_dir.file('.some_dotfile')
309
- config_dir.directory('someapp').file('.some_appfile')
310
-
311
- homesick.symlink('glencairn')
312
- homesick.unlink('glencairn')
313
-
314
- home_config_dir = home.join('.config')
315
- home_someapp_dir = home_config_dir.join('someapp')
316
- home_config_dir.should exist
317
- home_config_dir.join('.some_dotfile').should_not exist
318
- home_someapp_dir.should exist
319
- home_someapp_dir.join('.some_appfile').should_not exist
320
- end
321
- end
322
-
323
- context "when call with no castle name" do
324
- let(:castle) { given_castle('dotfiles') }
325
-
326
- it 'using default castle name: "dotfiles"' do
327
- castle.file('.some_dotfile')
328
-
329
- homesick.symlink
330
- homesick.unlink
331
-
332
- home.join('.some_dotfile').should_not exist
333
- end
334
- end
335
- end
336
-
337
- describe 'list' do
338
- it 'should say each castle in the castle directory' do
339
- given_castle('zomg')
340
- given_castle('wtf/zomg')
341
-
342
- homesick.should_receive(:say_status).with('zomg', 'git://github.com/technicalpickles/zomg.git', :cyan)
343
- homesick.should_receive(:say_status).with('wtf/zomg', 'git://github.com/technicalpickles/zomg.git', :cyan)
344
-
345
- homesick.list
346
- end
347
- end
348
-
349
- describe 'status' do
350
- it 'should say "nothing to commit" when there are no changes' do
351
- given_castle('castle_repo')
352
- text = Capture.stdout { homesick.status('castle_repo') }
353
- text.should =~ /nothing to commit \(create\/copy files and use "git add" to track\)$/
354
- end
355
-
356
- it 'should say "Changes to be committed" when there are changes' do
357
- given_castle('castle_repo')
358
- some_rc_file = home.file '.some_rc_file'
359
- homesick.track(some_rc_file.to_s, 'castle_repo')
360
- text = Capture.stdout { homesick.status('castle_repo') }
361
- text.should =~ /Changes to be committed:.*new file:\s*home\/.some_rc_file/m
362
- end
363
- end
364
-
365
- describe 'diff' do
366
- it 'should output an empty message when there are no changes to commit' do
367
- given_castle('castle_repo')
368
- some_rc_file = home.file '.some_rc_file'
369
- homesick.track(some_rc_file.to_s, 'castle_repo')
370
- Capture.stdout { homesick.commit 'castle_repo', 'Adding a file to the test' }
371
- text = Capture.stdout { homesick.diff('castle_repo') }
372
- text.should eq('')
373
- end
374
-
375
- it 'should output a diff message when there are changes to commit' do
376
- given_castle('castle_repo')
377
- some_rc_file = home.file '.some_rc_file'
378
- homesick.track(some_rc_file.to_s, 'castle_repo')
379
- Capture.stdout { homesick.commit 'castle_repo', 'Adding a file to the test' }
380
- File.open(some_rc_file.to_s, 'w') do |file|
381
- file.puts "Some test text"
382
- end
383
- text = Capture.stdout { homesick.diff('castle_repo') }
384
- text.should =~ /diff --git.+Some test text$/m
385
- end
386
- end
387
-
388
- describe 'show_path' do
389
- it 'should say the path of a castle' do
390
- castle = given_castle('castle_repo')
391
-
392
- homesick.should_receive(:say).with(castle.dirname)
393
-
394
- homesick.show_path('castle_repo')
395
- end
396
- end
397
-
398
- describe 'pull' do
399
- it 'should perform a pull, submodule init and update when the given castle exists' do
400
- given_castle('castle_repo')
401
- homesick.stub(:system).once.with('git pull --quiet')
402
- homesick.stub(:system).once.with('git submodule --quiet init')
403
- homesick.stub(:system).once.with('git submodule --quiet update --init --recursive >/dev/null 2>&1')
404
- homesick.pull 'castle_repo'
405
- end
406
-
407
- it 'should print an error message when trying to pull a non-existant castle' do
408
- homesick.should_receive("say_status").once.with(:error, /Could not pull castle_repo, expected \/tmp\/construct_container.* exist and contain dotfiles/, :red)
409
- expect { homesick.pull "castle_repo" }.to raise_error(SystemExit)
410
- end
411
-
412
- describe '--all' do
413
- it 'should pull each castle when invoked with --all' do
414
- given_castle('castle_repo')
415
- given_castle('glencairn')
416
- homesick.stub(:system).exactly(2).times.with('git pull --quiet')
417
- homesick.stub(:system).exactly(2).times.with('git submodule --quiet init')
418
- homesick.stub(:system).exactly(2).times.with('git submodule --quiet update --init --recursive >/dev/null 2>&1')
419
- Capture.stdout { Capture.stderr { homesick.invoke 'pull', [], all: true } }
420
- end
421
- end
422
-
423
- end
424
-
425
- describe 'push' do
426
- it 'should perform a git push on the given castle' do
427
- given_castle('castle_repo')
428
- homesick.stub(:system).once.with('git push')
429
- homesick.push 'castle_repo'
430
- end
431
-
432
- it 'should print an error message when trying to push a non-existant castle' do
433
- homesick.should_receive("say_status").once.with(:error, /Could not push castle_repo, expected \/tmp\/construct_container.* exist and contain dotfiles/, :red)
434
- expect { homesick.push "castle_repo" }.to raise_error(SystemExit)
435
- end
436
- end
437
-
438
- describe 'track' do
439
- it 'should move the tracked file into the castle' do
440
- castle = given_castle('castle_repo')
441
-
442
- some_rc_file = home.file '.some_rc_file'
443
-
444
- homesick.track(some_rc_file.to_s, 'castle_repo')
445
-
446
- tracked_file = castle.join('.some_rc_file')
447
- tracked_file.should exist
448
-
449
- some_rc_file.readlink.should == tracked_file
450
- end
451
-
452
- it 'should handle files with parens' do
453
- castle = given_castle('castle_repo')
454
-
455
- some_rc_file = home.file 'Default (Linux).sublime-keymap'
456
-
457
- homesick.track(some_rc_file.to_s, 'castle_repo')
458
-
459
- tracked_file = castle.join('Default (Linux).sublime-keymap')
460
- tracked_file.should exist
461
-
462
- some_rc_file.readlink.should == tracked_file
463
- end
464
-
465
- it 'should track a file in nested folder structure' do
466
- castle = given_castle('castle_repo')
467
-
468
- some_nested_file = home.file('some/nested/file.txt')
469
- homesick.track(some_nested_file.to_s, 'castle_repo')
470
-
471
- tracked_file = castle.join('some/nested/file.txt')
472
- tracked_file.should exist
473
- some_nested_file.readlink.should == tracked_file
474
- end
475
-
476
- it 'should track a nested directory' do
477
- castle = given_castle('castle_repo')
478
-
479
- some_nested_dir = home.directory('some/nested/directory/')
480
- homesick.track(some_nested_dir.to_s, 'castle_repo')
481
-
482
- tracked_file = castle.join('some/nested/directory/')
483
- tracked_file.should exist
484
- some_nested_dir.realpath.should == tracked_file.realpath
485
- end
486
-
487
- context "when call with no castle name" do
488
- it 'using default castle name: "dotfiles"' do
489
- castle = given_castle('dotfiles')
490
-
491
- some_rc_file = home.file '.some_rc_file'
492
-
493
- homesick.track(some_rc_file.to_s)
494
-
495
- tracked_file = castle.join('.some_rc_file')
496
- tracked_file.should exist
497
-
498
- some_rc_file.readlink.should == tracked_file
499
- end
500
- end
501
-
502
- describe 'commit' do
503
- it 'should have a commit message when the commit succeeds' do
504
- given_castle('castle_repo')
505
- some_rc_file = home.file '.a_random_rc_file'
506
- homesick.track(some_rc_file.to_s, 'castle_repo')
507
- text = Capture.stdout { homesick.commit('castle_repo', 'Test message') }
508
- text.should =~ /^\[master \(root-commit\) \w+\] Test message/
509
- end
510
- end
511
-
512
- describe 'subdir_file' do
513
-
514
- it 'should add the nested files parent to the subdir_file' do
515
- castle = given_castle('castle_repo')
516
-
517
- some_nested_file = home.file('some/nested/file.txt')
518
- homesick.track(some_nested_file.to_s, 'castle_repo')
519
-
520
- subdir_file = castle.parent.join(Homesick::SUBDIR_FILENAME)
521
- File.open(subdir_file, 'r') do |f|
522
- f.readline.should == "some/nested\n"
523
- end
524
- end
525
-
526
- it 'should NOT add anything if the files parent is already listed' do
527
- castle = given_castle('castle_repo')
528
-
529
- some_nested_file = home.file('some/nested/file.txt')
530
- other_nested_file = home.file('some/nested/other.txt')
531
- homesick.track(some_nested_file.to_s, 'castle_repo')
532
- homesick.track(other_nested_file.to_s, 'castle_repo')
533
-
534
- subdir_file = castle.parent.join(Homesick::SUBDIR_FILENAME)
535
- File.open(subdir_file, 'r') do |f|
536
- f.readlines.size.should == 1
537
- end
538
- end
539
-
540
- it 'should remove the parent of a tracked file from the subdir_file if the parent itself is tracked' do
541
- castle = given_castle('castle_repo')
542
-
543
- some_nested_file = home.file('some/nested/file.txt')
544
- nested_parent = home.directory('some/nested/')
545
- homesick.track(some_nested_file.to_s, 'castle_repo')
546
- homesick.track(nested_parent.to_s, 'castle_repo')
547
-
548
- subdir_file = castle.parent.join(Homesick::SUBDIR_FILENAME)
549
- File.open(subdir_file, 'r') do |f|
550
- f.each_line { |line| line.should_not == "some/nested\n" }
551
- end
552
- end
553
- end
554
- end
555
-
556
- describe "destroy" do
557
- it "removes the symlink files" do
558
- expect_any_instance_of(Thor::Shell::Basic).to receive(:yes?).and_return('y')
559
- given_castle("stronghold")
560
- some_rc_file = home.file '.some_rc_file'
561
- homesick.track(some_rc_file.to_s, "stronghold")
562
- homesick.destroy('stronghold')
563
-
564
- some_rc_file.should_not be_exist
565
- end
566
-
567
- it "deletes the cloned repository" do
568
- expect_any_instance_of(Thor::Shell::Basic).to receive(:yes?).and_return('y')
569
- castle = given_castle("stronghold")
570
- some_rc_file = home.file '.some_rc_file'
571
- homesick.track(some_rc_file.to_s, "stronghold")
572
- homesick.destroy('stronghold')
573
-
574
- castle.should_not be_exist
575
- end
576
- end
577
-
578
- describe "cd" do
579
- it "cd's to the root directory of the given castle" do
580
- given_castle('castle_repo')
581
- homesick.should_receive("inside").once.with(kind_of(Pathname)).and_yield
582
- homesick.should_receive("system").once.with(ENV["SHELL"])
583
- Capture.stdout { homesick.cd 'castle_repo' }
584
- end
585
-
586
- it "returns an error message when the given castle does not exist" do
587
- homesick.should_receive("say_status").once.with(:error, /Could not cd castle_repo, expected \/tmp\/construct_container.* exist and contain dotfiles/, :red)
588
- expect { homesick.cd "castle_repo" }.to raise_error(SystemExit)
589
- end
590
- end
591
-
592
- describe "open" do
593
- it "opens the system default editor in the root of the given castle" do
594
- ENV.stub(:[]).and_call_original # Make sure calls to ENV use default values for most things...
595
- ENV.stub(:[]).with('EDITOR').and_return('vim') # Set a default value for 'EDITOR' just in case none is set
596
- given_castle 'castle_repo'
597
- homesick.should_receive("inside").once.with(kind_of(Pathname)).and_yield
598
- homesick.should_receive("system").once.with('vim')
599
- Capture.stdout { homesick.open 'castle_repo' }
600
- end
601
-
602
- it "returns an error message when the $EDITOR environment variable is not set" do
603
- ENV.stub(:[]).with('EDITOR').and_return(nil) # Set the default editor to make sure it fails.
604
- homesick.should_receive("say_status").once.with(:error, "The $EDITOR environment variable must be set to use this command", :red)
605
- expect { homesick.open "castle_repo" }.to raise_error(SystemExit)
606
- end
607
-
608
- it "returns an error message when the given castle does not exist" do
609
- ENV.stub(:[]).with('EDITOR').and_return('vim') # Set a default just in case none is set
610
- homesick.should_receive("say_status").once.with(:error, /Could not open castle_repo, expected \/tmp\/construct_container.* exist and contain dotfiles/, :red)
611
- expect { homesick.open "castle_repo" }.to raise_error(SystemExit)
612
- end
613
- end
614
-
615
- describe 'version' do
616
- it 'should print the current version of homesick' do
617
- text = Capture.stdout { homesick.version }
618
- text.chomp.should =~ /\d+\.\d+\.\d+/
619
- end
620
- end
621
- end