nex3-github 0.1.2 → 0.1.3
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.
- data/Manifest +7 -7
- data/README +1 -0
- data/commands/commands.rb +57 -28
- data/commands/helpers.rb +36 -28
- data/lib/extensions.rb +28 -0
- data/lib/github.rb +41 -13
- data/lib/github/command.rb +1 -3
- data/spec/command_spec.rb +66 -0
- data/spec/extensions_spec.rb +34 -0
- data/spec/github_spec.rb +51 -0
- data/spec/helper_spec.rb +188 -0
- data/spec/spec_helper.rb +128 -4
- data/spec/ui_spec.rb +406 -0
- data/spec/windoze_spec.rb +36 -0
- metadata +21 -12
- data/spec/helpers/owner_spec.rb +0 -12
- data/spec/helpers/project_spec.rb +0 -24
- data/spec/helpers/public_url_for_spec.rb +0 -12
- data/spec/helpers/repo_for_spec.rb +0 -12
- data/spec/helpers/user_and_repo_from_spec.rb +0 -37
- data/spec/helpers/user_for_spec.rb +0 -12
@@ -0,0 +1,34 @@
|
|
1
|
+
require File.dirname(__FILE__) + "/spec_helper"
|
2
|
+
|
3
|
+
describe "When calling #try" do
|
4
|
+
specify "objects should return themselves" do
|
5
|
+
obj = 1; obj.try.should equal(obj)
|
6
|
+
obj = "foo"; obj.try.should equal(obj)
|
7
|
+
obj = { :foo => "bar" }; obj.try.should equal(obj)
|
8
|
+
end
|
9
|
+
|
10
|
+
specify "objects should behave as if #try wasn't called" do
|
11
|
+
"foo".try.size.should == 3
|
12
|
+
{ :foo => :bar }.try.fetch(:foo).should == :bar
|
13
|
+
end
|
14
|
+
|
15
|
+
specify "nil should return the singleton NilClass::NilProxy" do
|
16
|
+
nil.try.should equal(NilClass::NilProxy)
|
17
|
+
end
|
18
|
+
|
19
|
+
specify "nil should ignore any calls made past #try" do
|
20
|
+
nil.try.size.should == nil
|
21
|
+
nil.try.sdlfj.should == nil
|
22
|
+
end
|
23
|
+
|
24
|
+
specify "classes should respond just like objects" do
|
25
|
+
String.try.should equal(String)
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
describe "When calling #tap" do
|
30
|
+
specify "objects should behave like Ruby 1.9's #tap" do
|
31
|
+
obj = "foo"
|
32
|
+
obj.tap { |obj| obj.size.should == 3 }.should equal(obj)
|
33
|
+
end
|
34
|
+
end
|
data/spec/github_spec.rb
ADDED
@@ -0,0 +1,51 @@
|
|
1
|
+
require File.dirname(__FILE__) + '/spec_helper'
|
2
|
+
|
3
|
+
describe "GitHub.parse_options" do
|
4
|
+
it "should parse --bare options" do
|
5
|
+
args = ["--bare", "--test"]
|
6
|
+
GitHub.parse_options(args).should == {:bare => true, :test => true}
|
7
|
+
args.should == []
|
8
|
+
end
|
9
|
+
|
10
|
+
it "should parse options intermixed with non-options" do
|
11
|
+
args = ["text", "--bare", "more text", "--option", "--foo"]
|
12
|
+
GitHub.parse_options(args).should == {:bare => true, :option => true, :foo => true}
|
13
|
+
args.should == ["text", "more text"]
|
14
|
+
end
|
15
|
+
|
16
|
+
it "should parse --foo=bar style options" do
|
17
|
+
args = ["--foo=bar", "--bare"]
|
18
|
+
GitHub.parse_options(args).should == {:bare => true, :foo => "bar"}
|
19
|
+
args.should == []
|
20
|
+
end
|
21
|
+
|
22
|
+
it "should stop parsing options at --" do
|
23
|
+
args = ["text", "--bare", "--", "--foo"]
|
24
|
+
GitHub.parse_options(args).should == {:bare => true}
|
25
|
+
args.should == ["text", "--foo"]
|
26
|
+
end
|
27
|
+
|
28
|
+
it "should handle duplicate options" do
|
29
|
+
args = ["text", "--foo=bar", "--bare", "--foo=baz"]
|
30
|
+
GitHub.parse_options(args).should == {:foo => "baz", :bare => true}
|
31
|
+
args.should == ["text"]
|
32
|
+
end
|
33
|
+
|
34
|
+
it "should handle duplicate --bare options surrounding --" do
|
35
|
+
args = ["text", "--bare", "--", "--bare"]
|
36
|
+
GitHub.parse_options(args).should == {:bare => true}
|
37
|
+
args.should == ["text", "--bare"]
|
38
|
+
end
|
39
|
+
|
40
|
+
it "should handle no options" do
|
41
|
+
args = ["text", "more text"]
|
42
|
+
GitHub.parse_options(args).should == {}
|
43
|
+
args.should == ["text", "more text"]
|
44
|
+
end
|
45
|
+
|
46
|
+
it "should handle no args" do
|
47
|
+
args = []
|
48
|
+
GitHub.parse_options(args).should == {}
|
49
|
+
args.should == []
|
50
|
+
end
|
51
|
+
end
|
data/spec/helper_spec.rb
ADDED
@@ -0,0 +1,188 @@
|
|
1
|
+
require File.dirname(__FILE__) + '/spec_helper'
|
2
|
+
|
3
|
+
class HelperRunner
|
4
|
+
def initialize(parent, name)
|
5
|
+
@parent = parent
|
6
|
+
@name = name
|
7
|
+
end
|
8
|
+
|
9
|
+
def run(&block)
|
10
|
+
self.instance_eval(&block)
|
11
|
+
end
|
12
|
+
|
13
|
+
def it(str, &block)
|
14
|
+
@parent.send :it, "#{@name} #{str}", &block
|
15
|
+
end
|
16
|
+
alias specify it
|
17
|
+
end
|
18
|
+
|
19
|
+
describe GitHub::Helper do
|
20
|
+
include SetupMethods
|
21
|
+
|
22
|
+
def self.helper(name, &block)
|
23
|
+
HelperRunner.new(self, name).run(&block)
|
24
|
+
end
|
25
|
+
|
26
|
+
before(:each) do
|
27
|
+
@helper = GitHub::Helper.new
|
28
|
+
end
|
29
|
+
|
30
|
+
helper :owner do
|
31
|
+
it "should return repo owner" do
|
32
|
+
setup_url_for :origin, "hacker"
|
33
|
+
@helper.owner.should == "hacker"
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
helper :private_url_for do
|
38
|
+
it "should return ssh-style url" do
|
39
|
+
setup_url_for :origin, "user", "merb-core"
|
40
|
+
@helper.private_url_for("wycats").should == "git@github.com:wycats/merb-core.git"
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
helper :project do
|
45
|
+
it "should return project-awesome" do
|
46
|
+
setup_url_for :origin, "user", "project-awesome"
|
47
|
+
@helper.project.should == "project-awesome"
|
48
|
+
end
|
49
|
+
|
50
|
+
it "should exit due to missing origin" do
|
51
|
+
@helper.should_receive(:url_for).twice.with(:origin).and_return("")
|
52
|
+
STDERR.should_receive(:puts).with("Error: missing remote 'origin'")
|
53
|
+
lambda { @helper.project }.should raise_error(SystemExit)
|
54
|
+
end
|
55
|
+
|
56
|
+
it "should exit due to non-github origin" do
|
57
|
+
@helper.should_receive(:url_for).twice.with(:origin).and_return("home:path/to/repo.git")
|
58
|
+
STDERR.should_receive(:puts).with("Error: remote 'origin' is not a github URL")
|
59
|
+
lambda { @helper.project }.should raise_error(SystemExit)
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
helper :public_url_for do
|
64
|
+
it "should return git:// URL" do
|
65
|
+
setup_url_for :origin, "user", "merb-core"
|
66
|
+
@helper.public_url_for("wycats").should == "git://github.com/wycats/merb-core.git"
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
helper :repo_for do
|
71
|
+
it "should return mephisto.git" do
|
72
|
+
setup_url_for :mojombo, "mojombo", "mephisto"
|
73
|
+
@helper.repo_for(:mojombo).should == "mephisto.git"
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
77
|
+
helper :user_and_repo_from do
|
78
|
+
it "should parse a git:// url" do
|
79
|
+
@helper.user_and_repo_from("git://github.com/defunkt/github.git").should == ["defunkt", "github.git"]
|
80
|
+
end
|
81
|
+
|
82
|
+
it "should parse a ssh-based url" do
|
83
|
+
@helper.user_and_repo_from("git@github.com:mojombo/god.git").should == ["mojombo", "god.git"]
|
84
|
+
end
|
85
|
+
|
86
|
+
it "should parse a non-standard ssh-based url" do
|
87
|
+
@helper.user_and_repo_from("ssh://git@github.com:mojombo/god.git").should == ["mojombo", "god.git"]
|
88
|
+
@helper.user_and_repo_from("github.com:mojombo/god.git").should == ["mojombo", "god.git"]
|
89
|
+
@helper.user_and_repo_from("ssh://github.com:mojombo/god.git").should == ["mojombo", "god.git"]
|
90
|
+
end
|
91
|
+
|
92
|
+
it "should return nothing for other urls" do
|
93
|
+
@helper.user_and_repo_from("home:path/to/repo.git").should == nil
|
94
|
+
end
|
95
|
+
|
96
|
+
it "should return nothing for invalid git:// urls" do
|
97
|
+
@helper.user_and_repo_from("git://github.com/foo").should == nil
|
98
|
+
end
|
99
|
+
|
100
|
+
it "should return nothing for invalid ssh-based urls" do
|
101
|
+
@helper.user_and_repo_from("git@github.com:kballard").should == nil
|
102
|
+
@helper.user_and_repo_from("git@github.com:kballard/test/repo.git").should == nil
|
103
|
+
@helper.user_and_repo_from("ssh://git@github.com:kballard").should == nil
|
104
|
+
@helper.user_and_repo_from("github.com:kballard").should == nil
|
105
|
+
@helper.user_and_repo_from("ssh://github.com:kballard").should == nil
|
106
|
+
end
|
107
|
+
end
|
108
|
+
|
109
|
+
helper :user_for do
|
110
|
+
it "should return defunkt" do
|
111
|
+
setup_url_for :origin, "defunkt"
|
112
|
+
@helper.user_for(:origin).should == "defunkt"
|
113
|
+
end
|
114
|
+
end
|
115
|
+
|
116
|
+
helper :url_for do
|
117
|
+
it "should call out to the shell" do
|
118
|
+
@helper.should_receive(:`).with("git config --get remote.origin.url").and_return "git://github.com/user/project.git\n"
|
119
|
+
@helper.url_for(:origin).should == "git://github.com/user/project.git"
|
120
|
+
end
|
121
|
+
end
|
122
|
+
|
123
|
+
helper :remotes do
|
124
|
+
it "should return a list of remotes" do
|
125
|
+
@helper.should_receive(:`).with('git config --get-regexp \'^remote\.(.+)\.url$\'').and_return <<-EOF
|
126
|
+
remote.origin.url git@github.com:kballard/github-gem.git
|
127
|
+
remote.defunkt.url git://github.com/defunkt/github-gem.git
|
128
|
+
remote.nex3.url git://github.com/nex3/github-gem.git
|
129
|
+
EOF
|
130
|
+
@helper.remotes.should == {
|
131
|
+
:origin => "git@github.com:kballard/github-gem.git",
|
132
|
+
:defunkt => "git://github.com/defunkt/github-gem.git",
|
133
|
+
:nex3 => "git://github.com/nex3/github-gem.git"
|
134
|
+
}
|
135
|
+
end
|
136
|
+
end
|
137
|
+
|
138
|
+
helper :tracking do
|
139
|
+
it "should return a list of remote/user_or_url pairs" do
|
140
|
+
@helper.should_receive(:remotes).and_return({
|
141
|
+
:origin => "git@github.com:kballard/github-gem.git",
|
142
|
+
:defunkt => "git://github.com/defunkt/github-gem.git",
|
143
|
+
:external => "server:path/to/github-gem.git"
|
144
|
+
})
|
145
|
+
@helper.tracking.should == {
|
146
|
+
:origin => "kballard",
|
147
|
+
:defunkt => "defunkt",
|
148
|
+
:external => "server:path/to/github-gem.git"
|
149
|
+
}
|
150
|
+
end
|
151
|
+
end
|
152
|
+
|
153
|
+
helper :tracking? do
|
154
|
+
it "should return whether the user is tracked" do
|
155
|
+
@helper.should_receive(:tracking).any_number_of_times.and_return({
|
156
|
+
:origin => "kballard",
|
157
|
+
:defunkt => "defunkt",
|
158
|
+
:external => "server:path/to/github-gem.git"
|
159
|
+
})
|
160
|
+
@helper.tracking?("kballard").should == true
|
161
|
+
@helper.tracking?("defunkt").should == true
|
162
|
+
@helper.tracking?("nex3").should == false
|
163
|
+
end
|
164
|
+
end
|
165
|
+
|
166
|
+
helper :user_and_branch do
|
167
|
+
it "should return owner and branch for unqualified branches" do
|
168
|
+
setup_url_for
|
169
|
+
@helper.should_receive(:`).with("git rev-parse --symbolic-full-name HEAD").and_return "refs/heads/master"
|
170
|
+
@helper.user_and_branch.should == ["user", "master"]
|
171
|
+
end
|
172
|
+
|
173
|
+
it "should return user and branch for user/branch-style branches" do
|
174
|
+
@helper.should_receive(:`).with("git rev-parse --symbolic-full-name HEAD").and_return "refs/heads/defunkt/wip"
|
175
|
+
@helper.user_and_branch.should == ["defunkt", "wip"]
|
176
|
+
end
|
177
|
+
end
|
178
|
+
|
179
|
+
helper :open do
|
180
|
+
it "should launch the URL" do
|
181
|
+
Launchy::Browser.next_instance.tap do |browser|
|
182
|
+
browser.should_receive(:my_os_family).any_number_of_times.and_return :windows # avoid forking
|
183
|
+
browser.should_receive(:system).with("/usr/bin/open http://www.google.com")
|
184
|
+
end
|
185
|
+
@helper.open "http://www.google.com"
|
186
|
+
end
|
187
|
+
end
|
188
|
+
end
|
data/spec/spec_helper.rb
CHANGED
@@ -3,7 +3,131 @@ require 'spec'
|
|
3
3
|
|
4
4
|
require File.dirname(__FILE__) + '/../lib/github'
|
5
5
|
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
end
|
6
|
+
class Module
|
7
|
+
def metaclass
|
8
|
+
class << self;self;end
|
9
|
+
end
|
10
|
+
end
|
11
|
+
|
12
|
+
class Spec::NextInstanceProxy
|
13
|
+
def initialize
|
14
|
+
@deferred = []
|
15
|
+
end
|
16
|
+
|
17
|
+
def method_missing(sym, *args)
|
18
|
+
proxy = Spec::NextInstanceProxy.new
|
19
|
+
@deferred << [sym, args, proxy]
|
20
|
+
proxy
|
21
|
+
end
|
22
|
+
|
23
|
+
def should_receive(*args)
|
24
|
+
method_missing(:should_receive, *args)
|
25
|
+
end
|
26
|
+
alias stub! should_receive
|
27
|
+
|
28
|
+
def invoke(obj)
|
29
|
+
@deferred.each do |(sym, args, proxy)|
|
30
|
+
result = obj.send(sym, *args)
|
31
|
+
proxy.invoke(result)
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
class Class
|
37
|
+
def next_instance
|
38
|
+
meth = metaclass.instance_method(:new)
|
39
|
+
proxy = Spec::NextInstanceProxy.new
|
40
|
+
metaclass.send :define_method, :new do |*args|
|
41
|
+
instance = meth.bind(self).call(*args)
|
42
|
+
proxy.invoke(instance)
|
43
|
+
metaclass.send :define_method, :new, meth
|
44
|
+
instance
|
45
|
+
end
|
46
|
+
proxy
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
module Spec::Example::ExampleGroupSubclassMethods
|
51
|
+
def add_guard(klass, name, is_class = false)
|
52
|
+
guarded = nil # define variable now for scoping
|
53
|
+
target = (is_class ? klass.metaclass : klass)
|
54
|
+
sep = (is_class ? "." : "#")
|
55
|
+
target.class_eval do
|
56
|
+
guarded = instance_method(name)
|
57
|
+
define_method name do |*args|
|
58
|
+
raise "Testing guards violated: Cannot call #{klass}#{sep}#{name}"
|
59
|
+
end
|
60
|
+
end
|
61
|
+
@guards ||= []
|
62
|
+
@guards << [klass, name, is_class, guarded]
|
63
|
+
end
|
64
|
+
|
65
|
+
def add_class_guard(klass, name)
|
66
|
+
add_guard(klass, name, true)
|
67
|
+
end
|
68
|
+
|
69
|
+
def unguard(klass, name, is_class = false)
|
70
|
+
row = @guards.find { |(k,n,i)| k == klass and n == name and i == is_class }
|
71
|
+
raise "#{klass}#{is_class ? "." : "#"}#{name} is not guarded" if row.nil?
|
72
|
+
(is_class ? klass.metaclass : klass).class_eval do
|
73
|
+
define_method name, row.last
|
74
|
+
end
|
75
|
+
@guards.delete row
|
76
|
+
end
|
77
|
+
|
78
|
+
def class_unguard(klass, name)
|
79
|
+
unguard(klass, name, true)
|
80
|
+
end
|
81
|
+
|
82
|
+
def unguard_all
|
83
|
+
@guards ||= []
|
84
|
+
@guards.each do |klass, name, is_class, guarded|
|
85
|
+
(is_class ? klass.metaclass : klass).class_eval do
|
86
|
+
define_method name, guarded
|
87
|
+
end
|
88
|
+
end
|
89
|
+
@guards.clear
|
90
|
+
end
|
91
|
+
end
|
92
|
+
|
93
|
+
# prevent the use of `` in tests
|
94
|
+
Spec::Runner.configure do |configuration|
|
95
|
+
# load this here so it's covered by the `` guard
|
96
|
+
configuration.prepend_before(:all) do
|
97
|
+
module GitHub
|
98
|
+
load 'helpers.rb'
|
99
|
+
load 'commands.rb'
|
100
|
+
end
|
101
|
+
end
|
102
|
+
|
103
|
+
configuration.prepend_before(:all) do
|
104
|
+
self.class.send :include, Spec::Example::ExampleGroupSubclassMethods
|
105
|
+
end
|
106
|
+
|
107
|
+
configuration.prepend_before(:each) do
|
108
|
+
add_guard Kernel, :`
|
109
|
+
add_guard Kernel, :system
|
110
|
+
add_guard Kernel, :fork
|
111
|
+
add_guard Kernel, :exec
|
112
|
+
add_class_guard Process, :fork
|
113
|
+
end
|
114
|
+
|
115
|
+
configuration.append_after(:each) do
|
116
|
+
unguard_all
|
117
|
+
end
|
118
|
+
end
|
119
|
+
|
120
|
+
# include this in any example group that defines @helper
|
121
|
+
module SetupMethods
|
122
|
+
def setup_user_and_branch(user = :user, branch = :master)
|
123
|
+
@helper.should_receive(:user_and_branch).any_number_of_times.and_return([user, branch])
|
124
|
+
end
|
125
|
+
|
126
|
+
def setup_url_for(remote = :origin, user = nil, project = :project)
|
127
|
+
if user.nil?
|
128
|
+
user = remote
|
129
|
+
user = "user" if remote == :origin
|
130
|
+
end
|
131
|
+
@helper.should_receive(:url_for).any_number_of_times.with(remote).and_return("git://github.com/#{user}/#{project}.git")
|
132
|
+
end
|
133
|
+
end
|
data/spec/ui_spec.rb
ADDED
@@ -0,0 +1,406 @@
|
|
1
|
+
require File.dirname(__FILE__) + '/spec_helper'
|
2
|
+
|
3
|
+
describe "github" do
|
4
|
+
# -- home --
|
5
|
+
specify "home should open the project home page" do
|
6
|
+
running :home do
|
7
|
+
setup_url_for
|
8
|
+
@helper.should_receive(:open).once.with("https://github.com/user/project/tree/master")
|
9
|
+
end
|
10
|
+
end
|
11
|
+
|
12
|
+
specify "home defunkt should open the home page of defunkt's fork" do
|
13
|
+
running :home, "defunkt" do
|
14
|
+
setup_url_for
|
15
|
+
@helper.should_receive(:open).once.with("https://github.com/defunkt/project/tree/master")
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
# -- browse --
|
20
|
+
specify "browse should open the project home page with the current branch" do
|
21
|
+
running :browse do
|
22
|
+
setup_url_for
|
23
|
+
setup_user_and_branch("user", "test-branch")
|
24
|
+
@helper.should_receive(:open).once.with("https://github.com/user/project/tree/test-branch")
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
specify "browse pending should open the project home page with the 'pending' branch" do
|
29
|
+
running :browse, "pending" do
|
30
|
+
setup_url_for
|
31
|
+
setup_user_and_branch("user", "test-branch")
|
32
|
+
@helper.should_receive(:open).once.with("https://github.com/user/project/tree/pending")
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
specify "browse defunkt pending should open the home page of defunkt's fork with the 'pending' branch" do
|
37
|
+
running :browse, "defunkt", "pending" do
|
38
|
+
setup_url_for
|
39
|
+
@helper.should_receive(:open).once.with("https://github.com/defunkt/project/tree/pending")
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
specify "browse defunkt/pending should open the home page of defunkt's fork with the 'pending' branch" do
|
44
|
+
running :browse, "defunkt/pending" do
|
45
|
+
setup_url_for
|
46
|
+
@helper.should_receive(:open).once.with("https://github.com/defunkt/project/tree/pending")
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
# -- network --
|
51
|
+
specify "network should open the network page for this repo" do
|
52
|
+
running :network do
|
53
|
+
setup_url_for
|
54
|
+
@helper.should_receive(:open).once.with("https://github.com/user/project/network")
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
specify "network defunkt should open the network page for defunkt's fork" do
|
59
|
+
running :network, "defunkt" do
|
60
|
+
setup_url_for
|
61
|
+
@helper.should_receive(:open).once.with("https://github.com/defunkt/project/network")
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
# -- info --
|
66
|
+
specify "info should show info for this project" do
|
67
|
+
running :info do
|
68
|
+
setup_url_for
|
69
|
+
setup_remote(:origin, :user => "user", :ssh => true)
|
70
|
+
setup_remote(:defunkt)
|
71
|
+
setup_remote(:external, :url => "home:/path/to/project.git")
|
72
|
+
stdout.should == <<-EOF
|
73
|
+
== Info for project
|
74
|
+
You are user
|
75
|
+
Currently tracking:
|
76
|
+
- user (as origin)
|
77
|
+
- defunkt (as defunkt)
|
78
|
+
- home:/path/to/project.git (as external)
|
79
|
+
EOF
|
80
|
+
end
|
81
|
+
end
|
82
|
+
|
83
|
+
# -- track --
|
84
|
+
specify "track defunkt should track a new remote for defunkt" do
|
85
|
+
running :track, "defunkt" do
|
86
|
+
setup_url_for
|
87
|
+
@helper.should_receive(:tracking?).with("defunkt").once.and_return(false)
|
88
|
+
@command.should_receive(:git).with("remote add defunkt git://github.com/defunkt/project.git").once
|
89
|
+
end
|
90
|
+
end
|
91
|
+
|
92
|
+
specify "track --private defunkt should track a new remove for defunkt using ssh" do
|
93
|
+
running :track, "--private", "defunkt" do
|
94
|
+
setup_url_for
|
95
|
+
@helper.should_receive(:tracking?).with("defunkt").once.and_return(false)
|
96
|
+
@command.should_receive(:git).with("remote add defunkt git@github.com:defunkt/project.git").once
|
97
|
+
end
|
98
|
+
end
|
99
|
+
|
100
|
+
specify "track defunkt should die if the defunkt remote exists" do
|
101
|
+
running :track, "defunkt" do
|
102
|
+
setup_url_for
|
103
|
+
@helper.should_receive(:tracking?).with("defunkt").once.and_return(true)
|
104
|
+
@command.should_receive(:die).with("Already tracking defunkt").and_return { raise "Died" }
|
105
|
+
self.should raise_error("Died")
|
106
|
+
end
|
107
|
+
end
|
108
|
+
|
109
|
+
specify "track should die with no args" do
|
110
|
+
running :track do
|
111
|
+
@command.should_receive(:die).with("Specify a user to track").and_return { raise "Died" }
|
112
|
+
self.should raise_error("Died")
|
113
|
+
end
|
114
|
+
end
|
115
|
+
|
116
|
+
# -- pull --
|
117
|
+
specify "pull should die with no args" do
|
118
|
+
running :pull do
|
119
|
+
@command.should_receive(:die).with("Specify a user to pull from").and_return { raise "Died" }
|
120
|
+
self.should raise_error("Died")
|
121
|
+
end
|
122
|
+
end
|
123
|
+
|
124
|
+
specify "pull defunkt should start tracking defunkt if they're not already tracked" do
|
125
|
+
running :pull, "defunkt" do
|
126
|
+
setup_remote(:origin, :user => "user", :ssh => true)
|
127
|
+
setup_remote(:external, :url => "home:/path/to/project.git")
|
128
|
+
GitHub.should_receive(:invoke).with(:track, "defunkt").and_return { raise "Tracked" }
|
129
|
+
self.should raise_error("Tracked")
|
130
|
+
end
|
131
|
+
end
|
132
|
+
|
133
|
+
specify "pull defunkt should create defunkt/master and pull from the defunkt remote" do
|
134
|
+
running :pull, "defunkt" do
|
135
|
+
setup_remote(:defunkt)
|
136
|
+
@command.should_receive(:git).with("checkout -b defunkt/master").ordered.and_return do
|
137
|
+
mock("checkout -b defunkt/master").tap { |m| m.stub!(:error?) }
|
138
|
+
end
|
139
|
+
@command.should_receive(:git_exec).with("pull defunkt master").ordered
|
140
|
+
stdout.should == "Switching to defunkt/master"
|
141
|
+
end
|
142
|
+
end
|
143
|
+
|
144
|
+
specify "pull defunkt should switch to pre-existing defunkt/master and pull from the defunkt remote" do
|
145
|
+
running :pull, "defunkt" do
|
146
|
+
setup_remote(:defunkt)
|
147
|
+
@command.should_receive(:git).with("checkout -b defunkt/master").ordered.and_return do
|
148
|
+
mock("checkout -b defunkt/master").tap { |m| m.should_receive(:error?) { true } }
|
149
|
+
end
|
150
|
+
@command.should_receive(:git).with("checkout defunkt/master").ordered
|
151
|
+
@command.should_receive(:git_exec).with("pull defunkt master").ordered
|
152
|
+
stdout.should == "Switching to defunkt/master"
|
153
|
+
end
|
154
|
+
end
|
155
|
+
|
156
|
+
specify "pull defunkt wip should create defunkt/wip and pull from wip branch on defunkt remote" do
|
157
|
+
running :pull, "defunkt", "wip" do
|
158
|
+
setup_remote(:defunkt)
|
159
|
+
@command.should_receive(:git).with("checkout -b defunkt/wip").ordered.and_return do
|
160
|
+
mock("checkout -b defunkt/wip").tap { |m| m.stub!(:error?) }
|
161
|
+
end
|
162
|
+
@command.should_receive(:git_exec).with("pull defunkt wip").ordered
|
163
|
+
stdout.should == "Switching to defunkt/wip"
|
164
|
+
end
|
165
|
+
end
|
166
|
+
|
167
|
+
specify "pull defunkt/wip should switch to pre-existing defunkt/wip and pull from wip branch on defunkt remote" do
|
168
|
+
running :pull, "defunkt/wip" do
|
169
|
+
setup_remote(:defunkt)
|
170
|
+
@command.should_receive(:git).with("checkout -b defunkt/wip").ordered.and_return do
|
171
|
+
mock("checkout -b defunkt/wip").tap { |m| m.should_receive(:error?) { true } }
|
172
|
+
end
|
173
|
+
@command.should_receive(:git).with("checkout defunkt/wip").ordered
|
174
|
+
@command.should_receive(:git_exec).with("pull defunkt wip").ordered
|
175
|
+
stdout.should == "Switching to defunkt/wip"
|
176
|
+
end
|
177
|
+
end
|
178
|
+
|
179
|
+
specify "pull --merge defunkt should pull from defunkt remote into current branch" do
|
180
|
+
running :pull, "--merge", "defunkt" do
|
181
|
+
setup_remote(:defunkt)
|
182
|
+
@command.should_receive(:git_exec).with("pull defunkt master")
|
183
|
+
end
|
184
|
+
end
|
185
|
+
|
186
|
+
# -- clone --
|
187
|
+
specify "clone should die with no args" do
|
188
|
+
running :clone do
|
189
|
+
@command.should_receive(:die).with("Specify a user to pull from").and_return { raise "Died" }
|
190
|
+
self.should raise_error("Died")
|
191
|
+
end
|
192
|
+
end
|
193
|
+
|
194
|
+
specify "clone should die with just one arg" do
|
195
|
+
running :clone, "user" do
|
196
|
+
@command.should_receive(:die).with("Specify a repo to pull from").and_return { raise "Died" }
|
197
|
+
self.should raise_error("Died")
|
198
|
+
end
|
199
|
+
end
|
200
|
+
|
201
|
+
specify "clone defunkt github-gem should clone the repo" do
|
202
|
+
running :clone, "defunkt", "github-gem" do
|
203
|
+
@command.should_receive(:git_exec).with("clone git://github.com/defunkt/github-gem.git")
|
204
|
+
end
|
205
|
+
end
|
206
|
+
|
207
|
+
specify "clone --ssh defunkt github-gem should clone the repo using the private URL" do
|
208
|
+
running :clone, "--ssh", "defunkt", "github-gem" do
|
209
|
+
@command.should_receive(:git_exec).with("clone git@github.com:defunkt/github-gem.git")
|
210
|
+
end
|
211
|
+
end
|
212
|
+
|
213
|
+
specify "clone defunkt github-gem repo should clone the repo into the dir 'repo'" do
|
214
|
+
running :clone, "defunkt", "github-gem", "repo" do
|
215
|
+
@command.should_receive(:git_exec).with("clone git://github.com/defunkt/github-gem.git repo")
|
216
|
+
end
|
217
|
+
end
|
218
|
+
|
219
|
+
specify "clone --ssh defunkt github-gem repo should clone the repo using the private URL into the dir 'repo'" do
|
220
|
+
running :clone, "--ssh", "defunkt", "github-gem", "repo" do
|
221
|
+
@command.should_receive(:git_exec).with("clone git@github.com:defunkt/github-gem.git repo")
|
222
|
+
end
|
223
|
+
end
|
224
|
+
|
225
|
+
# -- pull-request --
|
226
|
+
specify "pull-request should die with no args" do
|
227
|
+
running :'pull-request' do
|
228
|
+
setup_url_for
|
229
|
+
@command.should_receive(:die).with("Specify a user for the pull request").and_return { raise "Died" }
|
230
|
+
self.should raise_error("Died")
|
231
|
+
end
|
232
|
+
end
|
233
|
+
|
234
|
+
specify "pull-request user should track user if untracked" do
|
235
|
+
running :'pull-request', "user" do
|
236
|
+
setup_url_for
|
237
|
+
setup_remote :origin, :user => "kballard"
|
238
|
+
setup_remote :defunkt
|
239
|
+
GitHub.should_receive(:invoke).with(:track, "user").and_return { raise "Tracked" }
|
240
|
+
self.should raise_error("Tracked")
|
241
|
+
end
|
242
|
+
end
|
243
|
+
|
244
|
+
specify "pull-request user/branch should generate a pull request" do
|
245
|
+
running :'pull-request', "user/branch" do
|
246
|
+
setup_url_for
|
247
|
+
setup_remote :origin, :user => "kballard"
|
248
|
+
setup_remote :user
|
249
|
+
@command.should_receive(:git_exec).with("request-pull user/branch origin")
|
250
|
+
end
|
251
|
+
end
|
252
|
+
|
253
|
+
specify "pull-request user should generate a pull request with branch master" do
|
254
|
+
running :'pull-request', "user" do
|
255
|
+
setup_url_for
|
256
|
+
setup_remote :origin, :user => "kballard"
|
257
|
+
setup_remote :user
|
258
|
+
@command.should_receive(:git_exec).with("request-pull user/master origin")
|
259
|
+
end
|
260
|
+
end
|
261
|
+
|
262
|
+
specify "pull-request user branch should generate a pull request" do
|
263
|
+
running:'pull-request', "user", "branch" do
|
264
|
+
setup_url_for
|
265
|
+
setup_remote :origin, :user => "kballard"
|
266
|
+
setup_remote :user
|
267
|
+
@command.should_receive(:git_exec).with("request-pull user/branch origin")
|
268
|
+
end
|
269
|
+
end
|
270
|
+
|
271
|
+
# -- default --
|
272
|
+
specify "should print the default message" do
|
273
|
+
running :default do
|
274
|
+
GitHub.should_receive(:descriptions).any_number_of_times.and_return({
|
275
|
+
"home" => "Open the home page",
|
276
|
+
"track" => "Track a new repo",
|
277
|
+
"browse" => "Browse the github page for this branch",
|
278
|
+
"command" => "description"
|
279
|
+
})
|
280
|
+
GitHub.should_receive(:flag_descriptions).any_number_of_times.and_return({
|
281
|
+
"home" => {:flag => "Flag description"},
|
282
|
+
"track" => {:flag1 => "Flag one", :flag2 => "Flag two"},
|
283
|
+
"browse" => {},
|
284
|
+
"command" => {}
|
285
|
+
})
|
286
|
+
@command.should_receive(:puts).with("Usage: github command <space separated arguments>", '').ordered
|
287
|
+
@command.should_receive(:puts).with("Available commands:", '').ordered
|
288
|
+
@command.should_receive(:puts).with(" home => Open the home page")
|
289
|
+
@command.should_receive(:puts).with(" --flag: Flag description")
|
290
|
+
@command.should_receive(:puts).with(" track => Track a new repo")
|
291
|
+
@command.should_receive(:puts).with(" --flag1: Flag one")
|
292
|
+
@command.should_receive(:puts).with(" --flag2: Flag two")
|
293
|
+
@command.should_receive(:puts).with(" browse => Browse the github page for this branch")
|
294
|
+
@command.should_receive(:puts).with(" command => description")
|
295
|
+
@command.should_receive(:puts).with()
|
296
|
+
end
|
297
|
+
end
|
298
|
+
|
299
|
+
# -----------------
|
300
|
+
|
301
|
+
def running(cmd, *args, &block)
|
302
|
+
Runner.new(self, cmd, *args, &block).run
|
303
|
+
end
|
304
|
+
|
305
|
+
class Runner
|
306
|
+
include SetupMethods
|
307
|
+
|
308
|
+
def initialize(parent, cmd, *args, &block)
|
309
|
+
@cmd_name = cmd.to_s
|
310
|
+
@command = GitHub.commands[cmd.to_s]
|
311
|
+
@helper = @command.helper
|
312
|
+
@args = args
|
313
|
+
@block = block
|
314
|
+
@parent = parent
|
315
|
+
end
|
316
|
+
|
317
|
+
def run
|
318
|
+
self.instance_eval &@block
|
319
|
+
mock_remotes unless @remotes.nil?
|
320
|
+
GitHub.should_receive(:load).with("commands.rb")
|
321
|
+
GitHub.should_receive(:load).with("helpers.rb")
|
322
|
+
args = @args.clone
|
323
|
+
GitHub.parse_options(args) # strip out the flags
|
324
|
+
GitHub.should_receive(:invoke).with(@cmd_name, *args).and_return do
|
325
|
+
GitHub.send(GitHub.send(:__mock_proxy).send(:munge, :invoke), @cmd_name, *args)
|
326
|
+
end
|
327
|
+
invoke = lambda { GitHub.activate([@cmd_name, *@args]) }
|
328
|
+
if @expected_result
|
329
|
+
expectation, result = @expected_result
|
330
|
+
case result
|
331
|
+
when Spec::Matchers::RaiseError, Spec::Matchers::Change, Spec::Matchers::ThrowSymbol
|
332
|
+
invoke.send expectation, result
|
333
|
+
else
|
334
|
+
invoke.call.send expectation, result
|
335
|
+
end
|
336
|
+
else
|
337
|
+
invoke.call
|
338
|
+
end
|
339
|
+
@stdout_mock.invoke unless @stdout_mock.nil?
|
340
|
+
end
|
341
|
+
|
342
|
+
def setup_remote(remote, options = {:user => nil, :project => "project"})
|
343
|
+
@remotes ||= {}
|
344
|
+
user = options[:user] || remote
|
345
|
+
project = options[:project]
|
346
|
+
ssh = options[:ssh]
|
347
|
+
url = options[:url]
|
348
|
+
if url
|
349
|
+
@remotes[remote.to_sym] = url
|
350
|
+
elsif ssh
|
351
|
+
@remotes[remote.to_sym] = "git@github.com:#{user}/#{project}.git"
|
352
|
+
else
|
353
|
+
@remotes[remote.to_sym] = "git://github.com/#{user}/#{project}.git"
|
354
|
+
end
|
355
|
+
end
|
356
|
+
|
357
|
+
def mock_remotes()
|
358
|
+
@helper.should_receive(:remotes).any_number_of_times.and_return(@remotes)
|
359
|
+
end
|
360
|
+
|
361
|
+
def should(result)
|
362
|
+
@expected_result = [:should, result]
|
363
|
+
end
|
364
|
+
|
365
|
+
def should_not(result)
|
366
|
+
@expected_result = [:should_not, result]
|
367
|
+
end
|
368
|
+
|
369
|
+
def stdout
|
370
|
+
if @stdout_mock.nil?
|
371
|
+
output = ""
|
372
|
+
@stdout_mock = DeferredMock.new(output)
|
373
|
+
STDOUT.should_receive(:write).any_number_of_times do |str|
|
374
|
+
output << str
|
375
|
+
end
|
376
|
+
end
|
377
|
+
@stdout_mock
|
378
|
+
end
|
379
|
+
|
380
|
+
class DeferredMock
|
381
|
+
def initialize(obj = nil)
|
382
|
+
@obj = obj
|
383
|
+
@calls = []
|
384
|
+
end
|
385
|
+
|
386
|
+
def invoke(obj = nil)
|
387
|
+
obj ||= @obj
|
388
|
+
@calls.each do |sym, args|
|
389
|
+
obj.send sym, *args
|
390
|
+
end
|
391
|
+
end
|
392
|
+
|
393
|
+
def should(*args)
|
394
|
+
@calls << [:should, args]
|
395
|
+
end
|
396
|
+
|
397
|
+
def should_not(*args)
|
398
|
+
@calls << [:should_not, args]
|
399
|
+
end
|
400
|
+
end
|
401
|
+
|
402
|
+
def method_missing(sym, *args)
|
403
|
+
@parent.send sym, *args
|
404
|
+
end
|
405
|
+
end
|
406
|
+
end
|