From 4c377392bf15fadbce962592e62fb8fccceeb7a3 Mon Sep 17 00:00:00 2001
From: Jose Ivan Vargas <jvargas@gitlab.com>
Date: Tue, 23 Jul 2024 21:59:29 +0000
Subject: [PATCH] Change Troubleshoot button position

This moves the Troubleshoot button from the
top bar of the job logs to the bottom just
underneath the log itself
---
 .../components/root_cause_analysis_button.vue |  4 ++
 .../javascripts/ci/job_details/job_app.vue    | 14 ++++-
 .../components/job_log_top_bar.vue            | 34 +-----------
 .../components/root_cause_analysis_button.vue | 52 +++++++++++++++++++
 .../root_cause_analysis_button_spec.js        | 45 ++++++++++++++++
 qa/qa/ee/page/project/job/show.rb             |  2 +-
 6 files changed, 117 insertions(+), 34 deletions(-)
 create mode 100644 app/assets/javascripts/ci/job_details/components/root_cause_analysis_button.vue
 create mode 100644 ee/app/assets/javascripts/ci/job_details/components/root_cause_analysis_button.vue
 create mode 100644 ee/spec/frontend/ci/job_details/components/root_cause_analysis_button_spec.js

diff --git a/app/assets/javascripts/ci/job_details/components/root_cause_analysis_button.vue b/app/assets/javascripts/ci/job_details/components/root_cause_analysis_button.vue
new file mode 100644
index 0000000000000..ad7d4c52ee6bf
--- /dev/null
+++ b/app/assets/javascripts/ci/job_details/components/root_cause_analysis_button.vue
@@ -0,0 +1,4 @@
+<template>
+  <!--NOOP in FOSS-->
+  <div></div>
+</template>
diff --git a/app/assets/javascripts/ci/job_details/job_app.vue b/app/assets/javascripts/ci/job_details/job_app.vue
index 36f61da79f0fd..ed6daa0a43ab4 100644
--- a/app/assets/javascripts/ci/job_details/job_app.vue
+++ b/app/assets/javascripts/ci/job_details/job_app.vue
@@ -5,7 +5,10 @@ import { throttle, isEmpty } from 'lodash';
 // eslint-disable-next-line no-restricted-imports
 import { mapGetters, mapState, mapActions } from 'vuex';
 import JobLogTopBar from 'ee_else_ce/ci/job_details/components/job_log_top_bar.vue';
+import RootCauseAnalysisButton from 'ee_else_ce/ci/job_details/components/root_cause_analysis_button.vue';
 import SafeHtml from '~/vue_shared/directives/safe_html';
+import glFeatureFlagMixin from '~/vue_shared/mixins/gl_feature_flags_mixin';
+import glAbilitiesMixin from '~/vue_shared/mixins/gl_abilities_mixin';
 import { isScrolledToBottom } from '~/lib/utils/scroll_utils';
 import { __, sprintf } from '~/locale';
 import delayedJobMixin from '~/ci/mixins/delayed_job_mixin';
@@ -29,6 +32,7 @@ export default {
     GlIcon,
     Log,
     JobLogTopBar,
+    RootCauseAnalysisButton,
     StuckBlock,
     UnmetPrerequisitesBlock,
     Sidebar,
@@ -39,7 +43,7 @@ export default {
   directives: {
     SafeHtml,
   },
-  mixins: [delayedJobMixin],
+  mixins: [delayedJobMixin, glAbilitiesMixin(), glFeatureFlagMixin()],
   props: {
     artifactHelpUrl: {
       type: String,
@@ -132,6 +136,13 @@ export default {
     jobConfirmationMessage() {
       return this.job.status?.action?.confirmation_message;
     },
+    jobFailed() {
+      const { status } = this.job;
+
+      const failedGroups = ['failed', 'failed-with-warnings'];
+
+      return failedGroups.includes(status.group);
+    },
   },
   watch: {
     // Once the job log is loaded,
@@ -314,6 +325,7 @@ export default {
             @exitFullscreen="exitFullscreen"
           />
           <log :search-results="searchResults" />
+          <root-cause-analysis-button :job-failed="jobFailed" />
         </div>
         <!-- EO job log -->
 
diff --git a/ee/app/assets/javascripts/ci/job_details/components/job_log_top_bar.vue b/ee/app/assets/javascripts/ci/job_details/components/job_log_top_bar.vue
index 3741d8fd5afe8..db73cebcce6aa 100644
--- a/ee/app/assets/javascripts/ci/job_details/components/job_log_top_bar.vue
+++ b/ee/app/assets/javascripts/ci/job_details/components/job_log_top_bar.vue
@@ -2,13 +2,10 @@
 import { GlButton } from '@gitlab/ui';
 // eslint-disable-next-line no-restricted-imports
 import { mapState } from 'vuex';
-import glAbilitiesMixin from '~/vue_shared/mixins/gl_abilities_mixin';
-import { sendDuoChatCommand } from 'ee/ai/utils';
 import glFeatureFlagMixin from '~/vue_shared/mixins/gl_feature_flags_mixin';
 import { convertToGraphQLId } from '~/graphql_shared/utils';
 import { TYPENAME_CI_BUILD } from '~/graphql_shared/constants';
 import CeJobLogTopBar from '~/ci/job_details/components/job_log_top_bar.vue';
-import { duoChatGlobalState } from '~/super_sidebar/constants';
 import RootCauseAnalysis from './sidebar/root_cause_analysis/root_cause_analysis_app.vue';
 
 export default {
@@ -17,7 +14,7 @@ export default {
     GlButton,
     RootCauseAnalysis,
   },
-  mixins: [glAbilitiesMixin(), glFeatureFlagMixin()],
+  mixins: [glFeatureFlagMixin()],
   inject: ['aiRootCauseAnalysisAvailable', 'duoFeaturesEnabled', 'jobGid'],
   props: {
     size: {
@@ -78,15 +75,6 @@ export default {
         !this.glFeatures.rootCauseAnalysisDuo
       );
     },
-    rootCauseAnalysisDuoIsAvailable() {
-      return (
-        this.glFeatures.aiBuildFailureCause &&
-        this.aiRootCauseAnalysisAvailable &&
-        this.duoFeaturesEnabled &&
-        this.glFeatures.rootCauseAnalysisDuo &&
-        this.glAbilities.troubleshootJobWithAi
-      );
-    },
     jobFailed() {
       const { status } = this.job;
 
@@ -97,9 +85,6 @@ export default {
     jobId() {
       return convertToGraphQLId(TYPENAME_CI_BUILD, this.job.id);
     },
-    duoDrawerOpen() {
-      return duoChatGlobalState.isShown;
-    },
     ...mapState(['job', 'isLoading']),
   },
   methods: {
@@ -121,12 +106,6 @@ export default {
     handleExitFullscreen() {
       this.$emit('exitFullscreen');
     },
-    callDuo() {
-      sendDuoChatCommand({
-        question: '/troubleshoot',
-        resourceId: this.jobId,
-      });
-    },
   },
 };
 </script>
@@ -158,6 +137,7 @@ export default {
       @exitFullscreen="handleExitFullscreen"
     >
       <template #controllers>
+        <!-- TODO: Remove the previous implementation of the RCA drawer https://gitlab.com/gitlab-org/gitlab/-/issues/473797 -->
         <gl-button
           v-if="rootCauseAnalysisIsAvailable && jobFailed"
           icon="tanuki-ai"
@@ -167,16 +147,6 @@ export default {
         >
           {{ s__('Jobs|Troubleshoot') }}
         </gl-button>
-        <gl-button
-          v-if="rootCauseAnalysisDuoIsAvailable && jobFailed"
-          :disabled="duoDrawerOpen"
-          icon="tanuki-ai"
-          class="gl-mr-3"
-          data-testid="rca-duo-button"
-          @click="callDuo"
-        >
-          {{ s__('Jobs|Troubleshoot') }}
-        </gl-button>
       </template>
     </ce-job-log-top-bar>
   </div>
diff --git a/ee/app/assets/javascripts/ci/job_details/components/root_cause_analysis_button.vue b/ee/app/assets/javascripts/ci/job_details/components/root_cause_analysis_button.vue
new file mode 100644
index 0000000000000..26105bfde663d
--- /dev/null
+++ b/ee/app/assets/javascripts/ci/job_details/components/root_cause_analysis_button.vue
@@ -0,0 +1,52 @@
+<script>
+import { GlButton } from '@gitlab/ui';
+import glFeatureFlagMixin from '~/vue_shared/mixins/gl_feature_flags_mixin';
+import glAbilitiesMixin from '~/vue_shared/mixins/gl_abilities_mixin';
+import { sendDuoChatCommand } from 'ee/ai/utils';
+
+export default {
+  components: {
+    GlButton,
+  },
+  mixins: [glAbilitiesMixin(), glFeatureFlagMixin()],
+  inject: ['aiRootCauseAnalysisAvailable', 'duoFeaturesEnabled', 'jobGid'],
+  props: {
+    jobFailed: {
+      type: Boolean,
+      required: false,
+      default: false,
+    },
+  },
+  computed: {
+    rootCauseAnalysisDuoIsAvailable() {
+      return (
+        this.glFeatures.aiBuildFailureCause &&
+        this.aiRootCauseAnalysisAvailable &&
+        this.duoFeaturesEnabled &&
+        this.glFeatures.rootCauseAnalysisDuo &&
+        this.glAbilities.troubleshootJobWithAi
+      );
+    },
+  },
+  methods: {
+    callDuo() {
+      sendDuoChatCommand({
+        question: '/troubleshoot',
+        resourceId: this.jobGid,
+      });
+    },
+  },
+};
+</script>
+<template>
+  <gl-button
+    v-if="rootCauseAnalysisDuoIsAvailable && jobFailed"
+    icon="tanuki-ai"
+    class="gl-mr-3"
+    variant="confirm"
+    data-testid="rca-duo-button"
+    @click="callDuo"
+  >
+    {{ s__('Jobs|Troubleshoot') }}
+  </gl-button>
+</template>
diff --git a/ee/spec/frontend/ci/job_details/components/root_cause_analysis_button_spec.js b/ee/spec/frontend/ci/job_details/components/root_cause_analysis_button_spec.js
new file mode 100644
index 0000000000000..3d82614bedf89
--- /dev/null
+++ b/ee/spec/frontend/ci/job_details/components/root_cause_analysis_button_spec.js
@@ -0,0 +1,45 @@
+import RootCauseAnalysisButton from 'ee/ci/job_details/components/root_cause_analysis_button.vue';
+import { shallowMountExtended } from 'helpers/vue_test_utils_helper';
+
+describe('Root cause analysis button', () => {
+  let wrapper;
+
+  const defaultProps = {
+    jobFailed: true,
+  };
+
+  const createComponent = (props) => {
+    wrapper = shallowMountExtended(RootCauseAnalysisButton, {
+      propsData: {
+        ...defaultProps,
+        ...props,
+      },
+      provide: {
+        aiRootCauseAnalysisAvailable: true,
+        duoFeaturesEnabled: true,
+        glFeatures: {
+          aiBuildFailureCause: true,
+          rootCauseAnalysisDuo: true,
+        },
+        glAbilities: {
+          troubleshootJobWithAi: true,
+        },
+        jobGid: 'gid://gitlab/Ci::Build/123',
+      },
+    });
+  };
+
+  const findTroubleshootButton = () => wrapper.findByTestId('rca-duo-button');
+
+  it('should display the Troubleshoot button', () => {
+    createComponent();
+
+    expect(findTroubleshootButton().exists()).toBe(true);
+  });
+
+  it('should not display the Troubleshoot button when no failure is detected', () => {
+    createComponent({ jobFailed: false });
+
+    expect(findTroubleshootButton().exists()).toBe(false);
+  });
+});
diff --git a/qa/qa/ee/page/project/job/show.rb b/qa/qa/ee/page/project/job/show.rb
index c96bab64f46f3..58783d030f0c8 100644
--- a/qa/qa/ee/page/project/job/show.rb
+++ b/qa/qa/ee/page/project/job/show.rb
@@ -12,7 +12,7 @@ def self.prepended(base)
               super
 
               base.class_eval do
-                view 'ee/app/assets/javascripts/ci/job_details/components/job_log_top_bar.vue' do
+                view 'ee/app/assets/javascripts/ci/job_details/components/root_cause_analysis_button.vue' do
                   element 'rca-duo-button'
                 end
               end
-- 
GitLab