octopolo 0.3.6 → 0.4.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,243 @@
1
+ require "spec_helper"
2
+ require_relative "../../../lib/octopolo/github/issue"
3
+ require_relative "../../../lib/octopolo/github/issue_creator"
4
+
5
+ module Octopolo
6
+ module GitHub
7
+ describe Issue do
8
+ let(:repo_name) { "account/repo" }
9
+ let(:issue_number) { 7 }
10
+ let(:issue_hash) { stub }
11
+ let(:comments) { stub }
12
+ let(:octo) { stub }
13
+
14
+ context ".new" do
15
+ it "remembers the issue identifiers" do
16
+ i = Issue.new repo_name, issue_number
17
+ i.repo_name.should == repo_name
18
+ i.number.should == issue_number
19
+ end
20
+
21
+ it "optionally accepts the github data" do
22
+ i = Issue.new repo_name, issue_number, octo
23
+ i.data.should == octo
24
+ end
25
+
26
+ it "fails if not given a repo name" do
27
+ expect { Issue.new nil, issue_number }.to raise_error(Issue::MissingParameter)
28
+ end
29
+
30
+ it "fails if not given a issue number" do
31
+ expect { Issue.new repo_name, nil }.to raise_error(Issue::MissingParameter)
32
+ end
33
+ end
34
+
35
+ context "#data" do
36
+ let(:issue) { Issue.new repo_name, issue_number }
37
+
38
+ it "fetches the details from GitHub" do
39
+ GitHub.should_receive(:issue).with(issue.repo_name, issue.number) { octo }
40
+ issue.data.should == octo
41
+ end
42
+
43
+ it "catches the information" do
44
+ GitHub.should_receive(:issue).once { octo }
45
+ issue.data
46
+ issue.data
47
+ end
48
+
49
+ it "fails if given invalid information" do
50
+ GitHub.should_receive(:issue).and_raise(Octokit::NotFound)
51
+ expect { issue.data }.to raise_error(Issue::NotFound)
52
+ end
53
+ end
54
+
55
+ context "fetching its attributes from Octokit" do
56
+ let(:issue) { Issue.new repo_name, issue_number }
57
+
58
+ before do
59
+ issue.stub(data: octo)
60
+ end
61
+
62
+ context "#title" do
63
+ let(:octo) { stub(title: "the title") }
64
+
65
+ it "retrieves from the github data" do
66
+ issue.title.should == octo.title
67
+ end
68
+ end
69
+
70
+ context "#comments" do
71
+ it "fetches through octokit" do
72
+ GitHub.should_receive(:issue_comments).with(issue.repo_name, issue.number) { comments }
73
+ issue.comments.should == comments
74
+ end
75
+
76
+ it "caches the result" do
77
+ GitHub.should_receive(:issue_comments).once { comments }
78
+ issue.comments
79
+ issue.comments
80
+ end
81
+ end
82
+
83
+ context "#commenter_names" do
84
+ let(:comment1) { stub(user: stub(login: "pbyrne")) }
85
+ let(:comment2) { stub(user: stub(login: "anfleene")) }
86
+
87
+ before do
88
+ issue.stub(comments: [comment1, comment2])
89
+ end
90
+
91
+ it "returns only unique values" do
92
+ # make it same commenter
93
+ comment2.user.stub(login: comment1.user.login)
94
+ names = issue.commenter_names
95
+ names.size.should == 1
96
+ end
97
+ end
98
+
99
+ context "#without_octopolo_users" do
100
+ let(:users) { ["anfleene", "tst-octopolo"] }
101
+
102
+ it "excludes the github octopolo users" do
103
+ issue.exclude_octopolo_user(users).should_not include("tst-octopolo")
104
+ issue.exclude_octopolo_user(users).should include("anfleene")
105
+ end
106
+ end
107
+
108
+ context "#url" do
109
+ let(:octo) { stub(html_url: "http://example.com") }
110
+
111
+ it "retrieves from the github data" do
112
+ issue.url.should == octo.html_url
113
+ end
114
+ end
115
+
116
+ context "#external_urls" do
117
+ # nicked from https://github.com/tstmedia/ngin/issue/1151
118
+ let(:body) do
119
+ <<-END
120
+ http://thedesk.tstmedia.com/admin.php?pg=request&reqid=44690 - verified
121
+ http://thedesk.tstmedia.com/admin.php?pg=request&reqid=44693 - verified
122
+
123
+ development_ftp_server: ftp.tstmedia.com
124
+ development_username: startribuneftptest@ftp.tstmedia.com
125
+ development_password: JUm1kU7STYt0
126
+ http://www.ngin.com.stage.ngin-staging.com/api/volleyball/stats/summaries?id=68382&gender=girls&tst_test=1&date=8/24/2012
127
+ END
128
+ end
129
+
130
+ before do
131
+ issue.stub(body: body)
132
+ end
133
+
134
+ it "parses from the body" do
135
+ urls = issue.external_urls
136
+ urls.size.should == 3
137
+ urls.should include "http://thedesk.tstmedia.com/admin.php?pg=request&reqid=44690"
138
+ urls.should include "http://thedesk.tstmedia.com/admin.php?pg=request&reqid=44693"
139
+ urls.should include "http://www.ngin.com.stage.ngin-staging.com/api/volleyball/stats/summaries?id=68382&gender=girls&tst_test=1&date=8/24/2012"
140
+ end
141
+ end
142
+
143
+ context "#body" do
144
+ let(:octo) { stub(body: "asdf") }
145
+
146
+ it "retrieves from the github data" do
147
+ issue.body.should == octo.body
148
+ end
149
+
150
+ it "returns an empty string if the GitHub data has no body" do
151
+ octo.stub(body: nil)
152
+ issue.body.should == ""
153
+ end
154
+ end
155
+ end
156
+
157
+ context "#human_app_name" do
158
+ let(:repo_name) { "account_name/repo_name" }
159
+ let(:issue) { Issue.new repo_name, issue_number }
160
+
161
+ it "infers from the repo_name" do
162
+ issue.repo_name = "account/foo"
163
+ issue.human_app_name.should == "Foo"
164
+ issue.repo_name = "account/foo_bar"
165
+ issue.human_app_name.should == "Foo Bar"
166
+ end
167
+ end
168
+
169
+ context "#write_comment(message)" do
170
+ let(:issue) { Issue.new repo_name, issue_number }
171
+ let(:message) { "Test message" }
172
+ let(:error) { Octokit::UnprocessableEntity.new }
173
+
174
+ it "creates the message through octokit" do
175
+ GitHub.should_receive(:add_comment).with(issue.repo_name, issue.number, ":octocat: #{message}")
176
+
177
+ issue.write_comment message
178
+ end
179
+
180
+ it "raises CommentFailed if an exception occurs" do
181
+ GitHub.should_receive(:add_comment).and_raise(error)
182
+
183
+ expect { issue.write_comment message }.to raise_error(Issue::CommentFailed, "Unable to write the comment: '#{error.message}'")
184
+ end
185
+ end
186
+
187
+ context ".create repo_name, options" do
188
+ let(:options) { stub(:hash) }
189
+ let(:number) { stub(:integer) }
190
+ let(:data) { stub(:data)}
191
+ let(:creator) { stub(:issue_creator, number: number, data: data)}
192
+ let(:issue) { stub(:issue) }
193
+
194
+ it "passes on to IssueCreator and returns a new Issue" do
195
+ IssueCreator.should_receive(:perform).with(repo_name, options) { creator }
196
+ Issue.should_receive(:new).with(repo_name, number, data) { issue }
197
+ Issue.create(repo_name, options).should == issue
198
+ end
199
+ end
200
+
201
+ context "labeling" do
202
+ let(:label1) { Label.new(name: "low-risk", color: "343434") }
203
+ let(:label2) { Label.new(name: "high-risk", color: '565656') }
204
+ let(:issue) { Issue.new repo_name, issue_number }
205
+
206
+ context "#add_labels" do
207
+ it "sends the correct arguments to add_labels_to_issue for multiple labels" do
208
+ allow(Label).to receive(:build_label_array) {[label1,label2]}
209
+ expect(GitHub).to receive(:add_labels_to_issue).with(repo_name, issue_number, ["low-risk","high-risk"])
210
+ issue.add_labels([label1, label2])
211
+ end
212
+
213
+ it "sends the correct arguments to add_labels_to_issue for a single label" do
214
+ allow(Label).to receive(:build_label_array) {[label1]}
215
+ expect(GitHub).to receive(:add_labels_to_issue).with(repo_name, issue_number, ["low-risk"])
216
+ issue.add_labels(label1)
217
+ end
218
+ end
219
+
220
+ context "#remove_from_issue" do
221
+
222
+ it "sends the correct arguments to remove_label" do
223
+ allow(Label).to receive(:build_label_array) {[label1]}
224
+ expect(GitHub).to receive(:remove_label).with(repo_name, issue_number, "low-risk")
225
+ issue.remove_labels(label1)
226
+ end
227
+
228
+ it "calls remove_label only once" do
229
+ allow(Label).to receive(:build_label_array) {[label1]}
230
+ expect(GitHub).to receive(:remove_label).once
231
+ issue.remove_labels(label1)
232
+ end
233
+
234
+ it "calls remove_label twice" do
235
+ allow(Label).to receive(:build_label_array) {[label1, label2]}
236
+ expect(GitHub).to receive(:remove_label).twice
237
+ issue.remove_labels([label1,label2])
238
+ end
239
+ end
240
+ end
241
+ end
242
+ end
243
+ end
@@ -34,7 +34,7 @@ module Octopolo
34
34
  end
35
35
 
36
36
  context "#perform" do
37
- let(:pull_request_data) { stub(:mash, number: 123) }
37
+ let(:data) { stub(:mash, number: 123) }
38
38
 
39
39
  before do
40
40
  creator.stub({
@@ -46,10 +46,10 @@ module Octopolo
46
46
  end
47
47
 
48
48
  it "generates the pull request with the given details and retains the information" do
49
- GitHub.should_receive(:create_pull_request).with(repo_name, destination_branch, source_branch, title, body) { pull_request_data }
50
- creator.perform.should == pull_request_data
51
- creator.number.should == pull_request_data.number
52
- creator.pull_request_data.should == pull_request_data
49
+ GitHub.should_receive(:create_pull_request).with(repo_name, destination_branch, source_branch, title, body) { data }
50
+ creator.perform.should == data
51
+ creator.number.should == data.number
52
+ creator.data.should == data
53
53
  end
54
54
 
55
55
  it "raises CannotCreate if any exception occurs" do
@@ -72,17 +72,17 @@ module Octopolo
72
72
  end
73
73
  end
74
74
 
75
- context "#pull_request_data" do
76
- let(:details) { stub(:pull_request_data) }
75
+ context "#data" do
76
+ let(:details) { stub(:data) }
77
77
 
78
78
  it "returns the stored pull request details" do
79
- creator.pull_request_data = details
80
- creator.pull_request_data.should == details
79
+ creator.data = details
80
+ creator.data.should == details
81
81
  end
82
82
 
83
83
  it "raises an exception if no information has been captured yet" do
84
- creator.pull_request_data = nil
85
- expect { creator.pull_request_data }.to raise_error(PullRequestCreator::NotYetCreated)
84
+ creator.data = nil
85
+ expect { creator.data }.to raise_error(PullRequestCreator::NotYetCreated)
86
86
  end
87
87
  end
88
88
 
@@ -20,7 +20,7 @@ module Octopolo
20
20
 
21
21
  it "optionally accepts the github data" do
22
22
  pr = PullRequest.new repo_name, pr_number, octo
23
- pr.pull_request_data.should == octo
23
+ pr.data.should == octo
24
24
  end
25
25
 
26
26
  it "fails if not given a repo name" do
@@ -32,23 +32,23 @@ module Octopolo
32
32
  end
33
33
  end
34
34
 
35
- context "#pull_request_data" do
35
+ context "#data" do
36
36
  let(:pull) { PullRequest.new repo_name, pr_number }
37
37
 
38
38
  it "fetches the details from GitHub" do
39
39
  GitHub.should_receive(:pull_request).with(pull.repo_name, pull.number) { octo }
40
- pull.pull_request_data.should == octo
40
+ pull.data.should == octo
41
41
  end
42
42
 
43
43
  it "catches the information" do
44
44
  GitHub.should_receive(:pull_request).once { octo }
45
- pull.pull_request_data
46
- pull.pull_request_data
45
+ pull.data
46
+ pull.data
47
47
  end
48
48
 
49
49
  it "fails if given invalid information" do
50
50
  GitHub.should_receive(:pull_request).and_raise(Octokit::NotFound)
51
- expect { pull.pull_request_data }.to raise_error(PullRequest::NotFound)
51
+ expect { pull.data }.to raise_error(PullRequest::NotFound)
52
52
  end
53
53
  end
54
54
 
@@ -56,7 +56,7 @@ module Octopolo
56
56
  let(:pull) { PullRequest.new repo_name, pr_number }
57
57
 
58
58
  before do
59
- pull.stub(pull_request_data: octo)
59
+ pull.stub(data: octo)
60
60
  end
61
61
 
62
62
  context "#title" do
@@ -130,11 +130,11 @@ module Octopolo
130
130
 
131
131
  before do
132
132
  pull.stub(comments: [comment1, comment2], author_names: [])
133
+ GitHub::User.stub(:new).with("pbyrne").and_return(stub(:author_name => "pbyrne"))
134
+ GitHub::User.stub(:new).with("anfleene").and_return(stub(:author_name => "anfleene"))
133
135
  end
134
136
 
135
137
  it "returns the names of the commit authors" do
136
- GitHub.stub(:user).with("pbyrne").and_return(Hashie::Mash.new(:name => "pbyrne"))
137
- GitHub.stub(:user).with("anfleene").and_return(Hashie::Mash.new(:name => "anfleene"))
138
138
  names = pull.commenter_names
139
139
  names.should_not be_empty
140
140
  names.size.should == 2
@@ -160,8 +160,8 @@ module Octopolo
160
160
  let(:users) { ["anfleene", "tst-octopolo"] }
161
161
 
162
162
  it "excludes the github octopolo users" do
163
- pull.exlude_octopolo_user(users).should_not include("tst-octopolo")
164
- pull.exlude_octopolo_user(users).should include("anfleene")
163
+ pull.exclude_octopolo_user(users).should_not include("tst-octopolo")
164
+ pull.exclude_octopolo_user(users).should include("anfleene")
165
165
  end
166
166
  end
167
167
 
@@ -276,13 +276,13 @@ module Octopolo
276
276
  context ".create repo_name, options" do
277
277
  let(:options) { stub(:hash) }
278
278
  let(:number) { stub(:integer) }
279
- let(:pull_request_data) { stub(:pull_request_data)}
280
- let(:creator) { stub(:pull_request_creator, number: number, pull_request_data: pull_request_data)}
279
+ let(:data) { stub(:data)}
280
+ let(:creator) { stub(:pull_request_creator, number: number, data: data)}
281
281
  let(:pull_request) { stub(:pull_request) }
282
282
 
283
283
  it "passes on to PullRequestCreator and returns a new PullRequest" do
284
284
  PullRequestCreator.should_receive(:perform).with(repo_name, options) { creator }
285
- PullRequest.should_receive(:new).with(repo_name, number, pull_request_data) { pull_request }
285
+ PullRequest.should_receive(:new).with(repo_name, number, data) { pull_request }
286
286
  PullRequest.create(repo_name, options).should == pull_request
287
287
  end
288
288
  end
@@ -295,13 +295,13 @@ module Octopolo
295
295
  context "#add_labels" do
296
296
  it "sends the correct arguments to add_labels_to_pull for multiple labels" do
297
297
  allow(Label).to receive(:build_label_array) {[label1,label2]}
298
- expect(GitHub).to receive(:add_labels_to_pull).with(repo_name, pr_number, ["low-risk","high-risk"])
298
+ expect(GitHub).to receive(:add_labels_to_issue).with(repo_name, pr_number, ["low-risk","high-risk"])
299
299
  pull_request.add_labels([label1, label2])
300
300
  end
301
301
 
302
302
  it "sends the correct arguments to add_labels_to_pull for a single label" do
303
303
  allow(Label).to receive(:build_label_array) {[label1]}
304
- expect(GitHub).to receive(:add_labels_to_pull).with(repo_name, pr_number, ["low-risk"])
304
+ expect(GitHub).to receive(:add_labels_to_issue).with(repo_name, pr_number, ["low-risk"])
305
305
  pull_request.add_labels(label1)
306
306
  end
307
307
  end
@@ -54,6 +54,14 @@ module Octopolo
54
54
  end
55
55
  end
56
56
 
57
+ context ".issue *args" do
58
+ it "sends onto the client wrapper" do
59
+ client.should_receive(:issue).with("a", "b") { data }
60
+ result = GitHub.issue("a", "b")
61
+ result.should == data
62
+ end
63
+ end
64
+
57
65
  context ".pull_request_commits *args" do
58
66
  it "sends onto the client wrapper" do
59
67
  client.should_receive(:pull_request_commits).with("a", "b") { data }
@@ -96,6 +104,13 @@ module Octopolo
96
104
  end
97
105
  end
98
106
 
107
+ context ".create_issue" do
108
+ it "sends the issue to the API" do
109
+ client.should_receive(:create_issue).with("repo", "title", "body") { data }
110
+ GitHub.create_issue("repo", "title", "body").should == data
111
+ end
112
+ end
113
+
99
114
  context ".add_comment" do
100
115
  it "sends the comment to the API" do
101
116
  client.should_receive(:add_comment).with("repo", 123, "contents of comment")
@@ -0,0 +1,233 @@
1
+ require "spec_helper"
2
+ require_relative "../../../lib/octopolo/scripts/issue"
3
+ require_relative "../../../lib/octopolo/github/issue"
4
+
5
+ module Octopolo
6
+ module Scripts
7
+ describe Issue do
8
+ let(:config) do
9
+ stub(:config, {
10
+ deploy_branch: "production",
11
+ github_repo: "tstmedia/foo",
12
+ use_pivotal_tracker: true,
13
+ use_jira: true
14
+ })
15
+ end
16
+ let(:cli) { stub(:cli) }
17
+ let(:git) { stub(:Git) }
18
+ let(:issue_url) { "http://github.com/tstmedia/octopolo/issues/0" }
19
+ let(:issue) { stub(:issue) }
20
+
21
+ subject { Issue.new }
22
+
23
+ before do
24
+ Issue.any_instance.stub({
25
+ :cli => cli,
26
+ :config => config,
27
+ :git => git
28
+ })
29
+ end
30
+
31
+ context "#new" do
32
+ it "accepts options" do
33
+ expect(Issue.new(:foo => 'bar').options).to eq(:foo => 'bar')
34
+ end
35
+ end
36
+
37
+ context "#execute" do
38
+ it "if connected to GitHub, asks some questions, creates the issue, and opens it" do
39
+ GitHub.should_receive(:connect).and_yield
40
+ expect(subject).to receive(:ask_questionaire)
41
+ expect(subject).to receive(:create_issue)
42
+ expect(subject).to receive(:update_pivotal)
43
+ expect(subject).to receive(:update_jira)
44
+ expect(subject).to receive(:update_label)
45
+ expect(subject).to receive(:open_in_browser)
46
+
47
+ subject.execute
48
+ end
49
+
50
+ it "if not connected to GitHub, does nothing" do
51
+ GitHub.should_receive(:connect) # and not yield, no github credentials
52
+ expect { subject.execute }.to_not raise_error
53
+ end
54
+ end
55
+
56
+ context "#ask_questionaire" do
57
+ it "asks appropriate questions to create a issue" do
58
+ expect(subject).to receive(:announce)
59
+ expect(subject).to receive(:ask_title)
60
+ expect(subject).to receive(:ask_pivotal_ids)
61
+ expect(subject).to receive(:ask_jira_ids)
62
+ expect(subject).to receive(:ask_label)
63
+
64
+ subject.send(:ask_questionaire)
65
+ end
66
+ end
67
+
68
+ context "#announce" do
69
+ it "displays information about the issue to be created" do
70
+ cli.should_receive(:say).with("Preparing an issue for #{config.github_repo}.")
71
+ subject.send(:announce)
72
+ end
73
+ end
74
+
75
+ context "#ask_title" do
76
+ let(:title) { "title" }
77
+
78
+ it "asks for and captures a title for the issue" do
79
+ cli.should_receive(:prompt).with("Title:") { title }
80
+ subject.send(:ask_title)
81
+ expect(subject.title).to eq(title)
82
+ end
83
+ end
84
+
85
+ context "#ask_label" do
86
+ let(:label1) {Octopolo::GitHub::Label.new(name: "low-risk", color: '151515')}
87
+ let(:label2) {Octopolo::GitHub::Label.new(name: "high-risk", color: '151515')}
88
+ let(:choices) {["low-risk","high-risk","None"]}
89
+
90
+ it "asks for and capture a label" do
91
+ allow(Octopolo::GitHub::Label).to receive(:all) {[label1,label2]}
92
+ expect(cli).to receive(:ask).with("Label:", choices)
93
+ subject.send(:ask_label)
94
+ end
95
+
96
+ it "asks for a label" do
97
+ allow(Octopolo::GitHub::Label).to receive(:all) {[label1,label2]}
98
+ allow(Octopolo::GitHub::Label).to receive(:get_names) {choices}
99
+ allow(cli).to receive(:ask) {"low-risk"}
100
+ expect(subject.send(:ask_label)).to eq(label1)
101
+ end
102
+ end
103
+
104
+ context "#ask_pivotal_ids" do
105
+ let(:ids_with_whitespace) { "123 456" }
106
+ let(:ids_with_commas) { "234, 567" }
107
+
108
+ it "asks for and captures IDs for related pivotal tasks" do
109
+ cli.should_receive(:prompt).with("Pivotal Tracker story ID(s):") { ids_with_whitespace }
110
+ subject.send(:ask_pivotal_ids)
111
+ expect(subject.pivotal_ids).to eq(%w(123 456))
112
+ end
113
+
114
+ it "asks for and captures IDs with commas" do
115
+ cli.should_receive(:prompt).with("Pivotal Tracker story ID(s):") { ids_with_commas }
116
+ subject.send(:ask_pivotal_ids)
117
+ expect(subject.pivotal_ids).to eq(%w(234 567))
118
+ end
119
+
120
+ it "sets to an empty array if not provided an ansswer" do
121
+ cli.should_receive(:prompt).with("Pivotal Tracker story ID(s):") { "" }
122
+ subject.send(:ask_pivotal_ids)
123
+ expect(subject.pivotal_ids).to eq([])
124
+ end
125
+ end
126
+
127
+ context "#create_issue" do
128
+ let(:attributes) { stub(:hash) }
129
+
130
+ before do
131
+ subject.stub(:issue_attributes) { attributes }
132
+ end
133
+
134
+ it "creates and stores the issue" do
135
+ GitHub::Issue.should_receive(:create).with(config.github_repo, attributes) { issue }
136
+ subject.send(:create_issue)
137
+ expect(subject.issue).to eq(issue)
138
+ end
139
+ end
140
+
141
+ context "#issue_attributes" do
142
+ before do
143
+ subject.title = "title"
144
+ subject.pivotal_ids = %w(123)
145
+ end
146
+
147
+ it "combines the anssers with a handful of deault values" do
148
+ subject.send(:issue_attributes).should == {
149
+ title: subject.title,
150
+ pivotal_ids: subject.pivotal_ids,
151
+ jira_ids: subject.jira_ids,
152
+ editor: nil
153
+ }
154
+ end
155
+ end
156
+
157
+ context "#label_choices" do
158
+ let(:label1) { Octopolo::GitHub::Label.new(name: "low-risk", color: '151515') }
159
+ let(:label2) { Octopolo::GitHub::Label.new(name: "high-risk", color: '151515') }
160
+ let(:github_labels) { [label1, label2] }
161
+
162
+ it "returns the labels plus 'None'" do
163
+ allow(Octopolo::GitHub::Label).to receive(:all) { github_labels }
164
+ expect(subject.send(:label_choices)).to eq github_labels
165
+ end
166
+ end
167
+
168
+ context "#update_pivotal" do
169
+ before do
170
+ subject.pivotal_ids = %w(123 234)
171
+ subject.issue = stub(url: "test")
172
+ end
173
+ let(:story_commenter) { stub(perform: true) }
174
+
175
+ it "creates a story commenter for each pivotal_id" do
176
+ Pivotal::StoryCommenter.should_receive(:new).with("123", "test") { story_commenter }
177
+ Pivotal::StoryCommenter.should_receive(:new).with("234", "test") { story_commenter }
178
+ subject.send(:update_pivotal)
179
+ end
180
+
181
+ end
182
+
183
+ context "#update_jira" do
184
+ before do
185
+ subject.jira_ids = %w(123 234)
186
+ subject.issue = stub(url: "test")
187
+ end
188
+ let(:story_commenter) { stub(perform: true) }
189
+
190
+ it "creates a story commenter for each pivotal_id" do
191
+ Jira::StoryCommenter.should_receive(:new).with("123", "test") { story_commenter }
192
+ Jira::StoryCommenter.should_receive(:new).with("234", "test") { story_commenter }
193
+ subject.send(:update_jira)
194
+ end
195
+ end
196
+
197
+ context "#update_label" do
198
+ before do
199
+ subject.label = "high-risk"
200
+ subject.issue = stub()
201
+ end
202
+ it "calls update_label with proper arguments" do
203
+ expect(subject.issue).to receive(:add_labels).with('high-risk')
204
+ subject.send(:update_label)
205
+ end
206
+
207
+ context "doesn't know yet label" do
208
+ before do
209
+ subject.label = nil
210
+ end
211
+ it "doesn't call update_label when label is don't know yet" do
212
+ expect(subject.issue).to_not receive(:add_labels)
213
+ subject.send(:update_label)
214
+ end
215
+ end
216
+
217
+ end
218
+
219
+ context "#open_in_browser" do
220
+ before do
221
+ subject.issue = issue
222
+ issue.stub(:url) { issue_url }
223
+ end
224
+
225
+ it "copies the issue's URL to the clipboard and opens it in the browser" do
226
+ cli.should_receive(:copy_to_clipboard) { issue.url}
227
+ cli.should_receive(:open) { issue.url }
228
+ subject.send(:open_in_browser)
229
+ end
230
+ end
231
+ end
232
+ end
233
+ end