diff --git a/ee/app/views/layouts/nav/_ask_duo_button.html.haml b/ee/app/views/layouts/nav/_ask_duo_button.html.haml index ccfb1296a98f38b5071f4b7558d57c47b4bc43a5..2642d9b9feac07667df4a23876d96beebdc83196 100644 --- a/ee/app/views/layouts/nav/_ask_duo_button.html.haml +++ b/ee/app/views/layouts/nav/_ask_duo_button.html.haml @@ -1,4 +1,4 @@ -- if ::Gitlab::Llm::TanukiBot.show_breadcrumbs_entry_point?(user: current_user) +- if ::Gitlab::Llm::TanukiBot.show_breadcrumbs_entry_point?(user: current_user, container: @group || @project) - label = s_('DuoChat|GitLab Duo Chat') - chat_disabled_reason = ::Gitlab::Llm::TanukiBot.chat_disabled_reason(user: current_user, container: @group || @project).to_s diff --git a/ee/lib/gitlab/llm/tanuki_bot.rb b/ee/lib/gitlab/llm/tanuki_bot.rb index 77422c59780ec6449c74af0d6e32e28c510599f3..d2343e230461cbd29c5ee1492ae5a66bd8fdbd78 100644 --- a/ee/lib/gitlab/llm/tanuki_bot.rb +++ b/ee/lib/gitlab/llm/tanuki_bot.rb @@ -15,8 +15,8 @@ def self.enabled_for?(user:, container: nil) authorizer_response.allowed? end - def self.show_breadcrumbs_entry_point?(user:) - return false unless chat_enabled?(user) + def self.show_breadcrumbs_entry_point?(user:, container: nil) + return false unless chat_enabled?(user) && container Gitlab::Llm::Chain::Utils::ChatAuthorizer.user(user: user).allowed? end diff --git a/ee/spec/features/duo_chat_spec.rb b/ee/spec/features/duo_chat_spec.rb index 8f935ff1c49a0373a23d54970d766fcc62251cce..6ef503f6f4cc46da779753c3db40b5a203b5e0e4 100644 --- a/ee/spec/features/duo_chat_spec.rb +++ b/ee/spec/features/duo_chat_spec.rb @@ -15,7 +15,7 @@ before do sign_in(user) - visit root_path + visit group_path(group) end it 'does not show the button to open chat' do @@ -24,9 +24,12 @@ end context 'when group has an AI features license', :sidekiq_inline do + using RSpec::Parameterized::TableSyntax + include_context 'with duo features enabled and ai chat available for group on SaaS' let_it_be_with_reload(:group) { create(:group_with_plan, plan: :premium_plan) } + let_it_be(:project) { create(:project, namespace: group) } let(:question) { 'Who are you?' } let(:answer) { "Hello! I'm GitLab Duo Chat" } @@ -41,37 +44,44 @@ sign_in(user) - visit root_path + visit group_path(group) end - it 'shows the disabled button with project tooltip when chat is disabled on project level' do - allow(::Gitlab::Llm::TanukiBot).to receive(:chat_disabled_reason).and_return('project') - - visit root_path - - expect(page).to have_selector( - 'span.has-tooltip[title*="An administrator has turned off GitLab Duo for this project"]' - ) - expect(page).to have_button('GitLab Duo Chat', disabled: true) + where(:disabled_reason, :visit_path, :expected_button_state, :expected_tooltip) do + 'project' | :visit_project | :disabled | "An administrator has turned off GitLab Duo for this project" + 'project' | :visit_root | :hidden | nil + 'group' | :visit_group | :disabled | "An administrator has turned off GitLab Duo for this group" + 'group' | :visit_root | :hidden | nil + nil | :visit_group | :enabled | nil + nil | :visit_project | :enabled | nil + nil | :visit_root | :hidden | nil end - it 'shows the disabled button with group tooltip when chat is disabled on group level' do - allow(::Gitlab::Llm::TanukiBot).to receive(:chat_disabled_reason).and_return('group') - - visit root_path - - expect(page).to have_selector( - 'span.has-tooltip[title*="An administrator has turned off GitLab Duo for this group"]' - ) - expect(page).to have_button('GitLab Duo Chat', disabled: true) - end - - it 'shows the enabled button when chat is enabled' do - allow(::Gitlab::Llm::TanukiBot).to receive(:chat_disabled_reason).and_return(nil) - - visit root_path + with_them do + it 'shows the correct button state and tooltip' do + allow(::Gitlab::Llm::TanukiBot).to receive(:chat_disabled_reason).and_return(disabled_reason) + + case visit_path + when :visit_group + visit group_path(group) + when :visit_project + visit project_path(project) + when :visit_root + visit root_path + end - expect(page).to have_button('GitLab Duo Chat', disabled: false) + case expected_button_state + when :disabled + expect(page).to have_selector( + "span.has-tooltip[title*=\"#{expected_tooltip}\"]" + ) + expect(page).to have_button('GitLab Duo Chat', disabled: true) + when :enabled + expect(page).to have_button('GitLab Duo Chat', disabled: false) + when :hidden + expect(page).not_to have_button('GitLab Duo Chat') + end + end end it 'returns response after asking a question', quarantine: 'https://gitlab.com/gitlab-org/gitlab/-/issues/462444' do diff --git a/ee/spec/features/tanuki_bot_chat_spec.rb b/ee/spec/features/tanuki_bot_chat_spec.rb index 8a5beec80abb56208707832298c62bdc1c66de86..ccd6927e116c3b9fc1f2a09765fbca8161f5ad43 100644 --- a/ee/spec/features/tanuki_bot_chat_spec.rb +++ b/ee/spec/features/tanuki_bot_chat_spec.rb @@ -20,7 +20,7 @@ include_context 'with ai features enabled for group' before do - visit root_path + visit group_path(group) end shared_examples 'GitLab Duo drawer' do @@ -46,6 +46,8 @@ end context 'for self-managed', :with_cloud_connector do + let_it_be_with_reload(:group) { create(:group) } + before do sign_in(user) end @@ -54,7 +56,7 @@ include_context 'with duo features enabled and ai chat available for self-managed' before do - visit root_path + visit group_path(group) end shared_examples 'GitLab Duo drawer' do diff --git a/ee/spec/lib/gitlab/llm/tanuki_bot_spec.rb b/ee/spec/lib/gitlab/llm/tanuki_bot_spec.rb index 2ddf65045ef18242685fec4b6fc5252098e4eca1..e4b7e87091816869a715521827997b062b43beb0 100644 --- a/ee/spec/lib/gitlab/llm/tanuki_bot_spec.rb +++ b/ee/spec/lib/gitlab/llm/tanuki_bot_spec.rb @@ -70,21 +70,29 @@ allow(described_class).to receive(:chat_enabled?).with(user) .and_return(ai_features_enabled_for_user) allow(Gitlab::Llm::Chain::Utils::ChatAuthorizer).to receive(:user).with(user: user) - .and_return(authorizer_response) + .and_return(authorizer_response) end - where(:ai_features_enabled_for_user, :allowed, :result) do + where(:container, :ai_features_enabled_for_user, :allowed, :result) do [ - [true, true, true], - [true, false, false], - [false, false, false], - [false, true, false] + [:project, true, true, true], + [:project, true, false, false], + [:project, false, false, false], + [:project, false, true, false], + [:group, true, true, true], + [:group, true, false, false], + [:group, false, false, false], + [:group, false, true, false], + [nil, true, true, false], + [nil, true, false, false], + [nil, false, false, false], + [nil, false, true, false] ] end with_them do it 'returns correct result' do - expect(described_class.show_breadcrumbs_entry_point?(user: user)).to be(result) + expect(described_class.show_breadcrumbs_entry_point?(user: user, container: container)).to be(result) end end end diff --git a/qa/qa/specs/features/ee/browser_ui/16_ai_powered/duo_chat/duo_chat_spec.rb b/qa/qa/specs/features/ee/browser_ui/16_ai_powered/duo_chat/duo_chat_spec.rb index 5ee5e73215de48849877478d5ff093f4895a07ab..f2cf2a4038246ecb91b04e1e0754bd2709cd0c60 100644 --- a/qa/qa/specs/features/ee/browser_ui/16_ai_powered/duo_chat/duo_chat_spec.rb +++ b/qa/qa/specs/features/ee/browser_ui/16_ai_powered/duo_chat/duo_chat_spec.rb @@ -4,6 +4,8 @@ module QA # https://docs.gitlab.com/ee/development/ai_features/duo_chat.html RSpec.describe 'Ai-powered', product_group: :duo_chat do describe 'Duo Chat' do + let(:project) { create(:project, name: 'duo-chat-project') } + shared_examples 'Duo Chat' do |testcase| it 'gets a response back from Duo Chat', testcase: testcase do QA::EE::Page::Component::DuoChat.perform do |duo_chat| @@ -23,6 +25,7 @@ module QA before do Flow::Login.sign_in + project.visit! end context 'when initiating Duo Chat' do