Commit e2741630 authored by Kamil Trzciński's avatar Kamil Trzciński

Merge branch 'zj-format-chat-messages' into 'master'

Format chat messages

Closes #24917 and #25746

See merge request !8528
parents 659cceb0 164eb3aa
......@@ -31,13 +31,13 @@ class ChatSlashCommandsService < Service
return unless valid_token?(params[:token])
user = find_chat_user(params)
unless user
if user
Gitlab::ChatCommands::Command.new(project, user, params).execute
else
url = authorize_chat_name_url(params)
return presenter.authorize_chat_name(url)
Gitlab::ChatCommands::Presenters::Access.new(url).authorize
end
Gitlab::ChatCommands::Command.new(project, user,
params).execute
end
private
......@@ -49,8 +49,4 @@ class ChatSlashCommandsService < Service
def authorize_chat_name_url(params)
ChatNames::AuthorizeUserService.new(self, params).execute
end
def presenter
Gitlab::ChatCommands::Presenter.new
end
end
---
title: Reformat messages ChatOps
merge_request: 8528
author:
......@@ -42,10 +42,6 @@ module Gitlab
def find_by_iid(iid)
collection.find_by(iid: iid)
end
def presenter
Gitlab::ChatCommands::Presenter.new
end
end
end
end
......@@ -3,7 +3,7 @@ module Gitlab
class Command < BaseCommand
COMMANDS = [
Gitlab::ChatCommands::IssueShow,
Gitlab::ChatCommands::IssueCreate,
Gitlab::ChatCommands::IssueNew,
Gitlab::ChatCommands::IssueSearch,
Gitlab::ChatCommands::Deploy,
].freeze
......@@ -13,51 +13,32 @@ module Gitlab
if command
if command.allowed?(project, current_user)
present command.new(project, current_user, params).execute(match)
command.new(project, current_user, params).execute(match)
else
access_denied
Gitlab::ChatCommands::Presenters::Access.new.access_denied
end
else
help(help_messages)
Gitlab::ChatCommands::Help.new(project, current_user, params).execute(available_commands, params[:text])
end
end
def match_command
match = nil
service = available_commands.find do |klass|
match = klass.match(command)
end
service =
available_commands.find do |klass|
match = klass.match(params[:text])
end
[service, match]
end
private
def help_messages
available_commands.map(&:help_message)
end
def available_commands
COMMANDS.select do |klass|
klass.available?(project)
end
end
def command
params[:text]
end
def help(messages)
presenter.help(messages, params[:command])
end
def access_denied
presenter.access_denied
end
def present(resource)
presenter.present(resource)
end
end
end
end
module Gitlab
module ChatCommands
class Deploy < BaseCommand
include Gitlab::Routing.url_helpers
def self.match(text)
/\Adeploy\s+(?<from>\S+.*)\s+to+\s+(?<to>\S+.*)\z/.match(text)
end
......@@ -24,35 +22,29 @@ module Gitlab
to = match[:to]
actions = find_actions(from, to)
return unless actions.present?
if actions.one?
play!(from, to, actions.first)
if actions.none?
Gitlab::ChatCommands::Presenters::Deploy.new(nil).no_actions
elsif actions.one?
action = play!(from, to, actions.first)
Gitlab::ChatCommands::Presenters::Deploy.new(action).present(from, to)
else
Result.new(:error, 'Too many actions defined')
Gitlab::ChatCommands::Presenters::Deploy.new(actions).too_many_actions
end
end
private
def play!(from, to, action)
new_action = action.play(current_user)
Result.new(:success, "Deployment from #{from} to #{to} started. Follow the progress: #{url(new_action)}.")
action.play(current_user)
end
def find_actions(from, to)
environment = project.environments.find_by(name: from)
return unless environment
return [] unless environment
environment.actions_for(to).select(&:starts_environment?)
end
def url(subject)
polymorphic_url(
[subject.project.namespace.becomes(Namespace), subject.project, subject]
)
end
end
end
end
module Gitlab
module ChatCommands
class Help < BaseCommand
# This class has to be used last, as it always matches. It has to match
# because other commands were not triggered and we want to show the help
# command
def self.match(_text)
true
end
def self.help_message
'help'
end
def self.allowed?(_project, _user)
true
end
def execute(commands, text)
Gitlab::ChatCommands::Presenters::Help.new(commands).present(trigger, text)
end
def trigger
params[:command]
end
end
end
end
module Gitlab
module ChatCommands
class IssueCreate < IssueCommand
class IssueNew < IssueCommand
def self.match(text)
# we can not match \n with the dot by passing the m modifier as than
# we can not match \n with the dot by passing the m modifier as than
# the title and description are not seperated
/\Aissue\s+(new|create)\s+(?<title>[^\n]*)\n*(?<description>(.|\n)*)/.match(text)
end
......@@ -19,8 +19,24 @@ module Gitlab
title = match[:title]
description = match[:description].to_s.rstrip
issue = create_issue(title: title, description: description)
if issue.persisted?
presenter(issue).present
else
presenter(issue).display_errors
end
end
private
def create_issue(title:, description:)
Issues::CreateService.new(project, current_user, title: title, description: description).execute
end
def presenter(issue)
Gitlab::ChatCommands::Presenters::IssueNew.new(issue)
end
end
end
end
......@@ -10,7 +10,13 @@ module Gitlab
end
def execute(match)
collection.search(match[:query]).limit(QUERY_LIMIT)
issues = collection.search(match[:query]).limit(QUERY_LIMIT)
if issues.present?
Presenters::IssueSearch.new(issues).present
else
Presenters::Access.new(issues).not_found
end
end
end
end
......
......@@ -10,7 +10,13 @@ module Gitlab
end
def execute(match)
find_by_iid(match[:iid])
issue = find_by_iid(match[:iid])
if issue
Gitlab::ChatCommands::Presenters::IssueShow.new(issue).present
else
Gitlab::ChatCommands::Presenters::Access.new.not_found
end
end
end
end
......
module Gitlab
module ChatCommands
class Presenter
include Gitlab::Routing
def authorize_chat_name(url)
message = if url
":wave: Hi there! Before I do anything for you, please [connect your GitLab account](#{url})."
else
":sweat_smile: Couldn't identify you, nor can I autorize you!"
end
ephemeral_response(message)
end
def help(commands, trigger)
if commands.none?
ephemeral_response("No commands configured")
else
commands.map! { |command| "#{trigger} #{command}" }
message = header_with_list("Available commands", commands)
ephemeral_response(message)
end
end
def present(subject)
return not_found unless subject
if subject.is_a?(Gitlab::ChatCommands::Result)
show_result(subject)
elsif subject.respond_to?(:count)
if subject.none?
not_found
elsif subject.one?
single_resource(subject.first)
else
multiple_resources(subject)
end
else
single_resource(subject)
end
end
def access_denied
ephemeral_response("Whoops! That action is not allowed. This incident will be [reported](https://xkcd.com/838/).")
end
private
def show_result(result)
case result.type
when :success
in_channel_response(result.message)
else
ephemeral_response(result.message)
end
end
def not_found
ephemeral_response("404 not found! GitLab couldn't find what you were looking for! :boom:")
end
def single_resource(resource)
return error(resource) if resource.errors.any? || !resource.persisted?
message = "#{title(resource)}:"
message << "\n\n#{resource.description}" if resource.try(:description)
in_channel_response(message)
end
def multiple_resources(resources)
titles = resources.map { |resource| title(resource) }
message = header_with_list("Multiple results were found:", titles)
ephemeral_response(message)
end
def error(resource)
message = header_with_list("The action was not successful, because:", resource.errors.messages)
ephemeral_response(message)
end
def title(resource)
reference = resource.try(:to_reference) || resource.try(:id)
title = resource.try(:title) || resource.try(:name)
"[#{reference} #{title}](#{url(resource)})"
end
def header_with_list(header, items)
message = [header]
items.each do |item|
message << "- #{item}"
end
message.join("\n")
end
def url(resource)
url_for(
[
resource.project.namespace.becomes(Namespace),
resource.project,
resource
]
)
end
def ephemeral_response(message)
{
response_type: :ephemeral,
text: message,
status: 200
}
end
def in_channel_response(message)
{
response_type: :in_channel,
text: message,
status: 200
}
end
end
end
end
module Gitlab
module ChatCommands
module Presenters
class Access < Presenters::Base
def access_denied
ephemeral_response(text: "Whoops! This action is not allowed. This incident will be [reported](https://xkcd.com/838/).")
end
def not_found
ephemeral_response(text: "404 not found! GitLab couldn't find what you were looking for! :boom:")
end
def authorize
message =
if @resource
":wave: Hi there! Before I do anything for you, please [connect your GitLab account](#{@resource})."
else
":sweat_smile: Couldn't identify you, nor can I autorize you!"
end
ephemeral_response(text: message)
end
def unknown_command(commands)
ephemeral_response(text: help_message(trigger))
end
private
def help_message(trigger)
header_with_list("Command not found, these are the commands you can use", full_commands(trigger))
end
def full_commands(trigger)
@resource.map { |command| "#{trigger} #{command.help_message}" }
end
end
end
end
end
module Gitlab
module ChatCommands
module Presenters
class Base
include Gitlab::Routing.url_helpers
def initialize(resource = nil)
@resource = resource
end
def display_errors
message = header_with_list("The action was not successful, because:", @resource.errors.full_messages)
ephemeral_response(text: message)
end
private
def header_with_list(header, items)
message = [header]
items.each do |item|
message << "- #{item}"
end
message.join("\n")
end
def ephemeral_response(message)
response = {
response_type: :ephemeral,
status: 200
}.merge(message)
format_response(response)
end
def in_channel_response(message)
response = {
response_type: :in_channel,
status: 200
}.merge(message)
format_response(response)
end
def format_response(response)
response[:text] = format(response[:text]) if response.has_key?(:text)
if response.has_key?(:attachments)
response[:attachments].each do |attachment|
attachment[:pretext] = format(attachment[:pretext]) if attachment[:pretext]
attachment[:text] = format(attachment[:text]) if attachment[:text]
end
end
response
end
# Convert Markdown to slacks format
def format(string)
Slack::Notifier::LinkFormatter.format(string)
end
def resource_url
url_for(
[
@resource.project.namespace.becomes(Namespace),
@resource.project,
@resource
]
)
end
end
end
end
end
module Gitlab
module ChatCommands
module Presenters
class Deploy < Presenters::Base
def present(from, to)
message = "Deployment started from #{from} to #{to}. [Follow its progress](#{resource_url})."
in_channel_response(text: message)
end
def no_actions
ephemeral_response(text: "No action found to be executed")
end
def too_many_actions
ephemeral_response(text: "Too many actions defined")
end
end
end
end
end
module Gitlab
module ChatCommands
module Presenters
class Help < Presenters::Base
def present(trigger, text)
ephemeral_response(text: help_message(trigger, text))
end
private
def help_message(trigger, text)
return "No commands available :thinking_face:" unless @resource.present?
if text.start_with?('help')
header_with_list("Available commands", full_commands(trigger))
else
header_with_list("Unknown command, these commands are available", full_commands(trigger))
end
end
def full_commands(trigger)
@resource.map { |command| "#{trigger} #{command.help_message}" }
end
end
end
end
end
module Gitlab
module ChatCommands
module Presenters
module Issuable
def color(issuable)
issuable.open? ? '#38ae67' : '#d22852'
end
def status_text(issuable)
issuable.open? ? 'Open' : 'Closed'
end
def project
@resource.project
end
def author
@resource.author
end
def fields
[
{
title: "Assignee",
value: @resource.assignee ? @resource.assignee.name : "_None_",
short: true
},
{
title: "Milestone",
value: @resource.milestone ? @resource.milestone.title : "_None_",
short: true
},
{
title: "Labels",
value: @resource.labels.any? ? @resource.label_names : "_None_",
short: true
}
]
end
end
end
end
end
module Gitlab
module ChatCommands
module Presenters
class IssueNew < Presenters::Base
include Presenters::Issuable
def present
in_channel_response(new_issue)
end
private
def new_issue
{
attachments: [
{
title: "#{@resource.title} · #{@resource.to_reference}",
title_link: resource_url,
author_name: author.name,
author_icon: author.avatar_url,
fallback: "New issue #{@resource.to_reference}: #{@resource.title}",
pretext: pretext,
color: color(@resource),
fields: fields,
mrkdwn_in: [
:title,
:pretext,
:text,
:fields
]
}
]
}
end
def pretext
"I created an issue on #{author_profile_link}'s behalf: **#{@resource.to_reference}** in #{project_link}"
end
def project_link
"[#{project.name_with_namespace}](#{projects_url(project)})"
end
def author_profile_link
"[#{author.to_reference}](#{url_for(author)})"
end
end
end
end
end
module Gitlab
module ChatCommands
module Presenters
class IssueSearch < Presenters::Base
include Presenters::Issuable
def present
text = if @resource.count >= 5
"Here are the first 5 issues I found:"
elsif @resource.one?
"Here is the only issue I found:"
else
"Here are the #{@resource.count} issues I found:"
end
ephemeral_response(text: text, attachments: attachments)
end
private
def attachments
@resource.map do |issue|
url = "[#{issue.to_reference}](#{url_for([namespace, project, issue])})"
{
color: color(issue),
fallback: "#{issue.to_reference} #{issue.title}",
text: "#{url} · #{issue.title} (#{status_text(issue)})",
mrkdwn_in: [
:text
]
}
end
end
def project
@project ||= @resource.first.project
end
def namespace
@namespace ||= project.namespace.becomes(Namespace)
end
end
end
end
end
module Gitlab
module ChatCommands
module Presenters
class IssueShow < Presenters::Base
include Presenters::Issuable
def present
if @resource.confidential?
ephemeral_response(show_issue)
else
in_channel_response(show_issue)
end
end
private
def show_issue
{
attachments: [
{
title: "#{@resource.title} · #{@resource.to_reference}",
title_link: resource_url,
author_name: author.name,
author_icon: author.avatar_url,
fallback: "Issue #{@resource.to_reference}: #{@resource.title}",
pretext: pretext,
text: text,
color: color(@resource),
fields: fields,
mrkdwn_in: [
:pretext,
:text,
:fields
]
}
]
}
end
def text
message = "**#{status_text(@resource)}**"
if @resource.upvotes.zero? && @resource.downvotes.zero? && @resource.user_notes_count.zero?
return message
end
message << " · "
message << ":+1: #{@resource.upvotes} " unless @resource.upvotes.zero?
message << ":-1: #{@resource.downvotes} " unless @resource.downvotes.zero?
message << ":speech_balloon: #{@resource.user_notes_count}" unless @resource.user_notes_count.zero?
message
end
def pretext
"Issue *#{@resource.to_reference}* from #{project.name_with_namespace}"
end
end
end
end
end
......@@ -24,7 +24,7 @@ describe Gitlab::ChatCommands::Command, service: true do
it 'displays the help message' do
expect(subject[:response_type]).to be(:ephemeral)
expect(subject[:text]).to start_with('Available commands')
expect(subject[:text]).to start_with('Unknown command')
expect(subject[:text]).to match('/gitlab issue show')
end
end
......@@ -34,47 +34,7 @@ describe Gitlab::ChatCommands::Command, service: true do
it 'rejects the actions' do
expect(subject[:response_type]).to be(:ephemeral)
expect(subject[:text]).to start_with('Whoops! That action is not allowed')
end
end
context 'issue is successfully created' do
let(:params) { { text: "issue create my new issue" } }
before do
project.team << [user, :master]
end
it 'presents the issue' do
expect(subject[:text]).to match("my new issue")
end
it 'shows a link to the new issue' do
expect(subject[:text]).to match(/\/issues\/\d+/)
end
end
context 'searching for an issue' do
let(:params) { { text: 'issue search find me' } }
let!(:issue) { create(:issue, project: project, title: 'find me') }
before do
project.team << [user, :master]
end
context 'a single issue is found' do
it 'presents the issue' do
expect(subject[:text]).to match(issue.title)
end
end
context 'multiple issues found' do
let!(:issue2) { create(:issue, project: project, title: "someone find me") }
it 'shows a link to the new issue' do
expect(subject[:text]).to match(issue.title)
expect(subject[:text]).to match(issue2.title)
end
expect(subject[:text]).to start_with('Whoops! This action is not allowed')
end
end
......@@ -90,7 +50,7 @@ describe Gitlab::ChatCommands::Command, service: true do
context 'and user can not create deployment' do
it 'returns action' do
expect(subject[:response_type]).to be(:ephemeral)
expect(subject[:text]).to start_with('Whoops! That action is not allowed')
expect(subject[:text]).to start_with('Whoops! This action is not allowed')
end
end
......@@ -100,7 +60,7 @@ describe Gitlab::ChatCommands::Command, service: true do
end
it 'returns action' do
expect(subject[:text]).to include('Deployment from staging to production started.')
expect(subject[:text]).to include('Deployment started from staging to production')
expect(subject[:response_type]).to be(:in_channel)
end
......@@ -130,7 +90,7 @@ describe Gitlab::ChatCommands::Command, service: true do
context 'IssueCreate is triggered' do
let(:params) { { text: 'issue create my title' } }
it { is_expected.to eq(Gitlab::ChatCommands::IssueCreate) }
it { is_expected.to eq(Gitlab::ChatCommands::IssueNew) }
end
context 'IssueSearch is triggered' do
......
......@@ -15,8 +15,9 @@ describe Gitlab::ChatCommands::Deploy, service: true do
end
context 'if no environment is defined' do
it 'returns nil' do
expect(subject).to be_nil
it 'does not execute an action' do
expect(subject[:response_type]).to be(:ephemeral)
expect(subject[:text]).to eq("No action found to be executed")
end
end
......@@ -26,8 +27,9 @@ describe Gitlab::ChatCommands::Deploy, service: true do
let!(:deployment) { create(:deployment, environment: staging, deployable: build) }
context 'without actions' do
it 'returns nil' do
expect(subject).to be_nil
it 'does not execute an action' do
expect(subject[:response_type]).to be(:ephemeral)
expect(subject[:text]).to eq("No action found to be executed")
end
end
......@@ -37,8 +39,8 @@ describe Gitlab::ChatCommands::Deploy, service: true do
end
it 'returns success result' do
expect(subject.type).to eq(:success)
expect(subject.message).to include('Deployment from staging to production started')
expect(subject[:response_type]).to be(:in_channel)
expect(subject[:text]).to start_with('Deployment started from staging to production')
end
context 'when duplicate action exists' do
......@@ -47,8 +49,8 @@ describe Gitlab::ChatCommands::Deploy, service: true do
end
it 'returns error' do
expect(subject.type).to eq(:error)
expect(subject.message).to include('Too many actions defined')
expect(subject[:response_type]).to be(:ephemeral)
expect(subject[:text]).to eq('Too many actions defined')
end
end
......@@ -59,9 +61,9 @@ describe Gitlab::ChatCommands::Deploy, service: true do
name: 'teardown', environment: 'production')
end
it 'returns success result' do
expect(subject.type).to eq(:success)
expect(subject.message).to include('Deployment from staging to production started')
it 'returns the success message' do
expect(subject[:response_type]).to be(:in_channel)
expect(subject[:text]).to start_with('Deployment started from staging to production')
end
end
end
......
require 'spec_helper'
describe Gitlab::ChatCommands::IssueCreate, service: true do
describe Gitlab::ChatCommands::IssueNew, service: true do
describe '#execute' do
let(:project) { create(:empty_project) }
let(:user) { create(:user) }
......@@ -18,7 +18,7 @@ describe Gitlab::ChatCommands::IssueCreate, service: true do
it 'creates the issue' do
expect { subject }.to change { project.issues.count }.by(1)
expect(subject.title).to eq('bird is the word')
expect(subject[:response_type]).to be(:in_channel)
end
end
......@@ -41,6 +41,16 @@ describe Gitlab::ChatCommands::IssueCreate, service: true do
expect { subject }.to change { project.issues.count }.by(1)
end
end
context 'issue cannot be created' do
let!(:issue) { create(:issue, project: project, title: 'bird is the word') }
let(:regex_match) { described_class.match("issue create #{'a' * 512}}") }
it 'displays the errors' do
expect(subject[:response_type]).to be(:ephemeral)
expect(subject[:text]).to match("- Title is too long")
end
end
end
describe '.match' do
......
......@@ -2,9 +2,9 @@ require 'spec_helper'
describe Gitlab::ChatCommands::IssueSearch, service: true do
describe '#execute' do
let!(:issue) { create(:issue, title: 'find me') }
let!(:issue) { create(:issue, project: project, title: 'find me') }
let!(:confidential) { create(:issue, :confidential, project: project, title: 'mepmep find') }
let(:project) { issue.project }
let(:project) { create(:empty_project) }
let(:user) { issue.author }
let(:regex_match) { described_class.match("issue search find") }
......@@ -14,7 +14,8 @@ describe Gitlab::ChatCommands::IssueSearch, service: true do
context 'when the user has no access' do
it 'only returns the open issues' do
expect(subject).not_to include(confidential)
expect(subject[:response_type]).to be(:ephemeral)
expect(subject[:text]).to match("not found")
end
end
......@@ -24,13 +25,14 @@ describe Gitlab::ChatCommands::IssueSearch, service: true do
end
it 'returns all results' do
expect(subject).to include(confidential, issue)
expect(subject).to have_key(:attachments)
expect(subject[:text]).to eq("Here are the 2 issues I found:")
end
end
context 'without hits on the query' do
it 'returns an empty collection' do
expect(subject).to be_empty
expect(subject[:text]).to match("not found")
end
end
end
......
......@@ -2,8 +2,8 @@ require 'spec_helper'
describe Gitlab::ChatCommands::IssueShow, service: true do
describe '#execute' do
let(:issue) { create(:issue) }
let(:project) { issue.project }
let(:issue) { create(:issue, project: project) }
let(:project) { create(:empty_project) }
let(:user) { issue.author }
let(:regex_match) { described_class.match("issue show #{issue.iid}") }
......@@ -16,15 +16,19 @@ describe Gitlab::ChatCommands::IssueShow, service: true do
end
context 'the issue exists' do
let(:title) { subject[:attachments].first[:title] }
it 'returns the issue' do
expect(subject.iid).to be issue.iid
expect(subject[:response_type]).to be(:in_channel)
expect(title).to start_with(issue.title)
end
context 'when its reference is given' do
let(:regex_match) { described_class.match("issue show #{issue.to_reference}") }
it 'shows the issue' do
expect(subject.iid).to be issue.iid
expect(subject[:response_type]).to be(:in_channel)
expect(title).to start_with(issue.title)
end
end
end
......@@ -32,17 +36,24 @@ describe Gitlab::ChatCommands::IssueShow, service: true do
context 'the issue does not exist' do
let(:regex_match) { described_class.match("issue show 2343242") }
it "returns nil" do
expect(subject).to be_nil
it "returns not found" do
expect(subject[:response_type]).to be(:ephemeral)
expect(subject[:text]).to match("not found")
end
end
end
describe 'self.match' do
describe '.match' do
it 'matches the iid' do
match = described_class.match("issue show 123")
expect(match[:iid]).to eq("123")
end
it 'accepts a reference' do
match = described_class.match("issue show #{Issue.reference_prefix}123")
expect(match[:iid]).to eq("123")
end
end
end
require 'spec_helper'
describe Gitlab::ChatCommands::Presenters::Access do
describe '#access_denied' do
subject { described_class.new.access_denied }
it { is_expected.to be_a(Hash) }
it 'displays an error message' do
expect(subject[:text]).to match("is not allowed")
expect(subject[:response_type]).to be(:ephemeral)
end
end
describe '#not_found' do
subject { described_class.new.not_found }
it { is_expected.to be_a(Hash) }
it 'tells the user the resource was not found' do
expect(subject[:text]).to match("not found!")
expect(subject[:response_type]).to be(:ephemeral)
end
end
describe '#authorize' do
context 'with an authorization URL' do
subject { described_class.new('http://authorize.me').authorize }
it { is_expected.to be_a(Hash) }
it 'tells the user to authorize' do
expect(subject[:text]).to match("connect your GitLab account")
expect(subject[:response_type]).to be(:ephemeral)
end
end
context 'without authorization url' do
subject { described_class.new.authorize }
it { is_expected.to be_a(Hash) }
it 'tells the user to authorize' do
expect(subject[:text]).to match("Couldn't identify you")
expect(subject[:response_type]).to be(:ephemeral)
end
end
end
end
require 'spec_helper'
describe Gitlab::ChatCommands::Presenters::Deploy do
let(:build) { create(:ci_build) }
describe '#present' do
subject { described_class.new(build).present('staging', 'prod') }
it { is_expected.to have_key(:text) }
it { is_expected.to have_key(:response_type) }
it { is_expected.to have_key(:status) }
it { is_expected.not_to have_key(:attachments) }
it 'messages the channel of the deploy' do
expect(subject[:response_type]).to be(:in_channel)
expect(subject[:text]).to start_with("Deployment started from staging to prod")
end
end
describe '#no_actions' do
subject { described_class.new(nil).no_actions }
it { is_expected.to have_key(:text) }
it { is_expected.to have_key(:response_type) }
it { is_expected.to have_key(:status) }
it { is_expected.not_to have_key(:attachments) }
it 'tells the user there is no action' do
expect(subject[:response_type]).to be(:ephemeral)
expect(subject[:text]).to eq("No action found to be executed")
end
end
describe '#too_many_actions' do
subject { described_class.new([]).too_many_actions }
it { is_expected.to have_key(:text) }
it { is_expected.to have_key(:response_type) }
it { is_expected.to have_key(:status) }
it { is_expected.not_to have_key(:attachments) }
it 'tells the user there is no action' do
expect(subject[:response_type]).to be(:ephemeral)
expect(subject[:text]).to eq("Too many actions defined")
end
end
end
require 'spec_helper'
describe Gitlab::ChatCommands::Presenters::IssueNew do
let(:project) { create(:empty_project) }
let(:issue) { create(:issue, project: project) }
let(:attachment) { subject[:attachments].first }
subject { described_class.new(issue).present }
it { is_expected.to be_a(Hash) }
it 'shows the issue' do
expect(subject[:response_type]).to be(:in_channel)
expect(subject).to have_key(:attachments)
expect(attachment[:title]).to start_with(issue.title)
end
end
require 'spec_helper'
describe Gitlab::ChatCommands::Presenters::IssueSearch do
let(:project) { create(:empty_project) }
let(:message) { subject[:text] }
before { create_list(:issue, 2, project: project) }
subject { described_class.new(project.issues).present }
it 'formats the message correct' do
is_expected.to have_key(:text)
is_expected.to have_key(:status)
is_expected.to have_key(:response_type)
is_expected.to have_key(:attachments)
end
it 'shows a list of results' do
expect(subject[:response_type]).to be(:ephemeral)
expect(message).to start_with("Here are the 2 issues I found")
end
end
require 'spec_helper'
describe Gitlab::ChatCommands::Presenters::IssueShow do
let(:project) { create(:empty_project) }
let(:issue) { create(:issue, project: project) }
let(:attachment) { subject[:attachments].first }
subject { described_class.new(issue).present }
it { is_expected.to be_a(Hash) }
it 'shows the issue' do
expect(subject[:response_type]).to be(:in_channel)
expect(subject).to have_key(:attachments)
expect(attachment[:title]).to start_with(issue.title)
end
context 'with upvotes' do
before do
create(:award_emoji, :upvote, awardable: issue)
end
it 'shows the upvote count' do
expect(subject[:response_type]).to be(:in_channel)
expect(attachment[:text]).to start_with("**Open** · :+1: 1")
end
end
context 'confidential issue' do
let(:issue) { create(:issue, project: project) }
it 'shows an ephemeral response' do
expect(subject[:response_type]).to be(:in_channel)
expect(attachment[:text]).to start_with("**Open**")
end
end
end
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment