diff options
author | GitLab Bot <gitlab-bot@gitlab.com> | 2023-06-26 15:10:05 +0300 |
---|---|---|
committer | GitLab Bot <gitlab-bot@gitlab.com> | 2023-06-26 15:10:05 +0300 |
commit | a46fed716c91d449fde2f08d25e0f4530170bc15 (patch) | |
tree | 058240d0b79144659185be61d6d04a8edf8d8cd8 | |
parent | abc0c2c7700bc4dfb6adc23a9594b2b7be6bd051 (diff) |
Add latest changes from gitlab-org/gitlab@master
9 files changed, 352 insertions, 218 deletions
diff --git a/app/assets/javascripts/pages/profiles/two_factor_auths/index.js b/app/assets/javascripts/pages/profiles/two_factor_auths/index.js index ea6bca644ed..8fe822e4639 100644 --- a/app/assets/javascripts/pages/profiles/two_factor_auths/index.js +++ b/app/assets/javascripts/pages/profiles/two_factor_auths/index.js @@ -7,7 +7,9 @@ const twoFactorNode = document.querySelector('.js-two-factor-auth'); const skippable = twoFactorNode ? parseBoolean(twoFactorNode.dataset.twoFactorSkippable) : false; if (skippable) { - const button = `<br/><a class="btn gl-button btn-sm btn-confirm gl-mt-3" data-qa-selector="configure_it_later_button" data-method="patch" href="${twoFactorNode.dataset.two_factor_skip_url}">Configure it later</a>`; + const button = `<div class="gl-alert-actions"> + <a class="btn gl-button btn-md btn-confirm gl-alert-action" data-qa-selector="configure_it_later_button" data-method="patch" href="${twoFactorNode.dataset.two_factor_skip_url}">Configure it later</a> + </div>`; const flashAlert = document.querySelector('.flash-alert'); if (flashAlert) { // eslint-disable-next-line no-unsanitized/method @@ -17,7 +19,5 @@ if (skippable) { mount2faRegistration(); initWebAuthnRegistration(); - initRecoveryCodes(); - initManageTwoFactorForm(); diff --git a/app/assets/javascripts/work_items/components/work_item_attributes_wrapper.vue b/app/assets/javascripts/work_items/components/work_item_attributes_wrapper.vue new file mode 100644 index 00000000000..3e82d603c1d --- /dev/null +++ b/app/assets/javascripts/work_items/components/work_item_attributes_wrapper.vue @@ -0,0 +1,180 @@ +<script> +import glFeatureFlagMixin from '~/vue_shared/mixins/gl_feature_flags_mixin'; +import { + sprintfWorkItem, + WIDGET_TYPE_ASSIGNEES, + WIDGET_TYPE_HEALTH_STATUS, + WIDGET_TYPE_ITERATION, + WIDGET_TYPE_LABELS, + WIDGET_TYPE_MILESTONE, + WIDGET_TYPE_PROGRESS, + WIDGET_TYPE_START_AND_DUE_DATE, + WIDGET_TYPE_WEIGHT, +} from '../constants'; +import WorkItemState from './work_item_state.vue'; +import WorkItemDueDate from './work_item_due_date.vue'; +import WorkItemAssignees from './work_item_assignees.vue'; +import WorkItemLabels from './work_item_labels.vue'; +import WorkItemMilestone from './work_item_milestone.vue'; + +export default { + components: { + WorkItemLabels, + WorkItemMilestone, + WorkItemAssignees, + WorkItemDueDate, + WorkItemState, + WorkItemWeight: () => import('ee_component/work_items/components/work_item_weight.vue'), + WorkItemProgress: () => import('ee_component/work_items/components/work_item_progress.vue'), + WorkItemIteration: () => import('ee_component/work_items/components/work_item_iteration.vue'), + WorkItemHealthStatus: () => + import('ee_component/work_items/components/work_item_health_status.vue'), + }, + mixins: [glFeatureFlagMixin()], + inject: ['fullPath'], + props: { + workItem: { + type: Object, + required: true, + }, + workItemParentId: { + type: String, + required: false, + default: null, + }, + }, + computed: { + workItemType() { + return this.workItem.workItemType?.name; + }, + canUpdate() { + return this.workItem?.userPermissions?.updateWorkItem; + }, + canDelete() { + return this.workItem?.userPermissions?.deleteWorkItem; + }, + canSetWorkItemMetadata() { + return this.workItem?.userPermissions?.setWorkItemMetadata; + }, + canAssignUnassignUser() { + return this.workItemAssignees && this.canSetWorkItemMetadata; + }, + confidentialTooltip() { + return sprintfWorkItem(this.$options.i18n.confidentialTooltip, this.workItemType); + }, + workItemAssignees() { + return this.isWidgetPresent(WIDGET_TYPE_ASSIGNEES); + }, + workItemLabels() { + return this.isWidgetPresent(WIDGET_TYPE_LABELS); + }, + workItemDueDate() { + return this.isWidgetPresent(WIDGET_TYPE_START_AND_DUE_DATE); + }, + workItemWeight() { + return this.isWidgetPresent(WIDGET_TYPE_WEIGHT); + }, + workItemProgress() { + return this.isWidgetPresent(WIDGET_TYPE_PROGRESS); + }, + workItemIteration() { + return this.isWidgetPresent(WIDGET_TYPE_ITERATION); + }, + workItemHealthStatus() { + return this.isWidgetPresent(WIDGET_TYPE_HEALTH_STATUS); + }, + workItemMilestone() { + return this.isWidgetPresent(WIDGET_TYPE_MILESTONE); + }, + }, + methods: { + isWidgetPresent(type) { + return this.workItem?.widgets?.find((widget) => widget.type === type); + }, + }, +}; +</script> + +<template> + <section> + <work-item-state + :work-item="workItem" + :work-item-parent-id="workItemParentId" + :can-update="canUpdate" + @error="$emit('error', $event)" + /> + <work-item-assignees + v-if="workItemAssignees" + :can-update="canUpdate" + :work-item-id="workItem.id" + :assignees="workItemAssignees.assignees.nodes" + :allows-multiple-assignees="workItemAssignees.allowsMultipleAssignees" + :work-item-type="workItemType" + :can-invite-members="workItemAssignees.canInviteMembers" + @error="$emit('error', $event)" + /> + <work-item-labels + v-if="workItemLabels" + :can-update="canUpdate" + :work-item-id="workItem.id" + :work-item-iid="workItem.iid" + @error="$emit('error', $event)" + /> + <work-item-due-date + v-if="workItemDueDate" + :can-update="canUpdate" + :due-date="workItemDueDate.dueDate" + :start-date="workItemDueDate.startDate" + :work-item-id="workItem.id" + :work-item-type="workItemType" + @error="$emit('error', $event)" + /> + <work-item-milestone + v-if="workItemMilestone" + :work-item-id="workItem.id" + :work-item-milestone="workItemMilestone.milestone" + :work-item-type="workItemType" + :can-update="canUpdate" + @error="$emit('error', $event)" + /> + <work-item-weight + v-if="workItemWeight" + class="gl-mb-5" + :can-update="canUpdate" + :weight="workItemWeight.weight" + :work-item-id="workItem.id" + :work-item-iid="workItem.iid" + :work-item-type="workItemType" + @error="$emit('error', $event)" + /> + <work-item-progress + v-if="workItemProgress" + class="gl-mb-5" + :can-update="canUpdate" + :progress="workItemProgress.progress" + :work-item-id="workItem.id" + :work-item-type="workItemType" + @error="$emit('error', $event)" + /> + <work-item-iteration + v-if="workItemIteration" + class="gl-mb-5" + :iteration="workItemIteration.iteration" + :can-update="canUpdate" + :work-item-id="workItem.id" + :work-item-iid="workItem.iid" + :work-item-type="workItemType" + @error="$emit('error', $event)" + /> + <work-item-health-status + v-if="workItemHealthStatus" + class="gl-mb-5" + :health-status="workItemHealthStatus.healthStatus" + :can-update="canUpdate" + :work-item-id="workItem.id" + :work-item-iid="workItem.iid" + :work-item-type="workItemType" + @error="$emit('error', $event)" + /> + </section> +</template> diff --git a/app/assets/javascripts/work_items/components/work_item_detail.vue b/app/assets/javascripts/work_items/components/work_item_detail.vue index 5539226e84e..3acdedf77aa 100644 --- a/app/assets/javascripts/work_items/components/work_item_detail.vue +++ b/app/assets/javascripts/work_items/components/work_item_detail.vue @@ -22,18 +22,11 @@ import { sprintfWorkItem, i18n, WIDGET_TYPE_ASSIGNEES, - WIDGET_TYPE_LABELS, WIDGET_TYPE_NOTIFICATIONS, WIDGET_TYPE_CURRENT_USER_TODOS, WIDGET_TYPE_DESCRIPTION, WIDGET_TYPE_AWARD_EMOJI, - WIDGET_TYPE_START_AND_DUE_DATE, - WIDGET_TYPE_WEIGHT, - WIDGET_TYPE_PROGRESS, WIDGET_TYPE_HIERARCHY, - WIDGET_TYPE_MILESTONE, - WIDGET_TYPE_ITERATION, - WIDGET_TYPE_HEALTH_STATUS, WORK_ITEM_TYPE_VALUE_ISSUE, WORK_ITEM_TYPE_VALUE_OBJECTIVE, WIDGET_TYPE_NOTES, @@ -48,17 +41,13 @@ import { findHierarchyWidgetChildren } from '../utils'; import WorkItemTree from './work_item_links/work_item_tree.vue'; import WorkItemActions from './work_item_actions.vue'; import WorkItemTodos from './work_item_todos.vue'; -import WorkItemState from './work_item_state.vue'; import WorkItemTitle from './work_item_title.vue'; +import WorkItemAttributesWrapper from './work_item_attributes_wrapper.vue'; import WorkItemCreatedUpdated from './work_item_created_updated.vue'; import WorkItemDescription from './work_item_description.vue'; -import WorkItemAwardEmoji from './work_item_award_emoji.vue'; -import WorkItemDueDate from './work_item_due_date.vue'; -import WorkItemAssignees from './work_item_assignees.vue'; -import WorkItemLabels from './work_item_labels.vue'; -import WorkItemMilestone from './work_item_milestone.vue'; import WorkItemNotes from './work_item_notes.vue'; import WorkItemDetailModal from './work_item_detail_modal.vue'; +import WorkItemAwardEmoji from './work_item_award_emoji.vue'; export default { i18n, @@ -74,23 +63,14 @@ export default { GlSkeletonLoader, GlIcon, GlEmptyState, - WorkItemAssignees, WorkItemActions, WorkItemTodos, WorkItemCreatedUpdated, WorkItemDescription, WorkItemAwardEmoji, - WorkItemDueDate, - WorkItemLabels, WorkItemTitle, - WorkItemState, - WorkItemWeight: () => import('ee_component/work_items/components/work_item_weight.vue'), - WorkItemProgress: () => import('ee_component/work_items/components/work_item_progress.vue'), + WorkItemAttributesWrapper, WorkItemTypeIcon, - WorkItemIteration: () => import('ee_component/work_items/components/work_item_iteration.vue'), - WorkItemHealthStatus: () => - import('ee_component/work_items/components/work_item_health_status.vue'), - WorkItemMilestone, WorkItemTree, WorkItemNotes, WorkItemDetailModal, @@ -256,33 +236,12 @@ export default { workItemAssignees() { return this.isWidgetPresent(WIDGET_TYPE_ASSIGNEES); }, - workItemLabels() { - return this.isWidgetPresent(WIDGET_TYPE_LABELS); - }, - workItemDueDate() { - return this.isWidgetPresent(WIDGET_TYPE_START_AND_DUE_DATE); - }, - workItemWeight() { - return this.isWidgetPresent(WIDGET_TYPE_WEIGHT); - }, - workItemProgress() { - return this.isWidgetPresent(WIDGET_TYPE_PROGRESS); - }, workItemAwardEmoji() { return this.isWidgetPresent(WIDGET_TYPE_AWARD_EMOJI); }, workItemHierarchy() { return this.isWidgetPresent(WIDGET_TYPE_HIERARCHY); }, - workItemIteration() { - return this.isWidgetPresent(WIDGET_TYPE_ITERATION); - }, - workItemHealthStatus() { - return this.isWidgetPresent(WIDGET_TYPE_HEALTH_STATUS); - }, - workItemMilestone() { - return this.isWidgetPresent(WIDGET_TYPE_MILESTONE); - }, workItemNotes() { return this.isWidgetPresent(WIDGET_TYPE_NOTES); }, @@ -511,83 +470,9 @@ export default { @error="updateError = $event" /> <work-item-created-updated :work-item-iid="workItemIid" /> - <work-item-state + <work-item-attributes-wrapper :work-item="workItem" :work-item-parent-id="workItemParentId" - :can-update="canUpdate" - @error="updateError = $event" - /> - <work-item-assignees - v-if="workItemAssignees" - :can-update="canUpdate" - :work-item-id="workItem.id" - :assignees="workItemAssignees.assignees.nodes" - :allows-multiple-assignees="workItemAssignees.allowsMultipleAssignees" - :work-item-type="workItemType" - :can-invite-members="workItemAssignees.canInviteMembers" - @error="updateError = $event" - /> - <work-item-labels - v-if="workItemLabels" - :can-update="canUpdate" - :work-item-id="workItem.id" - :work-item-iid="workItem.iid" - @error="updateError = $event" - /> - <work-item-due-date - v-if="workItemDueDate" - :can-update="canUpdate" - :due-date="workItemDueDate.dueDate" - :start-date="workItemDueDate.startDate" - :work-item-id="workItem.id" - :work-item-type="workItemType" - @error="updateError = $event" - /> - <work-item-milestone - v-if="workItemMilestone" - :work-item-id="workItem.id" - :work-item-milestone="workItemMilestone.milestone" - :work-item-type="workItemType" - :can-update="canUpdate" - @error="updateError = $event" - /> - <work-item-weight - v-if="workItemWeight" - class="gl-mb-5" - :can-update="canUpdate" - :weight="workItemWeight.weight" - :work-item-id="workItem.id" - :work-item-iid="workItem.iid" - :work-item-type="workItemType" - @error="updateError = $event" - /> - <work-item-progress - v-if="workItemProgress" - class="gl-mb-5" - :can-update="canUpdate" - :progress="workItemProgress.progress" - :work-item-id="workItem.id" - :work-item-type="workItemType" - @error="updateError = $event" - /> - <work-item-iteration - v-if="workItemIteration" - class="gl-mb-5" - :iteration="workItemIteration.iteration" - :can-update="canUpdate" - :work-item-id="workItem.id" - :work-item-iid="workItem.iid" - :work-item-type="workItemType" - @error="updateError = $event" - /> - <work-item-health-status - v-if="workItemHealthStatus" - class="gl-mb-5" - :health-status="workItemHealthStatus.healthStatus" - :can-update="canUpdate" - :work-item-id="workItem.id" - :work-item-iid="workItem.iid" - :work-item-type="workItemType" @error="updateError = $event" /> <work-item-description diff --git a/app/views/dashboard/todos/_todo.html.haml b/app/views/dashboard/todos/_todo.html.haml index ebd7f20c54a..e20fccc218a 100644 --- a/app/views/dashboard/todos/_todo.html.haml +++ b/app/views/dashboard/todos/_todo.html.haml @@ -20,7 +20,7 @@ - else = _("(removed)") - .todo-body.gl-mb-2.gl-px-2.gl-display-flex.gl-align-items-flex-start.gl-lg-align-items-center + .todo-body.gl-mb-2.gl-px-2.gl-display-flex.gl-align-items-flex-start .todo-avatar.gl-display-none.gl-sm-display-inline-block = author_avatar(todo, size: 24) .todo-note @@ -47,6 +47,8 @@ %span.action-description< = first_line_in_markdown(todo, :body, 125, is_todo: true, project: todo.project, group: todo.group) + = render_if_exists "dashboard/todos/diff_summary", local_assigns: { todo: todo } + .todo-timestamp.gl-white-space-nowrap.gl-sm-ml-3.gl-mt-2.gl-mb-2.gl-sm-my-0.gl-px-2.gl-sm-px-0 %span.todo-timestamp.gl-font-sm.gl-text-secondary = todo_due_date(todo) diff --git a/app/views/projects/pages_domains/_dns.html.haml b/app/views/projects/pages_domains/_dns.html.haml index 3e6a92d8bc0..eff8f9298fd 100644 --- a/app/views/projects/pages_domains/_dns.html.haml +++ b/app/views/projects/pages_domains/_dns.html.haml @@ -28,4 +28,4 @@ = clipboard_button(target: '#domain_verification', class: 'btn-default d-none d-sm-block') %p.form-text.text-muted - link_to_help = link_to(_('verify ownership'), help_page_path('user/project/pages/custom_domains_ssl_tls_certification/index.md', anchor: '4-verify-the-domains-ownership')) - = _("To %{link_to_help} of your domain, add the above key to a TXT record within your DNS configuration.").html_safe % { link_to_help: link_to_help } + = _("To %{link_to_help} of your domain, add the above key to a TXT record within your DNS configuration within seven days.").html_safe % { link_to_help: link_to_help } diff --git a/db/docs/organizations.yml b/db/docs/organizations.yml index ebc7e8b68d1..c0e4697fb37 100644 --- a/db/docs/organizations.yml +++ b/db/docs/organizations.yml @@ -7,4 +7,4 @@ feature_categories: description: Define ownership of namespaces, projects, and users by organizations introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/119421 milestone: '16.0' -gitlab_schema: gitlab_main +gitlab_schema: gitlab_main_clusterwide diff --git a/locale/gitlab.pot b/locale/gitlab.pot index 6e84e9d2e86..a09096b6b92 100644 --- a/locale/gitlab.pot +++ b/locale/gitlab.pot @@ -5239,7 +5239,7 @@ msgstr "" msgid "Analytics" msgstr "" -msgid "Analytics|Add to Dashboard" +msgid "Analytics|A visualization with that name already exists." msgstr "" msgid "Analytics|Add visualizations" @@ -5311,9 +5311,15 @@ msgstr "" msgid "Analytics|Edit" msgstr "" +msgid "Analytics|Enter a visualization name" +msgstr "" + msgid "Analytics|Error while saving dashboard" msgstr "" +msgid "Analytics|Error while saving visualization." +msgstr "" + msgid "Analytics|Host" msgstr "" @@ -5323,7 +5329,7 @@ msgstr "" msgid "Analytics|Line Chart" msgstr "" -msgid "Analytics|New Analytics Visualization Title" +msgid "Analytics|New analytics visualization name" msgstr "" msgid "Analytics|New dashboard" @@ -5362,6 +5368,18 @@ msgstr "" msgid "Analytics|Save" msgstr "" +msgid "Analytics|Save and add to Dashboard" +msgstr "" + +msgid "Analytics|Save new visualization" +msgstr "" + +msgid "Analytics|Select a measurement" +msgstr "" + +msgid "Analytics|Select a visualization type" +msgstr "" + msgid "Analytics|Single Statistic" msgstr "" @@ -5374,6 +5392,9 @@ msgstr "" msgid "Analytics|Updating dashboard %{dashboardId}" msgstr "" +msgid "Analytics|Updating visualization %{visualizationName}" +msgstr "" + msgid "Analytics|Users" msgstr "" @@ -5392,6 +5413,9 @@ msgstr "" msgid "Analytics|Visualization Type" msgstr "" +msgid "Analytics|Visualization was saved successfully" +msgstr "" + msgid "Analyze your dependencies for known vulnerabilities." msgstr "" @@ -16424,7 +16448,7 @@ msgstr "" msgid "DomainVerification|The following domains are configured for projects in this group. Users with email addresses that match a verified domain do not need to confirm their account." msgstr "" -msgid "DomainVerification|To verify ownership of your domain, add the above key to a TXT record within your DNS configuration. %{link_to_help}" +msgid "DomainVerification|To verify ownership of your domain, add the above key to a TXT record within your DNS configuration within seven days. %{link_to_help}" msgstr "" msgid "Don't have a group?" @@ -44775,6 +44799,9 @@ msgstr "" msgid "Summary comment (optional)" msgstr "" +msgid "Summary generated by AI" +msgstr "" + msgid "Sun" msgstr "" @@ -47737,7 +47764,7 @@ msgstr "" msgid "To" msgstr "" -msgid "To %{link_to_help} of your domain, add the above key to a TXT record within your DNS configuration." +msgid "To %{link_to_help} of your domain, add the above key to a TXT record within your DNS configuration within seven days." msgstr "" msgid "To Do" diff --git a/spec/frontend/work_items/components/work_item_attributes_wrapper_spec.js b/spec/frontend/work_items/components/work_item_attributes_wrapper_spec.js new file mode 100644 index 00000000000..ba9af7b2b68 --- /dev/null +++ b/spec/frontend/work_items/components/work_item_attributes_wrapper_spec.js @@ -0,0 +1,107 @@ +import { shallowMount } from '@vue/test-utils'; +import WorkItemAssignees from '~/work_items/components/work_item_assignees.vue'; +import WorkItemDueDate from '~/work_items/components/work_item_due_date.vue'; +import WorkItemState from '~/work_items/components/work_item_state.vue'; +import WorkItemLabels from '~/work_items/components/work_item_labels.vue'; +import WorkItemMilestone from '~/work_items/components/work_item_milestone.vue'; + +import WorkItemAttributesWrapper from '~/work_items/components/work_item_attributes_wrapper.vue'; +import { workItemResponseFactory } from '../mock_data'; + +describe('WorkItemAttributesWrapper component', () => { + let wrapper; + + const workItemQueryResponse = workItemResponseFactory({ canUpdate: true, canDelete: true }); + + const findWorkItemState = () => wrapper.findComponent(WorkItemState); + const findWorkItemDueDate = () => wrapper.findComponent(WorkItemDueDate); + const findWorkItemAssignees = () => wrapper.findComponent(WorkItemAssignees); + const findWorkItemLabels = () => wrapper.findComponent(WorkItemLabels); + const findWorkItemMilestone = () => wrapper.findComponent(WorkItemMilestone); + + const createComponent = ({ workItem = workItemQueryResponse.data.workItem } = {}) => { + wrapper = shallowMount(WorkItemAttributesWrapper, { + propsData: { + workItem, + }, + provide: { + hasIssueWeightsFeature: true, + hasIterationsFeature: true, + hasOkrsFeature: true, + hasIssuableHealthStatusFeature: true, + projectNamespace: 'namespace', + fullPath: 'group/project', + }, + stubs: { + WorkItemWeight: true, + WorkItemIteration: true, + WorkItemHealthStatus: true, + }, + }); + }; + + describe('work item state', () => { + it('renders the work item state', () => { + createComponent(); + + expect(findWorkItemState().exists()).toBe(true); + }); + }); + + describe('assignees widget', () => { + it('renders assignees component when widget is returned from the API', () => { + createComponent(); + + expect(findWorkItemAssignees().exists()).toBe(true); + }); + + it('does not render assignees component when widget is not returned from the API', () => { + createComponent({ + workItem: workItemResponseFactory({ assigneesWidgetPresent: false }).data.workItem, + }); + + expect(findWorkItemAssignees().exists()).toBe(false); + }); + }); + + describe('labels widget', () => { + it.each` + description | labelsWidgetPresent | exists + ${'renders when widget is returned from API'} | ${true} | ${true} + ${'does not render when widget is not returned from API'} | ${false} | ${false} + `('$description', ({ labelsWidgetPresent, exists }) => { + const response = workItemResponseFactory({ labelsWidgetPresent }); + createComponent({ workItem: response.data.workItem }); + + expect(findWorkItemLabels().exists()).toBe(exists); + }); + }); + + describe('dates widget', () => { + describe.each` + description | datesWidgetPresent | exists + ${'when widget is returned from API'} | ${true} | ${true} + ${'when widget is not returned from API'} | ${false} | ${false} + `('$description', ({ datesWidgetPresent, exists }) => { + it(`${datesWidgetPresent ? 'renders' : 'does not render'} due date component`, () => { + const response = workItemResponseFactory({ datesWidgetPresent }); + createComponent({ workItem: response.data.workItem }); + + expect(findWorkItemDueDate().exists()).toBe(exists); + }); + }); + }); + + describe('milestone widget', () => { + it.each` + description | milestoneWidgetPresent | exists + ${'renders when widget is returned from API'} | ${true} | ${true} + ${'does not render when widget is not returned from API'} | ${false} | ${false} + `('$description', ({ milestoneWidgetPresent, exists }) => { + const response = workItemResponseFactory({ milestoneWidgetPresent }); + createComponent({ workItem: response.data.workItem }); + + expect(findWorkItemMilestone().exists()).toBe(exists); + }); + }); +}); diff --git a/spec/frontend/work_items/components/work_item_detail_spec.js b/spec/frontend/work_items/components/work_item_detail_spec.js index 0266533a11c..14a6ada16bd 100644 --- a/spec/frontend/work_items/components/work_item_detail_spec.js +++ b/spec/frontend/work_items/components/work_item_detail_spec.js @@ -18,12 +18,8 @@ import WorkItemDetail from '~/work_items/components/work_item_detail.vue'; import WorkItemActions from '~/work_items/components/work_item_actions.vue'; import WorkItemDescription from '~/work_items/components/work_item_description.vue'; import WorkItemCreatedUpdated from '~/work_items/components/work_item_created_updated.vue'; -import WorkItemDueDate from '~/work_items/components/work_item_due_date.vue'; -import WorkItemState from '~/work_items/components/work_item_state.vue'; +import WorkItemAttributesWrapper from '~/work_items/components/work_item_attributes_wrapper.vue'; import WorkItemTitle from '~/work_items/components/work_item_title.vue'; -import WorkItemAssignees from '~/work_items/components/work_item_assignees.vue'; -import WorkItemLabels from '~/work_items/components/work_item_labels.vue'; -import WorkItemMilestone from '~/work_items/components/work_item_milestone.vue'; import WorkItemTree from '~/work_items/components/work_item_links/work_item_tree.vue'; import WorkItemNotes from '~/work_items/components/work_item_notes.vue'; import WorkItemDetailModal from '~/work_items/components/work_item_detail_modal.vue'; @@ -69,12 +65,8 @@ describe('WorkItemDetail component', () => { const findWorkItemActions = () => wrapper.findComponent(WorkItemActions); const findWorkItemTitle = () => wrapper.findComponent(WorkItemTitle); const findCreatedUpdated = () => wrapper.findComponent(WorkItemCreatedUpdated); - const findWorkItemState = () => wrapper.findComponent(WorkItemState); const findWorkItemDescription = () => wrapper.findComponent(WorkItemDescription); - const findWorkItemDueDate = () => wrapper.findComponent(WorkItemDueDate); - const findWorkItemAssignees = () => wrapper.findComponent(WorkItemAssignees); - const findWorkItemLabels = () => wrapper.findComponent(WorkItemLabels); - const findWorkItemMilestone = () => wrapper.findComponent(WorkItemMilestone); + const findWorkItemAttributesWrapper = () => wrapper.findComponent(WorkItemAttributesWrapper); const findParent = () => wrapper.find('[data-testid="work-item-parent"]'); const findParentButton = () => findParent().findComponent(GlButton); const findCloseButton = () => wrapper.find('[data-testid="work-item-close"]'); @@ -168,7 +160,6 @@ describe('WorkItemDetail component', () => { it('renders skeleton loader', () => { expect(findSkeleton().exists()).toBe(true); - expect(findWorkItemState().exists()).toBe(false); expect(findWorkItemTitle().exists()).toBe(false); }); }); @@ -181,7 +172,6 @@ describe('WorkItemDetail component', () => { it('does not render skeleton', () => { expect(findSkeleton().exists()).toBe(false); - expect(findWorkItemState().exists()).toBe(true); expect(findWorkItemTitle().exists()).toBe(true); }); @@ -480,83 +470,6 @@ describe('WorkItemDetail component', () => { expect(findAlert().text()).toBe(updateError); }); - describe('assignees widget', () => { - it('renders assignees component when widget is returned from the API', async () => { - createComponent(); - await waitForPromises(); - - expect(findWorkItemAssignees().exists()).toBe(true); - }); - - it('does not render assignees component when widget is not returned from the API', async () => { - createComponent({ - handler: jest - .fn() - .mockResolvedValue(workItemByIidResponseFactory({ assigneesWidgetPresent: false })), - }); - await waitForPromises(); - - expect(findWorkItemAssignees().exists()).toBe(false); - }); - }); - - describe('labels widget', () => { - it.each` - description | labelsWidgetPresent | exists - ${'renders when widget is returned from API'} | ${true} | ${true} - ${'does not render when widget is not returned from API'} | ${false} | ${false} - `('$description', async ({ labelsWidgetPresent, exists }) => { - const response = workItemByIidResponseFactory({ labelsWidgetPresent }); - const handler = jest.fn().mockResolvedValue(response); - createComponent({ handler }); - await waitForPromises(); - - expect(findWorkItemLabels().exists()).toBe(exists); - }); - }); - - describe('dates widget', () => { - describe.each` - description | datesWidgetPresent | exists - ${'when widget is returned from API'} | ${true} | ${true} - ${'when widget is not returned from API'} | ${false} | ${false} - `('$description', ({ datesWidgetPresent, exists }) => { - it(`${datesWidgetPresent ? 'renders' : 'does not render'} due date component`, async () => { - const response = workItemByIidResponseFactory({ datesWidgetPresent }); - const handler = jest.fn().mockResolvedValue(response); - createComponent({ handler }); - await waitForPromises(); - - expect(findWorkItemDueDate().exists()).toBe(exists); - }); - }); - - it('shows an error message when it emits an `error` event', async () => { - createComponent(); - await waitForPromises(); - const updateError = 'Failed to update'; - - findWorkItemDueDate().vm.$emit('error', updateError); - await waitForPromises(); - - expect(findAlert().text()).toBe(updateError); - }); - }); - - describe('milestone widget', () => { - it.each` - description | milestoneWidgetPresent | exists - ${'renders when widget is returned from API'} | ${true} | ${true} - ${'does not render when widget is not returned from API'} | ${false} | ${false} - `('$description', async ({ milestoneWidgetPresent, exists }) => { - const response = workItemByIidResponseFactory({ milestoneWidgetPresent }); - const handler = jest.fn().mockResolvedValue(response); - createComponent({ handler }); - await waitForPromises(); - - expect(findWorkItemMilestone().exists()).toBe(exists); - }); - }); it('calls the work item query', async () => { createComponent(); @@ -713,4 +626,24 @@ describe('WorkItemDetail component', () => { expect(findWorkItemTodos().exists()).toBe(false); }); }); + + describe('work item attributes wrapper', () => { + beforeEach(async () => { + createComponent(); + await waitForPromises(); + }); + + it('renders the work item attributes wrapper', () => { + expect(findWorkItemAttributesWrapper().exists()).toBe(true); + }); + + it('shows an error message when it emits an `error` event', async () => { + const updateError = 'Failed to update'; + + findWorkItemAttributesWrapper().vm.$emit('error', updateError); + await waitForPromises(); + + expect(findAlert().text()).toBe(updateError); + }); + }); }); |