Commit 853ed8f5 authored by Kushal Pandya's avatar Kushal Pandya

Use `issuable_show` for requirement show & create

Use `issuable_body.vue` component to render
requirement sidebar body.
parent 43e0d5ee
...@@ -2,6 +2,7 @@ ...@@ -2,6 +2,7 @@
// See: https://gitlab.com/gitlab-org/gitlab/-/issues/216102 // See: https://gitlab.com/gitlab-org/gitlab/-/issues/216102
export const BACKSPACE_KEY_CODE = 8; export const BACKSPACE_KEY_CODE = 8;
export const TAB_KEY_CODE = 9;
export const ENTER_KEY_CODE = 13; export const ENTER_KEY_CODE = 13;
export const ESC_KEY_CODE = 27; export const ESC_KEY_CODE = 27;
export const UP_KEY_CODE = 38; export const UP_KEY_CODE = 38;
......
...@@ -3,8 +3,6 @@ import '~/behaviors/markdown/render_gfm'; ...@@ -3,8 +3,6 @@ import '~/behaviors/markdown/render_gfm';
import $ from 'jquery'; import $ from 'jquery';
import { import {
GlDrawer, GlDrawer,
GlFormGroup,
GlFormTextarea,
GlButton, GlButton,
GlFormCheckbox, GlFormCheckbox,
GlTooltipDirective, GlTooltipDirective,
...@@ -13,7 +11,8 @@ import { ...@@ -13,7 +11,8 @@ import {
import { isEmpty } from 'lodash'; import { isEmpty } from 'lodash';
import { __, sprintf } from '~/locale'; import { __, sprintf } from '~/locale';
import ZenMode from '~/zen_mode'; import ZenMode from '~/zen_mode';
import MarkdownField from '~/vue_shared/components/markdown/field.vue'; import { TAB_KEY_CODE } from '~/lib/utils/keycodes';
import IssuableBody from '~/issuable_show/components/issuable_body.vue';
import RequirementStatusBadge from './requirement_status_badge.vue'; import RequirementStatusBadge from './requirement_status_badge.vue';
...@@ -21,6 +20,7 @@ import RequirementMeta from '../mixins/requirement_meta'; ...@@ -21,6 +20,7 @@ import RequirementMeta from '../mixins/requirement_meta';
import { MAX_TITLE_LENGTH, TestReportStatus } from '../constants'; import { MAX_TITLE_LENGTH, TestReportStatus } from '../constants';
export default { export default {
maxTitleLength: MAX_TITLE_LENGTH,
events: { events: {
drawerClose: 'drawer-close', drawerClose: 'drawer-close',
disableEdit: 'disable-edit', disableEdit: 'disable-edit',
...@@ -31,12 +31,10 @@ export default { ...@@ -31,12 +31,10 @@ export default {
}), }),
components: { components: {
GlDrawer, GlDrawer,
GlFormGroup,
GlFormTextarea,
GlFormCheckbox, GlFormCheckbox,
GlButton, GlButton,
MarkdownField,
RequirementStatusBadge, RequirementStatusBadge,
IssuableBody,
}, },
directives: { directives: {
GlTooltip: GlTooltipDirective, GlTooltip: GlTooltipDirective,
...@@ -67,9 +65,7 @@ export default { ...@@ -67,9 +65,7 @@ export default {
data() { data() {
return { return {
zenModeEnabled: false, zenModeEnabled: false,
title: this.requirement?.title || '',
satisfied: this.requirement?.satisfied || false, satisfied: this.requirement?.satisfied || false,
description: this.requirement?.description || '',
}; };
}, },
computed: { computed: {
...@@ -82,18 +78,24 @@ export default { ...@@ -82,18 +78,24 @@ export default {
saveButtonLabel() { saveButtonLabel() {
return this.isCreate ? __('Create requirement') : __('Save changes'); return this.isCreate ? __('Create requirement') : __('Save changes');
}, },
titleInvalid() { canEditRequirement() {
return this.title?.length > MAX_TITLE_LENGTH; return this.isCreate || (this.canUpdate && !this.isArchived);
}, },
disableSaveButton() { requirementObject() {
return this.title === '' || this.titleInvalid || this.requirementRequestActive; return this.isCreate
? {
iid: '',
title: '',
titleHtml: '',
description: '',
descriptionHtml: '',
}
: this.requirement;
}, },
}, },
watch: { watch: {
requirement: { requirement: {
handler(value) { handler(value) {
this.title = value?.title || '';
this.description = value?.description || '';
this.satisfied = value?.satisfied || false; this.satisfied = value?.satisfied || false;
}, },
deep: true, deep: true,
...@@ -101,13 +103,19 @@ export default { ...@@ -101,13 +103,19 @@ export default {
drawerOpen(value) { drawerOpen(value) {
// Clear `title` and `satisfied` value on drawer close. // Clear `title` and `satisfied` value on drawer close.
if (!value) { if (!value) {
this.title = '';
this.description = '';
this.satisfied = false; this.satisfied = false;
} else {
document.addEventListener('keydown', this.handleDocumentKeydown);
} }
}, },
enableRequirementEdit(value) {
this.$nextTick(() => {
this.lastEl = this.getDrawerLastEl(value);
});
},
}, },
mounted() { mounted() {
this.handleDocumentKeydown = this.handleDrawerKeydown.bind(this);
this.zenMode = new ZenMode(); this.zenMode = new ZenMode();
$(this.$refs.gfmContainer).renderGFM(); $(this.$refs.gfmContainer).renderGFM();
$(document).on('zen_mode:enter', () => { $(document).on('zen_mode:enter', () => {
...@@ -133,6 +141,11 @@ export default { ...@@ -133,6 +141,11 @@ export default {
return ''; return '';
}, },
getDrawerLastEl(isEditMode) {
return this.$refs.drawerEl.$el?.querySelector(
isEditMode ? '.js-requirement-cancel' : '.js-issuable-edit',
);
},
newLastTestReportState() { newLastTestReportState() {
// lastTestReportState determines whether a requirement is satisfied or not. // lastTestReportState determines whether a requirement is satisfied or not.
// Only create a new test report when manually marking/unmarking a requirement as satisfied: // Only create a new test report when manually marking/unmarking a requirement as satisfied:
...@@ -150,20 +163,50 @@ export default { ...@@ -150,20 +163,50 @@ export default {
return null; return null;
}, },
handleDrawerKeydown(e) {
const { keyCode, shiftKey } = e;
if (!this.firstEl) {
this.firstEl = this.$refs.drawerEl.$el?.querySelector('.gl-drawer-close-button');
}
if (!this.lastEl) {
this.lastEl = this.getDrawerLastEl(this.enableRequirementEdit || this.isCreate);
}
if (keyCode !== TAB_KEY_CODE) return;
if (!this.$refs.drawerEl.$el.contains(document.activeElement)) this.firstEl.focus();
if (shiftKey) {
if (document.activeElement === this.firstEl) {
this.lastEl.focus();
e.preventDefault();
}
} else if (document.activeElement === this.lastEl) {
this.firstEl.focus();
e.preventDefault();
}
},
handleDrawerClose() {
this.$emit(this.$options.events.drawerClose);
document.removeEventListener('keydown', this.handleDocumentKeydown);
this.firstEl = null;
this.lastEl = null;
},
handleFormInputKeyDown() { handleFormInputKeyDown() {
if (this.zenModeEnabled) { if (this.zenModeEnabled) {
// Exit Zen mode, don't close the drawer. // Exit Zen mode, don't close the drawer.
this.zenModeEnabled = false; this.zenModeEnabled = false;
this.zenMode.exit(); this.zenMode.exit();
} else { } else {
this.$emit(this.$options.events.disableEdit); this.handleCancel();
} }
}, },
handleSave() { handleSave({ issuableTitle, issuableDescription }) {
const { title, description } = this;
const eventParams = { const eventParams = {
title, title: issuableTitle,
description, description: issuableDescription,
}; };
if (!this.isCreate) { if (!this.isCreate) {
...@@ -184,11 +227,12 @@ export default { ...@@ -184,11 +227,12 @@ export default {
<template> <template>
<gl-drawer <gl-drawer
ref="drawerEl"
:open="drawerOpen" :open="drawerOpen"
:header-height="getDrawerHeaderHeight()" :header-height="getDrawerHeaderHeight()"
:class="{ 'zen-mode gl-absolute': zenModeEnabled }" :class="{ 'zen-mode gl-absolute': zenModeEnabled }"
class="requirement-form-drawer" class="requirement-form-drawer"
@close="$emit($options.events.drawerClose)" @close="handleDrawerClose"
> >
<template #header> <template #header>
<h4 v-if="isCreate" class="gl-m-0">{{ __('New Requirement') }}</h4> <h4 v-if="isCreate" class="gl-m-0">{{ __('New Requirement') }}</h4>
...@@ -203,91 +247,47 @@ export default { ...@@ -203,91 +247,47 @@ export default {
</div> </div>
</template> </template>
<template> <template>
<div v-if="!enableRequirementEdit && !isCreate" class="requirement-details"> <issuable-body
<div :issuable="requirementObject"
class="title-container gl-display-flex gl-border-b-1 gl-border-b-solid gl-border-gray-100" :enable-edit="canEditRequirement"
>
<h3 v-safe-html="titleHtml" class="title qa-title gl-flex-grow-1 gl-m-0 gl-mb-3"></h3>
<gl-button
v-if="canUpdate && !isArchived"
v-gl-tooltip.bottom
data-testid="edit"
:title="__('Edit title and description')"
icon="pencil"
class="btn-edit gl-align-self-start"
@click="$emit($options.events.enableEdit, $event)"
/>
</div>
<div data-testid="descriptionContainer" class="description-container gl-mt-3">
<div ref="gfmContainer" v-safe-html="descriptionHtml" class="md"></div>
</div>
</div>
<div v-else class="requirement-form">
<div class="requirement-form-container" :class="{ 'gl-flex-grow-1 gl-mt-2': !isCreate }">
<div data-testid="form-error-container" class="flash-container"></div>
<gl-form-group
data-testid="title"
:label="__('Title')"
:invalid-feedback="$options.titleInvalidMessage"
:state="!titleInvalid"
class="gl-show-field-errors"
label-for="requirementTitle"
>
<gl-form-textarea
id="requirementTitle"
v-model.trim="title"
autofocus
resize
:disabled="requirementRequestActive"
:placeholder="__('Requirement title')"
max-rows="25"
class="requirement-form-textarea"
:class="{ 'gl-field-error-outline': titleInvalid }"
@keydown.escape.exact.stop="handleFormInputKeyDown"
@keydown.meta.enter="handleSave"
@keydown.ctrl.enter="handleSave"
/>
</gl-form-group>
<gl-form-group data-testid="description" class="common-note-form">
<label for="requirementDescription" class="d-block col-form-label gl-pb-0!">
{{ __('Description') }}
</label>
<markdown-field
:markdown-preview-path="descriptionPreviewPath"
:markdown-docs-path="descriptionHelpPath"
:enable-autocomplete="false" :enable-autocomplete="false"
:textarea-value="description" :enable-autosave="false"
:edit-form-visible="enableRequirementEdit || isCreate"
:show-field-title="true"
:description-preview-path="descriptionPreviewPath"
:description-help-path="descriptionHelpPath"
status-badge-class="status-box-open"
status-icon="issue-open-m"
@edit-issuable="$emit($options.events.enableEdit, $event)"
@keydown-title.escape.exact.stop="handleFormInputKeyDown"
@keydown-description.escape.exact.stop="handleFormInputKeyDown"
@keydown-title.meta.enter="handleSave(arguments[1])"
@keydown-title.ctrl.enter="handleSave(arguments[1])"
@keydown-description.meta.enter="handleSave(arguments[1])"
@keydown-description.ctrl.enter="handleSave(arguments[1])"
> >
<template #textarea> <template #edit-form-actions="issuableMeta">
<textarea
id="requirementDescription"
v-model="description"
:data-supports-quick-actions="false"
:aria-label="__('Description')"
:placeholder="__('Describe the requirement here')"
class="note-textarea js-gfm-input js-autosize markdown-area qa-description-textarea"
@keydown.escape.exact.stop="handleFormInputKeyDown"
@keydown.meta.enter="handleSave"
@keydown.ctrl.enter="handleSave"
></textarea>
</template>
</markdown-field>
<gl-form-checkbox v-if="!isCreate" v-model="satisfied" class="gl-mt-6">{{ <gl-form-checkbox v-if="!isCreate" v-model="satisfied" class="gl-mt-6">{{
__('Satisfied') __('Satisfied')
}}</gl-form-checkbox> }}</gl-form-checkbox>
</gl-form-group>
<div class="gl-display-flex requirement-form-actions gl-mt-6"> <div class="gl-display-flex requirement-form-actions gl-mt-6">
<gl-button <gl-button
:disabled="disableSaveButton" :disabled="
requirementRequestActive ||
issuableMeta.issuableTitle.length > $options.maxTitleLength ||
!issuableMeta.issuableTitle.length
"
:loading="requirementRequestActive" :loading="requirementRequestActive"
data-testid="requirement-save"
variant="success" variant="success"
category="primary" category="primary"
class="gl-mr-auto js-requirement-save" class="gl-mr-auto js-requirement-save"
@click="handleSave" @click="handleSave(issuableMeta)"
> >
{{ saveButtonLabel }} {{ saveButtonLabel }}
</gl-button> </gl-button>
<gl-button <gl-button
data-testid="requirement-cancel"
variant="default" variant="default"
category="primary" category="primary"
class="js-requirement-cancel" class="js-requirement-cancel"
...@@ -296,8 +296,8 @@ export default { ...@@ -296,8 +296,8 @@ export default {
{{ __('Cancel') }} {{ __('Cancel') }}
</gl-button> </gl-button>
</div> </div>
</div> </template>
</div> </issuable-body>
</template> </template>
</gl-drawer> </gl-drawer>
</template> </template>
...@@ -21,11 +21,43 @@ ...@@ -21,11 +21,43 @@
} }
} }
.requirement-form-drawer.zen-mode { .requirement-form-drawer {
&.zen-mode {
// We need to override `z-index` provided to GlDrawer // We need to override `z-index` provided to GlDrawer
// in Zen mode to enable full-screen editing. // in Zen mode to enable full-screen editing.
z-index: auto !important; z-index: auto !important;
} }
// Following overrides are done to
// elements within `issuable_body.vue`
// and are specific to requirements.
.title-container {
@include gl-border-b-solid;
@include gl-border-b-gray-100;
@include gl-border-b-1;
&,
.title {
@include gl-mb-3;
}
.title {
@include gl-font-size-markdown-h2;
}
}
.issuable-details {
@include gl-py-0;
li.md-header-toolbar {
@include gl-py-3;
}
.detail-page-description {
@include gl-border-none;
}
}
}
} }
.requirements-list-container { .requirements-list-container {
......
---
title: Improve accessibility of keyboard navigation for Requirements
merge_request: 48325
author:
type: added
...@@ -16,8 +16,8 @@ RSpec.describe 'Requirements list', :js do ...@@ -16,8 +16,8 @@ RSpec.describe 'Requirements list', :js do
find('button.js-new-requirement').click find('button.js-new-requirement').click
end end
page.within('.requirements-list-container') do page.within('.requirement-form-drawer') do
find('textarea#requirementTitle').native.send_keys title find('input#issuable-title').native.send_keys title
find('button.js-requirement-save').click find('button.js-requirement-save').click
wait_for_all_requests wait_for_all_requests
...@@ -66,7 +66,7 @@ RSpec.describe 'Requirements list', :js do ...@@ -66,7 +66,7 @@ RSpec.describe 'Requirements list', :js do
end end
page.within('.requirements-list-container') do page.within('.requirements-list-container') do
expect(page).to have_selector('.requirement-form') expect(page).to have_selector('.requirement-form-drawer')
end end
end end
...@@ -137,7 +137,7 @@ RSpec.describe 'Requirements list', :js do ...@@ -137,7 +137,7 @@ RSpec.describe 'Requirements list', :js do
page.within('.requirement-form-drawer') do page.within('.requirement-form-drawer') do
expect(page.find('.title-container')).to have_content(requirement1.title) expect(page.find('.title-container')).to have_content(requirement1.title)
expect(page.find('.title-container')).to have_selector('button.btn-edit') expect(page.find('.title-container')).to have_selector('button.btn-edit')
expect(page.find('.description-container')).to have_content(requirement1.description) expect(page.find('.description')).to have_content(requirement1.description)
end end
end end
...@@ -148,8 +148,8 @@ RSpec.describe 'Requirements list', :js do ...@@ -148,8 +148,8 @@ RSpec.describe 'Requirements list', :js do
page.within('.requirement-form-drawer') do page.within('.requirement-form-drawer') do
expect(page.find('.gl-drawer-header span', match: :first)).to have_content("REQ-#{requirement1.iid}") expect(page.find('.gl-drawer-header span', match: :first)).to have_content("REQ-#{requirement1.iid}")
expect(page.find('textarea#requirementTitle')['value']).to have_content("#{requirement1.title}") expect(page.find('input#issuable-title')['value']).to have_content("#{requirement1.title}")
expect(page.find('textarea#requirementDescription')['value']).to have_content("#{requirement1.description}") expect(page.find('textarea#issuable-description')['value']).to have_content("#{requirement1.description}")
expect(page.find('input[type="checkbox"]')['checked']).to eq(requirement1.last_test_report_state) expect(page.find('input[type="checkbox"]')['checked']).to eq(requirement1.last_test_report_state)
expect(page.find('.js-requirement-save')).to have_content('Save changes') expect(page.find('.js-requirement-save')).to have_content('Save changes')
end end
...@@ -164,8 +164,8 @@ RSpec.describe 'Requirements list', :js do ...@@ -164,8 +164,8 @@ RSpec.describe 'Requirements list', :js do
end end
page.within('.requirement-form-drawer') do page.within('.requirement-form-drawer') do
find('textarea#requirementTitle').native.send_keys requirement_title find('input#issuable-title').native.send_keys requirement_title
find('textarea#requirementDescription').native.send_keys requirement_description find('textarea#issuable-description').native.send_keys requirement_description
find('input[type="checkbox"]').click find('input[type="checkbox"]').click
click_button 'Save changes' click_button 'Save changes'
......
import { GlDrawer, GlFormTextarea, GlFormCheckbox } from '@gitlab/ui'; import { GlDrawer, GlFormCheckbox } from '@gitlab/ui';
import { getByText } from '@testing-library/dom'; import { getByText } from '@testing-library/dom';
import { shallowMount } from '@vue/test-utils'; import { shallowMount } from '@vue/test-utils';
import $ from 'jquery'; import $ from 'jquery';
...@@ -6,8 +6,10 @@ import $ from 'jquery'; ...@@ -6,8 +6,10 @@ import $ from 'jquery';
import RequirementForm from 'ee/requirements/components/requirement_form.vue'; import RequirementForm from 'ee/requirements/components/requirement_form.vue';
import RequirementStatusBadge from 'ee/requirements/components/requirement_status_badge.vue'; import RequirementStatusBadge from 'ee/requirements/components/requirement_status_badge.vue';
import { TestReportStatus, MAX_TITLE_LENGTH } from 'ee/requirements/constants'; import { TestReportStatus } from 'ee/requirements/constants';
import IssuableBody from '~/issuable_show/components/issuable_body.vue';
import IssuableEditForm from '~/issuable_show/components/issuable_edit_form.vue';
import MarkdownField from '~/vue_shared/components/markdown/field.vue'; import MarkdownField from '~/vue_shared/components/markdown/field.vue';
import ZenMode from '~/zen_mode'; import ZenMode from '~/zen_mode';
...@@ -30,6 +32,8 @@ const createComponent = ({ ...@@ -30,6 +32,8 @@ const createComponent = ({
}, },
stubs: { stubs: {
GlDrawer, GlDrawer,
IssuableBody,
IssuableEditForm,
MarkdownField, MarkdownField,
}, },
}); });
...@@ -85,49 +89,38 @@ describe('RequirementForm', () => { ...@@ -85,49 +89,38 @@ describe('RequirementForm', () => {
}); });
}); });
describe('titleInvalid', () => { describe('requirementObject', () => {
it('returns `false` when `title` length is less than max title limit', () => { it('returns requirement object while in show/edit mode', async () => {
expect(wrapper.vm.titleInvalid).toBe(false); wrapper.setProps({
requirement: mockRequirementsOpen[0],
}); });
it('returns `true` when `title` length is more than max title limit', () => { await wrapper.vm.$nextTick();
wrapper.setData({
title: Array(MAX_TITLE_LENGTH + 1)
.fill()
.map(() => 'a')
.join(''),
});
return wrapper.vm.$nextTick(() => { expect(wrapper.vm.requirementObject).toBe(mockRequirementsOpen[0]);
expect(wrapper.vm.titleInvalid).toBe(true);
});
});
});
}); });
describe('watchers', () => { it('returns empty requirement object while in create mode', async () => {
describe('requirement', () => {
describe('when requirement is not null', () => {
it('renders the value of `requirement.title` as title and `requirement.description` as description', async () => {
wrapper.setProps({ wrapper.setProps({
requirement: mockRequirementsOpen[0], requirement: null,
enableRequirementEdit: true,
}); });
await wrapper.vm.$nextTick(); await wrapper.vm.$nextTick();
expect( expect(wrapper.vm.requirementObject).toMatchObject({
wrapper iid: '',
.find('[data-testid="title"]') title: '',
.find(GlFormTextarea) titleHtml: '',
.attributes('value'), description: '',
).toBe(mockRequirementsOpen[0].title); descriptionHtml: '',
});
expect(wrapper.find('[data-testid="description"] textarea').element.value).toBe( });
mockRequirementsOpen[0].description, });
);
}); });
describe('watchers', () => {
describe('requirement', () => {
describe('when requirement is not null', () => {
it.each` it.each`
requirement | satisfied requirement | satisfied
${mockRequirementsOpen[0]} | ${true} ${mockRequirementsOpen[0]} | ${true}
...@@ -153,37 +146,35 @@ describe('RequirementForm', () => { ...@@ -153,37 +146,35 @@ describe('RequirementForm', () => {
}); });
}); });
it('renders empty string as title and description', async () => { it('does not render the satisfied checkbox', async () => {
await wrapper.vm.$nextTick(); await wrapper.vm.$nextTick();
expect(
wrapper
.find('[data-testid="title"]')
.find(GlFormTextarea)
.attributes('value'),
).toBe('');
expect(wrapper.find('[data-testid="description"] textarea').element.value).toBe('');
expect(wrapper.find(GlFormCheckbox).exists()).toBe(false); expect(wrapper.find(GlFormCheckbox).exists()).toBe(false);
}); });
}); });
}); });
describe('drawerOpen', () => { describe('drawerOpen', () => {
it('clears `title` value when `drawerOpen` prop is changed to false', async () => { it('sets `satisfied` value to false when `drawerOpen` prop is changed to false', async () => {
wrapper.setData({
title: 'Foo',
});
wrapper.setProps({ wrapper.setProps({
drawerOpen: false, drawerOpen: false,
}); });
await wrapper.vm.$nextTick(); await wrapper.vm.$nextTick();
expect(wrapper.vm.title).toBe('');
expect(wrapper.vm.description).toBe('');
expect(wrapper.vm.satisfied).toBe(false); expect(wrapper.vm.satisfied).toBe(false);
}); });
it('binds `keydown` event listener on document when `drawerOpen` prop is changed to true', async () => {
jest.spyOn(document, 'addEventListener');
wrapper.setProps({
drawerOpen: true,
});
await wrapper.vm.$nextTick();
expect(document.addEventListener).toHaveBeenCalledWith('keydown', expect.any(Function));
});
}); });
}); });
...@@ -246,27 +237,29 @@ describe('RequirementForm', () => { ...@@ -246,27 +237,29 @@ describe('RequirementForm', () => {
describe('handleSave', () => { describe('handleSave', () => {
it('emits `save` event on component with object as param containing `title` & `description` when form is in create mode', () => { it('emits `save` event on component with object as param containing `title` & `description` when form is in create mode', () => {
const title = 'foo'; const issuableTitle = 'foo';
const description = '_bar_'; const issuableDescription = '_bar_';
wrapper.setData({
title,
description,
});
wrapper.vm.handleSave(); wrapper.vm.handleSave({
issuableTitle,
issuableDescription,
});
expect(wrapper.emitted('save')).toBeTruthy(); expect(wrapper.emitted('save')).toBeTruthy();
expect(wrapper.emitted('save')[0]).toEqual([ expect(wrapper.emitted('save')[0]).toEqual([
{ {
title, title: issuableTitle,
description, description: issuableDescription,
}, },
]); ]);
}); });
it('emits `save` event on component with object as param containing `iid`, `title`, `description` & `lastTestReportState` when form is in update mode', () => { it('emits `save` event on component with object as param containing `iid`, `title`, `description` & `lastTestReportState` when form is in update mode', () => {
const { iid, title, description } = mockRequirementsOpen[0]; const { iid, title, description } = mockRequirementsOpen[0];
wrapperWithRequirement.vm.handleSave(); wrapperWithRequirement.vm.handleSave({
issuableTitle: title,
issuableDescription: description,
});
expect(wrapperWithRequirement.emitted('save')).toBeTruthy(); expect(wrapperWithRequirement.emitted('save')).toBeTruthy();
expect(wrapperWithRequirement.emitted('save')[0]).toEqual([ expect(wrapperWithRequirement.emitted('save')[0]).toEqual([
...@@ -300,32 +293,6 @@ describe('RequirementForm', () => { ...@@ -300,32 +293,6 @@ describe('RequirementForm', () => {
expect(wrapper.find(GlDrawer).exists()).toBe(true); expect(wrapper.find(GlDrawer).exists()).toBe(true);
}); });
describe('create requirement', () => {
it('renders drawer header with string "New Requirement"', () => {
expect(getByText(wrapper.element, 'New Requirement')).not.toBeNull();
});
it('renders title and description input fields', () => {
expect(wrapper.find('[data-testid="title"]').exists()).toBe(true);
expect(wrapper.find('[data-testid="description"]').exists()).toBe(true);
});
it('renders save button component', () => {
const saveButton = wrapper.find('.js-requirement-save');
expect(saveButton.exists()).toBe(true);
expect(saveButton.text()).toBe('Create requirement');
});
it('renders cancel button component', () => {
const cancelButton = wrapper.find('.js-requirement-cancel');
expect(cancelButton.exists()).toBe(true);
expect(cancelButton.text()).toBe('Cancel');
});
});
describe('view requirement', () => {
it('renders drawer header with `requirement.reference` and test report badge', () => { it('renders drawer header with `requirement.reference` and test report badge', () => {
expect( expect(
getByText(wrapperWithRequirement.element, `REQ-${mockRequirementsOpen[0].iid}`), getByText(wrapperWithRequirement.element, `REQ-${mockRequirementsOpen[0].iid}`),
...@@ -336,92 +303,33 @@ describe('RequirementForm', () => { ...@@ -336,92 +303,33 @@ describe('RequirementForm', () => {
); );
}); });
it('renders requirement title', () => { it('renders issuable-body component', () => {
expect( const issuableBody = wrapperWithRequirement.find(IssuableBody);
getByText(wrapperWithRequirement.element, mockRequirementsOpen[0].titleHtml),
).not.toBeNull();
});
it('renders edit button', () => {
const editButtonEl = wrapperWithRequirement.find('[data-testid="edit"]');
expect(editButtonEl.exists()).toBe(true); expect(issuableBody.exists()).toBe(true);
expect(editButtonEl.props('icon')).toBe('pencil'); expect(issuableBody.props()).toMatchObject({
expect(editButtonEl.attributes('title')).toBe('Edit title and description'); enableEdit: wrapper.vm.canEditRequirement,
enableAutocomplete: false,
enableAutosave: false,
editFormVisible: false,
showFieldTitle: true,
descriptionPreviewPath: '/gitlab-org/gitlab-test/preview_markdown',
descriptionHelpPath: '/help/user/markdown',
}); });
it('renders requirement description', () => {
const descriptionEl = wrapperWithRequirement.find('[data-testid="descriptionContainer"]');
expect(descriptionEl.exists()).toBe(true);
expect(descriptionEl.text()).toBe('fortitudinis fomentis dolor mitigari solet.');
}); });
describe('edit', () => { it('renders edit-form-actions slot contents within issuable-body', async () => {
beforeEach(async () => {
wrapperWithRequirement.setProps({ wrapperWithRequirement.setProps({
enableRequirementEdit: true, enableRequirementEdit: true,
}); });
await wrapperWithRequirement.vm.$nextTick(); await wrapperWithRequirement.vm.$nextTick();
});
it('renders flash error container', () => {
expect(wrapperWithRequirement.find('[data-testid="form-error-container"]').exists()).toBe(
true,
);
});
it('renders title input field', () => {
const titleInputEl = wrapperWithRequirement.find('[data-testid="title"]');
const titleTextarea = titleInputEl.find(GlFormTextarea);
expect(titleInputEl.exists()).toBe(true);
expect(titleInputEl.attributes()).toMatchObject({
label: 'Title',
state: 'true',
'label-for': 'requirementTitle',
'invalid-feedback': `Requirement title cannot have more than ${MAX_TITLE_LENGTH} characters.`,
});
expect(titleTextarea.exists()).toBe(true);
expect(titleTextarea.attributes()).toMatchObject({
id: 'requirementTitle',
placeholder: 'Requirement title',
value: mockRequirementsOpen[0].title,
'max-rows': '25',
});
});
it('renders description input field', () => { const issuableBody = wrapperWithRequirement.find(IssuableBody);
const descriptionInputEl = wrapperWithRequirement.find('[data-testid="description"]');
const markdownEl = descriptionInputEl.find(MarkdownField);
const descriptionTextarea = markdownEl.find('textarea');
expect(descriptionInputEl.exists()).toBe(true); expect(issuableBody.find(GlFormCheckbox).exists()).toBe(true);
expect(descriptionInputEl.find('label').text()).toBe('Description'); expect(issuableBody.find('[data-testid="requirement-save"]').exists()).toBe(true);
expect(issuableBody.find('[data-testid="requirement-cancel"]').exists()).toBe(true);
expect(markdownEl.exists()).toBe(true);
expect(markdownEl.props()).toMatchObject({
markdownPreviewPath: '/gitlab-org/gitlab-test/preview_markdown',
markdownDocsPath: '/help/user/markdown',
enableAutocomplete: false,
textareaValue: mockRequirementsOpen[0].description,
});
expect(descriptionTextarea.exists()).toBe(true);
expect(descriptionTextarea.attributes()).toMatchObject({
id: 'requirementDescription',
placeholder: 'Describe the requirement here',
'aria-label': 'Description',
});
});
it('renders satisfied checkbox field', () => {
expect(wrapperWithRequirement.find(GlFormCheckbox).exists()).toBe(true);
expect(wrapperWithRequirement.find(GlFormCheckbox).text()).toBe('Satisfied');
});
});
}); });
}); });
}); });
...@@ -9415,9 +9415,6 @@ msgstr "" ...@@ -9415,9 +9415,6 @@ msgstr ""
msgid "Describe the goal of the changes and what reviewers should be aware of." msgid "Describe the goal of the changes and what reviewers should be aware of."
msgstr "" msgstr ""
msgid "Describe the requirement here"
msgstr ""
msgid "Description" msgid "Description"
msgstr "" msgstr ""
...@@ -23561,9 +23558,6 @@ msgstr "" ...@@ -23561,9 +23558,6 @@ msgstr ""
msgid "Requirement %{reference} has been updated" msgid "Requirement %{reference} has been updated"
msgstr "" msgstr ""
msgid "Requirement title"
msgstr ""
msgid "Requirement title cannot have more than %{limit} characters." msgid "Requirement title cannot have more than %{limit} characters."
msgstr "" msgstr ""
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment