redmine-mattermost 0.3 → 0.4.beta1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.travis.yml +15 -0
- data/LICENSE +1 -0
- data/README.md +30 -17
- data/Rakefile +9 -0
- data/app/views/settings/_mattermost_settings.html.erb +60 -20
- data/lib/redmine-mattermost.rb +9 -1
- data/lib/redmine_mattermost/bridge.rb +63 -0
- data/lib/redmine_mattermost/client.rb +28 -0
- data/lib/redmine_mattermost/extractors.rb +88 -0
- data/lib/redmine_mattermost/extractors/applied_changeset.rb +55 -0
- data/lib/redmine_mattermost/extractors/changed_wiki_page.rb +39 -0
- data/lib/redmine_mattermost/extractors/new_issue.rb +44 -0
- data/lib/redmine_mattermost/extractors/updated_issue.rb +42 -0
- data/lib/redmine_mattermost/infos.rb +1 -2
- data/lib/redmine_mattermost/issue_patch.rb +14 -14
- data/lib/redmine_mattermost/listener.rb +38 -268
- data/lib/redmine_mattermost/message_builder.rb +61 -0
- data/lib/redmine_mattermost/redmine_plugin.rb +3 -4
- data/lib/redmine_mattermost/utils.rb +3 -0
- data/lib/redmine_mattermost/utils/journal_mapper.rb +75 -0
- data/lib/redmine_mattermost/utils/text.rb +37 -0
- data/lib/redmine_mattermost/utils/urls.rb +20 -0
- data/lib/redmine_mattermost/version.rb +1 -1
- data/redmine-mattermost.gemspec +4 -1
- data/spec/client_spec.rb +29 -0
- data/spec/extractors/applied_changeset_spec.rb +82 -0
- data/spec/extractors/changed_wiki_page_spec.rb +63 -0
- data/spec/extractors/new_issue_spec.rb +209 -0
- data/spec/extractors/updated_issue_spec.rb +169 -0
- data/spec/message_builder_spec.rb +71 -0
- data/spec/redmine_mattermost_spec.rb +9 -0
- data/spec/spec_helper.rb +17 -0
- data/spec/support/mock_support.rb +113 -0
- data/spec/utils/text_spec.rb +142 -0
- data/spec/utils/text_spec_alt.rb +142 -0
- metadata +87 -9
@@ -0,0 +1,61 @@
|
|
1
|
+
module RedmineMattermost
|
2
|
+
class MessageBuilder
|
3
|
+
DEFAULTS = {
|
4
|
+
link_names: 1
|
5
|
+
}
|
6
|
+
|
7
|
+
def initialize(message)
|
8
|
+
@data = DEFAULTS.merge(text: message)
|
9
|
+
@attachments = []
|
10
|
+
end
|
11
|
+
|
12
|
+
def to_hash
|
13
|
+
@data.dup.tap do |hash|
|
14
|
+
unless @attachments.empty?
|
15
|
+
hash[:attachments] = @attachments.map(&:to_hash)
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
def username(name)
|
21
|
+
@data[:username] = name
|
22
|
+
end
|
23
|
+
|
24
|
+
def channel(id)
|
25
|
+
@data[:channel] = id
|
26
|
+
end
|
27
|
+
|
28
|
+
def icon(url)
|
29
|
+
@data[:icon_url] = url unless url.nil? || url.empty?
|
30
|
+
end
|
31
|
+
|
32
|
+
def attachment
|
33
|
+
Attachment.new.tap do |am|
|
34
|
+
@attachments << am
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
class Attachment
|
39
|
+
def initialize()
|
40
|
+
@data = { }
|
41
|
+
end
|
42
|
+
|
43
|
+
def text(message)
|
44
|
+
@data[:text] = message
|
45
|
+
self
|
46
|
+
end
|
47
|
+
|
48
|
+
def field(title, value, short = false)
|
49
|
+
{title: title, value: value}.tap do |hash|
|
50
|
+
hash[:short] = true if short
|
51
|
+
(@data[:fields] ||= []) << hash
|
52
|
+
end
|
53
|
+
self
|
54
|
+
end
|
55
|
+
|
56
|
+
def to_hash
|
57
|
+
@data.to_hash
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
@@ -6,9 +6,9 @@ module RedmineMattermost
|
|
6
6
|
DEFAULT_SETTINGS = {
|
7
7
|
"callback_url" => "http://example.com/callback/",
|
8
8
|
"channel" => nil,
|
9
|
-
"icon" => "https://raw.githubusercontent.com/
|
10
|
-
"username" => "
|
11
|
-
"display_watchers" => "
|
9
|
+
"icon" => "https://raw.githubusercontent.com/neopoly/redmine-mattermost/assets/icon.png",
|
10
|
+
"username" => "Redmine",
|
11
|
+
"display_watchers" => "0"
|
12
12
|
}.freeze
|
13
13
|
|
14
14
|
SETTING_PARTIAL = "settings/mattermost_settings"
|
@@ -27,7 +27,6 @@ module RedmineMattermost
|
|
27
27
|
description DESCRIPTION
|
28
28
|
version VERSION
|
29
29
|
url URL
|
30
|
-
author_url AUTHOR_URL
|
31
30
|
directory Engine.root
|
32
31
|
|
33
32
|
requires_redmine version_or_higher: "2.0.0"
|
@@ -0,0 +1,75 @@
|
|
1
|
+
module RedmineMattermost
|
2
|
+
module Utils
|
3
|
+
class JournalMapper
|
4
|
+
include Text
|
5
|
+
include Urls
|
6
|
+
|
7
|
+
attr_reader :bridge
|
8
|
+
|
9
|
+
def initialize(bridge)
|
10
|
+
@bridge = bridge
|
11
|
+
end
|
12
|
+
|
13
|
+
def to_fields(journal)
|
14
|
+
journal.details.map do |detail|
|
15
|
+
detail_to_field(detail)
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
private
|
20
|
+
|
21
|
+
def detail_to_field(detail)
|
22
|
+
short = true
|
23
|
+
value = h(detail.value)
|
24
|
+
|
25
|
+
if detail.property == "cf"
|
26
|
+
key = bridge.find_custom_field(detail.prop_key).name rescue nil
|
27
|
+
title = key
|
28
|
+
elsif detail.property == "attachment"
|
29
|
+
key = "attachment"
|
30
|
+
title = t("label_attachment")
|
31
|
+
else
|
32
|
+
key = detail.prop_key.to_s.sub("_id", "")
|
33
|
+
title = t("field_#{key}")
|
34
|
+
end
|
35
|
+
|
36
|
+
case key
|
37
|
+
when "title", "subject", "description"
|
38
|
+
short = false
|
39
|
+
when "tracker"
|
40
|
+
tracker = bridge.find_tracker(detail.value)
|
41
|
+
value = h tracker.to_s
|
42
|
+
when "project"
|
43
|
+
project = bridge.find_project(detail.value)
|
44
|
+
value = h project.to_s
|
45
|
+
when "status"
|
46
|
+
status = bridge.find_issue_status(detail.value)
|
47
|
+
value = h status.to_s
|
48
|
+
when "priority"
|
49
|
+
priority = bridge.find_issue_priority(detail.value)
|
50
|
+
value = h priority.to_s
|
51
|
+
when "category"
|
52
|
+
category = bridge.find_issue_category(detail.value)
|
53
|
+
value = h category.to_s
|
54
|
+
when "assigned_to"
|
55
|
+
user = bridge.find_user(detail.value)
|
56
|
+
value = h user.to_s
|
57
|
+
when "fixed_version"
|
58
|
+
version = bridge.find_version(detail.value)
|
59
|
+
value = h version.to_s
|
60
|
+
when "attachment"
|
61
|
+
if attachment = bridge.find_attachement(detail.prop_key)
|
62
|
+
value = "<#{event_url attachment}|#{h attachment.filename}>"
|
63
|
+
end
|
64
|
+
when "parent"
|
65
|
+
issue = bridge.find_issue(detail.value)
|
66
|
+
value = "<#{event_url issue}|#{h issue}>" if issue
|
67
|
+
end
|
68
|
+
|
69
|
+
value = "-" if value.nil? || value.empty?
|
70
|
+
|
71
|
+
[title, value, short]
|
72
|
+
end
|
73
|
+
end
|
74
|
+
end
|
75
|
+
end
|
@@ -0,0 +1,37 @@
|
|
1
|
+
require "reverse_markdown"
|
2
|
+
|
3
|
+
module RedmineMattermost
|
4
|
+
module Utils
|
5
|
+
module Text
|
6
|
+
MARKDOWN_OPTIONS = {
|
7
|
+
unknown_tags: :bypass,
|
8
|
+
github_flavored: true
|
9
|
+
}.freeze
|
10
|
+
|
11
|
+
def h(text)
|
12
|
+
text.to_s.gsub("&", "&").gsub("<", "<").gsub(">", ">") if text
|
13
|
+
end
|
14
|
+
|
15
|
+
def t(key, options = {})
|
16
|
+
opts = {locale: bridge.settings.default_language}.merge(options)
|
17
|
+
bridge.translate(key, opts)
|
18
|
+
end
|
19
|
+
|
20
|
+
def link(label, target)
|
21
|
+
"<#{target}|#{h label}>"
|
22
|
+
end
|
23
|
+
|
24
|
+
def to_markdown(text)
|
25
|
+
case formatting = bridge.settings.text_formatting
|
26
|
+
when "markdown"
|
27
|
+
text
|
28
|
+
else
|
29
|
+
html = bridge.to_html(formatting, text)
|
30
|
+
ReverseMarkdown.convert(html, MARKDOWN_OPTIONS).strip
|
31
|
+
end
|
32
|
+
rescue
|
33
|
+
h text
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
module RedmineMattermost
|
2
|
+
module Utils
|
3
|
+
module Urls
|
4
|
+
def default_url_options
|
5
|
+
settings = bridge.settings
|
6
|
+
protocol = settings.protocol
|
7
|
+
if settings.host_name.to_s =~ /\A(https?\:\/\/)?(.+?)(\:(\d+))?(\/.+)?\z/i
|
8
|
+
host, port, prefix = $2, $4, $5
|
9
|
+
{ host: host, protocol: protocol, port: port, script_name: prefix }
|
10
|
+
else
|
11
|
+
{ host: settings.host_name, protocol: settings.protocol }
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
def event_url(obj)
|
16
|
+
bridge.url_for(obj.event_url(default_url_options))
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
data/redmine-mattermost.gemspec
CHANGED
@@ -19,8 +19,11 @@ Gem::Specification.new do |spec|
|
|
19
19
|
spec.require_paths = ["lib"]
|
20
20
|
|
21
21
|
spec.add_dependency "rails", "~> 4.2.0"
|
22
|
-
spec.add_dependency "
|
22
|
+
spec.add_dependency "reverse_markdown", "~> 1.0.3"
|
23
23
|
|
24
24
|
spec.add_development_dependency "bundler", "~> 1.7"
|
25
25
|
spec.add_development_dependency "rake", "~> 10.0"
|
26
|
+
spec.add_development_dependency "minitest", "~> 5.9.0"
|
27
|
+
spec.add_development_dependency "webmock", "~> 2.1.0"
|
28
|
+
spec.add_development_dependency "RedCloth", "~> 4.3.2"
|
26
29
|
end
|
data/spec/client_spec.rb
ADDED
@@ -0,0 +1,29 @@
|
|
1
|
+
require "spec_helper"
|
2
|
+
|
3
|
+
describe RedmineMattermost::Client do
|
4
|
+
subject { RedmineMattermost::Client.new(url) }
|
5
|
+
let(:url) { "http://test.host/api/v3" }
|
6
|
+
|
7
|
+
it "use the given url as endpoint" do
|
8
|
+
subject.uri.must_equal URI(url)
|
9
|
+
end
|
10
|
+
|
11
|
+
it "speak the message as payload" do
|
12
|
+
message = {
|
13
|
+
text: "Some message",
|
14
|
+
username: "TestUser"
|
15
|
+
}
|
16
|
+
|
17
|
+
stub_endpoint
|
18
|
+
.with(body: { payload: message.to_json })
|
19
|
+
.to_return(body: "{}")
|
20
|
+
|
21
|
+
subject.speak(message).join
|
22
|
+
end
|
23
|
+
|
24
|
+
private
|
25
|
+
|
26
|
+
def stub_endpoint
|
27
|
+
stub_request(:post, url)
|
28
|
+
end
|
29
|
+
end
|
@@ -0,0 +1,82 @@
|
|
1
|
+
require "spec_helper"
|
2
|
+
|
3
|
+
describe RedmineMattermost::Extractors::AppliedChangeset do
|
4
|
+
subject { RedmineMattermost::Extractors::AppliedChangeset.new(bridge) }
|
5
|
+
let(:result) { subject.from_context(context) }
|
6
|
+
let(:msg) { result[:message] }
|
7
|
+
let(:context) do
|
8
|
+
{ issue: issue, changeset: changeset }
|
9
|
+
end
|
10
|
+
let(:bridge) { MockBridge.new(settings) }
|
11
|
+
let(:issue) { MockIssue.new(issue_data) }
|
12
|
+
let(:journal) { mock(journal_data) }
|
13
|
+
let(:changeset) { mock(changeset_data)}
|
14
|
+
let(:settings) { Defaults.settings }
|
15
|
+
|
16
|
+
let(:issue_data) do
|
17
|
+
{
|
18
|
+
title: "Some title",
|
19
|
+
project: EventMock.new(title: "Project A"),
|
20
|
+
current_journal: journal,
|
21
|
+
valid: true
|
22
|
+
}
|
23
|
+
end
|
24
|
+
let(:journal_data) do
|
25
|
+
{
|
26
|
+
user: "User A",
|
27
|
+
details: [mock(prop_key: "title", value: "Some title")]
|
28
|
+
}
|
29
|
+
end
|
30
|
+
let(:changeset_data) do
|
31
|
+
{
|
32
|
+
repository: mock({
|
33
|
+
project: EventMock.new(title: "Project A"),
|
34
|
+
identifier_param: "repo_1",
|
35
|
+
}),
|
36
|
+
revision: "revision_1"
|
37
|
+
}
|
38
|
+
end
|
39
|
+
|
40
|
+
describe "empty context" do
|
41
|
+
let(:context) { Hash.new }
|
42
|
+
|
43
|
+
it "raises" do
|
44
|
+
proc { result }.must_raise KeyError
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
describe "no project" do
|
49
|
+
let(:issue_data) do
|
50
|
+
{ title: "Some title" }
|
51
|
+
end
|
52
|
+
|
53
|
+
it "returns nil" do
|
54
|
+
result.must_be_nil
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
describe "invalid issue" do
|
59
|
+
let(:issue_data) do
|
60
|
+
{ valid: false }
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
describe "with changeset" do
|
65
|
+
it "should return a message" do
|
66
|
+
msg[:text].must_equal(
|
67
|
+
"[#{link_for("Project A")}] User A updated #{link_for("Some title")}"
|
68
|
+
)
|
69
|
+
attachments = msg[:attachments]
|
70
|
+
attachments.size.must_equal 1
|
71
|
+
attachment = attachments.shift
|
72
|
+
text = attachment[:text]
|
73
|
+
text.must_equal("text_status_changed_by_changeset")
|
74
|
+
fields = attachment[:fields]
|
75
|
+
fields.size.must_equal 1
|
76
|
+
fields.shift.must_equal({
|
77
|
+
title: "field_title",
|
78
|
+
value: "Some title"
|
79
|
+
})
|
80
|
+
end
|
81
|
+
end
|
82
|
+
end
|
@@ -0,0 +1,63 @@
|
|
1
|
+
require "spec_helper"
|
2
|
+
|
3
|
+
describe RedmineMattermost::Extractors::ChangedWikiPage do
|
4
|
+
subject { RedmineMattermost::Extractors::ChangedWikiPage.new(bridge) }
|
5
|
+
let(:result) { subject.from_context(context) }
|
6
|
+
let(:msg) { result[:message] }
|
7
|
+
let(:context) do
|
8
|
+
{ project: project, page: page }
|
9
|
+
end
|
10
|
+
let(:bridge) { MockBridge.new(settings) }
|
11
|
+
let(:project) { EventMock.new(title: "Project A")}
|
12
|
+
let(:page) { EventMock.new(page_data)}
|
13
|
+
|
14
|
+
let(:settings) { Defaults.settings }
|
15
|
+
let(:page_data) do
|
16
|
+
{
|
17
|
+
title: "Some title",
|
18
|
+
content: mock({
|
19
|
+
author: mock(title: "User A"),
|
20
|
+
comments: ""
|
21
|
+
})
|
22
|
+
}
|
23
|
+
end
|
24
|
+
|
25
|
+
describe "empty context" do
|
26
|
+
let(:context) { Hash.new }
|
27
|
+
|
28
|
+
it "raises" do
|
29
|
+
proc { result }.must_raise KeyError
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
describe "minimal page" do
|
34
|
+
it "should return a message" do
|
35
|
+
msg[:text].must_equal(
|
36
|
+
"[#{link_for("Project A")}] User A updated #{link_for("Some title")}"
|
37
|
+
)
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
describe "full page" do
|
42
|
+
let(:page_data) do
|
43
|
+
{
|
44
|
+
title: "Some title",
|
45
|
+
content: mock({
|
46
|
+
author: mock(title: "User A"),
|
47
|
+
comments: "The comments"
|
48
|
+
})
|
49
|
+
}
|
50
|
+
end
|
51
|
+
|
52
|
+
it "should return a message" do
|
53
|
+
msg[:text].must_equal(
|
54
|
+
"[#{link_for("Project A")}] User A updated #{link_for("Some title")}"
|
55
|
+
)
|
56
|
+
attachments = msg[:attachments]
|
57
|
+
attachments.size.must_equal 1
|
58
|
+
attachment = attachments.shift
|
59
|
+
text = attachment[:text]
|
60
|
+
text.must_equal "The comments"
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
@@ -0,0 +1,209 @@
|
|
1
|
+
require "spec_helper"
|
2
|
+
|
3
|
+
describe RedmineMattermost::Extractors::NewIssue do
|
4
|
+
subject { RedmineMattermost::Extractors::NewIssue.new(bridge) }
|
5
|
+
let(:result) { subject.from_context(context) }
|
6
|
+
let(:msg) { result[:message] }
|
7
|
+
let(:context) do
|
8
|
+
{ issue: issue }
|
9
|
+
end
|
10
|
+
let(:bridge) { MockBridge.new(settings) }
|
11
|
+
let(:settings) { Defaults.settings }
|
12
|
+
let(:issue) { MockIssue.new(issue_data) }
|
13
|
+
let(:issue_data) { Hash.new }
|
14
|
+
|
15
|
+
describe "empty context" do
|
16
|
+
let(:context) { Hash.new }
|
17
|
+
|
18
|
+
it "raises" do
|
19
|
+
proc { result }.must_raise KeyError
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
describe "no project" do
|
24
|
+
let(:issue_data) do
|
25
|
+
{
|
26
|
+
title: "Some title"
|
27
|
+
}
|
28
|
+
end
|
29
|
+
|
30
|
+
it "returns nil" do
|
31
|
+
result.must_be_nil
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
describe "minimal issue" do
|
36
|
+
let(:issue_data) do
|
37
|
+
{
|
38
|
+
title: "Some title",
|
39
|
+
project: EventMock.new(title: "Project A"),
|
40
|
+
author: mock(title: "User A"),
|
41
|
+
watcher_users: [],
|
42
|
+
status: "",
|
43
|
+
priority: "",
|
44
|
+
assigned_to: nil
|
45
|
+
}
|
46
|
+
end
|
47
|
+
|
48
|
+
it "should return a message" do
|
49
|
+
msg[:text].must_equal(
|
50
|
+
"[#{link_for("Project A")}] User A created #{link_for("Some title")}"
|
51
|
+
)
|
52
|
+
attachments = msg[:attachments]
|
53
|
+
attachments.size.must_equal 1
|
54
|
+
attachment = attachments.shift
|
55
|
+
fields = attachment[:fields]
|
56
|
+
fields.size.must_equal 3
|
57
|
+
fields.shift.must_equal({
|
58
|
+
title: "field_status",
|
59
|
+
value: "-",
|
60
|
+
short: true
|
61
|
+
})
|
62
|
+
fields.shift.must_equal({
|
63
|
+
title: "field_priority",
|
64
|
+
value: "-",
|
65
|
+
short: true
|
66
|
+
})
|
67
|
+
fields.shift.must_equal({
|
68
|
+
title: "field_assigned_to",
|
69
|
+
value: "-",
|
70
|
+
short: true
|
71
|
+
})
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
describe "default issue" do
|
76
|
+
let(:issue_data) do
|
77
|
+
{
|
78
|
+
title: "Some title",
|
79
|
+
project: EventMock.new(title: "Project A"),
|
80
|
+
author: mock(title: "User A"),
|
81
|
+
watcher_users: [],
|
82
|
+
status: "New",
|
83
|
+
priority: "Normal",
|
84
|
+
assigned_to: mock(title: "User B")
|
85
|
+
}
|
86
|
+
end
|
87
|
+
|
88
|
+
it "should return a message" do
|
89
|
+
msg[:text].must_equal(
|
90
|
+
"[#{link_for("Project A")}] User A created #{link_for("Some title")}"
|
91
|
+
)
|
92
|
+
attachments = msg[:attachments]
|
93
|
+
attachments.size.must_equal 1
|
94
|
+
attachment = attachments.shift
|
95
|
+
fields = attachment[:fields]
|
96
|
+
fields.size.must_equal 3
|
97
|
+
fields.shift.must_equal({
|
98
|
+
title: "field_status",
|
99
|
+
value: "New",
|
100
|
+
short: true
|
101
|
+
})
|
102
|
+
fields.shift.must_equal({
|
103
|
+
title: "field_priority",
|
104
|
+
value: "Normal",
|
105
|
+
short: true
|
106
|
+
})
|
107
|
+
fields.shift.must_equal({
|
108
|
+
title: "field_assigned_to",
|
109
|
+
value: "User B",
|
110
|
+
short: true
|
111
|
+
})
|
112
|
+
end
|
113
|
+
end
|
114
|
+
|
115
|
+
describe "full issue" do
|
116
|
+
let(:issue_data) do
|
117
|
+
{
|
118
|
+
title: "Some title",
|
119
|
+
project: EventMock.new(title: "Project A"),
|
120
|
+
author: mock(title: "User A"),
|
121
|
+
watcher_users: [mock(title: "User C")],
|
122
|
+
status: "New",
|
123
|
+
priority: "Normal",
|
124
|
+
assigned_to: mock(title: "User B"),
|
125
|
+
description: <<TEXTILE
|
126
|
+
Some description with @inline@ and
|
127
|
+
|
128
|
+
<pre>pre-text
|
129
|
+
over multiple lines
|
130
|
+
</pre>
|
131
|
+
|
132
|
+
<code>
|
133
|
+
and code
|
134
|
+
</code>
|
135
|
+
TEXTILE
|
136
|
+
}
|
137
|
+
end
|
138
|
+
|
139
|
+
it "should return a message" do
|
140
|
+
msg[:text].must_equal(
|
141
|
+
"[#{link_for("Project A")}] User A created #{link_for("Some title")}"
|
142
|
+
)
|
143
|
+
attachments = msg[:attachments]
|
144
|
+
attachments.size.must_equal 1
|
145
|
+
attachment = attachments.shift
|
146
|
+
|
147
|
+
text = attachment[:text]
|
148
|
+
text.must_equal <<MARKDOWN.strip
|
149
|
+
Some description with `inline` and
|
150
|
+
|
151
|
+
```
|
152
|
+
pre-text
|
153
|
+
over multiple lines
|
154
|
+
```
|
155
|
+
|
156
|
+
`
|
157
|
+
and code
|
158
|
+
`
|
159
|
+
MARKDOWN
|
160
|
+
fields = attachment[:fields]
|
161
|
+
fields.size.must_equal 4
|
162
|
+
fields.shift.must_equal({
|
163
|
+
title: "field_status",
|
164
|
+
value: "New",
|
165
|
+
short: true
|
166
|
+
})
|
167
|
+
fields.shift.must_equal({
|
168
|
+
title: "field_priority",
|
169
|
+
value: "Normal",
|
170
|
+
short: true
|
171
|
+
})
|
172
|
+
fields.shift.must_equal({
|
173
|
+
title: "field_assigned_to",
|
174
|
+
value: "User B",
|
175
|
+
short: true
|
176
|
+
})
|
177
|
+
fields.shift.must_equal({
|
178
|
+
title: "field_watcher",
|
179
|
+
value: "User C",
|
180
|
+
short: true
|
181
|
+
})
|
182
|
+
end
|
183
|
+
end
|
184
|
+
|
185
|
+
describe "extracts mentions" do
|
186
|
+
let(:issue_data) do
|
187
|
+
{
|
188
|
+
title: "Some title",
|
189
|
+
project: EventMock.new(title: "Project A"),
|
190
|
+
author: mock(title: "User A"),
|
191
|
+
watcher_users: [mock(title: "User C")],
|
192
|
+
status: "New",
|
193
|
+
priority: "Normal",
|
194
|
+
assigned_to: mock(title: "User B"),
|
195
|
+
description: "Some description @foo @NOT @bar"
|
196
|
+
}
|
197
|
+
end
|
198
|
+
|
199
|
+
it "should extract mentions for markdown" do
|
200
|
+
settings.text_formatting = "markdown"
|
201
|
+
msg[:text].must_include("To: @foo, @bar")
|
202
|
+
end
|
203
|
+
|
204
|
+
it "should NOT extract mentions for textile" do
|
205
|
+
settings.text_formatting = "textile"
|
206
|
+
msg[:text].wont_include("To: @foo, @bar")
|
207
|
+
end
|
208
|
+
end
|
209
|
+
end
|