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 ...@@ -31,13 +31,13 @@ class ChatSlashCommandsService < Service
return unless valid_token?(params[:token]) return unless valid_token?(params[:token])
user = find_chat_user(params) user = find_chat_user(params)
unless user
if user
Gitlab::ChatCommands::Command.new(project, user, params).execute
else
url = authorize_chat_name_url(params) url = authorize_chat_name_url(params)
return presenter.authorize_chat_name(url) Gitlab::ChatCommands::Presenters::Access.new(url).authorize
end end
Gitlab::ChatCommands::Command.new(project, user,
params).execute
end end
private private
...@@ -49,8 +49,4 @@ class ChatSlashCommandsService < Service ...@@ -49,8 +49,4 @@ class ChatSlashCommandsService < Service
def authorize_chat_name_url(params) def authorize_chat_name_url(params)
ChatNames::AuthorizeUserService.new(self, params).execute ChatNames::AuthorizeUserService.new(self, params).execute
end end
def presenter
Gitlab::ChatCommands::Presenter.new
end
end end
---
title: Reformat messages ChatOps
merge_request: 8528
author:
...@@ -42,10 +42,6 @@ module Gitlab ...@@ -42,10 +42,6 @@ module Gitlab
def find_by_iid(iid) def find_by_iid(iid)
collection.find_by(iid: iid) collection.find_by(iid: iid)
end end
def presenter
Gitlab::ChatCommands::Presenter.new
end
end end
end end
end end
...@@ -3,7 +3,7 @@ module Gitlab ...@@ -3,7 +3,7 @@ module Gitlab
class Command < BaseCommand class Command < BaseCommand
COMMANDS = [ COMMANDS = [
Gitlab::ChatCommands::IssueShow, Gitlab::ChatCommands::IssueShow,
Gitlab::ChatCommands::IssueCreate, Gitlab::ChatCommands::IssueNew,
Gitlab::ChatCommands::IssueSearch, Gitlab::ChatCommands::IssueSearch,
Gitlab::ChatCommands::Deploy, Gitlab::ChatCommands::Deploy,
].freeze ].freeze
...@@ -13,19 +13,20 @@ module Gitlab ...@@ -13,19 +13,20 @@ module Gitlab
if command if command
if command.allowed?(project, current_user) if command.allowed?(project, current_user)
present command.new(project, current_user, params).execute(match) command.new(project, current_user, params).execute(match)
else else
access_denied Gitlab::ChatCommands::Presenters::Access.new.access_denied
end end
else else
help(help_messages) Gitlab::ChatCommands::Help.new(project, current_user, params).execute(available_commands, params[:text])
end end
end end
def match_command def match_command
match = nil match = nil
service = available_commands.find do |klass| service =
match = klass.match(command) available_commands.find do |klass|
match = klass.match(params[:text])
end end
[service, match] [service, match]
...@@ -33,31 +34,11 @@ module Gitlab ...@@ -33,31 +34,11 @@ module Gitlab
private private
def help_messages
available_commands.map(&:help_message)
end
def available_commands def available_commands
COMMANDS.select do |klass| COMMANDS.select do |klass|
klass.available?(project) klass.available?(project)
end end
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 end
end end
module Gitlab module Gitlab
module ChatCommands module ChatCommands
class Deploy < BaseCommand class Deploy < BaseCommand
include Gitlab::Routing.url_helpers
def self.match(text) def self.match(text)
/\Adeploy\s+(?<from>\S+.*)\s+to+\s+(?<to>\S+.*)\z/.match(text) /\Adeploy\s+(?<from>\S+.*)\s+to+\s+(?<to>\S+.*)\z/.match(text)
end end
...@@ -24,35 +22,29 @@ module Gitlab ...@@ -24,35 +22,29 @@ module Gitlab
to = match[:to] to = match[:to]
actions = find_actions(from, to) actions = find_actions(from, to)
return unless actions.present?
if actions.one? if actions.none?
play!(from, to, actions.first) 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 else
Result.new(:error, 'Too many actions defined') Gitlab::ChatCommands::Presenters::Deploy.new(actions).too_many_actions
end end
end end
private private
def play!(from, to, action) def play!(from, to, action)
new_action = action.play(current_user) action.play(current_user)
Result.new(:success, "Deployment from #{from} to #{to} started. Follow the progress: #{url(new_action)}.")
end end
def find_actions(from, to) def find_actions(from, to)
environment = project.environments.find_by(name: from) environment = project.environments.find_by(name: from)
return unless environment return [] unless environment
environment.actions_for(to).select(&:starts_environment?) environment.actions_for(to).select(&:starts_environment?)
end end
def url(subject)
polymorphic_url(
[subject.project.namespace.becomes(Namespace), subject.project, subject]
)
end
end end
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 Gitlab
module ChatCommands module ChatCommands
class IssueCreate < IssueCommand class IssueNew < IssueCommand
def self.match(text) 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 # the title and description are not seperated
...@@ -19,8 +19,24 @@ module Gitlab ...@@ -19,8 +19,24 @@ module Gitlab
title = match[:title] title = match[:title]
description = match[:description].to_s.rstrip 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 Issues::CreateService.new(project, current_user, title: title, description: description).execute
end end
def presenter(issue)
Gitlab::ChatCommands::Presenters::IssueNew.new(issue)
end
end end
end end
end end
...@@ -10,7 +10,13 @@ module Gitlab ...@@ -10,7 +10,13 @@ module Gitlab
end end
def execute(match) 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 end
end end
......
...@@ -10,7 +10,13 @@ module Gitlab ...@@ -10,7 +10,13 @@ module Gitlab
end end
def execute(match) 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 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 ...@@ -24,7 +24,7 @@ describe Gitlab::ChatCommands::Command, service: true do
it 'displays the help message' do it 'displays the help message' do
expect(subject[:response_type]).to be(:ephemeral) 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') expect(subject[:text]).to match('/gitlab issue show')
end end
end end
...@@ -34,47 +34,7 @@ describe Gitlab::ChatCommands::Command, service: true do ...@@ -34,47 +34,7 @@ describe Gitlab::ChatCommands::Command, service: true do
it 'rejects the actions' do it 'rejects the actions' do
expect(subject[:response_type]).to be(:ephemeral) 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
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
end end
end end
...@@ -90,7 +50,7 @@ describe Gitlab::ChatCommands::Command, service: true do ...@@ -90,7 +50,7 @@ describe Gitlab::ChatCommands::Command, service: true do
context 'and user can not create deployment' do context 'and user can not create deployment' do
it 'returns action' do it 'returns action' do
expect(subject[:response_type]).to be(:ephemeral) 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
end end
...@@ -100,7 +60,7 @@ describe Gitlab::ChatCommands::Command, service: true do ...@@ -100,7 +60,7 @@ describe Gitlab::ChatCommands::Command, service: true do
end end
it 'returns action' do 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) expect(subject[:response_type]).to be(:in_channel)
end end
...@@ -130,7 +90,7 @@ describe Gitlab::ChatCommands::Command, service: true do ...@@ -130,7 +90,7 @@ describe Gitlab::ChatCommands::Command, service: true do
context 'IssueCreate is triggered' do context 'IssueCreate is triggered' do
let(:params) { { text: 'issue create my title' } } let(:params) { { text: 'issue create my title' } }
it { is_expected.to eq(Gitlab::ChatCommands::IssueCreate) } it { is_expected.to eq(Gitlab::ChatCommands::IssueNew) }
end end
context 'IssueSearch is triggered' do context 'IssueSearch is triggered' do
......
...@@ -15,8 +15,9 @@ describe Gitlab::ChatCommands::Deploy, service: true do ...@@ -15,8 +15,9 @@ describe Gitlab::ChatCommands::Deploy, service: true do
end end
context 'if no environment is defined' do context 'if no environment is defined' do
it 'returns nil' do it 'does not execute an action' do
expect(subject).to be_nil expect(subject[:response_type]).to be(:ephemeral)
expect(subject[:text]).to eq("No action found to be executed")
end end
end end
...@@ -26,8 +27,9 @@ describe Gitlab::ChatCommands::Deploy, service: true do ...@@ -26,8 +27,9 @@ describe Gitlab::ChatCommands::Deploy, service: true do
let!(:deployment) { create(:deployment, environment: staging, deployable: build) } let!(:deployment) { create(:deployment, environment: staging, deployable: build) }
context 'without actions' do context 'without actions' do
it 'returns nil' do it 'does not execute an action' do
expect(subject).to be_nil expect(subject[:response_type]).to be(:ephemeral)
expect(subject[:text]).to eq("No action found to be executed")
end end
end end
...@@ -37,8 +39,8 @@ describe Gitlab::ChatCommands::Deploy, service: true do ...@@ -37,8 +39,8 @@ describe Gitlab::ChatCommands::Deploy, service: true do
end end
it 'returns success result' do it 'returns success result' do
expect(subject.type).to eq(:success) expect(subject[:response_type]).to be(:in_channel)
expect(subject.message).to include('Deployment from staging to production started') expect(subject[:text]).to start_with('Deployment started from staging to production')
end end
context 'when duplicate action exists' do context 'when duplicate action exists' do
...@@ -47,8 +49,8 @@ describe Gitlab::ChatCommands::Deploy, service: true do ...@@ -47,8 +49,8 @@ describe Gitlab::ChatCommands::Deploy, service: true do
end end
it 'returns error' do it 'returns error' do
expect(subject.type).to eq(:error) expect(subject[:response_type]).to be(:ephemeral)
expect(subject.message).to include('Too many actions defined') expect(subject[:text]).to eq('Too many actions defined')
end end
end end
...@@ -59,9 +61,9 @@ describe Gitlab::ChatCommands::Deploy, service: true do ...@@ -59,9 +61,9 @@ describe Gitlab::ChatCommands::Deploy, service: true do
name: 'teardown', environment: 'production') name: 'teardown', environment: 'production')
end end
it 'returns success result' do it 'returns the success message' do
expect(subject.type).to eq(:success) expect(subject[:response_type]).to be(:in_channel)
expect(subject.message).to include('Deployment from staging to production started') expect(subject[:text]).to start_with('Deployment started from staging to production')
end end
end end
end end
......
require 'spec_helper' require 'spec_helper'
describe Gitlab::ChatCommands::IssueCreate, service: true do describe Gitlab::ChatCommands::IssueNew, service: true do
describe '#execute' do describe '#execute' do
let(:project) { create(:empty_project) } let(:project) { create(:empty_project) }
let(:user) { create(:user) } let(:user) { create(:user) }
...@@ -18,7 +18,7 @@ describe Gitlab::ChatCommands::IssueCreate, service: true do ...@@ -18,7 +18,7 @@ describe Gitlab::ChatCommands::IssueCreate, service: true do
it 'creates the issue' do it 'creates the issue' do
expect { subject }.to change { project.issues.count }.by(1) 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
end end
...@@ -41,6 +41,16 @@ describe Gitlab::ChatCommands::IssueCreate, service: true do ...@@ -41,6 +41,16 @@ describe Gitlab::ChatCommands::IssueCreate, service: true do
expect { subject }.to change { project.issues.count }.by(1) expect { subject }.to change { project.issues.count }.by(1)
end end
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 end
describe '.match' do describe '.match' do
......
...@@ -2,9 +2,9 @@ require 'spec_helper' ...@@ -2,9 +2,9 @@ require 'spec_helper'
describe Gitlab::ChatCommands::IssueSearch, service: true do describe Gitlab::ChatCommands::IssueSearch, service: true do
describe '#execute' 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!(:confidential) { create(:issue, :confidential, project: project, title: 'mepmep find') }
let(:project) { issue.project } let(:project) { create(:empty_project) }
let(:user) { issue.author } let(:user) { issue.author }
let(:regex_match) { described_class.match("issue search find") } let(:regex_match) { described_class.match("issue search find") }
...@@ -14,7 +14,8 @@ describe Gitlab::ChatCommands::IssueSearch, service: true do ...@@ -14,7 +14,8 @@ describe Gitlab::ChatCommands::IssueSearch, service: true do
context 'when the user has no access' do context 'when the user has no access' do
it 'only returns the open issues' 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
end end
...@@ -24,13 +25,14 @@ describe Gitlab::ChatCommands::IssueSearch, service: true do ...@@ -24,13 +25,14 @@ describe Gitlab::ChatCommands::IssueSearch, service: true do
end end
it 'returns all results' do 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
end end
context 'without hits on the query' do context 'without hits on the query' do
it 'returns an empty collection' do it 'returns an empty collection' do
expect(subject).to be_empty expect(subject[:text]).to match("not found")
end end
end end
end end
......
...@@ -2,8 +2,8 @@ require 'spec_helper' ...@@ -2,8 +2,8 @@ require 'spec_helper'
describe Gitlab::ChatCommands::IssueShow, service: true do describe Gitlab::ChatCommands::IssueShow, service: true do
describe '#execute' do describe '#execute' do
let(:issue) { create(:issue) } let(:issue) { create(:issue, project: project) }
let(:project) { issue.project } let(:project) { create(:empty_project) }
let(:user) { issue.author } let(:user) { issue.author }
let(:regex_match) { described_class.match("issue show #{issue.iid}") } let(:regex_match) { described_class.match("issue show #{issue.iid}") }
...@@ -16,15 +16,19 @@ describe Gitlab::ChatCommands::IssueShow, service: true do ...@@ -16,15 +16,19 @@ describe Gitlab::ChatCommands::IssueShow, service: true do
end end
context 'the issue exists' do context 'the issue exists' do
let(:title) { subject[:attachments].first[:title] }
it 'returns the issue' do 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 end
context 'when its reference is given' do context 'when its reference is given' do
let(:regex_match) { described_class.match("issue show #{issue.to_reference}") } let(:regex_match) { described_class.match("issue show #{issue.to_reference}") }
it 'shows the issue' do 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 end
end end
...@@ -32,17 +36,24 @@ describe Gitlab::ChatCommands::IssueShow, service: true do ...@@ -32,17 +36,24 @@ describe Gitlab::ChatCommands::IssueShow, service: true do
context 'the issue does not exist' do context 'the issue does not exist' do
let(:regex_match) { described_class.match("issue show 2343242") } let(:regex_match) { described_class.match("issue show 2343242") }
it "returns nil" do it "returns not found" do
expect(subject).to be_nil expect(subject[:response_type]).to be(:ephemeral)
expect(subject[:text]).to match("not found")
end end
end end
end end
describe 'self.match' do describe '.match' do
it 'matches the iid' do it 'matches the iid' do
match = described_class.match("issue show 123") match = described_class.match("issue show 123")
expect(match[:iid]).to eq("123") expect(match[:iid]).to eq("123")
end 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
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