Migrate issue boards specs to Jest & VTU

Migrated boards specs that rely on tooltips to Jest & Vue Test Utils
parent eafd98ec
import Vue from 'vue';
import IssueTimeEstimate from '~/boards/components/issue_time_estimate.vue'; import IssueTimeEstimate from '~/boards/components/issue_time_estimate.vue';
import boardsStore from '~/boards/stores/boards_store'; import boardsStore from '~/boards/stores/boards_store';
import mountComponent from '../../helpers/vue_mount_component_helper'; import { shallowMount } from '@vue/test-utils';
describe('Issue Time Estimate component', () => { describe('Issue Time Estimate component', () => {
let vm; let wrapper;
beforeEach(() => { beforeEach(() => {
boardsStore.create(); boardsStore.create();
}); });
afterEach(() => { afterEach(() => {
vm.$destroy(); wrapper.destroy();
}); });
describe('when limitToHours is false', () => { describe('when limitToHours is false', () => {
beforeEach(() => { beforeEach(() => {
boardsStore.timeTracking.limitToHours = false; boardsStore.timeTracking.limitToHours = false;
wrapper = shallowMount(IssueTimeEstimate, {
const Component = Vue.extend(IssueTimeEstimate); propsData: {
vm = mountComponent(Component, { estimate: 374460,
estimate: 374460, },
sync: false,
}); });
}); });
it('renders the correct time estimate', () => { it('renders the correct time estimate', () => {
expect(vm.$el.querySelector('time').textContent.trim()).toEqual('2w 3d 1m'); expect(
wrapper
.find('time')
.text()
.trim(),
).toEqual('2w 3d 1m');
}); });
it('renders expanded time estimate in tooltip', () => { it('renders expanded time estimate in tooltip', () => {
expect(vm.$el.querySelector('.js-issue-time-estimate').textContent).toContain( expect(wrapper.find('.js-issue-time-estimate').text()).toContain('2 weeks 3 days 1 minute');
'2 weeks 3 days 1 minute',
);
}); });
it('prevents tooltip xss', done => { it('prevents tooltip xss', done => {
const alertSpy = spyOn(window, 'alert'); const alertSpy = jest.spyOn(window, 'alert');
vm.estimate = 'Foo <script>alert("XSS")</script>'; wrapper.setProps({ estimate: 'Foo <script>alert("XSS")</script>' });
wrapper.vm.$nextTick(() => {
vm.$nextTick(() => {
expect(alertSpy).not.toHaveBeenCalled(); expect(alertSpy).not.toHaveBeenCalled();
expect(vm.$el.querySelector('time').textContent.trim()).toEqual('0m'); expect(
expect(vm.$el.querySelector('.js-issue-time-estimate').textContent).toContain('0m'); wrapper
.find('time')
.text()
.trim(),
).toEqual('0m');
expect(wrapper.find('.js-issue-time-estimate').text()).toContain('0m');
done(); done();
}); });
}); });
...@@ -50,21 +57,25 @@ describe('Issue Time Estimate component', () => { ...@@ -50,21 +57,25 @@ describe('Issue Time Estimate component', () => {
describe('when limitToHours is true', () => { describe('when limitToHours is true', () => {
beforeEach(() => { beforeEach(() => {
boardsStore.timeTracking.limitToHours = true; boardsStore.timeTracking.limitToHours = true;
wrapper = shallowMount(IssueTimeEstimate, {
const Component = Vue.extend(IssueTimeEstimate); propsData: {
vm = mountComponent(Component, { estimate: 374460,
estimate: 374460, },
sync: false,
}); });
}); });
it('renders the correct time estimate', () => { it('renders the correct time estimate', () => {
expect(vm.$el.querySelector('time').textContent.trim()).toEqual('104h 1m'); expect(
wrapper
.find('time')
.text()
.trim(),
).toEqual('104h 1m');
}); });
it('renders expanded time estimate in tooltip', () => { it('renders expanded time estimate in tooltip', () => {
expect(vm.$el.querySelector('.js-issue-time-estimate').textContent).toContain( expect(wrapper.find('.js-issue-time-estimate').text()).toContain('104 hours 1 minute');
'104 hours 1 minute',
);
}); });
}); });
}); });
/* global ListAssignee */ /* global ListAssignee, ListLabel, ListIssue */
/* global ListLabel */ import { mount } from '@vue/test-utils';
/* global ListIssue */ import _ from 'underscore';
import Vue from 'vue';
import '~/boards/models/label'; import '~/boards/models/label';
import '~/boards/models/assignee'; import '~/boards/models/assignee';
import '~/boards/models/issue'; import '~/boards/models/issue';
import '~/boards/models/list'; import '~/boards/models/list';
import IssueCardInner from '~/boards/components/issue_card_inner.vue'; import IssueCardInner from '~/boards/components/issue_card_inner.vue';
import { listObj } from './mock_data'; import { listObj } from '../../javascripts/boards/mock_data';
import store from '~/boards/stores'; import store from '~/boards/stores';
describe('Issue card component', () => { describe('Issue card component', () => {
...@@ -19,6 +16,7 @@ describe('Issue card component', () => { ...@@ -19,6 +16,7 @@ describe('Issue card component', () => {
username: 'test', username: 'test',
avatar: 'test_image', avatar: 'test_image',
}); });
const label1 = new ListLabel({ const label1 = new ListLabel({
id: 3, id: 3,
title: 'testing 123', title: 'testing 123',
...@@ -26,17 +24,13 @@ describe('Issue card component', () => { ...@@ -26,17 +24,13 @@ describe('Issue card component', () => {
text_color: 'white', text_color: 'white',
description: 'test', description: 'test',
}); });
let component;
let wrapper;
let issue; let issue;
let list; let list;
beforeEach(() => { beforeEach(() => {
setFixtures('<div class="test-container"></div>'); list = { ...listObj, type: 'label' };
list = {
...listObj,
type: 'label',
};
issue = new ListIssue({ issue = new ListIssue({
title: 'Testing', title: 'Testing',
id: 1, id: 1,
...@@ -48,116 +42,109 @@ describe('Issue card component', () => { ...@@ -48,116 +42,109 @@ describe('Issue card component', () => {
real_path: '/test/1', real_path: '/test/1',
weight: 1, weight: 1,
}); });
wrapper = mount(IssueCardInner, {
component = new Vue({ propsData: {
el: document.querySelector('.test-container'), list,
store, issue,
components: { issueLinkBase: '/test',
'issue-card': IssueCardInner, rootPath: '/',
},
data() {
return {
list,
issue,
issueLinkBase: '/test',
rootPath: '/',
};
}, },
template: ` store,
<issue-card sync: false,
:issue="issue"
:list="list"
:issue-link-base="issueLinkBase"
:root-path="rootPath"></issue-card>
`,
}); });
}); });
it('renders issue title', () => { it('renders issue title', () => {
expect(component.$el.querySelector('.board-card-title').textContent).toContain(issue.title); expect(wrapper.find('.board-card-title').text()).toContain(issue.title);
}); });
it('includes issue base in link', () => { it('includes issue base in link', () => {
expect(component.$el.querySelector('.board-card-title a').getAttribute('href')).toContain( expect(wrapper.find('.board-card-title a').attributes('href')).toContain('/test');
'/test',
);
}); });
it('includes issue title on link', () => { it('includes issue title on link', () => {
expect(component.$el.querySelector('.board-card-title a').getAttribute('title')).toBe( expect(wrapper.find('.board-card-title a').attributes('title')).toBe(issue.title);
issue.title,
);
}); });
it('does not render confidential icon', () => { it('does not render confidential icon', () => {
expect(component.$el.querySelector('.fa-eye-flash')).toBeNull(); expect(wrapper.find('.fa-eye-flash').exists()).toBe(false);
}); });
it('renders confidential icon', done => { it('renders confidential icon', done => {
component.issue.confidential = true; wrapper.setProps({
issue: {
Vue.nextTick(() => { ...wrapper.props('issue'),
expect(component.$el.querySelector('.confidential-icon')).not.toBeNull(); confidential: true,
},
});
wrapper.vm.$nextTick(() => {
expect(wrapper.find('.confidential-icon').exists()).toBe(true);
done(); done();
}); });
}); });
it('renders issue ID with #', () => { it('renders issue ID with #', () => {
expect(component.$el.querySelector('.board-card-number').textContent).toContain(`#${issue.id}`); expect(wrapper.find('.board-card-number').text()).toContain(`#${issue.id}`);
}); });
describe('assignee', () => { describe('assignee', () => {
it('does not render assignee', () => { it('does not render assignee', () => {
expect(component.$el.querySelector('.board-card-assignee .avatar')).toBeNull(); expect(wrapper.find('.board-card-assignee .avatar').exists()).toBe(false);
}); });
describe('exists', () => { describe('exists', () => {
beforeEach(done => { beforeEach(done => {
component.issue.assignees = [user]; wrapper.setProps({
issue: {
...wrapper.props('issue'),
assignees: [user],
},
});
Vue.nextTick(() => done()); wrapper.vm.$nextTick(done);
}); });
it('renders assignee', () => { it('renders assignee', () => {
expect(component.$el.querySelector('.board-card-assignee .avatar')).not.toBeNull(); expect(wrapper.find('.board-card-assignee .avatar').exists()).toBe(true);
}); });
it('sets title', () => { it('sets title', () => {
expect(component.$el.querySelector('.js-assignee-tooltip').textContent).toContain( expect(wrapper.find('.js-assignee-tooltip').text()).toContain(`${user.name}`);
`${user.name}`,
);
}); });
it('sets users path', () => { it('sets users path', () => {
expect(component.$el.querySelector('.board-card-assignee a').getAttribute('href')).toBe( expect(wrapper.find('.board-card-assignee a').attributes('href')).toBe('/test');
'/test',
);
}); });
it('renders avatar', () => { it('renders avatar', () => {
expect(component.$el.querySelector('.board-card-assignee img')).not.toBeNull(); expect(wrapper.find('.board-card-assignee img').exists()).toBe(true);
}); });
}); });
describe('assignee default avatar', () => { describe('assignee default avatar', () => {
beforeEach(done => { beforeEach(done => {
component.issue.assignees = [ wrapper.setProps({
new ListAssignee( issue: {
{ ...wrapper.props('issue'),
id: 1, assignees: [
name: 'testing 123', new ListAssignee(
username: 'test', {
}, id: 1,
'default_avatar', name: 'testing 123',
), username: 'test',
]; },
'default_avatar',
),
],
},
});
Vue.nextTick(done); wrapper.vm.$nextTick(done);
}); });
it('displays defaults avatar if users avatar is null', () => { it('displays defaults avatar if users avatar is null', () => {
expect(component.$el.querySelector('.board-card-assignee img')).not.toBeNull(); expect(wrapper.find('.board-card-assignee img').exists()).toBe(true);
expect(component.$el.querySelector('.board-card-assignee img').getAttribute('src')).toBe( expect(wrapper.find('.board-card-assignee img').attributes('src')).toBe(
'default_avatar?width=24', 'default_avatar?width=24',
); );
}); });
...@@ -166,37 +153,43 @@ describe('Issue card component', () => { ...@@ -166,37 +153,43 @@ describe('Issue card component', () => {
describe('multiple assignees', () => { describe('multiple assignees', () => {
beforeEach(done => { beforeEach(done => {
component.issue.assignees = [ wrapper.setProps({
new ListAssignee({ issue: {
id: 2, ...wrapper.props('issue'),
name: 'user2', assignees: [
username: 'user2', new ListAssignee({
avatar: 'test_image', id: 2,
}), name: 'user2',
new ListAssignee({ username: 'user2',
id: 3, avatar: 'test_image',
name: 'user3', }),
username: 'user3', new ListAssignee({
avatar: 'test_image', id: 3,
}), name: 'user3',
new ListAssignee({ username: 'user3',
id: 4, avatar: 'test_image',
name: 'user4', }),
username: 'user4', new ListAssignee({
avatar: 'test_image', id: 4,
}), name: 'user4',
]; username: 'user4',
avatar: 'test_image',
}),
],
},
});
Vue.nextTick(() => done()); wrapper.vm.$nextTick(done);
}); });
it('renders all three assignees', () => { it('renders all three assignees', () => {
expect(component.$el.querySelectorAll('.board-card-assignee .avatar').length).toEqual(3); expect(wrapper.findAll('.board-card-assignee .avatar').length).toEqual(3);
}); });
describe('more than three assignees', () => { describe('more than three assignees', () => {
beforeEach(done => { beforeEach(done => {
component.issue.assignees.push( const { assignees } = wrapper.props('issue');
assignees.push(
new ListAssignee({ new ListAssignee({
id: 5, id: 5,
name: 'user5', name: 'user5',
...@@ -205,33 +198,54 @@ describe('Issue card component', () => { ...@@ -205,33 +198,54 @@ describe('Issue card component', () => {
}), }),
); );
Vue.nextTick(() => done()); wrapper.setProps({
issue: {
...wrapper.props('issue'),
assignees,
},
});
wrapper.vm.$nextTick(done);
}); });
it('renders more avatar counter', () => { it('renders more avatar counter', () => {
expect( expect(
component.$el.querySelector('.board-card-assignee .avatar-counter').innerText.trim(), wrapper
.find('.board-card-assignee .avatar-counter')
.text()
.trim(),
).toEqual('+2'); ).toEqual('+2');
}); });
it('renders two assignees', () => { it('renders two assignees', () => {
expect(component.$el.querySelectorAll('.board-card-assignee .avatar').length).toEqual(2); expect(wrapper.findAll('.board-card-assignee .avatar').length).toEqual(2);
}); });
it('renders 99+ avatar counter', done => { it('renders 99+ avatar counter', done => {
for (let i = 5; i < 104; i += 1) { const assignees = [
const u = new ListAssignee({ ...wrapper.props('issue').assignees,
id: i, ..._.range(5, 103).map(
name: 'name', i =>
username: 'username', new ListAssignee({
avatar: 'test_image', id: i,
}); name: 'name',
component.issue.assignees.push(u); username: 'username',
} avatar: 'test_image',
}),
),
];
wrapper.setProps({
issue: {
...wrapper.props('issue'),
assignees,
},
});
Vue.nextTick(() => { wrapper.vm.$nextTick(() => {
expect( expect(
component.$el.querySelector('.board-card-assignee .avatar-counter').innerText.trim(), wrapper
.find('.board-card-assignee .avatar-counter')
.text()
.trim(),
).toEqual('99+'); ).toEqual('99+');
done(); done();
}); });
...@@ -241,51 +255,50 @@ describe('Issue card component', () => { ...@@ -241,51 +255,50 @@ describe('Issue card component', () => {
describe('labels', () => { describe('labels', () => {
beforeEach(done => { beforeEach(done => {
component.issue.addLabel(label1); issue.addLabel(label1);
wrapper.setProps({ issue: { ...issue } });
Vue.nextTick(() => done()); wrapper.vm.$nextTick(done);
}); });
it('does not render list label but renders all other labels', () => { it('does not render list label but renders all other labels', () => {
expect(component.$el.querySelectorAll('.badge').length).toBe(1); expect(wrapper.findAll('.badge').length).toBe(1);
}); });
it('renders label', () => { it('renders label', () => {
const nodes = []; const nodes = wrapper
component.$el.querySelectorAll('.badge').forEach(label => { .findAll('.badge')
nodes.push(label.getAttribute('data-original-title')); .wrappers.map(label => label.attributes('data-original-title'));
});
expect(nodes.includes(label1.description)).toBe(true); expect(nodes.includes(label1.description)).toBe(true);
}); });
it('sets label description as title', () => { it('sets label description as title', () => {
expect(component.$el.querySelector('.badge').getAttribute('data-original-title')).toContain( expect(wrapper.find('.badge').attributes('data-original-title')).toContain(
label1.description, label1.description,
); );
}); });
it('sets background color of button', () => { it('sets background color of button', () => {
const nodes = []; const nodes = wrapper
component.$el.querySelectorAll('.badge').forEach(label => { .findAll('.badge')
nodes.push(label.style.backgroundColor); .wrappers.map(label => label.element.style.backgroundColor);
});
expect(nodes.includes(label1.color)).toBe(true); expect(nodes.includes(label1.color)).toBe(true);
}); });
it('does not render label if label does not have an ID', done => { it('does not render label if label does not have an ID', done => {
component.issue.addLabel( issue.addLabel(
new ListLabel({ new ListLabel({
title: 'closed', title: 'closed',
}), }),
); );
wrapper.setProps({ issue: { ...issue } });
Vue.nextTick() wrapper.vm
.$nextTick()
.then(() => { .then(() => {
expect(component.$el.querySelectorAll('.badge').length).toBe(1); expect(wrapper.findAll('.badge').length).toBe(1);
expect(component.$el.textContent).not.toContain('closed'); expect(wrapper.text()).not.toContain('closed');
done(); done();
}) })
.catch(done.fail); .catch(done.fail);
......
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