From fd1d7cc4ee548cab0b3a69c2cb50b20ebbc8a9eb Mon Sep 17 00:00:00 2001 From: Roy Liu <rliu@gitlab.com> Date: Wed, 17 Apr 2024 18:53:28 +0000 Subject: [PATCH] Add Duo Chat GA alert Changelog: changed EE: true --- .../javascripts/persistent_user_callouts.js | 1 + app/models/users/callout.rb | 3 +- app/views/groups/show.html.haml | 1 + app/views/projects/empty.html.haml | 1 + app/views/projects/show.html.haml | 1 + .../gitlab_com_derisk/duo_chat_ga_alert.yml | 9 ++ doc/api/graphql/reference/index.md | 1 + .../global_callout/duo_chat_callout.vue | 15 ++- .../assets/javascripts/ai/tanuki_bot/index.js | 4 +- ee/app/helpers/ee/users/callouts_helper.rb | 10 ++ .../projects/_duo_chat_ga_alert.html.haml | 3 + .../views/shared/_duo_chat_ga_alert.html.haml | 24 +++++ ee/spec/features/duo_chat_ga_alert_spec.rb | 102 ++++++++++++++++++ .../global_callout/duo_chat_callout_spec.js | 16 ++- .../helpers/ee/users/callouts_helper_spec.rb | 41 +++++++ ee/spec/views/groups/show.html.haml_spec.rb | 6 ++ .../_duo_chat_ga_alert.html.haml_spec.rb | 37 +++++++ .../views/projects/empty.html.haml_spec.rb | 17 +++ ee/spec/views/projects/show.html.haml_spec.rb | 22 ++-- .../_duo_chat_ga_alert.html.haml_spec.rb | 41 +++++++ locale/gitlab.pot | 15 +++ 21 files changed, 357 insertions(+), 13 deletions(-) create mode 100644 config/feature_flags/gitlab_com_derisk/duo_chat_ga_alert.yml create mode 100644 ee/app/views/projects/_duo_chat_ga_alert.html.haml create mode 100644 ee/app/views/shared/_duo_chat_ga_alert.html.haml create mode 100644 ee/spec/features/duo_chat_ga_alert_spec.rb create mode 100644 ee/spec/views/projects/_duo_chat_ga_alert.html.haml_spec.rb create mode 100644 ee/spec/views/projects/empty.html.haml_spec.rb create mode 100644 ee/spec/views/shared/_duo_chat_ga_alert.html.haml_spec.rb diff --git a/app/assets/javascripts/persistent_user_callouts.js b/app/assets/javascripts/persistent_user_callouts.js index ffe235e452bd..120c71273fae 100644 --- a/app/assets/javascripts/persistent_user_callouts.js +++ b/app/assets/javascripts/persistent_user_callouts.js @@ -25,6 +25,7 @@ const PERSISTENT_USER_CALLOUTS = [ '.js-namespace-over-storage-users-combined-alert', '.js-joining-a-project-alert', '.js-duo-pro-trial-alert', + '.js-duo-chat-ga-alert', ]; const initCallouts = () => { diff --git a/app/models/users/callout.rb b/app/models/users/callout.rb index 140339e1bb09..0781cec1ec10 100644 --- a/app/models/users/callout.rb +++ b/app/models/users/callout.rb @@ -86,7 +86,8 @@ class Callout < MainClusterwide::ApplicationRecord transition_to_jihu_callout: 84, summarize_code_changes: 85, # EE-only duo_pro_trial_alert: 86, # EE-only - deployment_details_feedback: 87 + deployment_details_feedback: 87, + duo_chat_ga_alert: 88 # EE-only } validates :feature_name, diff --git a/app/views/groups/show.html.haml b/app/views/groups/show.html.haml index 11f65b5b5a1f..96693f927ddd 100644 --- a/app/views/groups/show.html.haml +++ b/app/views/groups/show.html.haml @@ -21,6 +21,7 @@ = render_if_exists 'subscriptions/trials/alert', namespace: @group = render_if_exists 'shared/duo_pro_trial_alert', resource: @group += render_if_exists 'shared/duo_chat_ga_alert', resource: @group = render 'groups/home_panel' diff --git a/app/views/projects/empty.html.haml b/app/views/projects/empty.html.haml index dc550dec6669..01ec7cb7a020 100644 --- a/app/views/projects/empty.html.haml +++ b/app/views/projects/empty.html.haml @@ -6,6 +6,7 @@ - @skip_current_level_breadcrumb = true = render_if_exists 'projects/duo_pro_trial_alert', project: @project += render_if_exists 'projects/duo_chat_ga_alert', project: @project = render partial: 'flash_messages', locals: { project: @project } = render 'clusters_deprecation_alert' diff --git a/app/views/projects/show.html.haml b/app/views/projects/show.html.haml index 3c3171638024..4c4f5d3f2e95 100644 --- a/app/views/projects/show.html.haml +++ b/app/views/projects/show.html.haml @@ -8,6 +8,7 @@ = render_if_exists 'shared/promotions/promote_mobile_devops', project: @project = render_if_exists 'projects/duo_pro_trial_alert', project: @project += render_if_exists 'projects/duo_chat_ga_alert', project: @project = render partial: 'flash_messages', locals: { project: @project } = render 'clusters_deprecation_alert' diff --git a/config/feature_flags/gitlab_com_derisk/duo_chat_ga_alert.yml b/config/feature_flags/gitlab_com_derisk/duo_chat_ga_alert.yml new file mode 100644 index 000000000000..bc0be8687cd2 --- /dev/null +++ b/config/feature_flags/gitlab_com_derisk/duo_chat_ga_alert.yml @@ -0,0 +1,9 @@ +--- +name: duo_chat_ga_alert +feature_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/442655 +introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/149329 +rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/455857 +milestone: '17.0' +type: gitlab_com_derisk +group: group::acquisition +default_enabled: false diff --git a/doc/api/graphql/reference/index.md b/doc/api/graphql/reference/index.md index 8b880d584def..40dc9fc7b952 100644 --- a/doc/api/graphql/reference/index.md +++ b/doc/api/graphql/reference/index.md @@ -34081,6 +34081,7 @@ Name of the feature that the callout is for. | <a id="usercalloutfeaturenameenumcluster_security_warning"></a>`CLUSTER_SECURITY_WARNING` | Callout feature name for cluster_security_warning. | | <a id="usercalloutfeaturenameenumdeployment_details_feedback"></a>`DEPLOYMENT_DETAILS_FEEDBACK` | Callout feature name for deployment_details_feedback. | | <a id="usercalloutfeaturenameenumduo_chat_callout"></a>`DUO_CHAT_CALLOUT` | Callout feature name for duo_chat_callout. | +| <a id="usercalloutfeaturenameenumduo_chat_ga_alert"></a>`DUO_CHAT_GA_ALERT` | Callout feature name for duo_chat_ga_alert. | | <a id="usercalloutfeaturenameenumduo_pro_trial_alert"></a>`DUO_PRO_TRIAL_ALERT` | Callout feature name for duo_pro_trial_alert. | | <a id="usercalloutfeaturenameenumfeature_flags_new_version"></a>`FEATURE_FLAGS_NEW_VERSION` | Callout feature name for feature_flags_new_version. | | <a id="usercalloutfeaturenameenumgcp_signup_offer"></a>`GCP_SIGNUP_OFFER` | Callout feature name for gcp_signup_offer. | diff --git a/ee/app/assets/javascripts/ai/components/global_callout/duo_chat_callout.vue b/ee/app/assets/javascripts/ai/components/global_callout/duo_chat_callout.vue index 2123905a9a8a..8808a97b479b 100644 --- a/ee/app/assets/javascripts/ai/components/global_callout/duo_chat_callout.vue +++ b/ee/app/assets/javascripts/ai/components/global_callout/duo_chat_callout.vue @@ -5,6 +5,7 @@ import UserCalloutDismisser from '~/vue_shared/components/user_callout_dismisser import DUO_CHAT_ILLUSTRATION from './callout_illustration.svg?url'; export const DUO_CHAT_GLOBAL_BUTTON_CSS_CLASS = 'js-tanuki-bot-chat-toggle'; +export const DUO_CHAT_GA_ALERT_IDENTIFIER = '.js-duo-chat-ga-alert'; const i18n = { POPOVER_LABEL: s__('DuoChat|GitLab Duo Chat'), @@ -23,6 +24,18 @@ export default { GlLink, UserCalloutDismisser, }, + computed: { + // This is a hack and needs to removed or refactored! + // This value is querying the DOM to check for another DuoChat alert + // to make sure we're not rendering 2 DuoChat alerts on top of each other. + // This needs to removed asap as soon as DUO_CHAT_GA_ALERT_IDENTIFIER is removed + // or refactored to DUO_CHAT_GA_ALERT_IDENTIFIER using global state + // without querying the DOM. + checkForGAAlert() { + return document.querySelector(DUO_CHAT_GA_ALERT_IDENTIFIER); + }, + }, + beforeMount() { const allButtons = Array.from( document.querySelectorAll(`.${DUO_CHAT_GLOBAL_BUTTON_CSS_CLASS}`), @@ -70,7 +83,7 @@ export default { <user-callout-dismisser v-if="popoverTarget" feature-name="duo_chat_callout"> <template #default="{ dismiss, shouldShowCallout }"> <gl-popover - v-if="shouldShowCallout" + v-if="shouldShowCallout && !checkForGAAlert" :target="popoverTarget" :show="shouldShowCallout" show-close-button diff --git a/ee/app/assets/javascripts/ai/tanuki_bot/index.js b/ee/app/assets/javascripts/ai/tanuki_bot/index.js index 3cd5effee45e..7938c67321a4 100644 --- a/ee/app/assets/javascripts/ai/tanuki_bot/index.js +++ b/ee/app/assets/javascripts/ai/tanuki_bot/index.js @@ -18,7 +18,9 @@ export const initTanukiBotChatDrawer = () => { return false; } - const toggleEls = document.querySelectorAll('.js-tanuki-bot-chat-toggle'); + const toggleEls = document.querySelectorAll( + '.js-tanuki-bot-chat-toggle, .js-tanuki-bot-chat-ga-alert-toggle', + ); if (toggleEls.length) { toggleEls.forEach((toggleEl) => { toggleEl.addEventListener('click', () => { diff --git a/ee/app/helpers/ee/users/callouts_helper.rb b/ee/app/helpers/ee/users/callouts_helper.rb index 4f5b551aad93..b2df5eb4bb3b 100644 --- a/ee/app/helpers/ee/users/callouts_helper.rb +++ b/ee/app/helpers/ee/users/callouts_helper.rb @@ -16,6 +16,7 @@ module CalloutsHelper PROFILE_PERSONAL_ACCESS_TOKEN_EXPIRY = 'profile_personal_access_token_expiry' JOINING_A_PROJECT_ALERT = 'joining_a_project_alert' DUO_PRO_TRIAL_ALERT = 'duo_pro_trial_alert' + DUO_CHAT_GA_ALERT = 'duo_chat_ga_alert' override :render_dashboard_ultimate_trial def render_dashboard_ultimate_trial(user) @@ -83,6 +84,15 @@ def show_duo_pro_trial_alert?(group) !user_dismissed?(DUO_PRO_TRIAL_ALERT) end + def show_duo_chat_ga_alert?(group) + return false unless ::Gitlab::Saas.feature_available?(:subscriptions_trials) + return false unless ::Feature.enabled?(:duo_chat_ga_alert, group, type: :gitlab_com_derisk) + return false unless group.paid? && !group.trial? + return false unless group.member?(current_user) + + !user_dismissed?(DUO_CHAT_GA_ALERT) + end + private override :dismissed_callout? diff --git a/ee/app/views/projects/_duo_chat_ga_alert.html.haml b/ee/app/views/projects/_duo_chat_ga_alert.html.haml new file mode 100644 index 000000000000..93863c1d8d4f --- /dev/null +++ b/ee/app/views/projects/_duo_chat_ga_alert.html.haml @@ -0,0 +1,3 @@ +- return if project.personal? + += render 'shared/duo_chat_ga_alert', resource: project diff --git a/ee/app/views/shared/_duo_chat_ga_alert.html.haml b/ee/app/views/shared/_duo_chat_ga_alert.html.haml new file mode 100644 index 000000000000..3103d5004329 --- /dev/null +++ b/ee/app/views/shared/_duo_chat_ga_alert.html.haml @@ -0,0 +1,24 @@ +- root_namespace = resource.root_ancestor +- return unless show_duo_chat_ga_alert?(root_namespace) +- tracking_label = 'duo_chat_ga_alert' + +- content_for :page_level_alert do + %div{ class: [container_class, @content_class, 'gl-pt-5!'], data: { track_action: 'render', track_label: tracking_label } } + = render Pajamas::BannerComponent.new(close_options: { data: { testid: 'hide-duo-chat-ga-alert', track_action: 'dismiss', track_label: tracking_label }, + 'aria-label' => s_('DuoChatGAAlert|Dismiss Duo Chat GA banner') }, + banner_options: { class: 'js-duo-chat-ga-alert', + data: { feature_id: ::EE::Users::CalloutsHelper::DUO_CHAT_GA_ALERT, + dismiss_endpoint: callouts_path, + testid: 'duo-chat-ga-alert'} }) do |c| + - c.with_illustration do + = image_tag image_path('illustrations/tanuki-ai-md.svg'), alt: 'Tanuki AI Logo', class: 'gl-p-3 gl-rounded-lg bg-duo-pro rounded-bottom-right-9' + - c.with_title do + = s_('DuoChatGAAlert|GitLab Duo Chat is generally available today') + - c.with_primary_action do + = render Pajamas::ButtonComponent.new(variant: :confirm, button_options: { class: 'gl-mr-3 js-close js-tanuki-bot-chat-ga-alert-toggle', data: { track_action: 'click_button', track_label: tracking_label} }) do + = s_('DuoChatGAAlert|Use GitLab Duo Chat') + + = render Pajamas::ButtonComponent.new(href: help_page_path('user/gitlab_duo_chat', anchor: 'use-gitlab-duo-chat-in-the-web-ide'), target: "_blank") do + = s_('DuoChatGAAlert|Access Chat in the IDE') + + %p= s_('DuoChatGAAlert|Access a broad range of GitLab Duo features with your personal chat assistant in the UI of GitLab.com. You can also use Chat to access Code explanation, Code refactoring, and Test generation in your preferred IDE. Later this year, a paid add-on subscription will be required to access GitLab Duo Chat.') diff --git a/ee/spec/features/duo_chat_ga_alert_spec.rb b/ee/spec/features/duo_chat_ga_alert_spec.rb new file mode 100644 index 000000000000..fbe2835b8916 --- /dev/null +++ b/ee/spec/features/duo_chat_ga_alert_spec.rb @@ -0,0 +1,102 @@ +# frozen_string_literal: true + +require 'spec_helper' + +RSpec.describe 'Duo Chat GA alert', :saas, :js, feature_category: :duo_chat do + let_it_be(:group) { create(:group_with_plan, plan: :ultimate_plan) } + let_it_be(:user_in_group) { create(:group_member, :guest, user: create(:user), group: group).user } + + before do + stub_feature_flags(duo_chat_ga_alert: true) + sign_in(user_in_group) + end + + it 'passes axe automated accessibility testing' do + visit group_path(group) + + wait_for_requests + + expect(page).to be_axe_clean.within('[data-testid="duo-chat-ga-alert"]') # rubocop:todo Capybara/TestidFinders -- Doesn't cover use case, see https://gitlab.com/gitlab-org/gitlab/-/issues/442224 + end + + context 'with Duo Chat features enabled' do + include_context 'with ai features enabled for group' + context 'when primary CTA button clicked' do + it 'opens Duo Chat drawer' do + visit group_path(group) + + wait_for_all_requests + + click_on 'Use GitLab Duo Chat' + + expect(page).to have_selector '[data-testid="chat-component"]' + end + + it 'dismisses the alert permanently' do + visit group_path(group) + click_on 'Use GitLab Duo Chat' + + wait_for_all_requests + + expect_banner_to_be_absent + + visit group_path(group) + + expect_group_page_for(group) + expect_banner_to_be_absent + end + end + + it 'does not show the Duo Chat callout popover' do + visit group_path(group) + + wait_for_all_requests + + expect_banner_to_be_present + + expect_popover_to_be_absent + end + end + + context 'when dismiss button clicked' do + it 'is dismissed permanently' do + visit group_path(group) + dismiss_button.click + + wait_for_all_requests + + expect_banner_to_be_absent + + visit group_path(group) + + expect_group_page_for(group) + expect_banner_to_be_absent + end + end + + def dismiss_button + find('button[data-testid="hide-duo-chat-ga-alert"]') + end + + def expect_group_page_for(group) + expect(page).to have_text group.name + expect(page).to have_text "Group ID: #{group.id}" + end + + def expect_banner_to_be_present + expect(page).to have_text 'GitLab Duo Chat is generally available today' + expect(page).to have_text 'Access a broad range of GitLab Duo features with your personal chat assistant' + + expect(page).to have_link('Access Chat in the IDE', + href: help_page_path('user/gitlab_duo_chat', anchor: 'use-gitlab-duo-chat-in-the-web-ide')) + end + + def expect_banner_to_be_absent + expect(page).not_to have_text 'GitLab Duo Chat is generally available today' + expect(page).not_to have_text 'Access a broad range of GitLab Duo features with your personal chat assistant' + end + + def expect_popover_to_be_absent + expect(page).not_to have_selector '[data-testid="duo-chat-promo-callout-popover"]' + end +end diff --git a/ee/spec/frontend/ai/components/global_callout/duo_chat_callout_spec.js b/ee/spec/frontend/ai/components/global_callout/duo_chat_callout_spec.js index 530229b02c53..1fe5fa1379c3 100644 --- a/ee/spec/frontend/ai/components/global_callout/duo_chat_callout_spec.js +++ b/ee/spec/frontend/ai/components/global_callout/duo_chat_callout_spec.js @@ -21,12 +21,15 @@ describe('DuoChatCallout', () => { const findParagraphWithinPopover = () => wrapper.find('[data-testid="duo-chat-callout-description"]'); - const createComponent = ({ shouldShowCallout = true } = {}) => { + const createComponent = ({ shouldShowCallout = true, checkForGAAlert = false } = {}) => { userCalloutDismissSpy = jest.fn(); wrapper = shallowMount(DuoChatCallout, { directives: { Outside: createMockDirective('outside'), }, + computed: { + checkForGAAlert: () => checkForGAAlert, + }, stubs: { UserCalloutDismisser: makeMockUserCalloutDismisser({ dismiss: userCalloutDismissSpy, @@ -69,6 +72,17 @@ describe('DuoChatCallout', () => { expect(findLinkWithinDismisser().exists()).toBe(false); }); + // This is a hack and needs to removed or refactored! + // This value is querying the DOM to check for another DuoChat alert + // to make sure we're not rendering 2 DuoChat alerts on top of each other. + // This needs to removed asap as soon as DUO_CHAT_GA_ALERT_IDENTIFIER is removed + // or refactored to DUO_CHAT_GA_ALERT_IDENTIFIER using global state + // without querying the DOM. + it('does not render the popover if other banner exists', () => { + createComponent({ shouldShowCallout: true, checkForGAAlert: true }); + expect(findPopoverWithinDismisser().exists()).toBe(false); + }); + it('does not throw if the popoverTarget button does not exist', () => { setHTMLFixture(`<button></button>`); expect(() => createComponent()).not.toThrow(); diff --git a/ee/spec/helpers/ee/users/callouts_helper_spec.rb b/ee/spec/helpers/ee/users/callouts_helper_spec.rb index ce467194e496..00682c218a2f 100644 --- a/ee/spec/helpers/ee/users/callouts_helper_spec.rb +++ b/ee/spec/helpers/ee/users/callouts_helper_spec.rb @@ -321,4 +321,45 @@ it { is_expected.to eq(expected_result) } end end + + describe '.show_duo_chat_ga_alert?', :saas, feature_category: :code_suggestions do + using RSpec::Parameterized::TableSyntax + + let_it_be(:user) { create(:user) } + let_it_be(:ultimate_plan) { create(:ultimate_plan) } + let(:group) { create(:group) } + + where( + saas_feature_available?: [true, false], + feature_flag_enabled?: [true, false], + member_of_group?: [true, false], + group_paid_and_not_trial?: [true, false], + user_dismissed_callout?: [true, false] + ) + + with_them do + before do + stub_saas_features(subscriptions_trials: saas_feature_available?) + stub_feature_flags(duo_chat_ga_alert: feature_flag_enabled?) + + allow(helper).to receive(:current_user).and_return(user) + group.add_member(user, GroupMember::GUEST) if member_of_group? + + create(:gitlab_subscription, namespace: group, hosted_plan: ultimate_plan) if group_paid_and_not_trial? + allow(helper).to receive(:user_dismissed?).with('duo_chat_ga_alert').and_return(user_dismissed_callout?) + end + + let(:expected_result) do + saas_feature_available? && + feature_flag_enabled? && + member_of_group? && + group_paid_and_not_trial? && + !user_dismissed_callout? + end + + subject { helper.show_duo_chat_ga_alert?(group) } + + it { is_expected.to eq(expected_result) } + end + end end diff --git a/ee/spec/views/groups/show.html.haml_spec.rb b/ee/spec/views/groups/show.html.haml_spec.rb index b5cc201b0861..56b38233810b 100644 --- a/ee/spec/views/groups/show.html.haml_spec.rb +++ b/ee/spec/views/groups/show.html.haml_spec.rb @@ -14,4 +14,10 @@ expect(rendered).to render_template('shared/_duo_pro_trial_alert') end + + it 'renders the Duo Chat GA alert partial' do + render + + expect(rendered).to render_template('shared/_duo_chat_ga_alert') + end end diff --git a/ee/spec/views/projects/_duo_chat_ga_alert.html.haml_spec.rb b/ee/spec/views/projects/_duo_chat_ga_alert.html.haml_spec.rb new file mode 100644 index 000000000000..2773e78b6cdb --- /dev/null +++ b/ee/spec/views/projects/_duo_chat_ga_alert.html.haml_spec.rb @@ -0,0 +1,37 @@ +# frozen_string_literal: true + +require 'spec_helper' + +RSpec.describe 'projects/_duo_chat_ga_alert', :saas, feature_category: :duo_chat do + let(:user) { create(:user) } # rubocop:todo RSpec/FactoryBot/AvoidCreate -- requires for checking member + let(:group) { create(:group_with_plan, plan: :ultimate_plan) } # rubocop:todo RSpec/FactoryBot/AvoidCreate -- requires for checking member + let(:project) { nil } + + before do + group.add_developer(user) + allow(view).to receive(:project).and_return(project) + allow(view).to receive(:current_user).and_return(user) + end + + context 'with personal project' do + let(:project) { build(:project, namespace: user.namespace) } + + it 'does not render duo_chat_ga_alert template' do + # Just `render` throws an exception in the case of early return in view + # https://github.com/rails/rails/issues/41320 + view.render('projects/duo_chat_ga_alert') + + expect(rendered).not_to render_template('shared/_duo_chat_ga_alert') + end + end + + context 'with project within a group' do + let(:project) { build(:project, namespace: group) } + + it 'renders duo_chat_ga_alert template' do + render + + expect(rendered).to render_template('shared/_duo_chat_ga_alert') + end + end +end diff --git a/ee/spec/views/projects/empty.html.haml_spec.rb b/ee/spec/views/projects/empty.html.haml_spec.rb new file mode 100644 index 000000000000..6ee34f578fab --- /dev/null +++ b/ee/spec/views/projects/empty.html.haml_spec.rb @@ -0,0 +1,17 @@ +# frozen_string_literal: true + +require 'spec_helper' + +RSpec.describe 'projects/empty', feature_category: :groups_and_projects do + let(:project) { ProjectPresenter.new(create(:project), current_user: build(:user)) } # rubocop:todo RSpec/FactoryBot/AvoidCreate -- sidebar rendering need to access project statistics + + before do + assign(:project, project) + end + + it 'renders the Duo Chat GA alert partial' do + render + + expect(rendered).to render_template('projects/_duo_chat_ga_alert') + end +end diff --git a/ee/spec/views/projects/show.html.haml_spec.rb b/ee/spec/views/projects/show.html.haml_spec.rb index 179b0fe80999..b9f9ca2e3783 100644 --- a/ee/spec/views/projects/show.html.haml_spec.rb +++ b/ee/spec/views/projects/show.html.haml_spec.rb @@ -5,16 +5,20 @@ RSpec.describe 'projects/show', feature_category: :groups_and_projects do let(:project) { ProjectPresenter.new(create(:project), current_user: build(:user)) } # rubocop:todo RSpec/FactoryBot/AvoidCreate -- sidebar rendering need to access project statistics - context 'with Duo Pro trial alert' do - before do - assign(:project, project) - allow(project).to receive(:default_view).and_return('wiki') - end + before do + assign(:project, project) + allow(project).to receive(:default_view).and_return('wiki') + end + + it 'renders the Duo Pro trial alert partial' do + render + + expect(rendered).to render_template('projects/_duo_pro_trial_alert') + end - it 'renders the Duo Pro trial alert partial' do - render + it 'renders the Duo Chat GA alert partial' do + render - expect(rendered).to render_template('projects/_duo_pro_trial_alert') - end + expect(rendered).to render_template('projects/_duo_chat_ga_alert') end end diff --git a/ee/spec/views/shared/_duo_chat_ga_alert.html.haml_spec.rb b/ee/spec/views/shared/_duo_chat_ga_alert.html.haml_spec.rb new file mode 100644 index 000000000000..b905cf789d99 --- /dev/null +++ b/ee/spec/views/shared/_duo_chat_ga_alert.html.haml_spec.rb @@ -0,0 +1,41 @@ +# frozen_string_literal: true + +require 'spec_helper' + +RSpec.describe 'shared/_duo_chat_ga_alert', :saas, feature_category: :code_suggestions do + let(:user) { create(:user) } # rubocop:todo RSpec/FactoryBot/AvoidCreate -- requires for checking membership + let(:group) { create(:group_with_plan, plan: :ultimate_plan) } # rubocop:todo RSpec/FactoryBot/AvoidCreate -- requires for checking membership + + before do + group.add_developer(user) + allow(view).to receive(:resource).and_return(group) + allow(view).to receive(:current_user).and_return(user) + end + + subject(:rendered_alert) { view.content_for(:page_level_alert) } + + context 'when duo_chat_ga_alert feature flag is enabled' do + it 'renders the duo pro trial alert' do + render + + expect(rendered_alert).to have_text(s_('DuoChatGAAlert|GitLab Duo Chat is generally available today')) + expect(rendered_alert).to have_text(s_('DuoChatGAAlert|Use GitLab Duo Chat')) + expect(rendered_alert).to have_link(s_('DuoChatGAAlert|Access Chat in the IDE'), + href: help_page_path('user/gitlab_duo_chat', anchor: 'use-gitlab-duo-chat-in-the-web-ide')) + end + end + + context 'when duo_chat_ga_alert feature flag is disabled' do + before do + stub_feature_flags(duo_chat_ga_alert: false) + end + + it 'does not render the duo pro trial alert' do + # Just `render` throws an exception in the case of early return in view + # https://github.com/rails/rails/issues/41320 + view.render('shared/duo_chat_ga_alert') + + expect(rendered_alert).not_to have_text(s_('DuoChatGAAlert|GitLab Duo Chat is generally available today')) + end + end +end diff --git a/locale/gitlab.pot b/locale/gitlab.pot index e1a369f76e76..8a16a9794d35 100644 --- a/locale/gitlab.pot +++ b/locale/gitlab.pot @@ -19008,6 +19008,21 @@ msgstr "" msgid "Due to inactivity, this project is scheduled to be deleted on %{deletion_date}. %{link_start}Why is this scheduled?%{link_end}" msgstr "" +msgid "DuoChatGAAlert|Access Chat in the IDE" +msgstr "" + +msgid "DuoChatGAAlert|Access a broad range of GitLab Duo features with your personal chat assistant in the UI of GitLab.com. You can also use Chat to access Code explanation, Code refactoring, and Test generation in your preferred IDE. Later this year, a paid add-on subscription will be required to access GitLab Duo Chat." +msgstr "" + +msgid "DuoChatGAAlert|Dismiss Duo Chat GA banner" +msgstr "" + +msgid "DuoChatGAAlert|GitLab Duo Chat is generally available today" +msgstr "" + +msgid "DuoChatGAAlert|Use GitLab Duo Chat" +msgstr "" + msgid "DuoChat|Ask a question about GitLab" msgstr "" -- GitLab