Commit a78ff257 authored by Clement Ho's avatar Clement Ho

[skip ci] Add vue component specs

parent 27a503a6
...@@ -250,7 +250,7 @@ import Vue from 'vue'; ...@@ -250,7 +250,7 @@ import Vue from 'vue';
defaultLabel: defaultLabel, defaultLabel: defaultLabel,
hidden: function(e) { hidden: function(e) {
if ($dropdown.hasClass('js-multiselect')) { if ($dropdown.hasClass('js-multiselect')) {
gl.sidebarAssigneesOptions.assignees.saveUsers(); gl.sidebarAssigneesOptions.store.saveUsers();
} }
$selectbox.hide(); $selectbox.hide();
...@@ -274,12 +274,12 @@ import Vue from 'vue'; ...@@ -274,12 +274,12 @@ import Vue from 'vue';
// Unassigned selected // Unassigned selected
previouslySelected.each((index, element) => { previouslySelected.each((index, element) => {
const id = parseInt(element.value, 10); const id = parseInt(element.value, 10);
gl.sidebarAssigneesOptions.assignees.removeUser(id); gl.sidebarAssigneesOptions.store.removeUser(id);
element.remove(); element.remove();
}); });
} else if (isActive) { } else if (isActive) {
// user selected // user selected
gl.sidebarAssigneesOptions.assignees.addUser({ gl.sidebarAssigneesOptions.store.addUser({
id: user.id, id: user.id,
name: user.name, name: user.name,
username: user.username, username: user.username,
...@@ -292,7 +292,7 @@ import Vue from 'vue'; ...@@ -292,7 +292,7 @@ import Vue from 'vue';
if (unassignedSelected) { if (unassignedSelected) {
unassignedSelected.remove(); unassignedSelected.remove();
gl.sidebarAssigneesOptions.assignees.removeUser(unassignedSelected); gl.sidebarAssigneesOptions.store.removeUser(unassignedSelected);
} }
} else { } else {
if (previouslySelected.length === 0) { if (previouslySelected.length === 0) {
...@@ -301,7 +301,7 @@ import Vue from 'vue'; ...@@ -301,7 +301,7 @@ import Vue from 'vue';
} }
// User unselected // User unselected
gl.sidebarAssigneesOptions.assignees.removeUser(user.id); gl.sidebarAssigneesOptions.store.removeUser(user.id);
} }
} }
......
...@@ -3,30 +3,27 @@ export default { ...@@ -3,30 +3,27 @@ export default {
props: { props: {
loading: { loading: {
type: Boolean, type: Boolean,
required: true required: false,
default: false,
}, },
numberOfAssignees: { numberOfAssignees: {
type: Number, type: Number,
required: true required: true,
}, },
editable: { editable: {
type: Boolean, type: Boolean,
required: true required: true,
}, },
}, },
computed: { computed: {
hasMultipleAssignees() { assigneeTitle() {
return this.numberOfAssignees > 1; const assignees = this.numberOfAssignees;
return assignees > 1 ? `${assignees} Assignees` : 'Assignee';
}, },
}, },
template: ` template: `
<div class="title hide-collapsed"> <div class="title hide-collapsed">
<template v-if="hasMultipleAssignees"> {{assigneeTitle}}
{{numberOfAssignees}} Assignees
</template>
<template v-else>
Assignee
</template>
<i v-if="loading" aria-hidden="true" class="fa fa-spinner fa-spin block-loading"></i> <i v-if="loading" aria-hidden="true" class="fa fa-spinner fa-spin block-loading"></i>
<a v-if="editable" class="edit-link pull-right" href="#">Edit</a> <a v-if="editable" class="edit-link pull-right" href="#">Edit</a>
</div> </div>
......
...@@ -2,15 +2,20 @@ import CollapsedAvatar from './avatar'; ...@@ -2,15 +2,20 @@ import CollapsedAvatar from './avatar';
export default { export default {
name: 'CollapsedAssignees', name: 'CollapsedAssignees',
data() {
return {
defaultMaxCounter: 99,
};
},
props: { props: {
users: { users: {
type: Array, type: Array,
required: true required: true,
},
defaultRenderCount: {
type: Number,
required: false,
default: 5,
},
defaultMaxCounter: {
type: Number,
required: false,
default: 99,
}, },
}, },
computed: { computed: {
...@@ -22,7 +27,7 @@ export default { ...@@ -22,7 +27,7 @@ export default {
renderUsers.forEach(u => names.push(u.name)); renderUsers.forEach(u => names.push(u.name));
if (this.users.length > maxRender) { if (this.users.length > maxRender) {
names.push(`+${this.users.length - maxRender} more`); names.push(`+ ${this.users.length - maxRender} more`);
} }
return names.join(', '); return names.join(', ');
...@@ -45,7 +50,7 @@ export default { ...@@ -45,7 +50,7 @@ export default {
moreThanOneAssignees() { moreThanOneAssignees() {
return this.users.length > 1; return this.users.length > 1;
}, },
morethanTwoAssignees() { moreThanTwoAssignees() {
return this.users.length > 2; return this.users.length > 2;
}, },
}, },
...@@ -63,9 +68,16 @@ export default { ...@@ -63,9 +68,16 @@ export default {
data-placement="left" data-placement="left"
data-toggle="tooltip" data-toggle="tooltip"
:data-original-title="title" > :data-original-title="title" >
<collapsed-avatar :user="users[0]" /> <collapsed-avatar
<collapsed-avatar :user="users[1]" v-if="hasTwoAssignees" /> :name="users[0].name"
<button class="btn-link" type="button" v-if="morethanTwoAssignees"> :avatarUrl="users[0].avatarUrl"
/>
<collapsed-avatar
v-if="hasTwoAssignees"
:name="users[1].name"
:avatarUrl="users[1].avatarUrl"
/>
<button class="btn-link" type="button" v-if="moreThanTwoAssignees">
<span class="avatar-counter sidebar-avatar-counter">{{counter}}</span> <span class="avatar-counter sidebar-avatar-counter">{{counter}}</span>
</button> </button>
</div> </div>
......
export default { export default {
name: 'CollapsedAvatar', name: 'CollapsedAvatar',
props: { props: {
user: { name: {
type: Object, type: String,
required: true required: true,
},
avatarUrl: {
type: String,
required: true,
}, },
}, },
computed: { computed: {
alt() { alt() {
return `${this.user.name}'s avatar`; return `${this.name}'s avatar`;
}, },
}, },
template: ` template: `
...@@ -16,8 +20,8 @@ export default { ...@@ -16,8 +20,8 @@ export default {
<img width="24" <img width="24"
class="avatar avatar-inline s24" class="avatar avatar-inline s24"
:alt="alt" :alt="alt"
:src="user.avatarUrl" > :src="avatarUrl" >
<span class="author">{{user.name}}</span> <span class="author">{{name}}</span>
</button> </button>
`, `,
}; };
...@@ -6,20 +6,17 @@ export default { ...@@ -6,20 +6,17 @@ export default {
}; };
}, },
props: { props: {
assignees: { store: {
type: Object, type: Object,
required: true required: true,
}, },
}, },
computed: { computed: {
rootPath() {
return this.assignees.rootPath;
},
renderShowMoreSection() { renderShowMoreSection() {
return this.assignees.users.length > this.assignees.defaultRenderCount; return this.store.users.length > this.store.defaultRenderCount;
}, },
numberOfHiddenAssignees() { numberOfHiddenAssignees() {
return this.assignees.users.length - this.assignees.defaultRenderCount; return this.store.users.length - this.store.defaultRenderCount;
}, },
isHiddenAssignees() { isHiddenAssignees() {
return this.numberOfHiddenAssignees > 0; return this.numberOfHiddenAssignees > 0;
...@@ -30,10 +27,10 @@ export default { ...@@ -30,10 +27,10 @@ export default {
this.showLess = !this.showLess; this.showLess = !this.showLess;
}, },
renderAssignee(index) { renderAssignee(index) {
return !this.showLess || (index < this.assignees.defaultRenderCount && this.showLess); return !this.showLess || (index < this.store.defaultRenderCount && this.showLess);
}, },
assigneeUrl(username) { assigneeUrl(username) {
return `${this.rootPath}${username}`; return `${this.store.rootPath}${username}`;
}, },
assigneeAlt(name) { assigneeAlt(name) {
return `${name}'s avatar`; return `${name}'s avatar`;
...@@ -43,7 +40,7 @@ export default { ...@@ -43,7 +40,7 @@ export default {
<div class="hide-collapsed"> <div class="hide-collapsed">
<div class="hide-collapsed"> <div class="hide-collapsed">
<div class="user-list"> <div class="user-list">
<div class="user-item" v-for="(user, index) in assignees.users" <div class="user-item" v-for="(user, index) in store.users"
v-if="renderAssignee(index)" > v-if="renderAssignee(index)" >
<a class="user-link has-tooltip" <a class="user-link has-tooltip"
data-placement="bottom" data-placement="bottom"
......
export default { export default {
name: 'NoAssignee', name: 'NoAssignee',
props: { props: {
assignees: { store: {
type: Object, type: Object,
required: true required: true,
}, },
}, },
methods: { methods: {
assignSelf() { assignSelf() {
this.assignees.addCurrentUser(); this.store.addCurrentUser();
}, },
}, },
template: ` template: `
......
export default { export default {
name: 'SingleAssignee', name: 'SingleAssignee',
props: { props: {
assignees: { store: {
type: Object, type: Object,
required: true required: true,
}, },
}, },
computed: { computed: {
rootPath() {
return this.assignees.rootPath;
},
user() { user() {
return this.assignees.users[0]; return this.store.users[0];
},
userUrl() {
return `${this.store.rootPath}${this.user.username}`;
},
username() {
return `@${this.user.username}`;
}, },
avatarAlt() { avatarAlt() {
return `${this.user.name}'s avatar`; return `${this.user.name}'s avatar`;
...@@ -19,13 +22,13 @@ export default { ...@@ -19,13 +22,13 @@ export default {
}, },
template: ` template: `
<div class="hide-collapsed"> <div class="hide-collapsed">
<a class="author_link bold" :href="rootPath + user.username"> <a class="author_link bold" :href="userUrl">
<img width="32" <img width="32"
class="avatar avatar-inline s32" class="avatar avatar-inline s32"
:alt="avatarAlt" :alt="avatarAlt"
:src="user.avatarUrl" > :src="user.avatarUrl" >
<span class="author">{{user.name}}</span> <span class="author">{{user.name}}</span>
<span class="username">@{{user.username}}</span> <span class="username">{{username}}</span>
</a> </a>
</div> </div>
`, `,
......
...@@ -25,15 +25,15 @@ const sidebarAssigneesOptions = () => ({ ...@@ -25,15 +25,15 @@ const sidebarAssigneesOptions = () => ({
const currentUserId = parseInt(element.dataset.userId, 10); const currentUserId = parseInt(element.dataset.userId, 10);
const service = new SidebarAssigneesService(path, field); const service = new SidebarAssigneesService(path, field);
const assignees = new SidebarAssigneesStore(currentUserId, service, rootPath, editable); const store = new SidebarAssigneesStore(currentUserId, service, rootPath, editable);
return { return {
assignees, store,
}; };
}, },
computed: { computed: {
numberOfAssignees() { numberOfAssignees() {
return this.assignees.users.length; return this.store.users.length;
}, },
componentName() { componentName() {
switch (this.numberOfAssignees) { switch (this.numberOfAssignees) {
...@@ -46,7 +46,7 @@ const sidebarAssigneesOptions = () => ({ ...@@ -46,7 +46,7 @@ const sidebarAssigneesOptions = () => ({
} }
}, },
hideComponent() { hideComponent() {
return !this.assignees.saved; return !this.store.saved;
}, },
}, },
components: { components: {
...@@ -59,15 +59,15 @@ const sidebarAssigneesOptions = () => ({ ...@@ -59,15 +59,15 @@ const sidebarAssigneesOptions = () => ({
template: ` template: `
<div> <div>
<assignee-title <assignee-title
:numberOfAssignees="assignees.users.length" :numberOfAssignees="store.users.length"
:loading="assignees.loading" :loading="store.loading"
:editable="assignees.editable" :editable="store.editable"
/> />
<collapsed-assignees :users="assignees.users"/> <collapsed-assignees :users="store.users"/>
<component v-if="assignees.saved" <component v-if="store.saved"
class="value" class="value"
:is="componentName" :is="componentName"
:assignees="assignees" :store="store"
/> />
</div> </div>
`, `,
......
...@@ -52,14 +52,12 @@ export default class SidebarAssigneesStore { ...@@ -52,14 +52,12 @@ export default class SidebarAssigneesStore {
this.users = []; this.users = [];
assignees.forEach(function(a) { assignees.forEach(a => this.addUser({
this.addUser({ id: a.id,
id: a.id, name: a.name,
name: a.name, username: a.username,
username: a.username, avatarUrl: a.avatar_url,
avatarUrl: a.avatar_url, }, true));
}, true)
}.bind(this));
this.saved = true; this.saved = true;
this.loading = false; this.loading = false;
......
...@@ -217,7 +217,7 @@ ...@@ -217,7 +217,7 @@
- issuable.assignees.each do |assignee| - issuable.assignees.each do |assignee|
:javascript :javascript
document.addEventListener('DOMContentLoaded', () => { document.addEventListener('DOMContentLoaded', () => {
gl.sidebarAssigneesOptions.assignees.addUser({ gl.sidebarAssigneesOptions.store.addUser({
id: parseInt("#{assignee.id}", 10), id: parseInt("#{assignee.id}", 10),
name: "#{assignee.name}", name: "#{assignee.name}",
username: "#{assignee.username}", username: "#{assignee.username}",
......
class VueSpecHelper {
static createComponent(Vue, componentName, propsData) {
const Component = Vue.extend.call(Vue, componentName);
return new Component({
el: document.createElement('div'),
propsData,
});
}
}
module.exports = VueSpecHelper;
import Vue from 'vue';
import VueSpecHelper from './vue_spec_helper';
import ClassSpecHelper from './class_spec_helper';
describe('VueSpecHelper', () => {
describe('createComponent', () => {
const sample = {
name: 'Sample',
props: {
content: {
type: String,
required: false,
},
},
template: `
<div>{{content}}</div>
`,
};
it('should be a static method', () => {
expect(ClassSpecHelper.itShouldBeAStaticMethod(VueSpecHelper, 'createComponent').status()).toBe('passed');
});
it('should call Vue.extend', () => {
spyOn(Vue, 'extend').and.callThrough();
VueSpecHelper.createComponent(Vue, sample, {});
expect(Vue.extend).toHaveBeenCalled();
});
it('should return view model', () => {
const vm = VueSpecHelper.createComponent(Vue, sample, {
content: 'content',
});
expect(vm.$el.textContent).toEqual('content');
});
});
});
import Vue from 'vue';
import assigneeTitleComponent from '~/vue_sidebar_assignees/components/assignee_title';
import VueSpecHelper from '../helpers/vue_spec_helper';
describe('AssigneeTitle', () => {
const createComponent = props =>
VueSpecHelper.createComponent(Vue, assigneeTitleComponent, props);
describe('props', () => {
it('should have loading prop', () => {
const { loading } = assigneeTitleComponent.props;
expect(loading.type).toBe(Boolean);
});
it('should have numberOfAssignees prop', () => {
const { numberOfAssignees } = assigneeTitleComponent.props;
expect(numberOfAssignees.type).toBe(Number);
});
it('should have editable prop', () => {
const { editable } = assigneeTitleComponent.props;
expect(editable.type).toBe(Boolean);
});
});
describe('computed', () => {
describe('assigneeTitle', () => {
it('returns "Assignee" when there is only one assignee', () => {
const vm = createComponent({
numberOfAssignees: 1,
editable: true,
});
expect(vm.assigneeTitle).toEqual('Assignee');
});
it('returns "Assignee" when there is only no assignee', () => {
const vm = createComponent({
numberOfAssignees: 0,
editable: true,
});
expect(vm.assigneeTitle).toEqual('Assignee');
});
it('returns "2 Assignees" when there is two assignee', () => {
const vm = createComponent({
numberOfAssignees: 2,
editable: false,
});
expect(vm.assigneeTitle).toEqual('2 Assignees');
});
});
});
describe('template', () => {
it('should render assigneeTitle', () => {
const vm = createComponent({
numberOfAssignees: 100,
editable: false,
});
const el = vm.$el;
expect(el.tagName).toEqual('DIV');
expect(el.textContent.trim()).toEqual(vm.assigneeTitle);
});
it('should display spinner when loading', () => {
const el = createComponent({
numberOfAssignees: 0,
loading: true,
editable: false,
}).$el;
const i = el.querySelector('i');
expect(i).toBeDefined();
});
it('should not display spinner when not loading', () => {
const el = createComponent({
numberOfAssignees: 0,
editable: true,
}).$el;
const i = el.querySelector('i');
expect(i).toBeNull();
});
it('should display edit link when editable', () => {
const el = createComponent({
numberOfAssignees: 0,
editable: true,
}).$el;
const editLink = el.querySelector('.edit-link');
expect(editLink).toBeDefined();
});
it('should display edit link when not editable', () => {
const el = createComponent({
numberOfAssignees: 0,
editable: false,
}).$el;
const editLink = el.querySelector('.edit-link');
expect(editLink).toBeNull();
});
});
});
import Vue from 'vue';
import assigneesComponent from '~/vue_sidebar_assignees/components/collapsed/assignees';
import avatarComponent from '~/vue_sidebar_assignees/components/collapsed/avatar';
import VueSpecHelper from '../../helpers/vue_spec_helper';
import { mockUser, mockUser2, mockUser3 } from '../mock_data';
describe('CollapsedAssignees', () => {
const mockUsers = [mockUser, mockUser2];
const createAssigneesComponent = props =>
VueSpecHelper.createComponent(Vue, assigneesComponent, props);
const createAvatarComponent = props =>
VueSpecHelper.createComponent(Vue, avatarComponent, props);
describe('props', () => {
it('should have users prop', () => {
const { users } = assigneesComponent.props;
expect(users.type).toBe(Array);
});
it('should have defaultRenderCount prop', () => {
const { defaultRenderCount } = assigneesComponent.props;
expect(defaultRenderCount.type).toBe(Number);
});
it('should have defaultMaxCounter prop', () => {
const { defaultMaxCounter } = assigneesComponent.props;
expect(defaultMaxCounter.type).toBe(Number);
});
});
describe('computed', () => {
describe('title', () => {
it('returns one name when there is one assignee', () => {
const users = Object.assign([], mockUsers);
users.pop();
const vm = createAssigneesComponent({
users,
});
expect(vm.title).toEqual('Clark Kent');
});
it('returns two names when there are two assignees', () => {
const vm = createAssigneesComponent({
users: mockUsers,
});
expect(vm.title).toEqual('Clark Kent, Bruce Wayne');
});
it('returns more text when there are more than defaultRenderCount assignees', () => {
const vm = createAssigneesComponent({
users: mockUsers,
defaultRenderCount: 1,
});
expect(vm.title).toEqual('Clark Kent, + 1 more');
});
});
describe('counter', () => {
it('should return one less than users.length', () => {
const vm = createAssigneesComponent({
users: mockUsers,
});
expect(vm.counter).toEqual('+1');
});
it('should return defaultMaxCounter+ when users.length is greater than defaultMaxCounter', () => {
const vm = createAssigneesComponent({
users: mockUsers,
defaultMaxCounter: 1,
});
expect(vm.counter).toEqual('1+');
});
});
describe('hasNoAssignees', () => {
it('returns true when there are no assignees', () => {
const vm = createAssigneesComponent({
users: [],
});
expect(vm.hasNoAssignees).toEqual(true);
});
it('returns false when there are assignees', () => {
const vm = createAssigneesComponent({
users: mockUsers,
});
expect(vm.hasNoAssignees).toEqual(false);
});
});
describe('hasTwoAssignees', () => {
it('returns true when there are two assignees', () => {
const vm = createAssigneesComponent({
users: mockUsers,
});
expect(vm.hasTwoAssignees).toEqual(true);
});
it('returns false when there is no assignes', () => {
const vm = createAssigneesComponent({
users: [],
});
expect(vm.hasTwoAssignees).toEqual(false);
});
});
describe('moreThanOneAssignees', () => {
it('returns true when there are more than one assignee', () => {
const vm = createAssigneesComponent({
users: mockUsers,
});
expect(vm.moreThanOneAssignees).toEqual(true);
});
it('returns false when there is one assignee', () => {
const users = Object.assign([], mockUsers);
users.pop();
const vm = createAssigneesComponent({
users,
});
expect(vm.moreThanOneAssignees).toEqual(false);
});
});
describe('moreThanTwoAssignees', () => {
it('returns true when there are more than two assignees', () => {
const users = Object.assign([], mockUsers);
users.push(mockUser3);
const vm = createAssigneesComponent({
users,
});
expect(vm.moreThanTwoAssignees).toEqual(true);
});
it('returns false when there are two assignees', () => {
const vm = createAssigneesComponent({
users: mockUsers,
});
expect(vm.moreThanTwoAssignees).toEqual(false);
});
});
});
describe('components', () => {
it('should have components added', () => {
expect(assigneesComponent.components['collapsed-avatar']).toBeDefined();
});
});
describe('template', () => {
function avatarProp(user) {
return {
name: user.name,
avatarUrl: user.avatarUrl,
};
}
it('should render fa-user if there are no assignees', () => {
const el = createAssigneesComponent({
users: [],
}).$el;
const sidebarCollapsedIcons = el.querySelectorAll('.sidebar-collapsed-icon');
expect(sidebarCollapsedIcons.length).toEqual(1);
const userIcon = sidebarCollapsedIcons[0].querySelector('.fa-user');
expect(userIcon).toBeDefined();
});
it('should not render fa-user if there are assignees', () => {
const el = createAssigneesComponent({
users: mockUsers,
}).$el;
const sidebarCollapsedIcons = el.querySelectorAll('.sidebar-collapsed-icon');
expect(sidebarCollapsedIcons.length).toEqual(1);
const userIcon = sidebarCollapsedIcons[0].querySelector('.fa-user');
expect(userIcon).toBeNull();
});
it('should render one assignee if there is one assignee', () => {
const users = Object.assign([], mockUsers);
users.pop();
const vm = createAssigneesComponent({
users,
});
const el = vm.$el;
const sidebarCollapsedIcons = el.querySelectorAll('.sidebar-collapsed-icon');
expect(sidebarCollapsedIcons.length).toEqual(1);
const div = sidebarCollapsedIcons[0];
expect(div.getAttribute('data-original-title')).toEqual(vm.title);
expect(div.classList.contains('multiple-users')).toEqual(false);
expect(div.querySelector('.avatar-counter')).toBeNull();
const avatarEl = createAvatarComponent(avatarProp(users[0])).$el;
const divWithoutComments = div.innerHTML.replace(/<!---->/g, '').trim();
expect(divWithoutComments).toEqual(avatarEl.outerHTML);
});
it('should render two assignees if there are two assignees', () => {
const vm = createAssigneesComponent({
users: mockUsers,
});
const el = vm.$el;
const sidebarCollapsedIcons = el.querySelectorAll('.sidebar-collapsed-icon');
expect(sidebarCollapsedIcons.length).toEqual(1);
const div = sidebarCollapsedIcons[0];
expect(div.getAttribute('data-original-title')).toEqual(vm.title);
expect(div.classList.contains('multiple-users')).toEqual(true);
expect(div.querySelector('.avatar-counter')).toBeNull();
const avatarEl = [
createAvatarComponent(avatarProp(mockUsers[0])).$el,
createAvatarComponent(avatarProp(mockUsers[1])).$el,
];
const divWithoutComments = div.innerHTML.replace(/<!---->/g, '').trim();
expect(divWithoutComments).toEqual(`${avatarEl[0].outerHTML} ${avatarEl[1].outerHTML}`);
});
it('should render counter if there are more than two assignees', () => {
const users = Object.assign([], mockUsers);
users.push(mockUser3);
const vm = createAssigneesComponent({
users,
});
const el = vm.$el;
const sidebarCollapsedIcons = el.querySelectorAll('.sidebar-collapsed-icon');
expect(sidebarCollapsedIcons.length).toEqual(1);
const div = sidebarCollapsedIcons[0];
expect(div.getAttribute('data-original-title')).toEqual(vm.title);
expect(div.classList.contains('multiple-users')).toEqual(true);
const avatarCounter = div.querySelector('.avatar-counter');
expect(avatarCounter).toBeDefined();
expect(avatarCounter.textContent).toEqual(vm.counter);
const avatarEl = createAvatarComponent(avatarProp(users[0])).$el;
expect(div.innerHTML.indexOf(avatarEl.outerHTML) !== -1).toEqual(true);
});
});
});
import Vue from 'vue';
import avatarComponent from '~/vue_sidebar_assignees/components/collapsed/avatar';
import VueSpecHelper from '../../helpers/vue_spec_helper';
import { mockUser } from '../mock_data';
describe('CollapsedAvatar', () => {
const createComponent = props =>
VueSpecHelper.createComponent(Vue, avatarComponent, props);
describe('props', () => {
it('should have name prop', () => {
const { name } = avatarComponent.props;
expect(name.type).toBe(String);
});
it('should have avatarUrl prop', () => {
const { avatarUrl } = avatarComponent.props;
expect(avatarUrl.type).toBe(String);
});
});
describe('computed', () => {
describe('alt', () => {
it('returns avatar alt text', () => {
const vm = createComponent(mockUser);
expect(vm.alt).toEqual(`${mockUser.name}'s avatar`);
});
});
});
describe('template', () => {
it('should render alt text', () => {
const vm = createComponent(mockUser);
const el = vm.$el;
const avatar = el.querySelector('.avatar');
expect(avatar.getAttribute('alt')).toEqual(vm.alt);
});
it('should render avatar src url', () => {
const el = createComponent(mockUser).$el;
const avatar = el.querySelector('.avatar');
expect(avatar.getAttribute('src')).toEqual(mockUser.avatarUrl);
});
it('should render name', () => {
const el = createComponent(mockUser).$el;
const span = el.querySelector('.author');
expect(span.textContent).toEqual(mockUser.name);
});
});
});
import Vue from 'vue';
import multipleAssigneesComponent from '~/vue_sidebar_assignees/components/expanded/multiple_assignees';
import VueSpecHelper from '../../helpers/vue_spec_helper';
import { mockUser, mockUser2, mockUser3 } from '../mock_data';
describe('MultipleAssignees', () => {
const mockStore = {
users: [mockUser, mockUser2],
defaultRenderCount: 1,
rootPath: 'rootPath',
};
const createComponent = props =>
VueSpecHelper.createComponent(Vue, multipleAssigneesComponent, props);
describe('props', () => {
it('should have store prop', () => {
const { store } = multipleAssigneesComponent.props;
expect(store.type).toBe(Object);
});
});
describe('computed', () => {
describe('renderShowMoreSection', () => {
it('should return true when users.length is greater than defaultRenderCount', () => {
const vm = createComponent({
store: mockStore,
});
expect(vm.renderShowMoreSection).toEqual(true);
});
it('should return false when users.length is not greater than defaultRenderCount', () => {
const newStore = Object.assign({}, mockStore);
newStore.defaultRenderCount = 5;
const vm = createComponent({
store: newStore,
});
expect(vm.renderShowMoreSection).toEqual(false);
});
});
describe('numberOfHiddenAssignees', () => {
it('should return number of assignees that are not rendered', () => {
const vm = createComponent({
store: mockStore,
});
expect(vm.numberOfHiddenAssignees).toEqual(1);
});
});
describe('isHiddenAssignees', () => {
it('should return true when numberOfHiddenAssignees is greater than zero', () => {
const vm = createComponent({
store: mockStore,
});
expect(vm.isHiddenAssignees).toEqual(true);
});
it('should return false when numberOfHiddenAssignees is zero', () => {
const newStore = Object.assign({}, mockStore);
newStore.defaultRenderCount = 2;
const vm = createComponent({
store: newStore,
});
expect(vm.isHiddenAssignees).toEqual(false);
});
});
});
describe('methods', () => {
describe('toggleShowLess', () => {
it('should toggle showLess', () => {
const vm = createComponent({
store: mockStore,
});
expect(vm.showLess).toEqual(true);
vm.toggleShowLess();
expect(vm.showLess).toEqual(false);
});
});
describe('renderAssignee', () => {
it('should return true if showLess is false', () => {
const vm = createComponent({
store: mockStore,
});
vm.showLess = false;
expect(vm.renderAssignee(0)).toEqual(true);
});
it('should return true if showLess is true and index is less than defaultRenderCount', () => {
const vm = createComponent({
store: mockStore,
});
vm.showLess = true;
expect(vm.renderAssignee(0)).toEqual(true);
});
it('should return false if showLess is true and index is greater than defaultRenderCount', () => {
const vm = createComponent({
store: mockStore,
});
vm.showLess = true;
expect(vm.renderAssignee(10)).toEqual(false);
});
});
describe('assigneeUrl', () => {
it('should return url', () => {
const vm = createComponent({
store: mockStore,
});
const username = 'username';
expect(vm.assigneeUrl(username)).toEqual(`${mockStore.rootPath}${username}`);
});
});
describe('assigneeAlt', () => {
it('should return alt', () => {
const vm = createComponent({
store: mockStore,
});
const name = 'name';
expect(vm.assigneeAlt(name)).toEqual(`${name}'s avatar`);
});
});
});
describe('template', () => {
let vm;
let el;
describe('userItem', () => {
let userItems;
beforeEach(() => {
const newStore = Object.assign({}, mockStore);
newStore.defaultRenderCount = 2;
// Create a new copy to prevent mutating `mockStore.users`
const users = Object.assign([], mockStore.users);
users.push(mockUser3);
newStore.users = users;
vm = createComponent({
store: newStore,
});
el = vm.$el;
userItems = el.querySelectorAll('.user-item');
});
it('should render multiple user-item', () => {
expect(userItems.length).toEqual(2);
});
it('should render href', () => {
[].forEach.call(userItems, (item, index) => {
const user = vm.store.users[index];
const a = item.querySelector('a');
expect(a.getAttribute('href')).toEqual(vm.assigneeUrl(user.username));
});
});
it('should render anchor title', () => {
[].forEach.call(userItems, (item, index) => {
const user = vm.store.users[index];
const a = item.querySelector('a');
expect(a.getAttribute('data-title')).toEqual(user.name);
});
});
it('should render image alt', () => {
[].forEach.call(userItems, (item, index) => {
const user = vm.store.users[index];
const img = item.querySelector('img');
expect(img.getAttribute('alt')).toEqual(vm.assigneeAlt(user.name));
});
});
it('should render image', () => {
[].forEach.call(userItems, (item, index) => {
const user = vm.store.users[index];
const img = item.querySelector('img');
expect(img.getAttribute('src')).toEqual(user.avatarUrl);
});
});
});
describe('userListMore', () => {
beforeEach(() => {
vm = createComponent({
store: mockStore,
});
el = vm.$el;
});
it('should render user-list-more', () => {
const userListMore = el.querySelector('.user-list-more');
expect(userListMore).toBeDefined();
});
it('should toggle user-list-more', (done) => {
const button = el.querySelector('button');
const buttonContent = button.textContent;
button.click();
Vue.nextTick(() => {
expect(button.textContent.trim()).not.toEqual(buttonContent);
done();
});
});
it('should render show less', () => {
const button = el.querySelector('button');
expect(button.textContent.trim()).toEqual('+ 1 more');
});
describe('show more', () => {
let button;
beforeEach(() => {
button = el.querySelector('button');
});
it('should render show more', (done) => {
button.click();
Vue.nextTick(() => {
expect(button.textContent.trim()).toEqual('- show less');
done();
});
});
it('should render number of hidden assignees', (done) => {
const count = el.querySelectorAll('.user-item').length;
button.click();
Vue.nextTick(() => {
expect(el.querySelectorAll('.user-item').length > count).toEqual(true);
done();
});
});
});
});
});
});
import Vue from 'vue';
import noAssigneeComponent from '~/vue_sidebar_assignees/components/expanded/no_assignee';
import VueSpecHelper from '../../helpers/vue_spec_helper';
describe('NoAssignee', () => {
const mockStore = {
addCurrentUser: () => {},
};
const createComponent = props =>
VueSpecHelper.createComponent(Vue, noAssigneeComponent, props);
describe('props', () => {
it('should have store prop', () => {
const { store } = noAssigneeComponent.props;
expect(store.type).toBe(Object);
});
});
describe('methods', () => {
describe('assignSelf', () => {
it('should call addCurrentUser in store', () => {
spyOn(mockStore, 'addCurrentUser').and.callThrough();
const vm = createComponent({
store: mockStore,
});
vm.assignSelf();
expect(mockStore.addCurrentUser).toHaveBeenCalled();
});
});
});
describe('template', () => {
it('should call addCurrentUser when button is clicked', () => {
spyOn(mockStore, 'addCurrentUser').and.callThrough();
const vm = createComponent({
store: mockStore,
});
const el = vm.$el;
const button = el.querySelector('button');
button.click();
expect(mockStore.addCurrentUser).toHaveBeenCalled();
});
});
});
import Vue from 'vue';
import singleAssigneeComponent from '~/vue_sidebar_assignees/components/expanded/single_assignee';
import VueSpecHelper from '../../helpers/vue_spec_helper';
import { mockUser, mockUser2 } from '../mock_data';
describe('SingleAssignee', () => {
const mockStore = {
users: [mockUser],
rootPath: 'rootPath',
};
const createComponent = props =>
VueSpecHelper.createComponent(Vue, singleAssigneeComponent, props);
describe('props', () => {
it('should have mockStore prop', () => {
const { store } = singleAssigneeComponent.props;
expect(store.type).toBe(Object);
});
});
describe('computed', () => {
describe('user', () => {
it('should return first user', () => {
const newMockStore = Object.assign({}, mockStore);
newMockStore.users.push(mockUser2);
const vm = createComponent({
store: newMockStore,
});
expect(vm.user).toEqual(newMockStore.users[0]);
});
});
describe('userUrl', () => {
it('should return url', () => {
const vm = createComponent({
store: mockStore,
});
expect(vm.userUrl).toEqual(`${mockStore.rootPath}${mockStore.users[0].username}`);
});
});
describe('username', () => {
it('should return username', () => {
const vm = createComponent({
store: mockStore,
});
expect(vm.username).toEqual(`@${mockStore.users[0].username}`);
});
});
describe('avatarAlt', () => {
it('should return alt text', () => {
const vm = createComponent({
store: mockStore,
});
expect(vm.avatarAlt).toEqual(`${mockStore.users[0].name}'s avatar`);
});
});
});
describe('template', () => {
let vm;
let el;
beforeEach(() => {
vm = createComponent({
store: mockStore,
});
el = vm.$el;
});
it('should load the userUrl in href ', () => {
const link = el.querySelector('a');
expect(link.href).toEqual(`${window.location.origin}/${vm.userUrl}`);
});
it('should load the avatarAlt', () => {
const img = el.querySelector('img');
expect(img.alt).toEqual(vm.avatarAlt);
});
it('should load the avatar image', () => {
const img = el.querySelector('img');
expect(img.src).toEqual(vm.user.avatarUrl);
});
it('should load the user\'s name', () => {
const name = el.querySelector('.author');
expect(name.textContent).toEqual(vm.user.name);
});
it('should load the user\'s username', () => {
const username = el.querySelector('.username');
expect(username.textContent).toEqual(vm.username);
});
});
});
const mockUser = {
name: 'Clark Kent',
username: 'superman',
avatarUrl: 'https://superman.com/avatar.png',
};
const mockUser2 = {
name: 'Bruce Wayne',
username: 'batman',
avatarUrl: 'https://batman.com/avatar.png',
};
const mockUser3 = {
name: 'Tony Stark',
username: 'ironman',
avatarUrl: 'https://ironman.com/avatar.png',
};
module.exports = {
mockUser,
mockUser2,
mockUser3,
};
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