octopolo 0.3.6 → 0.4.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +8 -8
- data/.travis.yml +4 -0
- data/CHANGELOG.markdown +5 -0
- data/lib/octopolo/commands/issue.rb +14 -0
- data/lib/octopolo/github.rb +9 -2
- data/lib/octopolo/github/issue.rb +106 -0
- data/lib/octopolo/github/issue_creator.rb +147 -0
- data/lib/octopolo/github/pull_request.rb +10 -81
- data/lib/octopolo/github/pull_request_creator.rb +12 -97
- data/lib/octopolo/renderer.rb +1 -0
- data/lib/octopolo/scripts/issue.rb +140 -0
- data/lib/octopolo/scripts/pull_request.rb +5 -71
- data/lib/octopolo/templates/issue_body.erb +22 -0
- data/lib/octopolo/version.rb +1 -1
- data/spec/octopolo/github/issue_creator_spec.rb +217 -0
- data/spec/octopolo/github/issue_spec.rb +243 -0
- data/spec/octopolo/github/pull_request_creator_spec.rb +11 -11
- data/spec/octopolo/github/pull_request_spec.rb +16 -16
- data/spec/octopolo/github_spec.rb +15 -0
- data/spec/octopolo/scripts/issue_spec.rb +233 -0
- data/spec/octopolo/scripts/pull_request_spec.rb +3 -3
- metadata +13 -2
@@ -1,16 +1,10 @@
|
|
1
|
+
require_relative "issue_creator"
|
1
2
|
require_relative "../renderer"
|
2
3
|
require 'tempfile'
|
3
4
|
|
4
5
|
module Octopolo
|
5
6
|
module GitHub
|
6
|
-
class PullRequestCreator
|
7
|
-
include ConfigWrapper
|
8
|
-
# for instantiating the pull request creator
|
9
|
-
attr_accessor :repo_name
|
10
|
-
attr_accessor :options
|
11
|
-
# for caputuring the created pull request information
|
12
|
-
attr_accessor :number
|
13
|
-
attr_accessor :pull_request_data
|
7
|
+
class PullRequestCreator < IssueCreator
|
14
8
|
|
15
9
|
# Public: Create a pull request for the given repo with the given options
|
16
10
|
#
|
@@ -20,23 +14,7 @@ module Octopolo
|
|
20
14
|
# destination_branch: Which branch to merge into
|
21
15
|
# source_branch: Which branch to be merged
|
22
16
|
def initialize repo_name, options
|
23
|
-
|
24
|
-
self.options = options
|
25
|
-
end
|
26
|
-
|
27
|
-
# Public: Create a pull request for the given repo with the given options
|
28
|
-
#
|
29
|
-
# repo_name - Full name ("account/repo") of the repo in question
|
30
|
-
# options - Hash of pull request information
|
31
|
-
# title: Title of the pull request
|
32
|
-
# destination_branch: Which branch to merge into
|
33
|
-
# source_branch: Which branch to be merged
|
34
|
-
#
|
35
|
-
# Returns the PullRequestCreator instance
|
36
|
-
def self.perform repo_name, options
|
37
|
-
new(repo_name, options).tap do |creator|
|
38
|
-
creator.perform
|
39
|
-
end
|
17
|
+
super(repo_name, options)
|
40
18
|
end
|
41
19
|
|
42
20
|
# Public: Create the pull request
|
@@ -47,21 +25,11 @@ module Octopolo
|
|
47
25
|
result = GitHub.create_pull_request(repo_name, destination_branch, source_branch, title, body)
|
48
26
|
# capture the information
|
49
27
|
self.number = result.number
|
50
|
-
self.
|
28
|
+
self.data = result
|
51
29
|
rescue => e
|
52
30
|
raise CannotCreate, e.message
|
53
31
|
end
|
54
32
|
|
55
|
-
# Public: The created pull request's details
|
56
|
-
def pull_request_data
|
57
|
-
@pull_request_data || raise(NotYetCreated)
|
58
|
-
end
|
59
|
-
|
60
|
-
# Public: The created pull request's number
|
61
|
-
def number
|
62
|
-
@number || raise(NotYetCreated)
|
63
|
-
end
|
64
|
-
|
65
33
|
# Public: Branch to merge the pull request into
|
66
34
|
#
|
67
35
|
# Returns a String with the branch name
|
@@ -76,74 +44,21 @@ module Octopolo
|
|
76
44
|
options[:source_branch] || raise(MissingAttribute)
|
77
45
|
end
|
78
46
|
|
79
|
-
# Public:
|
80
|
-
#
|
81
|
-
# Returns a String with the title
|
82
|
-
def title
|
83
|
-
options[:title] || raise(MissingAttribute)
|
84
|
-
end
|
85
|
-
|
86
|
-
# Public: The Pivotal Tracker story IDs associated with the pull request
|
87
|
-
#
|
88
|
-
# Returns an Array of Strings
|
89
|
-
def pivotal_ids
|
90
|
-
options[:pivotal_ids] || []
|
91
|
-
end
|
92
|
-
|
93
|
-
# Public: Jira Issue IDs associated with the pull request
|
47
|
+
# Public: Rendering template for body property
|
94
48
|
#
|
95
|
-
# Returns
|
96
|
-
def
|
97
|
-
|
49
|
+
# Returns Name of template file
|
50
|
+
def renderer_template
|
51
|
+
Renderer::PULL_REQUEST_BODY
|
98
52
|
end
|
99
53
|
|
100
|
-
# Public: Jira Url associated with the pull request
|
101
|
-
#
|
102
|
-
# Returns Jira Url
|
103
|
-
def jira_url
|
104
|
-
config.jira_url
|
105
|
-
end
|
106
54
|
|
107
|
-
# Public:
|
55
|
+
# Public: Temporary file for body editing
|
108
56
|
#
|
109
|
-
# Returns
|
110
|
-
def
|
111
|
-
|
112
|
-
output = edit_body(output) if options[:editor]
|
113
|
-
output
|
114
|
-
end
|
115
|
-
|
116
|
-
def edit_body(body)
|
117
|
-
return body unless ENV['EDITOR']
|
118
|
-
|
119
|
-
# Open the file, write the contents, and close it
|
120
|
-
tempfile = Tempfile.new(['octopolo_pull_request', '.md'])
|
121
|
-
tempfile.write(body)
|
122
|
-
tempfile.close
|
123
|
-
|
124
|
-
# Allow the user to edit the file
|
125
|
-
system "#{ENV['EDITOR']} #{tempfile.path}"
|
126
|
-
|
127
|
-
# Reopen the file, read the contents, and delete it
|
128
|
-
tempfile.open
|
129
|
-
output = tempfile.read
|
130
|
-
tempfile.unlink
|
131
|
-
|
132
|
-
output
|
133
|
-
end
|
134
|
-
|
135
|
-
# Public: The local variables to pass into the template
|
136
|
-
def body_locals
|
137
|
-
{
|
138
|
-
pivotal_ids: pivotal_ids,
|
139
|
-
jira_ids: jira_ids,
|
140
|
-
jira_url: jira_url,
|
141
|
-
}
|
57
|
+
# Returns Name of temporary file
|
58
|
+
def body_edit_temp_name
|
59
|
+
'octopolo_pull_request'
|
142
60
|
end
|
143
61
|
|
144
|
-
MissingAttribute = Class.new(StandardError)
|
145
|
-
NotYetCreated = Class.new(StandardError)
|
146
|
-
CannotCreate = Class.new(StandardError)
|
147
62
|
end
|
148
63
|
end
|
149
64
|
end
|
data/lib/octopolo/renderer.rb
CHANGED
@@ -0,0 +1,140 @@
|
|
1
|
+
require_relative "../scripts"
|
2
|
+
require_relative "../github"
|
3
|
+
require_relative "../github/issue"
|
4
|
+
require_relative "../github/issue_creator"
|
5
|
+
require_relative "../pivotal/story_commenter"
|
6
|
+
require_relative "../jira/story_commenter"
|
7
|
+
|
8
|
+
module Octopolo
|
9
|
+
module Scripts
|
10
|
+
class Issue
|
11
|
+
include CLIWrapper
|
12
|
+
include ConfigWrapper
|
13
|
+
include GitWrapper
|
14
|
+
|
15
|
+
attr_accessor :title
|
16
|
+
attr_accessor :issue
|
17
|
+
attr_accessor :pivotal_ids
|
18
|
+
attr_accessor :jira_ids
|
19
|
+
attr_accessor :label
|
20
|
+
attr_accessor :options
|
21
|
+
|
22
|
+
def self.execute(options={})
|
23
|
+
new(options).execute
|
24
|
+
end
|
25
|
+
|
26
|
+
def initialize(options={})
|
27
|
+
@options = options
|
28
|
+
end
|
29
|
+
|
30
|
+
def execute
|
31
|
+
GitHub.connect do
|
32
|
+
ask_questionaire
|
33
|
+
create_issue
|
34
|
+
update_pivotal
|
35
|
+
update_jira
|
36
|
+
update_label
|
37
|
+
open_in_browser
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
# Protected: Ask questions to create an issue
|
42
|
+
def ask_questionaire
|
43
|
+
announce
|
44
|
+
ask_title
|
45
|
+
ask_label
|
46
|
+
ask_pivotal_ids if config.use_pivotal_tracker
|
47
|
+
ask_jira_ids if config.use_jira
|
48
|
+
end
|
49
|
+
protected :ask_questionaire
|
50
|
+
|
51
|
+
# Protected: Announce to the user the branches the issue will reference
|
52
|
+
def announce
|
53
|
+
cli.say "Preparing an issue for #{config.github_repo}."
|
54
|
+
end
|
55
|
+
protected :announce
|
56
|
+
|
57
|
+
# Protected: Ask for a title for the issue
|
58
|
+
def ask_title
|
59
|
+
self.title = cli.prompt "Title:"
|
60
|
+
end
|
61
|
+
protected :ask_title
|
62
|
+
|
63
|
+
# Protected: Ask for a label for the issue
|
64
|
+
def ask_label
|
65
|
+
choices = Octopolo::GitHub::Label.get_names(label_choices).concat(["None"])
|
66
|
+
response = cli.ask(label_prompt, choices)
|
67
|
+
self.label = Hash[label_choices.map{|l| [l.name,l]}][response]
|
68
|
+
end
|
69
|
+
protected :ask_label
|
70
|
+
|
71
|
+
# Protected: Ask for a Pivotal Tracker story IDs
|
72
|
+
def ask_pivotal_ids
|
73
|
+
self.pivotal_ids = cli.prompt("Pivotal Tracker story ID(s):").split(/[\s,]+/)
|
74
|
+
end
|
75
|
+
protected :ask_pivotal_ids
|
76
|
+
|
77
|
+
# Protected: Ask for a Pivotal Tracker story IDs
|
78
|
+
def ask_jira_ids
|
79
|
+
self.jira_ids = cli.prompt("Jira story ID(s):").split(/[\s,]+/)
|
80
|
+
end
|
81
|
+
protected :ask_pivotal_ids
|
82
|
+
|
83
|
+
# Protected: Create the issue
|
84
|
+
#
|
85
|
+
# Returns a GitHub::Issue object
|
86
|
+
def create_issue
|
87
|
+
self.issue = GitHub::Issue.create config.github_repo, issue_attributes
|
88
|
+
end
|
89
|
+
protected :create_issue
|
90
|
+
|
91
|
+
# Protected: The attributes to send to create the issue
|
92
|
+
#
|
93
|
+
# Returns a Hash
|
94
|
+
def issue_attributes
|
95
|
+
{
|
96
|
+
title: title,
|
97
|
+
pivotal_ids: pivotal_ids,
|
98
|
+
jira_ids: jira_ids,
|
99
|
+
editor: options[:editor]
|
100
|
+
}
|
101
|
+
end
|
102
|
+
protected :issue_attributes
|
103
|
+
|
104
|
+
# Protected: Handle the newly created issue
|
105
|
+
def open_in_browser
|
106
|
+
cli.copy_to_clipboard issue.url
|
107
|
+
cli.open issue.url
|
108
|
+
end
|
109
|
+
protected :open_in_browser
|
110
|
+
|
111
|
+
def label_prompt
|
112
|
+
'Label:'
|
113
|
+
end
|
114
|
+
|
115
|
+
def label_choices
|
116
|
+
Octopolo::GitHub::Label.all
|
117
|
+
end
|
118
|
+
|
119
|
+
def update_pivotal
|
120
|
+
pivotal_ids.each do |story_id|
|
121
|
+
Pivotal::StoryCommenter.new(story_id, issue.url).perform
|
122
|
+
end if pivotal_ids
|
123
|
+
end
|
124
|
+
protected :update_pivotal
|
125
|
+
|
126
|
+
def update_jira
|
127
|
+
jira_ids.each do |story_id|
|
128
|
+
Jira::StoryCommenter.new(story_id, issue.url).perform
|
129
|
+
end if jira_ids
|
130
|
+
end
|
131
|
+
protected :update_jira
|
132
|
+
|
133
|
+
def update_label
|
134
|
+
issue.add_labels(label) if label
|
135
|
+
end
|
136
|
+
protected :update_label
|
137
|
+
|
138
|
+
end
|
139
|
+
end
|
140
|
+
end
|
@@ -1,22 +1,16 @@
|
|
1
1
|
require_relative "../scripts"
|
2
|
+
require_relative "../scripts/issue"
|
2
3
|
require_relative "../github"
|
3
4
|
require_relative "../pivotal/story_commenter"
|
4
5
|
require_relative "../jira/story_commenter"
|
5
6
|
|
6
7
|
module Octopolo
|
7
8
|
module Scripts
|
8
|
-
class PullRequest
|
9
|
-
include CLIWrapper
|
10
|
-
include ConfigWrapper
|
11
|
-
include GitWrapper
|
12
|
-
|
13
|
-
attr_accessor :title
|
9
|
+
class PullRequest < Issue
|
14
10
|
attr_accessor :pull_request
|
15
|
-
attr_accessor :pivotal_ids
|
16
|
-
attr_accessor :jira_ids
|
17
11
|
attr_accessor :destination_branch
|
18
|
-
|
19
|
-
|
12
|
+
|
13
|
+
alias_method :issue, :pull_request
|
20
14
|
|
21
15
|
def self.execute(destination_branch=nil, options={})
|
22
16
|
new(destination_branch, options).execute
|
@@ -38,7 +32,7 @@ module Octopolo
|
|
38
32
|
update_pivotal
|
39
33
|
update_jira
|
40
34
|
update_label
|
41
|
-
|
35
|
+
open_in_browser
|
42
36
|
end
|
43
37
|
end
|
44
38
|
|
@@ -66,32 +60,6 @@ module Octopolo
|
|
66
60
|
end
|
67
61
|
private :alert_reserved_and_exit
|
68
62
|
|
69
|
-
# Private: Ask for a title for the pull request
|
70
|
-
def ask_title
|
71
|
-
self.title = cli.prompt "Title:"
|
72
|
-
end
|
73
|
-
private :ask_title
|
74
|
-
|
75
|
-
# Private: Ask for a label for the pull request
|
76
|
-
def ask_label
|
77
|
-
choices = Octopolo::GitHub::Label.get_names(label_choices).concat(["None"])
|
78
|
-
response = cli.ask(label_prompt, choices)
|
79
|
-
self.label = Hash[label_choices.map{|l| [l.name,l]}][response]
|
80
|
-
end
|
81
|
-
private :ask_label
|
82
|
-
|
83
|
-
# Private: Ask for a Pivotal Tracker story IDs
|
84
|
-
def ask_pivotal_ids
|
85
|
-
self.pivotal_ids = cli.prompt("Pivotal Tracker story ID(s):").split(/[\s,]+/)
|
86
|
-
end
|
87
|
-
private :ask_pivotal_ids
|
88
|
-
|
89
|
-
# Private: Ask for a Pivotal Tracker story IDs
|
90
|
-
def ask_jira_ids
|
91
|
-
self.jira_ids = cli.prompt("Jira story ID(s):").split(/[\s,]+/)
|
92
|
-
end
|
93
|
-
private :ask_pivotal_ids
|
94
|
-
|
95
63
|
# Private: Create the pull request
|
96
64
|
#
|
97
65
|
# Returns a GitHub::PullRequest object
|
@@ -115,40 +83,6 @@ module Octopolo
|
|
115
83
|
end
|
116
84
|
private :pull_request_attributes
|
117
85
|
|
118
|
-
# Private: Handle the newly created pull request
|
119
|
-
def open_pull_request
|
120
|
-
cli.copy_to_clipboard pull_request.url
|
121
|
-
cli.open pull_request.url
|
122
|
-
end
|
123
|
-
private :open_pull_request
|
124
|
-
|
125
|
-
def label_prompt
|
126
|
-
'Label:'
|
127
|
-
end
|
128
|
-
|
129
|
-
def label_choices
|
130
|
-
Octopolo::GitHub::Label.all
|
131
|
-
end
|
132
|
-
|
133
|
-
def update_pivotal
|
134
|
-
pivotal_ids.each do |story_id|
|
135
|
-
Pivotal::StoryCommenter.new(story_id, pull_request.url).perform
|
136
|
-
end if pivotal_ids
|
137
|
-
end
|
138
|
-
private :update_pivotal
|
139
|
-
|
140
|
-
def update_jira
|
141
|
-
jira_ids.each do |story_id|
|
142
|
-
Jira::StoryCommenter.new(story_id, pull_request.url).perform
|
143
|
-
end if jira_ids
|
144
|
-
end
|
145
|
-
private :update_jira
|
146
|
-
|
147
|
-
def update_label
|
148
|
-
pull_request.add_labels(label) if label
|
149
|
-
end
|
150
|
-
private :update_label
|
151
|
-
|
152
86
|
end
|
153
87
|
end
|
154
88
|
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
<%= description %>
|
2
|
+
|
3
|
+
Deploy Plan
|
4
|
+
-----------
|
5
|
+
Describe how this change will be deployed.
|
6
|
+
|
7
|
+
Rollback Plan
|
8
|
+
-------------
|
9
|
+
Describe how this change can be rolled back.
|
10
|
+
|
11
|
+
URLs
|
12
|
+
----
|
13
|
+
<% pivotal_ids.each do |pivotal_id| -%>
|
14
|
+
* [pivotal tracker story <%= pivotal_id %>](https://www.pivotaltracker.com/story/show/<%= pivotal_id %>)
|
15
|
+
<% end -%>
|
16
|
+
<% jira_ids.each do |jira_id| -%>
|
17
|
+
* [Jira issue <%= jira_id %>](<%= jira_url %>/browse/<%= jira_id %>)
|
18
|
+
<% end -%>
|
19
|
+
|
20
|
+
QA Plan
|
21
|
+
-------
|
22
|
+
Provide a detailed QA plan, or other developers will retain the right to mock you mercilessly.
|
data/lib/octopolo/version.rb
CHANGED
@@ -0,0 +1,217 @@
|
|
1
|
+
require "spec_helper"
|
2
|
+
require_relative "../../../lib/octopolo/github/issue_creator"
|
3
|
+
|
4
|
+
module Octopolo
|
5
|
+
module GitHub
|
6
|
+
describe IssueCreator do
|
7
|
+
let(:creator) { IssueCreator.new repo_name, options }
|
8
|
+
let(:repo_name) { "foo/bar" }
|
9
|
+
let(:options) { {} }
|
10
|
+
let(:title) { "title" }
|
11
|
+
let(:body) { "body" }
|
12
|
+
let(:pivotal_ids) { %w(123 456) }
|
13
|
+
let(:jira_ids) { %w(123 456) }
|
14
|
+
let(:jira_url) { "https://example-jira.com" }
|
15
|
+
|
16
|
+
context ".perform repo_name, options" do
|
17
|
+
let(:creator) { stub }
|
18
|
+
|
19
|
+
it "instantiates a creator and perfoms it" do
|
20
|
+
IssueCreator.should_receive(:new).with(repo_name, options) { creator }
|
21
|
+
creator.should_receive(:perform)
|
22
|
+
IssueCreator.perform(repo_name, options).should == creator
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
context ".new repo_name, options" do
|
27
|
+
it "remembers the repo name and options" do
|
28
|
+
creator = IssueCreator.new repo_name, options
|
29
|
+
creator.repo_name.should == repo_name
|
30
|
+
creator.options.should == options
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
context "#perform" do
|
35
|
+
let(:data) { stub(:mash, number: 123) }
|
36
|
+
|
37
|
+
before do
|
38
|
+
creator.stub({
|
39
|
+
title: title,
|
40
|
+
body: body,
|
41
|
+
})
|
42
|
+
end
|
43
|
+
|
44
|
+
it "generates the issue with the given details and retains the information" do
|
45
|
+
GitHub.should_receive(:create_issue).with(repo_name, title, body, labels: []) { data }
|
46
|
+
creator.perform.should == data
|
47
|
+
creator.number.should == data.number
|
48
|
+
creator.data.should == data
|
49
|
+
end
|
50
|
+
|
51
|
+
it "raises CannotCreate if any exception occurs" do
|
52
|
+
GitHub.should_receive(:create_issue).and_raise(Octokit::UnprocessableEntity)
|
53
|
+
expect { creator.perform }.to raise_error(IssueCreator::CannotCreate)
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
context "#number" do
|
58
|
+
let(:number) { 123 }
|
59
|
+
|
60
|
+
it "returns the stored issue number" do
|
61
|
+
creator.number = number
|
62
|
+
creator.number.should == number
|
63
|
+
end
|
64
|
+
|
65
|
+
it "raises an exception if no issue has been created yet" do
|
66
|
+
creator.number = nil
|
67
|
+
expect { creator.number }.to raise_error(IssueCreator::NotYetCreated)
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
context "#data" do
|
72
|
+
let(:details) { stub(:data) }
|
73
|
+
|
74
|
+
it "returns the stored issue details" do
|
75
|
+
creator.data = details
|
76
|
+
creator.data.should == details
|
77
|
+
end
|
78
|
+
|
79
|
+
it "raises an exception if no information has been captured yet" do
|
80
|
+
creator.data = nil
|
81
|
+
expect { creator.data }.to raise_error(IssueCreator::NotYetCreated)
|
82
|
+
end
|
83
|
+
end
|
84
|
+
|
85
|
+
context "#title" do
|
86
|
+
context "having the option set" do
|
87
|
+
before { creator.options[:title] = title }
|
88
|
+
|
89
|
+
it "fetches from the options" do
|
90
|
+
creator.title.should == title
|
91
|
+
end
|
92
|
+
end
|
93
|
+
|
94
|
+
it "raises an exception if it's missing" do
|
95
|
+
creator.options[:title] = nil
|
96
|
+
expect { creator.title }.to raise_error(IssueCreator::MissingAttribute)
|
97
|
+
end
|
98
|
+
end
|
99
|
+
|
100
|
+
context "#pivotal_ids" do
|
101
|
+
it "fetches from the options" do
|
102
|
+
creator.options[:pivotal_ids] = pivotal_ids
|
103
|
+
creator.pivotal_ids.should == pivotal_ids
|
104
|
+
end
|
105
|
+
|
106
|
+
it "defaults to an empty array if it's missing" do
|
107
|
+
creator.options[:pivotal_ids] = nil
|
108
|
+
creator.pivotal_ids.should == []
|
109
|
+
end
|
110
|
+
end
|
111
|
+
|
112
|
+
context "#body_locals" do
|
113
|
+
let(:urls) { %w(link1 link2) }
|
114
|
+
|
115
|
+
before do
|
116
|
+
creator.stub({
|
117
|
+
pivotal_ids: pivotal_ids,
|
118
|
+
jira_ids: jira_ids,
|
119
|
+
jira_url: jira_url,
|
120
|
+
})
|
121
|
+
end
|
122
|
+
it "includes the necessary keys to render the template" do
|
123
|
+
creator.body_locals[:pivotal_ids].should == creator.pivotal_ids
|
124
|
+
creator.body_locals[:jira_ids].should == creator.jira_ids
|
125
|
+
creator.body_locals[:jira_url].should == creator.jira_url
|
126
|
+
end
|
127
|
+
end
|
128
|
+
|
129
|
+
context "#edit_body" do
|
130
|
+
let(:path) { stub(:path) }
|
131
|
+
let(:body) { stub(:string) }
|
132
|
+
let(:tempfile) { stub(:tempfile) }
|
133
|
+
let(:edited_body) { stub(:edited_body) }
|
134
|
+
|
135
|
+
before do
|
136
|
+
Tempfile.stub(:new) { tempfile }
|
137
|
+
tempfile.stub(path: path, write: nil, read: edited_body, unlink: nil, close: nil, open: nil)
|
138
|
+
creator.stub(:system)
|
139
|
+
end
|
140
|
+
|
141
|
+
context "without the $EDITOR env var set" do
|
142
|
+
before do
|
143
|
+
stub_const('ENV', {'EDITOR' => nil})
|
144
|
+
end
|
145
|
+
|
146
|
+
it "returns the un-edited output" do
|
147
|
+
creator.edit_body(body).should == body
|
148
|
+
end
|
149
|
+
end
|
150
|
+
|
151
|
+
context "with the $EDITOR env set" do
|
152
|
+
|
153
|
+
before do
|
154
|
+
stub_const('ENV', {'EDITOR' => 'vim'})
|
155
|
+
end
|
156
|
+
|
157
|
+
it "creates a tempfile, write default contents, and close it" do
|
158
|
+
Tempfile.should_receive(:new).with(['octopolo_issue', '.md']) { tempfile }
|
159
|
+
tempfile.should_receive(:write).with(body)
|
160
|
+
tempfile.should_receive(:close)
|
161
|
+
creator.edit_body body
|
162
|
+
end
|
163
|
+
|
164
|
+
it "edits the tempfile with the $EDITOR" do
|
165
|
+
tempfile.should_receive(:path) { path }
|
166
|
+
creator.should_receive(:system).with("vim #{path}")
|
167
|
+
creator.edit_body body
|
168
|
+
end
|
169
|
+
|
170
|
+
it "reopens the file, gets the contents, and deletes the temp file" do
|
171
|
+
tempfile.should_receive(:open)
|
172
|
+
tempfile.should_receive(:read) { edited_body }
|
173
|
+
tempfile.should_receive(:unlink)
|
174
|
+
creator.edit_body body
|
175
|
+
end
|
176
|
+
|
177
|
+
it "returns the user edited output" do
|
178
|
+
creator.edit_body(body).should == edited_body
|
179
|
+
end
|
180
|
+
end
|
181
|
+
end
|
182
|
+
|
183
|
+
context "#body" do
|
184
|
+
let(:locals) { stub(:hash) }
|
185
|
+
let(:output) { stub(:string) }
|
186
|
+
|
187
|
+
before do
|
188
|
+
creator.stub({
|
189
|
+
body_locals: locals,
|
190
|
+
})
|
191
|
+
end
|
192
|
+
|
193
|
+
it "renders the body template with the body locals" do
|
194
|
+
Renderer.should_receive(:render).with(Renderer::ISSUE_BODY, locals) { output }
|
195
|
+
creator.body.should == output
|
196
|
+
end
|
197
|
+
|
198
|
+
context "when the editor option is set" do
|
199
|
+
let(:edited_output) { stub(:output) }
|
200
|
+
|
201
|
+
before do
|
202
|
+
creator.stub({
|
203
|
+
body_locals: locals,
|
204
|
+
options: { editor: true }
|
205
|
+
})
|
206
|
+
end
|
207
|
+
|
208
|
+
it "calls the edit_body method" do
|
209
|
+
Renderer.should_receive(:render).with(Renderer::ISSUE_BODY, locals) { output }
|
210
|
+
creator.should_receive(:edit_body).with(output) { edited_output }
|
211
|
+
creator.body.should == edited_output
|
212
|
+
end
|
213
|
+
end
|
214
|
+
end
|
215
|
+
end
|
216
|
+
end
|
217
|
+
end
|