redmine-mattermost 0.3 → 0.4.beta1
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.
- 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
|