loca 0.0.1 → 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,12 @@
1
+ module Loca
2
+ module Utils
3
+ # returns false if str is nil, or not a String, or an empty String
4
+ def non_empty_string?(str)
5
+ !str.strip.empty?
6
+ rescue NameError, NoMethodError
7
+ false
8
+ end
9
+
10
+ module_function :non_empty_string?
11
+ end
12
+ end
@@ -1,3 +1,3 @@
1
1
  module Loca
2
- VERSION = '0.0.1'
2
+ VERSION = "0.1.0"
3
3
  end
@@ -1,69 +1,47 @@
1
1
  describe Loca::CLI do
2
- let(:invalid_url) { 'https://github.com/fakez/fakezz/pull/1' }
3
- let(:url) { 'https://github.com/smoll/loca/pull/1' }
4
- subject { described_class.new }
2
+ let(:cli) { described_class.new(argv, stdin, stdout, stderr) }
3
+ let(:stdin) { StringIO.new } # from http://stackoverflow.com/a/16507814/3456726
4
+ let(:stdout) { StringIO.new }
5
+ let(:stderr) { StringIO.new }
5
6
 
6
- before do # ensure we don't shell out to git
7
- allow_any_instance_of(Loca::Git).to receive(:fetch)
8
- allow_any_instance_of(Loca::Git).to receive(:checkout)
9
- end
10
-
11
- describe 'c' do
12
- it 'fetches and checks out the branch locally' do
13
- expect_any_instance_of(Loca::Git).to receive(:branch_name).and_return 'PULL_1'
14
- expect_any_instance_of(Loca::Git).to receive(:first_time_creating?).and_return true
15
-
16
- output = capture(:stdout) { described_class.start(%W(c #{url})) }.strip
17
- expect(output).to end_with 'Checked out PULL_1!'
18
- end
19
-
20
- it 'fetches and overwrites existing branch if the user says yes to overwrite' do
21
- expect_any_instance_of(Loca::Git).to receive(:branch_name).and_return 'PULL_1'
22
- expect_any_instance_of(Loca::Git).to receive(:first_time_creating?).and_return false
23
- silence(:stdout) { allow_any_instance_of(described_class).to receive(:yes?).and_return true }
24
-
25
- output = capture(:stdout) { described_class.start(%W(c #{url})) }.strip
26
- expect(output).to end_with 'Checked out PULL_1!'
27
- end
7
+ describe "#execute!" do
8
+ context "-v" do
9
+ let(:argv) { ["-v"] }
28
10
 
29
- it 'raises an error when the branch exists and the user says no to overwrite' do
30
- expect_any_instance_of(Loca::Git).to receive(:branch_name).and_return 'PULL_1'
31
- expect_any_instance_of(Loca::Git).to receive(:first_time_creating?).and_return false
32
- silence(:stdout) { allow_any_instance_of(described_class).to receive(:yes?).and_return false }
11
+ it "returns the version" do
12
+ expect do
13
+ cli.execute!
14
+ expect(stdout.string).to eq Loca::VERSION
15
+ end.to raise_error(SystemExit) # because execute! calls `exit 0` (or 1, etc.)
16
+ # from http://stackoverflow.com/a/28047771/3456726
17
+ end
33
18
 
34
- expect { silence(:stderr) { described_class.start(%W(c #{url})) } }.to raise_error
19
+ it "exits cleanly" do
20
+ expect { cli.execute! }.to terminate.with_code 0
21
+ end
35
22
  end
36
23
 
37
- it 'raises an error when an invalid repo is supplied' do
38
- expect { silence(:stderr) { described_class.start(%W(c #{invalid_url})) } }.to raise_error
39
- end
24
+ context "-h" do
25
+ let(:argv) { ["-h"] }
40
26
 
41
- it 'deletes when the delete flag is appended' do
42
- expect_any_instance_of(Loca::Git).to receive(:branch_name).and_return 'PULL_1'
43
- expect_any_instance_of(Loca::Git).to receive(:delete)
27
+ it "returns the version" do
28
+ expect do
29
+ cli.execute!
30
+ expect(stdout.string).to include "Usage: loca <url> [options]"
31
+ end.to raise_error(SystemExit)
32
+ end
44
33
 
45
- output = capture(:stdout) { described_class.start(%W(c #{url} -d)) }.strip
46
- expect(output).to eq 'Deleted PULL_1!'
34
+ it "exits cleanly" do
35
+ expect { cli.execute! }.to terminate.with_code 0
36
+ end
47
37
  end
48
- end
49
38
 
50
- describe 'd' do
51
- it 'deletes' do
52
- expect_any_instance_of(Loca::Git).to receive(:branch_name).and_return 'PULL_1'
53
- expect_any_instance_of(Loca::Git).to receive(:delete)
54
-
55
- output = capture(:stdout) { described_class.start(%W(d #{url})) }.strip
56
- expect(output).to eq 'Deleted PULL_1!'
57
- end
58
-
59
- it 'raises an error when an invalid repo is supplied' do
60
- expect { silence(:stderr) { described_class.start(%W(d #{invalid_url})) } }.to raise_error
61
- end
62
- end
39
+ context "invalid option" do
40
+ let(:argv) { ["--invalid-option"] }
63
41
 
64
- describe '--version' do
65
- it 'returns the correct version' do
66
- expect(capture(:stdout) { described_class.start(%w(-v)) }.strip).to eq(Loca::VERSION)
42
+ it "exits 1" do
43
+ expect { cli.execute! }.to terminate.with_code 1
44
+ end
67
45
  end
68
46
  end
69
47
  end
@@ -0,0 +1,18 @@
1
+ describe Loca::Git::BranchCreator do
2
+ subject { described_class.new("1337", "PULL_1337", "loca_r_smoll", "https://github.com/smoll/loca") }
3
+
4
+ # This is probably better off being functionally tested
5
+ # Otherwise, we end up stubbing out specific `git` commands which is very brittle
6
+ # See https://sethvargo.com/unit-and-functional-testing-git-with-rspec/
7
+
8
+ describe "#add_remote" do
9
+ it "uses and avoids duplicating a preexisting remote" do
10
+ expect(subject).to receive(:git).with("remote show -n").and_return "origin\n"
11
+ expect(subject).to receive(:git)
12
+ .with("config --get remote.origin.url")
13
+ .and_return "https://github.com/smoll/loca\n"
14
+
15
+ subject.send :add_remote
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,46 @@
1
+ describe Loca::Git::Common do
2
+ let(:dummy_class) { Class.new { include Loca::Git::Common } }
3
+ subject { dummy_class.new }
4
+
5
+ describe "#git" do
6
+ context "nonexistent command" do
7
+ let(:command) { "fakegitcmd" }
8
+
9
+ it "raises an error" do
10
+ expect { subject.git(command) }.to raise_error
11
+ end
12
+ end
13
+ end
14
+
15
+ describe "#git_urls_match?" do
16
+ # These should all equate with one another:
17
+ let(:remote_url) { "https://github.com/smoll/loca" }
18
+ let(:remote_url_with_git) { "https://github.com/smoll/loca.git" }
19
+ let(:remote_url_with_slash) { "https://github.com/smoll/loca/" }
20
+
21
+ it "matches when both have trailing '.git'" do
22
+ result = subject.git_urls_match?(remote_url_with_git, remote_url_with_git)
23
+ expect(result).to eq true
24
+ end
25
+
26
+ it "matches when both lack trailing '.git'" do
27
+ result = subject.git_urls_match?(remote_url, remote_url)
28
+ expect(result).to eq true
29
+ end
30
+
31
+ it "matches when only one has trailing '.git'" do
32
+ result = subject.git_urls_match?(remote_url_with_git, remote_url)
33
+ expect(result).to eq true
34
+ end
35
+
36
+ it "matches when one has a trailing slash" do
37
+ result = subject.git_urls_match?(remote_url_with_git, remote_url_with_slash)
38
+ expect(result).to eq true
39
+ end
40
+
41
+ it "returns false when they do not match" do
42
+ result = subject.git_urls_match?(remote_url, "https://github.com/smoll/locaz")
43
+ expect(result).to eq false
44
+ end
45
+ end
46
+ end
@@ -0,0 +1,88 @@
1
+ describe Loca::URL::Parser do
2
+ subject { described_class.new(url) }
3
+
4
+ describe "#to_s" do
5
+ let(:url) { "https://github.com/smoll/loca/pull/1337/" }
6
+ it "returns the URL passed to the constructor" do
7
+ expect(subject.to_s).to eq url
8
+ end
9
+ end
10
+
11
+ describe "#parse" do
12
+ let(:parsed) { subject.parse }
13
+
14
+ context "valid commits url" do
15
+ let(:url) { "https://github.com/smoll/loca/pull/1337/commits" }
16
+
17
+ it "returns a hash" do
18
+ expect(parsed).to be_a Hash
19
+ end
20
+
21
+ it "returns the correct pull num" do
22
+ expect(parsed[:pull][:num].to_s).to eq "1337"
23
+ end
24
+
25
+ it "returns the correct pull url" do
26
+ expect(parsed[:pull][:url]).to eq "https://github.com/smoll/loca/pull/1337"
27
+ end
28
+
29
+ it "returns the correct remote url" do
30
+ expect(parsed[:remote][:url]).to eq "https://github.com/smoll/loca.git"
31
+ end
32
+
33
+ it "returns the expected branch name" do
34
+ expect(parsed[:branch_name]).to eq "PULL_1337"
35
+ end
36
+ end
37
+
38
+ context "valid files url" do
39
+ let(:url) { "https://github.com/smoll/loca/pull/1337/files" }
40
+
41
+ it "returns the correct remote url" do
42
+ expect(parsed[:remote][:url]).to eq "https://github.com/smoll/loca.git"
43
+ end
44
+
45
+ it "returns the expected branch name" do
46
+ expect(parsed[:branch_name]).to eq "PULL_1337"
47
+ end
48
+ end
49
+
50
+ context "valid pull url" do
51
+ let(:url) { "https://github.com/smoll/loca/pull/1337" }
52
+
53
+ it "returns the correct remote url" do
54
+ expect(parsed[:remote][:url]).to eq "https://github.com/smoll/loca.git"
55
+ end
56
+
57
+ it "returns the expected branch name" do
58
+ expect(parsed[:branch_name]).to eq "PULL_1337"
59
+ end
60
+ end
61
+
62
+ context "valid pull url with trailing slash" do
63
+ let(:url) { "https://github.com/smoll/loca/pull/1337/" }
64
+
65
+ it "returns the correct remote url" do
66
+ expect(parsed[:remote][:url]).to eq "https://github.com/smoll/loca.git"
67
+ end
68
+
69
+ it "returns the expected branch name" do
70
+ expect(parsed[:branch_name]).to eq "PULL_1337"
71
+ end
72
+ end
73
+
74
+ context "invalid host" do
75
+ let(:url) { "https://badhub.com/smoll/loca/pull/1337" }
76
+ it "raises an error" do
77
+ expect { parsed }.to raise_error
78
+ end
79
+ end
80
+
81
+ context "invalid url" do
82
+ let(:url) { "badhub.com" }
83
+ it "raises an error" do
84
+ expect { parsed }.to raise_error
85
+ end
86
+ end
87
+ end
88
+ end
@@ -0,0 +1,26 @@
1
+ describe Loca::URL::Validator do
2
+ subject { described_class.new(url) }
3
+
4
+ describe "#validate" do
5
+ context "good url" do
6
+ let(:url) { "https://github.com/smoll/loca/pull/1337/commits" }
7
+ it "does not raise an error" do
8
+ expect { subject.validate }.not_to raise_error
9
+ end
10
+ end
11
+
12
+ context "bad url" do
13
+ let(:url) { "bad-url.com" }
14
+ it "raises an error" do
15
+ expect { subject.validate }.to raise_error
16
+ end
17
+ end
18
+
19
+ context "invalid pull number" do
20
+ let(:url) { "https://github.com/smoll/loca/pull/xyz/commits" }
21
+ it "raises an error" do
22
+ expect { subject.validate }.to raise_error
23
+ end
24
+ end
25
+ end
26
+ end
@@ -0,0 +1,40 @@
1
+ describe Loca::Utils do
2
+ describe "#non_empty_string?" do
3
+ subject { Loca::Utils.non_empty_string?(param) }
4
+
5
+ context "nonempty string passed" do
6
+ let(:param) { "hi" }
7
+ it { is_expected.to eq true }
8
+ end
9
+
10
+ context "nonempty text after newline passed" do
11
+ let(:param) { "\nOK" }
12
+ it { is_expected.to eq true }
13
+ end
14
+
15
+ context "whitespace string passed" do
16
+ let(:param) { " " }
17
+ it { is_expected.to eq false }
18
+ end
19
+
20
+ context "newline string passed" do
21
+ let(:param) { "\n" }
22
+ it { is_expected.to eq false }
23
+ end
24
+
25
+ context "integer passed" do
26
+ let(:param) { 1 }
27
+ it { is_expected.to eq false }
28
+ end
29
+
30
+ context "float passed" do
31
+ let(:param) { 1.0 }
32
+ it { is_expected.to eq false }
33
+ end
34
+
35
+ context "nil passed" do
36
+ let(:param) { nil }
37
+ it { is_expected.to eq false }
38
+ end
39
+ end
40
+ end
@@ -1,27 +1,25 @@
1
1
  # Using https://raw.githubusercontent.com/bbatsov/rubocop/master/spec/spec_helper.rb as a guide
2
- require 'simplecov'
3
- require 'coveralls'
4
- require 'codeclimate-test-reporter'
5
-
6
- SimpleCov.formatter = SimpleCov::Formatter::MultiFormatter[
7
- SimpleCov::Formatter::HTMLFormatter,
8
- Coveralls::SimpleCov::Formatter,
9
- CodeClimate::TestReporter::Formatter
10
- ]
11
- SimpleCov.start
12
-
13
- require 'bundler/setup'
2
+ require "simplecov"
3
+ require "codeclimate-test-reporter"
4
+
5
+ # Sending to Coveralls requires no set up, is done by rake task
6
+ # Sending to Code Climate is set up here, but pushed in a rake task
7
+ # ref: https://github.com/codeclimate/ruby-test-reporter#using-with-parallel_tests
8
+ # ref: https://coveralls.zendesk.com/hc/en-us/articles/201769485-Ruby-Rails
9
+ SimpleCov.add_filter "vendor"
10
+ SimpleCov.formatters = [SimpleCov::Formatter::HTMLFormatter]
11
+ SimpleCov.start CodeClimate::TestReporter.configuration.profile
12
+
13
+ require "bundler/setup"
14
14
  Bundler.setup # From http://stackoverflow.com/a/4402193
15
15
 
16
- require 'loca'
16
+ require "loca"
17
17
 
18
18
  # Requires supporting files with custom matchers and macros, etc,
19
19
  # in ./support/ and its subdirectories.
20
20
  Dir["#{File.dirname(__FILE__)}/support/**/*.rb"].each { |f| require f }
21
21
 
22
22
  RSpec.configure do |config|
23
- config.include Features::GitHubHelper
24
-
25
23
  config.order = :random
26
24
 
27
25
  config.expect_with :rspec do |expectations|
@@ -37,48 +35,4 @@ RSpec.configure do |config|
37
35
  config.before do
38
36
  ARGV.replace []
39
37
  end
40
-
41
- # From https://raw.githubusercontent.com/erikhuda/thor/master/spec/helper.rb
42
- # Captures the output for analysis later
43
- #
44
- # @example Capture `$stderr`
45
- #
46
- # output = capture(:stderr) { $stderr.puts "this is captured" }
47
- #
48
- # @param [Symbol] stream `:stdout` or `:stderr`
49
- # @yield The block to capture stdout/stderr for.
50
- # @return [String] The contents of $stdout or $stderr
51
- # rubocop:disable Lint/Eval
52
- def capture(stream)
53
- begin
54
- stream = stream.to_s
55
- eval "$#{stream} = StringIO.new"
56
- yield
57
- result = eval("$#{stream}").string
58
- ensure
59
- eval("$#{stream} = #{stream.upcase}")
60
- end
61
-
62
- result
63
- end
64
- # rubocop:enable Lint/Eval
65
-
66
- # This code was adapted from Ruby on Rails, available under MIT-LICENSE
67
- # Copyright (c) 2004-2013 David Heinemeier Hansson
68
- def silence_warnings
69
- old_verbose, $VERBOSE = $VERBOSE, nil
70
- yield
71
- ensure
72
- $VERBOSE = old_verbose
73
- end
74
-
75
- # Silences the output stream
76
- #
77
- # @example Silence `$stdout`
78
- #
79
- # silence(:stdout) { $stdout.puts "hi" }
80
- #
81
- # @param [IO] stream The stream to use such as $stderr or $stdout
82
- # @return [nil]
83
- alias :silence :capture # rubocop:disable Style/Alias
84
38
  end
@@ -0,0 +1,37 @@
1
+ RSpec::Matchers.define :terminate do |_code|
2
+ actual = nil
3
+
4
+ def supports_block_expectations?
5
+ true
6
+ end
7
+
8
+ match do |block|
9
+ begin
10
+ block.call
11
+ rescue SystemExit => e
12
+ actual = e.status
13
+ end
14
+ actual && actual == status_code
15
+ end
16
+
17
+ chain :with_code do |status_code|
18
+ @status_code = status_code
19
+ end
20
+
21
+ failure_message do |_block|
22
+ "expected block to call exit(#{status_code}) but exit" +
23
+ (actual.nil? ? " not called" : "(#{actual}) was called")
24
+ end
25
+
26
+ failure_message_when_negated do |_block|
27
+ "expected block not to call exit(#{status_code})"
28
+ end
29
+
30
+ description do
31
+ "expect block to call exit(#{status_code})"
32
+ end
33
+
34
+ def status_code
35
+ @status_code ||= 0
36
+ end
37
+ end