Welcome to mirror list, hosted at ThFree Co, Russian Federation.

gitlab.com/gitlab-org/gitlab-foss.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGitLab Bot <gitlab-bot@gitlab.com>2023-06-26 15:10:05 +0300
committerGitLab Bot <gitlab-bot@gitlab.com>2023-06-26 15:10:05 +0300
commita46fed716c91d449fde2f08d25e0f4530170bc15 (patch)
tree058240d0b79144659185be61d6d04a8edf8d8cd8
parentabc0c2c7700bc4dfb6adc23a9594b2b7be6bd051 (diff)
Add latest changes from gitlab-org/gitlab@master
-rw-r--r--app/assets/javascripts/pages/profiles/two_factor_auths/index.js6
-rw-r--r--app/assets/javascripts/work_items/components/work_item_attributes_wrapper.vue180
-rw-r--r--app/assets/javascripts/work_items/components/work_item_detail.vue123
-rw-r--r--app/views/dashboard/todos/_todo.html.haml4
-rw-r--r--app/views/projects/pages_domains/_dns.html.haml2
-rw-r--r--db/docs/organizations.yml2
-rw-r--r--locale/gitlab.pot35
-rw-r--r--spec/frontend/work_items/components/work_item_attributes_wrapper_spec.js107
-rw-r--r--spec/frontend/work_items/components/work_item_detail_spec.js111
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);
+ });
+ });
});