diff options
7 files changed, 317 insertions, 3 deletions
diff --git a/app/assets/javascripts/boards/components/issue_card_inner.js.es6 b/app/assets/javascripts/boards/components/issue_card_inner.js.es6 index b7eeb5e04c9..383b329edc3 100644 --- a/app/assets/javascripts/boards/components/issue_card_inner.js.es6 +++ b/app/assets/javascripts/boards/components/issue_card_inner.js.es6 @@ -2,6 +2,9 @@ (() => { const Store = gl.issueBoards.BoardsStore; + window.gl = window.gl || {}; + window.gl.issueBoards = window.gl.issueBoards || {}; + gl.issueBoards.IssueCardInner = Vue.extend({ props: [ 'issue', 'issueLinkBase', 'list', @@ -57,7 +60,7 @@ #{{issue.id}} </span> <a - class="has-tooltip" + class="card-assignee has-tooltip" :href="issue.assignee.username" :title="'Assigned to ' + issue.assignee.name" v-if="issue.assignee" diff --git a/app/assets/javascripts/boards/components/modal/empty_state.js.es6 b/app/assets/javascripts/boards/components/modal/empty_state.js.es6 index 689504f397c..70f2ea51620 100644 --- a/app/assets/javascripts/boards/components/modal/empty_state.js.es6 +++ b/app/assets/javascripts/boards/components/modal/empty_state.js.es6 @@ -28,7 +28,7 @@ } return obj; - } + }, }, template: ` <div class="empty-state"> diff --git a/app/assets/javascripts/boards/components/modal/index.js.es6 b/app/assets/javascripts/boards/components/modal/index.js.es6 index a6718e899f7..52f652ccfe1 100644 --- a/app/assets/javascripts/boards/components/modal/index.js.es6 +++ b/app/assets/javascripts/boards/components/modal/index.js.es6 @@ -1,4 +1,5 @@ /* global Vue */ +/* global ListIssue */ //= require ./header //= require ./list //= require ./footer @@ -32,7 +33,7 @@ }, }, methods: { - searchOperation: _.debounce(function() { + searchOperation: _.debounce(function searchOperationDebounce() { this.loadIssues(); }, 500), loadIssues() { diff --git a/app/assets/javascripts/boards/models/issue.js.es6 b/app/assets/javascripts/boards/models/issue.js.es6 index a36a146b023..8d93ac637f9 100644 --- a/app/assets/javascripts/boards/models/issue.js.es6 +++ b/app/assets/javascripts/boards/models/issue.js.es6 @@ -13,6 +13,7 @@ class ListIssue { this.subscribed = obj.subscribed; this.labels = []; this.selected = false; + this.assignee = false; if (obj.assignee) { this.assignee = new ListUser(obj.assignee); diff --git a/app/assets/javascripts/boards/stores/modal_store.js.es6 b/app/assets/javascripts/boards/stores/modal_store.js.es6 index b5d183b1bf4..54419751433 100644 --- a/app/assets/javascripts/boards/stores/modal_store.js.es6 +++ b/app/assets/javascripts/boards/stores/modal_store.js.es6 @@ -1,4 +1,7 @@ (() => { + window.gl = window.gl || {}; + window.gl.issueBoards = window.gl.issueBoards || {}; + class ModalStore { constructor() { this.store = { diff --git a/spec/javascripts/boards/issue_card_spec.js.es6 b/spec/javascripts/boards/issue_card_spec.js.es6 new file mode 100644 index 00000000000..181fb684c23 --- /dev/null +++ b/spec/javascripts/boards/issue_card_spec.js.es6 @@ -0,0 +1,191 @@ +/* global Vue */ +/* global ListUser */ +/* global ListLabel */ +/* global listObj */ +/* global ListIssue */ + +//= require jquery +//= require 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', + }; + }, + components: { + 'issue-card': gl.issueBoards.IssueCardInner, + }, + template: ` + <issue-card + :issue="issue" + :list="list" + :issue-link-base="issueLinkBase"></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('.fa-eye-flash'), + ).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); + }); + }); + }); +}); diff --git a/spec/javascripts/boards/modal_store_spec.js.es6 b/spec/javascripts/boards/modal_store_spec.js.es6 new file mode 100644 index 00000000000..9c0625357bd --- /dev/null +++ b/spec/javascripts/boards/modal_store_spec.js.es6 @@ -0,0 +1,115 @@ +/* global Vue */ +/* global ListIssue */ + +//= require jquery +//= require vue +//= require boards/models/issue +//= require boards/models/label +//= require boards/models/list +//= require boards/models/user +//= require boards/stores/modal_store + +describe('Modal store', () => { + let issue; + let issue2; + const Store = gl.issueBoards.ModalStore; + + beforeEach(() => { + // Setup default state + Store.store.issues = []; + Store.store.selectedIssues = []; + + issue = new ListIssue({ + title: 'Testing', + iid: 1, + confidential: false, + labels: [], + }); + issue2 = new ListIssue({ + title: 'Testing', + iid: 2, + confidential: false, + labels: [], + }); + Store.store.issues.push(issue); + Store.store.issues.push(issue2); + }); + + it('returns selected count', () => { + expect(Store.selectedCount()).toBe(0); + }); + + it('toggles the issue as selected', () => { + Store.toggleIssue(issue); + + expect(issue.selected).toBe(true); + expect(Store.selectedCount()).toBe(1); + }); + + it('toggles the issue as un-selected', () => { + Store.toggleIssue(issue); + Store.toggleIssue(issue); + + expect(issue.selected).toBe(false); + expect(Store.selectedCount()).toBe(0); + }); + + it('toggles all issues as selected', () => { + Store.toggleAll(); + + expect(issue.selected).toBe(true); + expect(issue2.selected).toBe(true); + expect(Store.selectedCount()).toBe(2); + }); + + it('toggles all issues as un-selected', () => { + Store.toggleAll(); + Store.toggleAll(); + + expect(issue.selected).toBe(false); + expect(issue2.selected).toBe(false); + expect(Store.selectedCount()).toBe(0); + }); + + it('toggles all if a single issue is selected', () => { + Store.toggleIssue(issue); + Store.toggleAll(); + + expect(issue.selected).toBe(true); + expect(issue2.selected).toBe(true); + expect(Store.selectedCount()).toBe(2); + }); + + it('adds issue to selected array', () => { + Store.addSelectedIssue(issue); + + expect(Store.selectedCount()).toBe(1); + }); + + it('removes issue from selected array', () => { + Store.addSelectedIssue(issue); + Store.removeSelectedIssue(issue); + + expect(Store.selectedCount()).toBe(0); + }); + + it('returns selected issue index if present', () => { + Store.toggleIssue(issue); + + expect(Store.selectedIssueIndex(issue)).toBe(0); + }); + + it('returns -1 if issue is not selected', () => { + expect(Store.selectedIssueIndex(issue)).toBe(-1); + }); + + it('finds the selected issue', () => { + Store.toggleIssue(issue); + + expect(Store.findSelectedIssue(issue)).toBe(issue); + }); + + it('does not find a selected issue', () => { + expect(Store.findSelectedIssue(issue)).toBe(undefined); + }); +}); |