loca 0.0.1 → 0.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.
@@ -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