From a02c620894752feecd032b8b3c994791b2340b89 Mon Sep 17 00:00:00 2001
From: Valerie Burton <vburton@gitlab.com>
Date: Tue, 17 Oct 2023 01:57:16 +0000
Subject: [PATCH] Add Confidentiality Checkbox to New Test Case Form

Adds a checkbox to the new test case form for setting confidentiality

Changelog: changed
EE: true
---
 .../components/issuable_create_root.vue       |  5 ++
 .../create/components/issuable_form.vue       | 32 +++++++-
 doc/ci/test_cases/index.md                    |  7 +-
 .../components/test_case_create_root.vue      | 11 ++-
 .../projects/quality/test_case_create_spec.rb | 75 ++++++++++++-------
 .../components/test_case_create_root_spec.js  |  4 +-
 locale/gitlab.pot                             |  3 +
 .../components/issuable_create_root_spec.js   |  3 +
 .../create/components/issuable_form_spec.js   | 32 ++++++--
 9 files changed, 132 insertions(+), 40 deletions(-)

diff --git a/app/assets/javascripts/vue_shared/issuable/create/components/issuable_create_root.vue b/app/assets/javascripts/vue_shared/issuable/create/components/issuable_create_root.vue
index 033bb8c3885c..679332163b59 100644
--- a/app/assets/javascripts/vue_shared/issuable/create/components/issuable_create_root.vue
+++ b/app/assets/javascripts/vue_shared/issuable/create/components/issuable_create_root.vue
@@ -22,6 +22,10 @@ export default {
       type: String,
       required: true,
     },
+    issuableType: {
+      type: String,
+      required: true,
+    },
   },
 };
 </script>
@@ -34,6 +38,7 @@ export default {
       :description-help-path="descriptionHelpPath"
       :labels-fetch-path="labelsFetchPath"
       :labels-manage-path="labelsManagePath"
+      :issuable-type="issuableType"
     >
       <template #actions="issuableMeta">
         <slot name="actions" v-bind="issuableMeta"></slot>
diff --git a/app/assets/javascripts/vue_shared/issuable/create/components/issuable_form.vue b/app/assets/javascripts/vue_shared/issuable/create/components/issuable_form.vue
index 1cfa3f6d3d76..64f0ec3fbc7c 100644
--- a/app/assets/javascripts/vue_shared/issuable/create/components/issuable_form.vue
+++ b/app/assets/javascripts/vue_shared/issuable/create/components/issuable_form.vue
@@ -1,15 +1,17 @@
 <script>
-import { GlForm, GlFormInput, GlFormGroup } from '@gitlab/ui';
+import { GlForm, GlFormInput, GlFormCheckbox, GlFormGroup } from '@gitlab/ui';
 import LabelsSelect from '~/sidebar/components/labels/labels_select_vue/labels_select_root.vue';
 import { VARIANT_EMBEDDED } from '~/sidebar/components/labels/labels_select_widget/constants';
 import MarkdownEditor from '~/vue_shared/components/markdown/markdown_editor.vue';
-import { __ } from '~/locale';
+import { __, sprintf } from '~/locale';
+import { issuableTypeText } from '~/issues/constants';
 
 export default {
   VARIANT_EMBEDDED,
   components: {
     GlForm,
     GlFormInput,
+    GlFormCheckbox,
     GlFormGroup,
     MarkdownEditor,
     LabelsSelect,
@@ -31,6 +33,10 @@ export default {
       type: String,
       required: true,
     },
+    issuableType: {
+      type: String,
+      required: true,
+    },
   },
   descriptionFormFieldProps: {
     ariaLabel: __('Description'),
@@ -44,10 +50,20 @@ export default {
     return {
       issuableTitle: '',
       issuableDescription: '',
+      issuableConfidential: false,
       selectedLabels: [],
     };
   },
-  computed: {},
+  computed: {
+    confidentialityText() {
+      return sprintf(
+        __(
+          'This %{issuableType} is confidential and should only be visible to team members with at least Reporter access.',
+        ),
+        { issuableType: issuableTypeText[this.issuableType] },
+      );
+    },
+  },
   methods: {
     handleUpdateSelectedLabels(labels) {
       if (labels.length) {
@@ -85,6 +101,15 @@ export default {
         />
       </div>
     </div>
+    <div data-testid="issuable-confidential" class="form-group row">
+      <div class="col-12">
+        <gl-form-group :label="__('Confidentiality')" label-for="issuable-confidential">
+          <gl-form-checkbox id="issuable-confidential" v-model="issuableConfidential">
+            {{ confidentialityText }}
+          </gl-form-checkbox>
+        </gl-form-group>
+      </div>
+    </div>
     <div data-testid="issuable-labels" class="form-group row">
       <label for="issuable-labels" class="col-12">{{ __('Labels') }}</label>
       <div class="col-12">
@@ -111,6 +136,7 @@ export default {
         name="actions"
         :issuable-title="issuableTitle"
         :issuable-description="issuableDescription"
+        :issuable-confidential="issuableConfidential"
         :selected-labels="selectedLabels"
       ></slot>
     </div>
diff --git a/doc/ci/test_cases/index.md b/doc/ci/test_cases/index.md
index 3885b450a709..071884f2bed4 100644
--- a/doc/ci/test_cases/index.md
+++ b/doc/ci/test_cases/index.md
@@ -71,7 +71,7 @@ To edit a test case:
 
 ## Make a test case confidential
 
-> Ability to make a test case confidential from the right sidebar [introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/422120) in GitLab 16.5.
+> Introduced for [new](https://gitlab.com/gitlab-org/gitlab/-/issues/422121) and [existing](https://gitlab.com/gitlab-org/gitlab/-/issues/422120) test cases in GitLab 16.5.
 
 If you're working on a test case that contains private information, you can make it confidential.
 
@@ -79,7 +79,10 @@ Prerequisites:
 
 - You must have at least the Reporter role.
 
-To make a test case confidential: on the right sidebar, select **Edit** next to **Confidentiality**, and then select **Turn on**.
+To make a test case confidential:
+
+- When you [create a test case](#create-a-test-case): under **Confidentiality**, select the **This test case is confidential...** checkbox.
+- When you [edit a test case](#edit-a-test-case): on the right sidebar, next to **Confidentiality**, select **Edit**, then select **Turn on**.
 
 You can also use the `/confidential` [quick action](../../user/project/quick_actions.md) when both creating a new test case
 or editing an existing one.
diff --git a/ee/app/assets/javascripts/test_case_create/components/test_case_create_root.vue b/ee/app/assets/javascripts/test_case_create/components/test_case_create_root.vue
index d18a5d7d3ddf..0e3fe1211a85 100644
--- a/ee/app/assets/javascripts/test_case_create/components/test_case_create_root.vue
+++ b/ee/app/assets/javascripts/test_case_create/components/test_case_create_root.vue
@@ -3,12 +3,14 @@ import { GlButton } from '@gitlab/ui';
 import { createAlert } from '~/alert';
 import IssuableCreate from '~/vue_shared/issuable/create/components/issuable_create_root.vue';
 import { redirectTo } from '~/lib/utils/url_utility'; // eslint-disable-line import/no-deprecated
+import { TYPE_TEST_CASE } from '~/issues/constants';
 
 import { s__ } from '~/locale';
 
 import createTestCase from '../queries/create_test_case.mutation.graphql';
 
 export default {
+  TYPE_TEST_CASE,
   components: {
     GlButton,
     IssuableCreate,
@@ -27,7 +29,12 @@ export default {
     };
   },
   methods: {
-    handleTestCaseSubmitClick({ issuableTitle, issuableDescription, selectedLabels }) {
+    handleTestCaseSubmitClick({
+      issuableTitle,
+      issuableDescription,
+      issuableConfidential,
+      selectedLabels,
+    }) {
       this.createTestCaseRequestActive = true;
       return this.$apollo
         .mutate({
@@ -37,6 +44,7 @@ export default {
               projectPath: this.projectFullPath,
               title: issuableTitle,
               description: issuableDescription,
+              confidential: issuableConfidential,
               labelIds: selectedLabels.map((label) => label.id),
             },
           },
@@ -75,6 +83,7 @@ export default {
     :description-help-path="descriptionHelpPath"
     :labels-fetch-path="labelsFetchPath"
     :labels-manage-path="labelsManagePath"
+    :issuable-type="$options.TYPE_TEST_CASE"
   >
     <template #title>
       <h1 class="page-title gl-font-size-h-display">{{ s__('TestCases|New test case') }}</h1>
diff --git a/ee/spec/features/projects/quality/test_case_create_spec.rb b/ee/spec/features/projects/quality/test_case_create_spec.rb
index 2586c81a9555..e99e8cc5be5b 100644
--- a/ee/spec/features/projects/quality/test_case_create_spec.rb
+++ b/ee/spec/features/projects/quality/test_case_create_spec.rb
@@ -17,28 +17,25 @@
   end
 
   context 'test case create form' do
+    let(:title) { 'Sample title' }
+    let(:description) { 'Sample _test case_ description.' }
+
     before do
       visit new_project_quality_test_case_path(project)
 
       wait_for_requests
     end
 
-    it 'shows page title, title, description and label input fields' do
+    it 'shows page title, title, description, confidentiality and label input fields' do
       page.within('.issuable-create-container') do
         expect(page.find('.page-title')).to have_content('New test case')
       end
 
       page.within('.issuable-create-container form') do
-        form_fields = page.find_all('.row')
-
-        expect(form_fields[0].find('label')).to have_content('Title')
-        expect(form_fields[0]).to have_selector('input#issuable-title')
-
-        expect(form_fields[1].find('label')).to have_content('Description')
-        expect(form_fields[1]).to have_selector('.js-vue-markdown-field')
-
-        expect(form_fields[2].find('label')).to have_content('Labels')
-        expect(form_fields[2]).to have_selector('.labels-select-wrapper')
+        expect(find_by_testid('issuable-title')).to have_selector('input#issuable-title')
+        expect(find_by_testid('issuable-description')).to have_selector('.js-vue-markdown-field')
+        expect(find_by_testid('issuable-confidential')).to have_selector('input#issuable-confidential')
+        expect(find_by_testid('issuable-labels')).to have_selector('.labels-select-wrapper')
       end
     end
 
@@ -60,27 +57,53 @@
       end
     end
 
-    it 'creates a test case on saving form' do
-      title = 'Sample title'
-      description = 'Sample _test case_ description.'
-
-      page.within('.issuable-create-container form') do
-        form_fields = page.find_all('.row')
+    context 'when creating a confidential test case' do
+      before do
+        fill_and_submit_form(confidential: true)
+      end
 
-        form_fields[0].find('input#issuable-title').native.send_keys title
-        form_fields[1].find('textarea#issuable-description').native.send_keys description
-        form_fields[2].find('.js-dropdown-button').click
+      it 'saves test case as confidential' do
+        page.within('.content-wrapper .project-test-cases') do
+          expect(page).to have_content(title)
+          expect(page).to have_css('[data-testid="eye-slash-icon"]')
+        end
+      end
+    end
 
-        wait_for_requests
+    context 'when creating a non-confidential test case' do
+      before do
+        fill_and_submit_form(confidential: false)
+      end
 
-        form_fields[2].find_all('.js-labels-list .dropdown-content li')[0].click
+      it 'saves test case as non-confidential' do
+        page.within('.content-wrapper .project-test-cases') do
+          expect(page).to have_content(title)
+          expect(page).not_to have_css('[data-testid="eye-slash-icon"]')
+        end
       end
+    end
+  end
+end
 
-      click_button 'Submit test case'
+private
 
-      wait_for_requests
+def fill_and_submit_form(confidential:)
+  page.within('.issuable-create-container form') do
+    fill_in _('Title'), with: title
+    fill_in _('Description'), with: description
 
-      expect(page).to have_selector('.content-wrapper .project-test-cases')
-    end
+    find('#issuable-confidential').set(confidential)
+
+    click_button _('Label')
+
+    wait_for_requests
+
+    click_link _('bug')
+    click_link _('enhancement')
+    click_link _('documentation')
   end
+
+  click_button 'Submit test case'
+
+  wait_for_requests
 end
diff --git a/ee/spec/frontend/test_case_create/components/test_case_create_root_spec.js b/ee/spec/frontend/test_case_create/components/test_case_create_root_spec.js
index 1233951cad21..2db0454770fa 100644
--- a/ee/spec/frontend/test_case_create/components/test_case_create_root_spec.js
+++ b/ee/spec/frontend/test_case_create/components/test_case_create_root_spec.js
@@ -66,6 +66,7 @@ describe('TestCaseCreateRoot', () => {
                 name="actions"
                 issuable-title="${title}"
                 issuable-description="Test description"
+                :issuable-confidential="true"
                 :selected-labels="[]"
               ></slot>
             </div>
@@ -95,6 +96,7 @@ describe('TestCaseCreateRoot', () => {
       expect(mutationSuccessHandler).toHaveBeenCalledWith({
         createTestCaseInput: {
           description: 'Test description',
+          confidential: true,
           labelIds: [],
           projectPath: 'gitlab-org/gitlab-test',
           title: 'Test title',
@@ -144,7 +146,7 @@ describe('TestCaseCreateRoot', () => {
     });
   });
 
-  it('shows a warning  when mutation has recoverable error', async () => {
+  it('shows a warning when mutation has recoverable error', async () => {
     createComponent({
       title: 'Test title',
       handler: jest.fn().mockResolvedValue(mutationResponseError),
diff --git a/locale/gitlab.pot b/locale/gitlab.pot
index fcd8c86969eb..0cac3949b1f2 100644
--- a/locale/gitlab.pot
+++ b/locale/gitlab.pot
@@ -48288,6 +48288,9 @@ msgstr ""
 msgid "This %{issuableDisplayName} is locked. Only project members can comment."
 msgstr ""
 
+msgid "This %{issuableType} is confidential and should only be visible to team members with at least Reporter access."
+msgstr ""
+
 msgid "This %{issuable} is hidden because its author has been banned."
 msgstr ""
 
diff --git a/spec/frontend/vue_shared/issuable/create/components/issuable_create_root_spec.js b/spec/frontend/vue_shared/issuable/create/components/issuable_create_root_spec.js
index 03f509a3fa32..35e3564c5990 100644
--- a/spec/frontend/vue_shared/issuable/create/components/issuable_create_root_spec.js
+++ b/spec/frontend/vue_shared/issuable/create/components/issuable_create_root_spec.js
@@ -5,6 +5,7 @@ import VueApollo from 'vue-apollo';
 import createMockApollo from 'helpers/mock_apollo_helper';
 import IssuableCreateRoot from '~/vue_shared/issuable/create/components/issuable_create_root.vue';
 import IssuableForm from '~/vue_shared/issuable/create/components/issuable_form.vue';
+import { TYPE_TEST_CASE } from '~/issues/constants';
 
 Vue.use(VueApollo);
 
@@ -13,6 +14,7 @@ const createComponent = ({
   descriptionHelpPath = '/help/user/markdown',
   labelsFetchPath = '/gitlab-org/gitlab-shell/-/labels.json',
   labelsManagePath = '/gitlab-org/gitlab-shell/-/labels',
+  issuableType = TYPE_TEST_CASE,
 } = {}) => {
   return mount(IssuableCreateRoot, {
     propsData: {
@@ -20,6 +22,7 @@ const createComponent = ({
       descriptionHelpPath,
       labelsFetchPath,
       labelsManagePath,
+      issuableType,
     },
     apolloProvider: createMockApollo(),
     slots: {
diff --git a/spec/frontend/vue_shared/issuable/create/components/issuable_form_spec.js b/spec/frontend/vue_shared/issuable/create/components/issuable_form_spec.js
index 623617058434..61185f913d91 100644
--- a/spec/frontend/vue_shared/issuable/create/components/issuable_form_spec.js
+++ b/spec/frontend/vue_shared/issuable/create/components/issuable_form_spec.js
@@ -1,9 +1,10 @@
-import { GlFormInput } from '@gitlab/ui';
-import { shallowMount } from '@vue/test-utils';
+import { GlFormInput, GlFormGroup, GlFormCheckbox } from '@gitlab/ui';
+import { shallowMountExtended } from 'helpers/vue_test_utils_helper';
 
 import IssuableForm from '~/vue_shared/issuable/create/components/issuable_form.vue';
 import MarkdownEditor from '~/vue_shared/components/markdown/markdown_editor.vue';
 import LabelsSelect from '~/sidebar/components/labels/labels_select_vue/labels_select_root.vue';
+import { TYPE_TEST_CASE } from '~/issues/constants';
 import { __ } from '~/locale';
 
 const createComponent = ({
@@ -11,13 +12,15 @@ const createComponent = ({
   descriptionHelpPath = '/help/user/markdown',
   labelsFetchPath = '/gitlab-org/gitlab-shell/-/labels.json',
   labelsManagePath = '/gitlab-org/gitlab-shell/-/labels',
+  issuableType = TYPE_TEST_CASE,
 } = {}) => {
-  return shallowMount(IssuableForm, {
+  return shallowMountExtended(IssuableForm, {
     propsData: {
       descriptionPreviewPath,
       descriptionHelpPath,
       labelsFetchPath,
       labelsManagePath,
+      issuableType,
     },
     slots: {
       actions: `
@@ -58,7 +61,7 @@ describe('IssuableForm', () => {
 
   describe('template', () => {
     it('renders issuable title input field', () => {
-      const titleFieldEl = wrapper.find('[data-testid="issuable-title"]');
+      const titleFieldEl = wrapper.findByTestId('issuable-title');
 
       expect(titleFieldEl.exists()).toBe(true);
       expect(titleFieldEl.find('label').text()).toBe('Title');
@@ -68,7 +71,7 @@ describe('IssuableForm', () => {
     });
 
     it('renders issuable description input field', () => {
-      const descriptionFieldEl = wrapper.find('[data-testid="issuable-description"]');
+      const descriptionFieldEl = wrapper.findByTestId('issuable-description');
 
       expect(descriptionFieldEl.exists()).toBe(true);
       expect(descriptionFieldEl.find('label').text()).toBe('Description');
@@ -88,8 +91,23 @@ describe('IssuableForm', () => {
       });
     });
 
+    it('renders issuable confidential checkbox', () => {
+      const confidentialCheckboxEl = wrapper.findByTestId('issuable-confidential');
+      expect(confidentialCheckboxEl.exists()).toBe(true);
+
+      expect(confidentialCheckboxEl.findComponent(GlFormGroup).exists()).toBe(true);
+      expect(confidentialCheckboxEl.findComponent(GlFormGroup).attributes('label')).toBe(
+        'Confidentiality',
+      );
+
+      expect(confidentialCheckboxEl.findComponent(GlFormCheckbox).exists()).toBe(true);
+      expect(confidentialCheckboxEl.findComponent(GlFormCheckbox).text()).toBe(
+        'This test case is confidential and should only be visible to team members with at least Reporter access.',
+      );
+    });
+
     it('renders labels select field', () => {
-      const labelsSelectEl = wrapper.find('[data-testid="issuable-labels"]');
+      const labelsSelectEl = wrapper.findByTestId('issuable-labels');
 
       expect(labelsSelectEl.exists()).toBe(true);
       expect(labelsSelectEl.find('label').text()).toBe('Labels');
@@ -111,7 +129,7 @@ describe('IssuableForm', () => {
 
     it('renders contents for slot "actions"', () => {
       const buttonEl = wrapper
-        .find('[data-testid="issuable-create-actions"]')
+        .findByTestId('issuable-create-actions')
         .find('button.js-issuable-save');
 
       expect(buttonEl.exists()).toBe(true);
-- 
GitLab