Commit 6bd0f8ed authored by Clement Ho's avatar Clement Ho
Browse files

Add vue specs

parent ea5ed498
export default {
name: 'IssueCardHeader',
props: {
confidential: { type: Boolean, required: false, default: false },
confidential: { type: Boolean, required: true },
title: { type: String, required: true },
issueId: { type: Number, required: true },
assignee: { type: Object, required: true },
......@@ -10,7 +10,7 @@ export default {
},
computed: {
hasAssignee() {
return Object.keys(this.assignee).length;
return Object.keys(this.assignee).length > 0;
},
},
template: `
......@@ -21,10 +21,7 @@ export default {
<h4 class="card-title">
<a :href="issueLinkBase + '/' + issueId"
:title="title">{{ title }}</a>
<span class="card-number"
v-if="issueId">
#{{ issueId }}
</span>
<span class="card-number">#{{ issueId }}</span>
</h4>
<a class="card-assignee has-tooltip"
:href="rootPath + assignee.username"
......
......@@ -35,7 +35,7 @@ export default {
<issue-card-labels
:labels="issue.labels"
:list="list"
:update-filters="true" />
:update-filters="updateFilters" />
</div>
`,
};
......@@ -40,6 +40,8 @@ export default {
eventHub.$emit('updateTokens');
},
labelStyle(label) {
// TODO: What happens if label.color and/or label.textColor is not defined?
return {
backgroundColor: label.color,
color: label.textColor,
......
import Vue from 'vue';
import headerComponent from '~/boards/components/issue_card_header';
const propData = {
confidential: true,
title: 'this is a title',
issueId: 23,
assignee: {
username: 'batman',
name: 'Bruce Wayne',
avatar: 'https://batman.gravatar',
},
issueLinkBase: 'linkBase',
rootPath: 'rootPath',
};
const createComponent = (propsData) => {
const Component = Vue.extend(headerComponent);
return new Component({
el: document.createElement('div'),
propsData,
});
};
describe('IssueCardHeader', () => {
describe('props', () => {
const props = headerComponent.props;
it('should have confidential prop', () => {
const { confidential } = props;
const ConfidentialClass = confidential.type;
expect(confidential).toBeDefined();
expect(new ConfidentialClass() instanceof Boolean).toBeTruthy();
expect(confidential.required).toBeTruthy();
});
it('should have title prop', () => {
const { title } = props;
const TitleClass = title.type;
expect(title).toBeDefined();
expect(new TitleClass() instanceof String).toBeTruthy();
expect(title.required).toBeTruthy();
});
it('should have issueId prop', () => {
const { issueId } = props;
const IssueIdClass = issueId.type;
expect(issueId).toBeDefined();
expect(new IssueIdClass() instanceof Number).toBeTruthy();
expect(issueId.required).toBeTruthy();
});
it('should have assignee prop', () => {
const { assignee } = props;
expect(assignee).toBeDefined();
expect(assignee instanceof Object).toBeTruthy();
expect(assignee.required).toBeTruthy();
});
it('should have issueLinkBase prop', () => {
const { issueLinkBase } = props;
const IssueLinkBaseClass = issueLinkBase.type;
expect(issueLinkBase).toBeDefined();
expect(new IssueLinkBaseClass() instanceof String).toBeTruthy();
expect(issueLinkBase.required).toBeTruthy();
});
it('should have rootPath prop', () => {
const { rootPath } = props;
const RootPathClass = rootPath.type;
expect(rootPath).toBeDefined();
expect(new RootPathClass() instanceof String).toBeTruthy();
expect(rootPath.required).toBeTruthy();
});
});
describe('computed', () => {
describe('hasAssignee', () => {
it('should return whether there is an assignee', () => {
const data = Object.assign({}, propData);
let vm = createComponent(data);
expect(vm.hasAssignee).toEqual(true);
data.assignee = {};
vm = createComponent(data);
expect(vm.hasAssignee).toEqual(false);
});
});
});
describe('template', () => {
it('should have correct elements', () => {
const el = createComponent(propData).$el;
expect(el.tagName).toEqual('DIV');
expect(el.classList.contains('card-header')).toEqual(true);
const confidentialIcon = el.querySelector('.confidential-icon');
expect(confidentialIcon.tagName).toEqual('I');
expect(confidentialIcon.classList.contains('fa')).toEqual(true);
expect(confidentialIcon.classList.contains('fa-eye-slash')).toEqual(true);
const cardTitle = el.querySelector('.card-title');
expect(cardTitle.tagName).toEqual('H4');
const cardTitleLink = cardTitle.querySelector('a');
expect(cardTitleLink.getAttribute('href')).toEqual(`${propData.issueLinkBase}/${propData.issueId}`);
expect(cardTitleLink.getAttribute('title')).toEqual(propData.title);
expect(cardTitleLink.innerText).toEqual(propData.title);
const cardNumber = cardTitle.querySelector('.card-number');
expect(cardNumber.tagName).toEqual('SPAN');
expect(cardNumber.innerText).toEqual(`#${propData.issueId}`);
const cardAssignee = el.querySelector('.card-assignee');
expect(cardAssignee.tagName).toEqual('A');
expect(cardAssignee.getAttribute('href')).toEqual(`${propData.rootPath}${propData.assignee.username}`);
expect(cardAssignee.getAttribute('title')).toEqual(`Assigned to ${propData.assignee.name}`);
const cardAssigneeImage = cardAssignee.querySelector('img');
expect(cardAssigneeImage.getAttribute('src')).toEqual(propData.assignee.avatar);
expect(cardAssigneeImage.getAttribute('alt')).toEqual(`Avatar for ${propData.assignee.name}`);
});
it('should not display confidential icon if confidential is false', () => {
const data = Object.assign({}, propData);
data.confidential = false;
const el = createComponent(data).$el;
expect(el.querySelector('.confidential-icon')).toEqual(null);
});
it('should not render assignee if there is no assignee', () => {
const data = Object.assign({}, propData);
data.assignee = {};
const el = createComponent(data).$el;
expect(el.querySelector('.card-assignee')).toEqual(null);
});
});
});
import Vue from 'vue';
import cardComponent from '~/boards/components/issue_card_inner';
import headerComponent from '~/boards/components/issue_card_header';
import labelsComponent from '~/boards/components/issue_card_labels';
const propData = {
issue: {
title: 'title',
id: 1,
confidential: true,
assignee: {},
labels: [],
},
issueLinkBase: 'issueLinkBase',
list: {},
rootPath: 'rootPath',
updateFilters: false,
};
const createComponent = (componentName, propsData) => {
const Component = Vue.extend.call(Vue, componentName);
return new Component({
el: document.createElement('div'),
propsData,
});
};
describe('IssueCardInner', () => {
describe('props', () => {
const props = cardComponent.props;
it('should have issue prop', () => {
const { issue } = props;
expect(issue).toBeDefined();
expect(issue instanceof Object).toBeTruthy();
expect(issue.required).toBeTruthy();
});
it('should have issueLinkBase prop', () => {
const { issueLinkBase } = props;
const IssueLinkBaseClass = issueLinkBase.type;
expect(issueLinkBase).toBeDefined();
expect(new IssueLinkBaseClass() instanceof String).toBeTruthy();
expect(issueLinkBase.required).toBeTruthy();
});
it('should have list prop', () => {
const { list } = props;
expect(list).toBeDefined();
expect(list instanceof Object).toBeTruthy();
expect(list.required).toBeFalsy();
});
it('should have rootPath prop', () => {
const { rootPath } = props;
const RootPathClass = rootPath.type;
expect(rootPath).toBeDefined();
expect(new RootPathClass() instanceof String).toBeTruthy();
expect(rootPath.required).toBeTruthy();
});
it('should have updateFilters prop', () => {
const { updateFilters } = props;
const UpdateFiltersClass = updateFilters.type;
expect(updateFilters).toBeDefined();
expect(new UpdateFiltersClass() instanceof Boolean).toBeTruthy();
expect(updateFilters.required).toBeFalsy();
expect(updateFilters.default).toBeFalsy();
});
});
describe('computed', () => {
describe('assignee', () => {
it('should return assignee object by default', () => {
const vm = createComponent(cardComponent, propData);
expect(vm.assignee instanceof Object).toBeTruthy();
});
it('should return empty object if assignee is false', () => {
const data = Object.assign({}, propData);
data.issue.assignee = false;
const vm = createComponent(cardComponent, data);
expect(vm.assignee instanceof Object).toBeTruthy();
});
});
});
describe('components', () => {
it('should have components added', () => {
expect(cardComponent.components['issue-card-header']).toBeDefined();
expect(cardComponent.components['issue-card-labels']).toBeDefined();
});
});
describe('template', () => {
it('should have correct elements', () => {
const vm = createComponent(cardComponent, propData);
const el = vm.$el;
const headerComponentEl = createComponent(headerComponent, {
confidential: propData.issue.confidential,
title: propData.issue.title,
issueId: propData.issue.id,
assignee: vm.assignee,
issueLinkBase: propData.issueLinkBase,
rootPath: propData.rootPath,
}).$el;
const labelsComponentEl = createComponent(labelsComponent, {
labels: propData.issue.labels,
list: propData.list,
updateFilters: propData.updateFilters,
}).$el;
const contents = `${headerComponentEl.innerHTML}${labelsComponentEl.innerHTML}`;
expect(el.innerHTML.indexOf(contents) !== -1).toEqual(true);
});
});
});
import Vue from 'vue';
import labelsComponent from '~/boards/components/issue_card_labels';
import eventHub from '~/boards/eventhub';
import '~/boards/stores/boards_store';
const propData = {
labels: [{
color: 'rgb(0, 0, 0)',
textColor: 'rgb(255, 255, 255)',
description: '',
id: 1,
title: 'Frontend',
}, {
color: 'rgb(255, 255, 255)',
textColor: 'rgb(0, 0, 0)',
description: '',
id: 2,
title: 'Community Contribution',
}],
list: {
color: 'rgb(0, 0, 0)',
id: 3,
title: 'bug',
},
updateFilters: true,
};
const createComponent = (propsData) => {
const Component = Vue.extend(labelsComponent);
return new Component({
el: document.createElement('div'),
propsData,
});
};
describe('IssueCardLabels', () => {
describe('props', () => {
const props = labelsComponent.props;
it('should have labels prop', () => {
const { labels } = props;
const LabelsClass = labels.type;
expect(labels).toBeDefined();
expect(new LabelsClass() instanceof Array).toBeTruthy();
expect(labels.required).toBeTruthy();
});
it('should have list prop', () => {
const { list } = props;
expect(list).toBeDefined();
expect(list instanceof Object).toBeTruthy();
expect(list.required).toBeFalsy();
});
it('should have updateFilters prop', () => {
const { updateFilters } = props;
const UpdateFiltersClass = updateFilters.type;
expect(updateFilters).toBeDefined();
expect(new UpdateFiltersClass() instanceof Boolean).toBeTruthy();
expect(updateFilters.required).toBeFalsy();
expect(updateFilters.default).toBeFalsy();
});
});
describe('methods', () => {
describe('showLabel', () => {
it('should return true if there is no list', () => {
const data = Object.assign({}, propData);
data.list = null;
const vm = createComponent(data);
expect(vm.showLabel()).toEqual(true);
});
it('should return true if there is a list and no list.label', () => {
const vm = createComponent(propData);
expect(vm.showLabel()).toEqual(true);
});
it('should return true if list.label.id does not match label.id', () => {
const data = Object.assign({}, propData);
data.list.label = {
id: 100,
};
const vm = createComponent(data);
const showLabel = vm.showLabel({
id: 1,
});
expect(showLabel).toEqual(true);
});
});
describe('filterByLabel', () => {
it('should not continue if there is no updateFilters set', () => {
const data = Object.assign({}, propData);
data.updateFilters = null;
const spy = spyOn(gl.issueBoards.BoardsStore.filter.path, 'split').and.callThrough();
const vm = createComponent(data);
vm.filterByLabel({ title: 'title' }, { currentTarget: '' });
expect(spy).not.toHaveBeenCalled();
});
it('should hide tooltip', () => {
const spy = spyOn($.fn, 'tooltip').and.callFake(() => {});
const vm = createComponent(propData);
vm.filterByLabel({ title: 'title' }, { currentTarget: '' });
expect(spy).toHaveBeenCalledWith('hide');
});
it('should add/remove label to BoardsStore filter path', () => {
const originalPath = gl.issueBoards.BoardsStore.filter.path;
const vm = createComponent(propData);
const label = { title: 'special' };
vm.filterByLabel(label, { currentTarget: '' });
expect(gl.issueBoards.BoardsStore.filter.path).toEqual(`${originalPath}&label_name[]=${label.title}`);
vm.filterByLabel(label, { currentTarget: '' });
expect(gl.issueBoards.BoardsStore.filter.path).toEqual(originalPath);
});
it('should encode label title', () => {
const originalPath = gl.issueBoards.BoardsStore.filter.path;
const vm = createComponent(propData);
const label = { title: '!@#$%^ &*()' };
vm.filterByLabel(label, { currentTarget: '' });
expect(gl.issueBoards.BoardsStore.filter.path).toEqual(`${originalPath}&label_name[]=${encodeURIComponent(label.title)}`);
});
it('should updateFiltersUrl', () => {
spyOn(gl.issueBoards.BoardsStore, 'updateFiltersUrl').and.callFake(() => {});
const vm = createComponent(propData);
vm.filterByLabel({ title: 'title' }, { currentTarget: '' });
expect(gl.issueBoards.BoardsStore.updateFiltersUrl).toHaveBeenCalled();
expect(gl.issueBoards.BoardsStore.updateFiltersUrl.calls.count()).toEqual(1);
});
it('should emit updateTokens to eventHub', (done) => {
const vm = createComponent(propData);
spyOn(eventHub, '$emit').and.callFake((message) => {
expect(message).toEqual('updateTokens');
done();
});
vm.filterByLabel({ title: 'title' }, { currentTarget: '' });
});
});
describe('labelStyle', () => {
it('should return style object with backgroundColor and color', () => {
const data = {
color: '#000000',
textColor: '#FFFFFF',
};
const vm = createComponent(propData);
const style = vm.labelStyle(data);
expect(style.backgroundColor).toEqual(data.color);
expect(style.color).toEqual(data.textColor);
});
});
});
describe('template', () => {
it('should have correct elements', () => {
const vm = createComponent(propData);
const el = vm.$el;
spyOn(vm, 'filterByLabel').and.callThrough();
expect(el.tagName).toEqual('DIV');
expect(el.classList.contains('card-footer')).toEqual(true);
const labels = el.querySelectorAll('button');
expect(labels.length).toEqual(2);
const firstLabel = labels[0];
expect(firstLabel.getAttribute('type')).toEqual('button');
expect(firstLabel.textContent.trim()).toEqual(propData.labels[0].title);
expect(firstLabel.getAttribute('title')).toEqual(propData.labels[0].description);
expect(firstLabel.style.backgroundColor).toEqual(propData.labels[0].color);
expect(firstLabel.style.color).toEqual(propData.labels[0].textColor);
firstLabel.click();
expect(vm.filterByLabel).toHaveBeenCalled();
expect(vm.filterByLabel.calls.count()).toEqual(1);
vm.filterByLabel.calls.reset();
const secondLabel = labels[1];
expect(secondLabel.getAttribute('type')).toEqual('button');
expect(secondLabel.textContent.trim()).toEqual(propData.labels[1].title);
expect(secondLabel.getAttribute('title')).toEqual(propData.labels[1].description);
expect(secondLabel.style.backgroundColor).toEqual(propData.labels[1].color);
expect(secondLabel.style.color).toEqual(propData.labels[1].textColor);
secondLabel.click();
expect(vm.filterByLabel).toHaveBeenCalled();
expect(vm.filterByLabel.calls.count()).toEqual(1);
});
it('should not display label if showLabel is false', () => {
const data = Object.assign({}, propData);
data.list.label = {
id: 1,
};
const vm = createComponent(data);
const el = vm.$el;
const labels = el.querySelectorAll('button');
expect(labels.length).toEqual(1);
});
});
});
/* global ListUser */
/* global ListLabel */
/* global listObj */
/* global ListIssue */
import Vue from 'vue';
require('~/boards/models/issue');
require('~/boards/models/label');
require('~/boards/models/list');
require('~/boards/models/user');
require('~/boards/stores/boards_store');
require('~/boards/components/issue_card_inner');
require('./mock_data');
describe('Issue card component', () => {
const user = new ListUser({
id: 1,
name: 'testing 123',
username: 'test',
avatar: 'test_image',
});
const label1 = new ListLabel({
id: 3,
title: 'testing 123',
color: 'blue',
text_color: 'white',
description: 'test',
});
let component;
let issue;
let list;
beforeEach(() => {
setFixtures('<div class="test-container"></div>');
list = listObj;
issue = new ListIssue({
title: 'Testing',
iid: 1,
confidential: false,
labels: [list.label],
});
component = new Vue({
el: document.querySelector('.test-container'),
data() {
return {
list,
issue,
issueLinkBase: '/test',
rootPath: '/',
};
},
components: {
'issue-card': gl.issueBoards.IssueCardInner,
},
template: `
<issue-card
:issue="issue"
:list="list"
:issue-link-base="issueLinkBase"
:root-path="rootPath"></issue-card>
`,
});
});
it('renders issue title', () => {
expect(
component.$el.querySelector('.card-title').textContent,
).toContain(issue.title);
});
it('includes issue base in link', () => {
expect(
component.$el.querySelector('.card-title a').getAttribute('href'),
).toContain('/test');
});
it('includes issue title on link', () => {
expect(
component.$el.querySelector('.card-title a').getAttribute('title'),
).toBe(issue.title);
});
it('does not render confidential icon', () => {
expect(
component.$el.querySelector('.fa-eye-flash'),
).toBeNull();
});
it('renders confidential icon', (done) => {
component.issue.confidential = true;
setTimeout(() => {
expect(
component.$el.querySelector('.confidential-icon'),
).not.toBeNull();
done();
}, 0);
});
it('renders issue ID with #', () => {
expect(
component.$el.querySelector('.card-number').textContent,
).toContain(`#${issue.id}`);
});
describe('assignee', () => {
it('does not render assignee', () => {
expect(
component.$el.querySelector('.card-assignee'),
).toBeNull();
});
describe('exists', () => {
beforeEach((done) => {
component.issue.assignee = user;
setTimeout(() => {
done();
}, 0);
});
it('renders assignee', () => {
expect(
component.$el.querySelector('.card-assignee'),
).not.toBeNull();
});
it('sets title', () => {
expect(
component.$el.querySelector('.card-assignee').getAttribute('title'),
).toContain(`Assigned to ${user.name}`);
});
it('sets users path', () => {
expect(
component.$el.querySelector('.card-assignee').getAttribute('href'),
).toBe('/test');
});
it('renders avatar', () => {
expect(
component.$el.querySelector('.card-assignee img'),
).not.toBeNull();
});
});
});
describe('labels', () => {
it('does not render any', () => {
expect(
component.$el.querySelector('.label'),
).toBeNull();
});
describe('exists', () => {
beforeEach((done) => {
component.issue.addLabel(label1);
setTimeout(() => {
done();
}, 0);
});
it('does not render list label', () => {
expect(
component.$el.querySelectorAll('.label').length,
).toBe(1);
});
it('renders label', () => {
expect(
component.$el.querySelector('.label').textContent,
).toContain(label1.title);
});
it('sets label description as title', () => {
expect(
component.$el.querySelector('.label').getAttribute('title'),
).toContain(label1.description);
});
it('sets background color of button', () => {
expect(
component.$el.querySelector('.label').style.backgroundColor,
).toContain(label1.color);
});
});
});
});
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