summaryrefslogtreecommitdiff
path: root/spec
diff options
context:
space:
mode:
authorPhil Hughes <me@iamphill.com>2018-12-08 09:58:45 +0000
committerYorick Peterse <yorickpeterse@gmail.com>2018-12-17 16:11:36 +0100
commitb87c364b0d3f96157d257dcb10e67c1c3b9f8ef8 (patch)
tree5591b63b74f3d643a5dbc9341d96ac4a0b7e2171 /spec
parentf4c1799be361da34f9c50d1e437bbb6e64bdb345 (diff)
downloadgitlab-ce-b87c364b0d3f96157d257dcb10e67c1c3b9f8ef8.tar.gz
Merge branch 'kp-issue_6086' into 'master'
CE Backport: Epic issue list and related issue list re-design See merge request gitlab-org/gitlab-ce!23658
Diffstat (limited to 'spec')
-rw-r--r--spec/javascripts/boards/mock_data.js69
-rw-r--r--spec/javascripts/vue_shared/components/issue/issue_assignees_spec.js114
-rw-r--r--spec/javascripts/vue_shared/components/issue/issue_milestone_spec.js234
3 files changed, 417 insertions, 0 deletions
diff --git a/spec/javascripts/boards/mock_data.js b/spec/javascripts/boards/mock_data.js
index c28e41ec175..14fff9223f4 100644
--- a/spec/javascripts/boards/mock_data.js
+++ b/spec/javascripts/boards/mock_data.js
@@ -1,5 +1,11 @@
import BoardService from '~/boards/services/board_service';
+export const boardObj = {
+ id: 1,
+ name: 'test',
+ milestone_id: null,
+};
+
export const listObj = {
id: 300,
position: 0,
@@ -40,6 +46,12 @@ export const BoardsMockData = {
},
],
},
+ '/test/issue-boards/milestones.json': [
+ {
+ id: 1,
+ title: 'test',
+ },
+ ],
},
POST: {
'/test/-/boards/1/lists': listObj,
@@ -70,3 +82,60 @@ export const mockBoardService = (opts = {}) => {
boardId,
});
};
+
+export const mockAssigneesList = [
+ {
+ id: 2,
+ name: 'Terrell Graham',
+ username: 'monserrate.gleichner',
+ state: 'active',
+ avatar_url: 'https://www.gravatar.com/avatar/598fd02741ac58b88854a99d16704309?s=80&d=identicon',
+ web_url: 'http://127.0.0.1:3001/monserrate.gleichner',
+ path: '/monserrate.gleichner',
+ },
+ {
+ id: 12,
+ name: 'Susy Johnson',
+ username: 'tana_harvey',
+ state: 'active',
+ avatar_url: 'https://www.gravatar.com/avatar/e021a7b0f3e4ae53b5068d487e68c031?s=80&d=identicon',
+ web_url: 'http://127.0.0.1:3001/tana_harvey',
+ path: '/tana_harvey',
+ },
+ {
+ id: 20,
+ name: 'Conchita Eichmann',
+ username: 'juliana_gulgowski',
+ state: 'active',
+ avatar_url: 'https://www.gravatar.com/avatar/c43c506cb6fd7b37017d3b54b94aa937?s=80&d=identicon',
+ web_url: 'http://127.0.0.1:3001/juliana_gulgowski',
+ path: '/juliana_gulgowski',
+ },
+ {
+ id: 6,
+ name: 'Bryce Turcotte',
+ username: 'melynda',
+ state: 'active',
+ avatar_url: 'https://www.gravatar.com/avatar/cc2518f2c6f19f8fac49e1a5ee092a9b?s=80&d=identicon',
+ web_url: 'http://127.0.0.1:3001/melynda',
+ path: '/melynda',
+ },
+ {
+ id: 1,
+ name: 'Administrator',
+ username: 'root',
+ state: 'active',
+ avatar_url: 'https://www.gravatar.com/avatar/e64c7d89f26bd1972efa854d13d7dd61?s=80&d=identicon',
+ web_url: 'http://127.0.0.1:3001/root',
+ path: '/root',
+ },
+];
+
+export const mockMilestone = {
+ id: 1,
+ state: 'active',
+ title: 'Milestone title',
+ description: 'Harum corporis aut consequatur quae dolorem error sequi quia.',
+ start_date: '2018-01-01',
+ due_date: '2019-12-31',
+};
diff --git a/spec/javascripts/vue_shared/components/issue/issue_assignees_spec.js b/spec/javascripts/vue_shared/components/issue/issue_assignees_spec.js
new file mode 100644
index 00000000000..9eac75fac96
--- /dev/null
+++ b/spec/javascripts/vue_shared/components/issue/issue_assignees_spec.js
@@ -0,0 +1,114 @@
+import Vue from 'vue';
+
+import IssueAssignees from '~/vue_shared/components/issue/issue_assignees.vue';
+
+import mountComponent from 'spec/helpers/vue_mount_component_helper';
+import { mockAssigneesList } from 'spec/boards/mock_data';
+
+const createComponent = (assignees = mockAssigneesList, cssClass = '') => {
+ const Component = Vue.extend(IssueAssignees);
+
+ return mountComponent(Component, {
+ assignees,
+ cssClass,
+ });
+};
+
+describe('IssueAssigneesComponent', () => {
+ let vm;
+
+ beforeEach(() => {
+ vm = createComponent();
+ });
+
+ afterEach(() => {
+ vm.$destroy();
+ });
+
+ describe('data', () => {
+ it('returns default data props', () => {
+ expect(vm.maxVisibleAssignees).toBe(2);
+ expect(vm.maxAssigneeAvatars).toBe(3);
+ expect(vm.maxAssignees).toBe(99);
+ });
+ });
+
+ describe('computed', () => {
+ describe('countOverLimit', () => {
+ it('should return difference between assignees count and maxVisibleAssignees', () => {
+ expect(vm.countOverLimit).toBe(mockAssigneesList.length - vm.maxVisibleAssignees);
+ });
+ });
+
+ describe('assigneesToShow', () => {
+ it('should return assignees containing only 2 items when count more than maxAssigneeAvatars', () => {
+ expect(vm.assigneesToShow.length).toBe(2);
+ });
+
+ it('should return all assignees as it is when count less than maxAssigneeAvatars', () => {
+ vm.assignees = mockAssigneesList.slice(0, 3); // Set 3 Assignees
+
+ expect(vm.assigneesToShow.length).toBe(3);
+ });
+ });
+
+ describe('assigneesCounterTooltip', () => {
+ it('should return string containing count of remaining assignees when count more than maxAssigneeAvatars', () => {
+ expect(vm.assigneesCounterTooltip).toBe('3 more assignees');
+ });
+ });
+
+ describe('shouldRenderAssigneesCounter', () => {
+ it('should return `false` when assignees count less than maxAssigneeAvatars', () => {
+ vm.assignees = mockAssigneesList.slice(0, 3); // Set 3 Assignees
+
+ expect(vm.shouldRenderAssigneesCounter).toBe(false);
+ });
+
+ it('should return `true` when assignees count more than maxAssigneeAvatars', () => {
+ expect(vm.shouldRenderAssigneesCounter).toBe(true);
+ });
+ });
+
+ describe('assigneeCounterLabel', () => {
+ it('should return count of additional assignees total assignees count more than maxAssigneeAvatars', () => {
+ expect(vm.assigneeCounterLabel).toBe('+3');
+ });
+ });
+ });
+
+ describe('methods', () => {
+ describe('avatarUrlTitle', () => {
+ it('returns string containing alt text for assignee avatar', () => {
+ expect(vm.avatarUrlTitle(mockAssigneesList[0])).toBe('Avatar for Terrell Graham');
+ });
+ });
+ });
+
+ describe('template', () => {
+ it('renders component root element with class `issue-assignees`', () => {
+ expect(vm.$el.classList.contains('issue-assignees')).toBe(true);
+ });
+
+ it('renders assignee avatars', () => {
+ expect(vm.$el.querySelectorAll('.user-avatar-link').length).toBe(2);
+ });
+
+ it('renders assignee tooltips', () => {
+ const tooltipText = vm.$el
+ .querySelectorAll('.user-avatar-link')[0]
+ .querySelector('.js-assignee-tooltip').innerText;
+
+ expect(tooltipText).toContain('Assignee');
+ expect(tooltipText).toContain('Terrell Graham');
+ expect(tooltipText).toContain('@monserrate.gleichner');
+ });
+
+ it('renders additional assignees count', () => {
+ const avatarCounterEl = vm.$el.querySelector('.avatar-counter');
+
+ expect(avatarCounterEl.innerText.trim()).toBe('+3');
+ expect(avatarCounterEl.getAttribute('data-original-title')).toBe('3 more assignees');
+ });
+ });
+});
diff --git a/spec/javascripts/vue_shared/components/issue/issue_milestone_spec.js b/spec/javascripts/vue_shared/components/issue/issue_milestone_spec.js
new file mode 100644
index 00000000000..8fca2637326
--- /dev/null
+++ b/spec/javascripts/vue_shared/components/issue/issue_milestone_spec.js
@@ -0,0 +1,234 @@
+import Vue from 'vue';
+
+import IssueMilestone from '~/vue_shared/components/issue/issue_milestone.vue';
+
+import mountComponent from 'spec/helpers/vue_mount_component_helper';
+import { mockMilestone } from 'spec/boards/mock_data';
+
+const createComponent = (milestone = mockMilestone) => {
+ const Component = Vue.extend(IssueMilestone);
+
+ return mountComponent(Component, {
+ milestone,
+ });
+};
+
+describe('IssueMilestoneComponent', () => {
+ let vm;
+
+ beforeEach(() => {
+ vm = createComponent();
+ });
+
+ afterEach(() => {
+ vm.$destroy();
+ });
+
+ describe('computed', () => {
+ describe('isMilestoneStarted', () => {
+ it('should return `false` when milestoneStart prop is not defined', done => {
+ const vmStartUndefined = createComponent(
+ Object.assign({}, mockMilestone, {
+ start_date: '',
+ }),
+ );
+
+ Vue.nextTick()
+ .then(() => {
+ expect(vmStartUndefined.isMilestoneStarted).toBe(false);
+ })
+ .then(done)
+ .catch(done.fail);
+
+ vmStartUndefined.$destroy();
+ });
+
+ it('should return `true` when milestone start date is past current date', done => {
+ const vmStarted = createComponent(
+ Object.assign({}, mockMilestone, {
+ start_date: '1990-07-22',
+ }),
+ );
+
+ Vue.nextTick()
+ .then(() => {
+ expect(vmStarted.isMilestoneStarted).toBe(true);
+ })
+ .then(done)
+ .catch(done.fail);
+
+ vmStarted.$destroy();
+ });
+ });
+
+ describe('isMilestonePastDue', () => {
+ it('should return `false` when milestoneDue prop is not defined', done => {
+ const vmDueUndefined = createComponent(
+ Object.assign({}, mockMilestone, {
+ due_date: '',
+ }),
+ );
+
+ Vue.nextTick()
+ .then(() => {
+ expect(vmDueUndefined.isMilestonePastDue).toBe(false);
+ })
+ .then(done)
+ .catch(done.fail);
+
+ vmDueUndefined.$destroy();
+ });
+
+ it('should return `true` when milestone due is past current date', done => {
+ const vmPastDue = createComponent(
+ Object.assign({}, mockMilestone, {
+ due_date: '1990-07-22',
+ }),
+ );
+
+ Vue.nextTick()
+ .then(() => {
+ expect(vmPastDue.isMilestonePastDue).toBe(true);
+ })
+ .then(done)
+ .catch(done.fail);
+
+ vmPastDue.$destroy();
+ });
+ });
+
+ describe('milestoneDatesAbsolute', () => {
+ it('returns string containing absolute milestone due date', () => {
+ expect(vm.milestoneDatesAbsolute).toBe('(December 31, 2019)');
+ });
+
+ it('returns string containing absolute milestone start date when due date is not present', done => {
+ const vmDueUndefined = createComponent(
+ Object.assign({}, mockMilestone, {
+ due_date: '',
+ }),
+ );
+
+ Vue.nextTick()
+ .then(() => {
+ expect(vmDueUndefined.milestoneDatesAbsolute).toBe('(January 1, 2018)');
+ })
+ .then(done)
+ .catch(done.fail);
+
+ vmDueUndefined.$destroy();
+ });
+
+ it('returns empty string when both milestone start and due dates are not present', done => {
+ const vmDatesUndefined = createComponent(
+ Object.assign({}, mockMilestone, {
+ start_date: '',
+ due_date: '',
+ }),
+ );
+
+ Vue.nextTick()
+ .then(() => {
+ expect(vmDatesUndefined.milestoneDatesAbsolute).toBe('');
+ })
+ .then(done)
+ .catch(done.fail);
+
+ vmDatesUndefined.$destroy();
+ });
+ });
+
+ describe('milestoneDatesHuman', () => {
+ it('returns string containing milestone due date when date is yet to be due', done => {
+ const vmFuture = createComponent(
+ Object.assign({}, mockMilestone, {
+ due_date: `${new Date().getFullYear() + 10}-01-01`,
+ }),
+ );
+
+ Vue.nextTick()
+ .then(() => {
+ expect(vmFuture.milestoneDatesHuman).toContain('years remaining');
+ })
+ .then(done)
+ .catch(done.fail);
+
+ vmFuture.$destroy();
+ });
+
+ it('returns string containing milestone start date when date has already started and due date is not present', done => {
+ const vmStarted = createComponent(
+ Object.assign({}, mockMilestone, {
+ start_date: '1990-07-22',
+ due_date: '',
+ }),
+ );
+
+ Vue.nextTick()
+ .then(() => {
+ expect(vmStarted.milestoneDatesHuman).toContain('Started');
+ })
+ .then(done)
+ .catch(done.fail);
+
+ vmStarted.$destroy();
+ });
+
+ it('returns string containing milestone start date when date is yet to start and due date is not present', done => {
+ const vmStarts = createComponent(
+ Object.assign({}, mockMilestone, {
+ start_date: `${new Date().getFullYear() + 10}-01-01`,
+ due_date: '',
+ }),
+ );
+
+ Vue.nextTick()
+ .then(() => {
+ expect(vmStarts.milestoneDatesHuman).toContain('Starts');
+ })
+ .then(done)
+ .catch(done.fail);
+
+ vmStarts.$destroy();
+ });
+
+ it('returns empty string when milestone start and due dates are not present', done => {
+ const vmDatesUndefined = createComponent(
+ Object.assign({}, mockMilestone, {
+ start_date: '',
+ due_date: '',
+ }),
+ );
+
+ Vue.nextTick()
+ .then(() => {
+ expect(vmDatesUndefined.milestoneDatesHuman).toBe('');
+ })
+ .then(done)
+ .catch(done.fail);
+
+ vmDatesUndefined.$destroy();
+ });
+ });
+ });
+
+ describe('template', () => {
+ it('renders component root element with class `issue-milestone-details`', () => {
+ expect(vm.$el.classList.contains('issue-milestone-details')).toBe(true);
+ });
+
+ it('renders milestone icon', () => {
+ expect(vm.$el.querySelector('svg use').getAttribute('xlink:href')).toContain('clock');
+ });
+
+ it('renders milestone title', () => {
+ expect(vm.$el.querySelector('.milestone-title').innerText.trim()).toBe(mockMilestone.title);
+ });
+
+ it('renders milestone tooltip', () => {
+ expect(vm.$el.querySelector('.js-item-milestone').innerText.trim()).toContain(
+ mockMilestone.title,
+ );
+ });
+ });
+});