diff --git a/ee/app/assets/javascripts/ci/job_details/components/sidebar/root_cause_analysis/root_cause_analysis_app.vue b/ee/app/assets/javascripts/ci/job_details/components/sidebar/root_cause_analysis/root_cause_analysis_app.vue index c5a62051be98507b862f18957723391213842b12..2d14d53e36cbf65e297dfabf34adab8e5b89848d 100644 --- a/ee/app/assets/javascripts/ci/job_details/components/sidebar/root_cause_analysis/root_cause_analysis_app.vue +++ b/ee/app/assets/javascripts/ci/job_details/components/sidebar/root_cause_analysis/root_cause_analysis_app.vue @@ -1,6 +1,15 @@ <script> -import { GlDrawer, GlEmptyState, GlSkeletonLoader, GlSprintf, GlLink, GlButton } from '@gitlab/ui'; -import { __ } from '~/locale'; +import { + GlDrawer, + GlAlert, + GlExperimentBadge, + GlEmptyState, + GlSkeletonLoader, + GlSprintf, + GlLink, + GlButton, +} from '@gitlab/ui'; +import { __, s__ } from '~/locale'; import Markdown from '~/vue_shared/components/markdown/non_gfm_markdown.vue'; import { helpPagePath } from '~/helpers/help_page_helper'; import rootCauseMutation from './graphql/root_cause.mutation.graphql'; @@ -8,6 +17,8 @@ import rootCauseQuery from './graphql/root_cause.query.graphql'; export default { components: { + GlAlert, + GlExperimentBadge, GlButton, GlEmptyState, GlLink, @@ -98,17 +109,30 @@ export default { explanationText: __( 'Root cause analysis is a feature that analyzes your logs to determine why a job may have failed and the potential ways to fix it. To generate this analysis, we may share information in your job logs with %{linkStart}Third-Party AI providers%{linkEnd}. Before initiating this analysis, please do not include in your logs any information that could impact the security or privacy of your account.', ), + CHAT_LEGAL_GENERATED_BY_AI: s__('AI|Responses generated by AI'), }, }; </script> <template> <gl-drawer :open="isShown" class="gl-z-index-9999!" @close="closeDrawer"> <template #title> - <h3>{{ $options.i18n.drawerTitle }}</h3> + <div class="gl--flex-center"> + <h3 class="gl-m-0">{{ $options.i18n.drawerTitle }}</h3> + <gl-experiment-badge /> + </div> </template> + <gl-alert + :dismissible="false" + variant="tip" + :show-icon="false" + class="gl-text-center gl-text-gray-700" + >{{ $options.i18n.CHAT_LEGAL_GENERATED_BY_AI }}</gl-alert + > <gl-empty-state v-if="!isResponseReady && !isRootCauseLoading" compact> <template #title> - <h3>{{ $options.i18n.bannerTitle }}</h3> + <h3> + {{ $options.i18n.bannerTitle }} + </h3> </template> <template #description> <p> @@ -120,9 +144,15 @@ export default { </p> </template> <template #actions> - <gl-button class="gl-mb-3" category="primary" variant="confirm" @click="fetchData">{{ - $options.i18n.actionButtonText - }}</gl-button> + <gl-button + class="gl-mb-3" + category="primary" + variant="confirm" + data-testid="root-cause-analysis-action-button" + @click="fetchData" + > + {{ $options.i18n.actionButtonText }} + </gl-button> </template> </gl-empty-state> <gl-skeleton-loader v-if="isRootCauseLoading" /> diff --git a/ee/spec/frontend/ci/job_details/components/sidebar/root_cause_analysis/root_cause_analysis_spec.js b/ee/spec/frontend/ci/job_details/components/sidebar/root_cause_analysis/root_cause_analysis_spec.js index 279fcb49bfa5450fe7794669ed59e7e1856c218e..0645025058ce22d21d910bf18815b6e04e2f1329 100644 --- a/ee/spec/frontend/ci/job_details/components/sidebar/root_cause_analysis/root_cause_analysis_spec.js +++ b/ee/spec/frontend/ci/job_details/components/sidebar/root_cause_analysis/root_cause_analysis_spec.js @@ -1,7 +1,7 @@ import Vue from 'vue'; import VueApollo from 'vue-apollo'; -import { shallowMount } from '@vue/test-utils'; -import { GlButton, GlEmptyState, GlSkeletonLoader } from '@gitlab/ui'; +import { GlAlert, GlEmptyState, GlSkeletonLoader, GlExperimentBadge, GlDrawer } from '@gitlab/ui'; +import { shallowMountExtended } from 'helpers/vue_test_utils_helper'; import RootCauseAnalysis from 'ee/ci/job_details/components/sidebar/root_cause_analysis/root_cause_analysis_app.vue'; import createMockApollo from 'helpers/mock_apollo_helper'; import waitForPromises from 'helpers/wait_for_promises'; @@ -44,9 +44,11 @@ describe('rootCauseAnalysis', () => { }; const findEmptyState = () => wrapper.findComponent(GlEmptyState); - const findActionButton = () => wrapper.findComponent(GlButton); + const findActionButton = () => wrapper.findByTestId('root-cause-analysis-action-button'); const findMarkdown = () => wrapper.findComponent(Markdown); const findLoader = () => wrapper.findComponent(GlSkeletonLoader); + const findExperimentBadge = () => wrapper.findComponent(GlExperimentBadge); + const findAlert = () => wrapper.findComponent(GlAlert); const rootCauseQueryHandlerMock = jest.fn(); const rootCauseMutationHandlerMock = jest.fn().mockResolvedValue({ @@ -79,21 +81,41 @@ describe('rootCauseAnalysis', () => { mockApollo = createMockApollo(handlers); - wrapper = shallowMount(RootCauseAnalysis, { + wrapper = shallowMountExtended(RootCauseAnalysis, { propsData, provide: { projectPath }, apolloProvider: mockApollo, stubs: { GlEmptyState, + GlDrawer, }, }); }; + const itHasLegalDisclaimer = () => { + describe('legal disclaimer', () => { + it('renders experiment badge', () => { + expect(findExperimentBadge().exists()).toBe(true); + }); + + it('renders warning alert', () => { + expect(findAlert().exists()).toBe(true); + expect(findAlert().props()).toMatchObject({ + dismissible: false, + variant: 'tip', + showIcon: false, + }); + }); + }); + }; + describe('when the data is not ready', () => { beforeEach(() => { createComponent(); }); + itHasLegalDisclaimer(); + it('the empty state is shown', () => { const emptyState = findEmptyState(); expect(emptyState.exists()).toBe(true); @@ -134,6 +156,8 @@ describe('rootCauseAnalysis', () => { await waitForPromises(); }); + itHasLegalDisclaimer(); + it('the empty state is not shown', () => { const emptyState = findEmptyState(); expect(emptyState.exists()).toBe(false);