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.
@@ -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
@@ -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
@@ -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
- module GitHub
7
- load 'helpers.rb'
8
- load 'commands.rb'
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