From ea090291bba6bb665b3631cc5a2659e6673a6959 Mon Sep 17 00:00:00 2001 From: Eric Eastwood Date: Wed, 31 May 2017 00:50:53 -0500 Subject: Rename "Slash commands" to "Quick actions" Fix https://gitlab.com/gitlab-org/gitlab-ce/issues/27070 Deprecate "chat commands" in favor of "slash commands" We looked for things like: - `slash commmand` - `slash_command` - `slash-command` - `SlashCommand` --- .../slash_commands/command_definition_spec.rb | 225 --------------------- spec/lib/gitlab/slash_commands/command_spec.rb | 111 ++++++++++ spec/lib/gitlab/slash_commands/deploy_spec.rb | 90 +++++++++ spec/lib/gitlab/slash_commands/dsl_spec.rb | 109 ---------- spec/lib/gitlab/slash_commands/extractor_spec.rb | 223 -------------------- spec/lib/gitlab/slash_commands/issue_new_spec.rb | 78 +++++++ .../lib/gitlab/slash_commands/issue_search_spec.rb | 48 +++++ spec/lib/gitlab/slash_commands/issue_show_spec.rb | 59 ++++++ .../slash_commands/presenters/access_spec.rb | 49 +++++ .../slash_commands/presenters/deploy_spec.rb | 47 +++++ .../slash_commands/presenters/issue_new_spec.rb | 17 ++ .../slash_commands/presenters/issue_search_spec.rb | 25 +++ .../slash_commands/presenters/issue_show_spec.rb | 52 +++++ 13 files changed, 576 insertions(+), 557 deletions(-) delete mode 100644 spec/lib/gitlab/slash_commands/command_definition_spec.rb create mode 100644 spec/lib/gitlab/slash_commands/command_spec.rb create mode 100644 spec/lib/gitlab/slash_commands/deploy_spec.rb delete mode 100644 spec/lib/gitlab/slash_commands/dsl_spec.rb delete mode 100644 spec/lib/gitlab/slash_commands/extractor_spec.rb create mode 100644 spec/lib/gitlab/slash_commands/issue_new_spec.rb create mode 100644 spec/lib/gitlab/slash_commands/issue_search_spec.rb create mode 100644 spec/lib/gitlab/slash_commands/issue_show_spec.rb create mode 100644 spec/lib/gitlab/slash_commands/presenters/access_spec.rb create mode 100644 spec/lib/gitlab/slash_commands/presenters/deploy_spec.rb create mode 100644 spec/lib/gitlab/slash_commands/presenters/issue_new_spec.rb create mode 100644 spec/lib/gitlab/slash_commands/presenters/issue_search_spec.rb create mode 100644 spec/lib/gitlab/slash_commands/presenters/issue_show_spec.rb (limited to 'spec/lib/gitlab/slash_commands') diff --git a/spec/lib/gitlab/slash_commands/command_definition_spec.rb b/spec/lib/gitlab/slash_commands/command_definition_spec.rb deleted file mode 100644 index 5b9173d3d3f..00000000000 --- a/spec/lib/gitlab/slash_commands/command_definition_spec.rb +++ /dev/null @@ -1,225 +0,0 @@ -require 'spec_helper' - -describe Gitlab::SlashCommands::CommandDefinition do - subject { described_class.new(:command) } - - describe "#all_names" do - context "when the command has aliases" do - before do - subject.aliases = [:alias1, :alias2] - end - - it "returns an array with the name and aliases" do - expect(subject.all_names).to eq([:command, :alias1, :alias2]) - end - end - - context "when the command doesn't have aliases" do - it "returns an array with the name" do - expect(subject.all_names).to eq([:command]) - end - end - end - - describe "#noop?" do - context "when the command has an action block" do - before do - subject.action_block = proc { } - end - - it "returns false" do - expect(subject.noop?).to be false - end - end - - context "when the command doesn't have an action block" do - it "returns true" do - expect(subject.noop?).to be true - end - end - end - - describe "#available?" do - let(:opts) { { go: false } } - - context "when the command has a condition block" do - before do - subject.condition_block = proc { go } - end - - context "when the condition block returns true" do - before do - opts[:go] = true - end - - it "returns true" do - expect(subject.available?(opts)).to be true - end - end - - context "when the condition block returns false" do - it "returns false" do - expect(subject.available?(opts)).to be false - end - end - end - - context "when the command doesn't have a condition block" do - it "returns true" do - expect(subject.available?(opts)).to be true - end - end - end - - describe "#execute" do - let(:context) { OpenStruct.new(run: false) } - - context "when the command is a noop" do - it "doesn't execute the command" do - expect(context).not_to receive(:instance_exec) - - subject.execute(context, {}, nil) - - expect(context.run).to be false - end - end - - context "when the command is not a noop" do - before do - subject.action_block = proc { self.run = true } - end - - context "when the command is not available" do - before do - subject.condition_block = proc { false } - end - - it "doesn't execute the command" do - subject.execute(context, {}, nil) - - expect(context.run).to be false - end - end - - context "when the command is available" do - context "when the commnd has no arguments" do - before do - subject.action_block = proc { self.run = true } - end - - context "when the command is provided an argument" do - it "executes the command" do - subject.execute(context, {}, true) - - expect(context.run).to be true - end - end - - context "when the command is not provided an argument" do - it "executes the command" do - subject.execute(context, {}, nil) - - expect(context.run).to be true - end - end - end - - context "when the command has 1 required argument" do - before do - subject.action_block = ->(arg) { self.run = arg } - end - - context "when the command is provided an argument" do - it "executes the command" do - subject.execute(context, {}, true) - - expect(context.run).to be true - end - end - - context "when the command is not provided an argument" do - it "doesn't execute the command" do - subject.execute(context, {}, nil) - - expect(context.run).to be false - end - end - end - - context "when the command has 1 optional argument" do - before do - subject.action_block = proc { |arg = nil| self.run = arg || true } - end - - context "when the command is provided an argument" do - it "executes the command" do - subject.execute(context, {}, true) - - expect(context.run).to be true - end - end - - context "when the command is not provided an argument" do - it "executes the command" do - subject.execute(context, {}, nil) - - expect(context.run).to be true - end - end - end - - context 'when the command defines parse_params block' do - before do - subject.parse_params_block = ->(raw) { raw.strip } - subject.action_block = ->(parsed) { self.received_arg = parsed } - end - - it 'executes the command passing the parsed param' do - subject.execute(context, {}, 'something ') - - expect(context.received_arg).to eq('something') - end - end - end - end - end - - describe '#explain' do - context 'when the command is not available' do - before do - subject.condition_block = proc { false } - subject.explanation = 'Explanation' - end - - it 'returns nil' do - result = subject.explain({}, {}, nil) - - expect(result).to be_nil - end - end - - context 'when the explanation is a static string' do - before do - subject.explanation = 'Explanation' - end - - it 'returns this static string' do - result = subject.explain({}, {}, nil) - - expect(result).to eq 'Explanation' - end - end - - context 'when the explanation is dynamic' do - before do - subject.explanation = proc { |arg| "Dynamic #{arg}" } - end - - it 'invokes the proc' do - result = subject.explain({}, {}, 'explanation') - - expect(result).to eq 'Dynamic explanation' - end - end - end -end diff --git a/spec/lib/gitlab/slash_commands/command_spec.rb b/spec/lib/gitlab/slash_commands/command_spec.rb new file mode 100644 index 00000000000..28d7f9858c3 --- /dev/null +++ b/spec/lib/gitlab/slash_commands/command_spec.rb @@ -0,0 +1,111 @@ +require 'spec_helper' + +describe Gitlab::SlashCommands::Command, service: true do + let(:project) { create(:empty_project) } + let(:user) { create(:user) } + + describe '#execute' do + subject do + described_class.new(project, user, params).execute + end + + context 'when no command is available' do + let(:params) { { text: 'issue show 1' } } + let(:project) { create(:empty_project, has_external_issue_tracker: true) } + + it 'displays 404 messages' do + expect(subject[:response_type]).to be(:ephemeral) + expect(subject[:text]).to start_with('404 not found') + end + end + + context 'when an unknown command is triggered' do + let(:params) { { command: '/gitlab', text: "unknown command 123" } } + + it 'displays the help message' do + expect(subject[:response_type]).to be(:ephemeral) + expect(subject[:text]).to start_with('Unknown command') + expect(subject[:text]).to match('/gitlab issue show') + end + end + + context 'the user can not create an issue' do + let(:params) { { text: "issue create my new issue" } } + + it 'rejects the actions' do + expect(subject[:response_type]).to be(:ephemeral) + expect(subject[:text]).to start_with('Whoops! This action is not allowed') + end + end + + context 'when trying to do deployment' do + let(:params) { { text: 'deploy staging to production' } } + let!(:build) { create(:ci_build, pipeline: pipeline) } + let!(:pipeline) { create(:ci_pipeline, project: project) } + let!(:staging) { create(:environment, name: 'staging', project: project) } + let!(:deployment) { create(:deployment, environment: staging, deployable: build) } + + let!(:manual) do + create(:ci_build, :manual, pipeline: pipeline, + name: 'first', + environment: 'production') + end + + 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! This action is not allowed') + end + end + + context 'and user has deployment permission' do + before do + build.project.add_developer(user) + + create(:protected_branch, :developers_can_merge, + name: build.ref, project: project) + end + + it 'returns action' do + expect(subject[:text]).to include('Deployment started from staging to production') + expect(subject[:response_type]).to be(:in_channel) + end + + context 'when duplicate action exists' do + let!(:manual2) do + create(:ci_build, :manual, pipeline: pipeline, + name: 'second', + environment: 'production') + end + + it 'returns error' do + expect(subject[:response_type]).to be(:ephemeral) + expect(subject[:text]).to include('Too many actions defined') + end + end + end + end + end + + describe '#match_command' do + subject { described_class.new(project, user, params).match_command.first } + + context 'IssueShow is triggered' do + let(:params) { { text: 'issue show 123' } } + + it { is_expected.to eq(Gitlab::SlashCommands::IssueShow) } + end + + context 'IssueCreate is triggered' do + let(:params) { { text: 'issue create my title' } } + + it { is_expected.to eq(Gitlab::SlashCommands::IssueNew) } + end + + context 'IssueSearch is triggered' do + let(:params) { { text: 'issue search my query' } } + + it { is_expected.to eq(Gitlab::SlashCommands::IssueSearch) } + end + end +end diff --git a/spec/lib/gitlab/slash_commands/deploy_spec.rb b/spec/lib/gitlab/slash_commands/deploy_spec.rb new file mode 100644 index 00000000000..d919f7260db --- /dev/null +++ b/spec/lib/gitlab/slash_commands/deploy_spec.rb @@ -0,0 +1,90 @@ +require 'spec_helper' + +describe Gitlab::SlashCommands::Deploy, service: true do + describe '#execute' do + let(:project) { create(:empty_project) } + let(:user) { create(:user) } + let(:regex_match) { described_class.match('deploy staging to production') } + + before do + # Make it possible to trigger protected manual actions for developers. + # + project.add_developer(user) + + create(:protected_branch, :developers_can_merge, + name: 'master', project: project) + end + + subject do + described_class.new(project, user).execute(regex_match) + end + + context 'if no environment is defined' do + 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 + + context 'with environment' do + let!(:staging) { create(:environment, name: 'staging', project: project) } + let!(:pipeline) { create(:ci_pipeline, project: project) } + let!(:build) { create(:ci_build, pipeline: pipeline) } + let!(:deployment) { create(:deployment, environment: staging, deployable: build) } + + context 'without actions' do + 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 + + context 'with action' do + let!(:manual1) do + create(:ci_build, :manual, pipeline: pipeline, + name: 'first', + environment: 'production') + end + + it 'returns success result' do + 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 + let!(:manual2) do + create(:ci_build, :manual, pipeline: pipeline, + name: 'second', + environment: 'production') + end + + it 'returns error' do + expect(subject[:response_type]).to be(:ephemeral) + expect(subject[:text]).to eq('Too many actions defined') + end + end + + context 'when teardown action exists' do + let!(:teardown) do + create(:ci_build, :manual, :teardown_environment, + pipeline: pipeline, name: 'teardown', environment: 'production') + end + + 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 + end + end + + describe 'self.match' do + it 'matches the environment' do + match = described_class.match('deploy staging to production') + + expect(match[:from]).to eq('staging') + expect(match[:to]).to eq('production') + end + end +end diff --git a/spec/lib/gitlab/slash_commands/dsl_spec.rb b/spec/lib/gitlab/slash_commands/dsl_spec.rb deleted file mode 100644 index 33b49a5ddf9..00000000000 --- a/spec/lib/gitlab/slash_commands/dsl_spec.rb +++ /dev/null @@ -1,109 +0,0 @@ -require 'spec_helper' - -describe Gitlab::SlashCommands::Dsl do - before :all do - DummyClass = Struct.new(:project) do - include Gitlab::SlashCommands::Dsl # rubocop:disable RSpec/DescribedClass - - desc 'A command with no args' - command :no_args, :none do - "Hello World!" - end - - params 'The first argument' - explanation 'Static explanation' - command :explanation_with_aliases, :once, :first do |arg| - arg - end - - desc do - "A dynamic description for #{noteable.upcase}" - end - params 'The first argument', 'The second argument' - command :dynamic_description do |args| - args.split - end - - command :cc - - explanation do |arg| - "Action does something with #{arg}" - end - condition do - project == 'foo' - end - command :cond_action do |arg| - arg - end - - parse_params do |raw_arg| - raw_arg.strip - end - command :with_params_parsing do |parsed| - parsed - end - end - end - - describe '.command_definitions' do - it 'returns an array with commands definitions' do - no_args_def, explanation_with_aliases_def, dynamic_description_def, - cc_def, cond_action_def, with_params_parsing_def = - DummyClass.command_definitions - - expect(no_args_def.name).to eq(:no_args) - expect(no_args_def.aliases).to eq([:none]) - expect(no_args_def.description).to eq('A command with no args') - expect(no_args_def.explanation).to eq('') - expect(no_args_def.params).to eq([]) - expect(no_args_def.condition_block).to be_nil - expect(no_args_def.action_block).to be_a_kind_of(Proc) - expect(no_args_def.parse_params_block).to be_nil - - expect(explanation_with_aliases_def.name).to eq(:explanation_with_aliases) - expect(explanation_with_aliases_def.aliases).to eq([:once, :first]) - expect(explanation_with_aliases_def.description).to eq('') - expect(explanation_with_aliases_def.explanation).to eq('Static explanation') - expect(explanation_with_aliases_def.params).to eq(['The first argument']) - expect(explanation_with_aliases_def.condition_block).to be_nil - expect(explanation_with_aliases_def.action_block).to be_a_kind_of(Proc) - expect(explanation_with_aliases_def.parse_params_block).to be_nil - - expect(dynamic_description_def.name).to eq(:dynamic_description) - expect(dynamic_description_def.aliases).to eq([]) - expect(dynamic_description_def.to_h(noteable: 'issue')[:description]).to eq('A dynamic description for ISSUE') - expect(dynamic_description_def.explanation).to eq('') - expect(dynamic_description_def.params).to eq(['The first argument', 'The second argument']) - expect(dynamic_description_def.condition_block).to be_nil - expect(dynamic_description_def.action_block).to be_a_kind_of(Proc) - expect(dynamic_description_def.parse_params_block).to be_nil - - expect(cc_def.name).to eq(:cc) - expect(cc_def.aliases).to eq([]) - expect(cc_def.description).to eq('') - expect(cc_def.explanation).to eq('') - expect(cc_def.params).to eq([]) - expect(cc_def.condition_block).to be_nil - expect(cc_def.action_block).to be_nil - expect(cc_def.parse_params_block).to be_nil - - expect(cond_action_def.name).to eq(:cond_action) - expect(cond_action_def.aliases).to eq([]) - expect(cond_action_def.description).to eq('') - expect(cond_action_def.explanation).to be_a_kind_of(Proc) - expect(cond_action_def.params).to eq([]) - expect(cond_action_def.condition_block).to be_a_kind_of(Proc) - expect(cond_action_def.action_block).to be_a_kind_of(Proc) - expect(cond_action_def.parse_params_block).to be_nil - - expect(with_params_parsing_def.name).to eq(:with_params_parsing) - expect(with_params_parsing_def.aliases).to eq([]) - expect(with_params_parsing_def.description).to eq('') - expect(with_params_parsing_def.explanation).to eq('') - expect(with_params_parsing_def.params).to eq([]) - expect(with_params_parsing_def.condition_block).to be_nil - expect(with_params_parsing_def.action_block).to be_a_kind_of(Proc) - expect(with_params_parsing_def.parse_params_block).to be_a_kind_of(Proc) - end - end -end diff --git a/spec/lib/gitlab/slash_commands/extractor_spec.rb b/spec/lib/gitlab/slash_commands/extractor_spec.rb deleted file mode 100644 index d7f77486b3e..00000000000 --- a/spec/lib/gitlab/slash_commands/extractor_spec.rb +++ /dev/null @@ -1,223 +0,0 @@ -require 'spec_helper' - -describe Gitlab::SlashCommands::Extractor do - let(:definitions) do - Class.new do - include Gitlab::SlashCommands::Dsl - - command(:reopen, :open) { } - command(:assign) { } - command(:labels) { } - command(:power) { } - end.command_definitions - end - - let(:extractor) { described_class.new(definitions) } - - shared_examples 'command with no argument' do - it 'extracts command' do - msg, commands = extractor.extract_commands(original_msg) - - expect(commands).to eq [['reopen']] - expect(msg).to eq final_msg - end - end - - shared_examples 'command with a single argument' do - it 'extracts command' do - msg, commands = extractor.extract_commands(original_msg) - - expect(commands).to eq [['assign', '@joe']] - expect(msg).to eq final_msg - end - end - - shared_examples 'command with multiple arguments' do - it 'extracts command' do - msg, commands = extractor.extract_commands(original_msg) - - expect(commands).to eq [['labels', '~foo ~"bar baz" label']] - expect(msg).to eq final_msg - end - end - - describe '#extract_commands' do - describe 'command with no argument' do - context 'at the start of content' do - it_behaves_like 'command with no argument' do - let(:original_msg) { "/reopen\nworld" } - let(:final_msg) { "world" } - end - end - - context 'in the middle of content' do - it_behaves_like 'command with no argument' do - let(:original_msg) { "hello\n/reopen\nworld" } - let(:final_msg) { "hello\nworld" } - end - end - - context 'in the middle of a line' do - it 'does not extract command' do - msg = "hello\nworld /reopen" - msg, commands = extractor.extract_commands(msg) - - expect(commands).to be_empty - expect(msg).to eq "hello\nworld /reopen" - end - end - - context 'at the end of content' do - it_behaves_like 'command with no argument' do - let(:original_msg) { "hello\n/reopen" } - let(:final_msg) { "hello" } - end - end - end - - describe 'command with a single argument' do - context 'at the start of content' do - it_behaves_like 'command with a single argument' do - let(:original_msg) { "/assign @joe\nworld" } - let(:final_msg) { "world" } - end - - it 'allows slash in command arguments' do - msg = "/assign @joe / @jane\nworld" - msg, commands = extractor.extract_commands(msg) - - expect(commands).to eq [['assign', '@joe / @jane']] - expect(msg).to eq 'world' - end - end - - context 'in the middle of content' do - it_behaves_like 'command with a single argument' do - let(:original_msg) { "hello\n/assign @joe\nworld" } - let(:final_msg) { "hello\nworld" } - end - end - - context 'in the middle of a line' do - it 'does not extract command' do - msg = "hello\nworld /assign @joe" - msg, commands = extractor.extract_commands(msg) - - expect(commands).to be_empty - expect(msg).to eq "hello\nworld /assign @joe" - end - end - - context 'at the end of content' do - it_behaves_like 'command with a single argument' do - let(:original_msg) { "hello\n/assign @joe" } - let(:final_msg) { "hello" } - end - end - - context 'when argument is not separated with a space' do - it 'does not extract command' do - msg = "hello\n/assign@joe\nworld" - msg, commands = extractor.extract_commands(msg) - - expect(commands).to be_empty - expect(msg).to eq "hello\n/assign@joe\nworld" - end - end - end - - describe 'command with multiple arguments' do - context 'at the start of content' do - it_behaves_like 'command with multiple arguments' do - let(:original_msg) { %(/labels ~foo ~"bar baz" label\nworld) } - let(:final_msg) { "world" } - end - end - - context 'in the middle of content' do - it_behaves_like 'command with multiple arguments' do - let(:original_msg) { %(hello\n/labels ~foo ~"bar baz" label\nworld) } - let(:final_msg) { "hello\nworld" } - end - end - - context 'in the middle of a line' do - it 'does not extract command' do - msg = %(hello\nworld /labels ~foo ~"bar baz" label) - msg, commands = extractor.extract_commands(msg) - - expect(commands).to be_empty - expect(msg).to eq %(hello\nworld /labels ~foo ~"bar baz" label) - end - end - - context 'at the end of content' do - it_behaves_like 'command with multiple arguments' do - let(:original_msg) { %(hello\n/labels ~foo ~"bar baz" label) } - let(:final_msg) { "hello" } - end - end - - context 'when argument is not separated with a space' do - it 'does not extract command' do - msg = %(hello\n/labels~foo ~"bar baz" label\nworld) - msg, commands = extractor.extract_commands(msg) - - expect(commands).to be_empty - expect(msg).to eq %(hello\n/labels~foo ~"bar baz" label\nworld) - end - end - end - - it 'extracts command with multiple arguments and various prefixes' do - msg = %(hello\n/power @user.name %9.10 ~"bar baz.2"\nworld) - msg, commands = extractor.extract_commands(msg) - - expect(commands).to eq [['power', '@user.name %9.10 ~"bar baz.2"']] - expect(msg).to eq "hello\nworld" - end - - it 'extracts multiple commands' do - msg = %(hello\n/power @user.name %9.10 ~"bar baz.2" label\nworld\n/reopen) - msg, commands = extractor.extract_commands(msg) - - expect(commands).to eq [['power', '@user.name %9.10 ~"bar baz.2" label'], ['reopen']] - expect(msg).to eq "hello\nworld" - end - - it 'does not alter original content if no command is found' do - msg = 'Fixes #123' - msg, commands = extractor.extract_commands(msg) - - expect(commands).to be_empty - expect(msg).to eq 'Fixes #123' - end - - it 'does not extract commands inside a blockcode' do - msg = "Hello\r\n```\r\nThis is some text\r\n/close\r\n/assign @user\r\n```\r\n\r\nWorld" - expected = msg.delete("\r") - msg, commands = extractor.extract_commands(msg) - - expect(commands).to be_empty - expect(msg).to eq expected - end - - it 'does not extract commands inside a blockquote' do - msg = "Hello\r\n>>>\r\nThis is some text\r\n/close\r\n/assign @user\r\n>>>\r\n\r\nWorld" - expected = msg.delete("\r") - msg, commands = extractor.extract_commands(msg) - - expect(commands).to be_empty - expect(msg).to eq expected - end - - it 'does not extract commands inside a HTML tag' do - msg = "Hello\r\n
\r\nThis is some text\r\n/close\r\n/assign @user\r\n
\r\n\r\nWorld" - expected = msg.delete("\r") - msg, commands = extractor.extract_commands(msg) - - expect(commands).to be_empty - expect(msg).to eq expected - end - end -end diff --git a/spec/lib/gitlab/slash_commands/issue_new_spec.rb b/spec/lib/gitlab/slash_commands/issue_new_spec.rb new file mode 100644 index 00000000000..4de50d4a8bb --- /dev/null +++ b/spec/lib/gitlab/slash_commands/issue_new_spec.rb @@ -0,0 +1,78 @@ +require 'spec_helper' + +describe Gitlab::SlashCommands::IssueNew, service: true do + describe '#execute' do + let(:project) { create(:empty_project) } + let(:user) { create(:user) } + let(:regex_match) { described_class.match("issue create bird is the word") } + + before do + project.team << [user, :master] + end + + subject do + described_class.new(project, user).execute(regex_match) + end + + context 'without description' do + it 'creates the issue' do + expect { subject }.to change { project.issues.count }.by(1) + + expect(subject[:response_type]).to be(:in_channel) + end + end + + context 'with description' do + let(:description) { "Surfin bird" } + let(:regex_match) { described_class.match("issue create bird is the word\n#{description}") } + + it 'creates the issue with description' do + subject + + expect(Issue.last.description).to eq(description) + end + end + + context "with more newlines between the title and the description" do + let(:description) { "Surfin bird" } + let(:regex_match) { described_class.match("issue create bird is the word\n\n#{description}\n") } + + it 'creates the issue' 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 + it 'matches the title without description' do + match = described_class.match("issue create my title") + + expect(match[:title]).to eq('my title') + expect(match[:description]).to eq("") + end + + it 'matches the title with description' do + match = described_class.match("issue create my title\n\ndescription") + + expect(match[:title]).to eq('my title') + expect(match[:description]).to eq('description') + end + + it 'matches the alias new' do + match = described_class.match("issue new my title") + + expect(match).not_to be_nil + expect(match[:title]).to eq('my title') + end + end +end diff --git a/spec/lib/gitlab/slash_commands/issue_search_spec.rb b/spec/lib/gitlab/slash_commands/issue_search_spec.rb new file mode 100644 index 00000000000..06fff0afc50 --- /dev/null +++ b/spec/lib/gitlab/slash_commands/issue_search_spec.rb @@ -0,0 +1,48 @@ +require 'spec_helper' + +describe Gitlab::SlashCommands::IssueSearch, service: true do + describe '#execute' do + let!(:issue) { create(:issue, project: project, title: 'find me') } + let!(:confidential) { create(:issue, :confidential, project: project, title: 'mepmep find') } + let(:project) { create(:empty_project) } + let(:user) { issue.author } + let(:regex_match) { described_class.match("issue search find") } + + subject do + described_class.new(project, user).execute(regex_match) + end + + context 'when the user has no access' do + it 'only returns the open issues' do + expect(subject[:response_type]).to be(:ephemeral) + expect(subject[:text]).to match("not found") + end + end + + context 'the user has access' do + before do + project.team << [user, :master] + end + + it 'returns all results' do + 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[:text]).to match("not found") + end + end + end + + describe 'self.match' do + let(:query) { "my search keywords" } + it 'matches the query' do + match = described_class.match("issue search #{query}") + + expect(match[:query]).to eq(query) + end + end +end diff --git a/spec/lib/gitlab/slash_commands/issue_show_spec.rb b/spec/lib/gitlab/slash_commands/issue_show_spec.rb new file mode 100644 index 00000000000..1899f664ccd --- /dev/null +++ b/spec/lib/gitlab/slash_commands/issue_show_spec.rb @@ -0,0 +1,59 @@ +require 'spec_helper' + +describe Gitlab::SlashCommands::IssueShow, service: true do + describe '#execute' do + 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}") } + + before do + project.team << [user, :master] + end + + subject do + described_class.new(project, user).execute(regex_match) + end + + context 'the issue exists' do + let(:title) { subject[:attachments].first[:title] } + + it 'returns the issue' do + 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[:response_type]).to be(:in_channel) + expect(title).to start_with(issue.title) + end + end + end + + context 'the issue does not exist' do + let(:regex_match) { described_class.match("issue show 2343242") } + + it "returns not found" do + expect(subject[:response_type]).to be(:ephemeral) + expect(subject[:text]).to match("not found") + end + end + end + + 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 diff --git a/spec/lib/gitlab/slash_commands/presenters/access_spec.rb b/spec/lib/gitlab/slash_commands/presenters/access_spec.rb new file mode 100644 index 00000000000..ef3d217f7be --- /dev/null +++ b/spec/lib/gitlab/slash_commands/presenters/access_spec.rb @@ -0,0 +1,49 @@ +require 'spec_helper' + +describe Gitlab::SlashCommands::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 diff --git a/spec/lib/gitlab/slash_commands/presenters/deploy_spec.rb b/spec/lib/gitlab/slash_commands/presenters/deploy_spec.rb new file mode 100644 index 00000000000..dee3c77db27 --- /dev/null +++ b/spec/lib/gitlab/slash_commands/presenters/deploy_spec.rb @@ -0,0 +1,47 @@ +require 'spec_helper' + +describe Gitlab::SlashCommands::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 diff --git a/spec/lib/gitlab/slash_commands/presenters/issue_new_spec.rb b/spec/lib/gitlab/slash_commands/presenters/issue_new_spec.rb new file mode 100644 index 00000000000..7f81ebb47db --- /dev/null +++ b/spec/lib/gitlab/slash_commands/presenters/issue_new_spec.rb @@ -0,0 +1,17 @@ +require 'spec_helper' + +describe Gitlab::SlashCommands::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 diff --git a/spec/lib/gitlab/slash_commands/presenters/issue_search_spec.rb b/spec/lib/gitlab/slash_commands/presenters/issue_search_spec.rb new file mode 100644 index 00000000000..7e57a0addcb --- /dev/null +++ b/spec/lib/gitlab/slash_commands/presenters/issue_search_spec.rb @@ -0,0 +1,25 @@ +require 'spec_helper' + +describe Gitlab::SlashCommands::Presenters::IssueSearch do + let(:project) { create(:empty_project) } + let(:message) { subject[:text] } + + before do + create_list(:issue, 2, project: project) + end + + 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 diff --git a/spec/lib/gitlab/slash_commands/presenters/issue_show_spec.rb b/spec/lib/gitlab/slash_commands/presenters/issue_show_spec.rb new file mode 100644 index 00000000000..2a6ed860737 --- /dev/null +++ b/spec/lib/gitlab/slash_commands/presenters/issue_show_spec.rb @@ -0,0 +1,52 @@ +require 'spec_helper' + +describe Gitlab::SlashCommands::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 'with labels' do + let(:label) { create(:label, project: project, title: 'mep') } + let(:label1) { create(:label, project: project, title: 'mop') } + + before do + issue.labels << [label, label1] + end + + it 'shows the labels' do + labels = attachment[:fields].find { |f| f[:title] == 'Labels' } + + expect(labels[:value]).to eq("mep, mop") + 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 -- cgit v1.2.3