diff options
author | Paul Slaughter <pslaughter@gitlab.com> | 2018-08-07 15:15:56 +0000 |
---|---|---|
committer | Phil Hughes <me@iamphill.com> | 2018-08-07 15:15:56 +0000 |
commit | 0d6e50d54270a973647f828047828b80fdf8d013 (patch) | |
tree | 9bf41acf27d039f673f45520187daff9d47cb04f /spec/javascripts/ide/components | |
parent | 0e90f27ff79d1743d8ec5e49e003d4c68a689f78 (diff) | |
download | gitlab-ce-0d6e50d54270a973647f828047828b80fdf8d013.tar.gz |
Create Web IDE MR and branch picker
Diffstat (limited to 'spec/javascripts/ide/components')
8 files changed, 456 insertions, 95 deletions
diff --git a/spec/javascripts/ide/components/branches/item_spec.js b/spec/javascripts/ide/components/branches/item_spec.js new file mode 100644 index 00000000000..8b756c8f168 --- /dev/null +++ b/spec/javascripts/ide/components/branches/item_spec.js @@ -0,0 +1,53 @@ +import Vue from 'vue'; +import mountCompontent from 'spec/helpers/vue_mount_component_helper'; +import router from '~/ide/ide_router'; +import Item from '~/ide/components/branches/item.vue'; +import { getTimeago } from '~/lib/utils/datetime_utility'; +import { projectData } from '../../mock_data'; + +const TEST_BRANCH = { + name: 'master', + committedDate: '2018-01-05T05:50Z', +}; +const TEST_PROJECT_ID = projectData.name_with_namespace; + +describe('IDE branch item', () => { + const Component = Vue.extend(Item); + let vm; + + beforeEach(() => { + vm = mountCompontent(Component, { + item: { ...TEST_BRANCH }, + projectId: TEST_PROJECT_ID, + isActive: false, + }); + }); + + afterEach(() => { + vm.$destroy(); + }); + + it('renders branch name and timeago', () => { + const timeText = getTimeago().format(TEST_BRANCH.committedDate); + expect(vm.$el).toContainText(TEST_BRANCH.name); + expect(vm.$el.querySelector('time')).toHaveText(timeText); + expect(vm.$el.querySelector('.ic-mobile-issue-close')).toBe(null); + }); + + it('renders link to branch', () => { + const expectedHref = router.resolve(`/project/${TEST_PROJECT_ID}/edit/${TEST_BRANCH.name}`).href; + expect(vm.$el).toMatch('a'); + expect(vm.$el).toHaveAttr('href', expectedHref); + }); + + it('renders icon if isActive', done => { + vm.isActive = true; + + vm.$nextTick() + .then(() => { + expect(vm.$el.querySelector('.ic-mobile-issue-close')).not.toBe(null); + }) + .then(done) + .catch(done.fail); + }); +}); diff --git a/spec/javascripts/ide/components/branches/search_list_spec.js b/spec/javascripts/ide/components/branches/search_list_spec.js new file mode 100644 index 00000000000..c3f84ba1c24 --- /dev/null +++ b/spec/javascripts/ide/components/branches/search_list_spec.js @@ -0,0 +1,79 @@ +import Vue from 'vue'; +import store from '~/ide/stores'; +import * as types from '~/ide/stores/modules/branches/mutation_types'; +import List from '~/ide/components/branches/search_list.vue'; +import { createComponentWithStore } from '../../../helpers/vue_mount_component_helper'; +import { branches as testBranches } from '../../mock_data'; +import { resetStore } from '../../helpers'; + +describe('IDE branches search list', () => { + const Component = Vue.extend(List); + let vm; + + beforeEach(() => { + vm = createComponentWithStore(Component, store, {}); + + spyOn(vm, 'fetchBranches'); + + vm.$mount(); + }); + + afterEach(() => { + vm.$destroy(); + + resetStore(store); + }); + + it('calls fetch on mounted', () => { + expect(vm.fetchBranches).toHaveBeenCalledWith({ + search: '', + }); + }); + + it('renders loading icon', done => { + vm.$store.state.branches.isLoading = true; + + vm.$nextTick() + .then(() => { + expect(vm.$el).toContainElement('.loading-container'); + }) + .then(done) + .catch(done.fail); + }); + + it('renders branches not found when search is not empty', done => { + vm.search = 'testing'; + + vm.$nextTick(() => { + expect(vm.$el).toContainText('No branches found'); + + done(); + }); + }); + + describe('with branches', () => { + const currentBranch = testBranches[1]; + + beforeEach(done => { + vm.$store.state.currentBranchId = currentBranch.name; + vm.$store.commit(`branches/${types.RECEIVE_BRANCHES_SUCCESS}`, testBranches); + + vm.$nextTick(done); + }); + + it('renders list', () => { + const elementText = Array.from(vm.$el.querySelectorAll('li strong')) + .map(x => x.textContent.trim()); + + expect(elementText).toEqual(testBranches.map(x => x.name)); + }); + + it('renders check next to active branch', () => { + const checkedText = Array.from(vm.$el.querySelectorAll('li')) + .filter(x => x.querySelector('.ide-search-list-current-icon svg')) + .map(x => x.querySelector('strong').textContent.trim()); + + expect(checkedText).toEqual([currentBranch.name]); + }); + }); +}); diff --git a/spec/javascripts/ide/components/merge_requests/dropdown_spec.js b/spec/javascripts/ide/components/merge_requests/dropdown_spec.js deleted file mode 100644 index 74884c9a362..00000000000 --- a/spec/javascripts/ide/components/merge_requests/dropdown_spec.js +++ /dev/null @@ -1,47 +0,0 @@ -import Vue from 'vue'; -import { createStore } from '~/ide/stores'; -import Dropdown from '~/ide/components/merge_requests/dropdown.vue'; -import { createComponentWithStore } from '../../../helpers/vue_mount_component_helper'; -import { mergeRequests } from '../../mock_data'; - -describe('IDE merge requests dropdown', () => { - const Component = Vue.extend(Dropdown); - let vm; - - beforeEach(() => { - const store = createStore(); - - vm = createComponentWithStore(Component, store, { show: false }).$mount(); - }); - - afterEach(() => { - vm.$destroy(); - }); - - it('does not render tabs when show is false', () => { - expect(vm.$el.querySelector('.nav-links')).toBe(null); - }); - - describe('when show is true', () => { - beforeEach(done => { - vm.show = true; - vm.$store.state.mergeRequests.assigned.mergeRequests.push(mergeRequests[0]); - - vm.$nextTick(done); - }); - - it('renders tabs', () => { - expect(vm.$el.querySelector('.nav-links')).not.toBe(null); - }); - - it('renders count for assigned & created data', () => { - expect(vm.$el.querySelector('.nav-links a').textContent).toContain('Created by me'); - expect(vm.$el.querySelector('.nav-links a .badge').textContent).toContain('0'); - - expect(vm.$el.querySelectorAll('.nav-links a')[1].textContent).toContain('Assigned to me'); - expect( - vm.$el.querySelectorAll('.nav-links a')[1].querySelector('.badge').textContent, - ).toContain('1'); - }); - }); -}); diff --git a/spec/javascripts/ide/components/merge_requests/item_spec.js b/spec/javascripts/ide/components/merge_requests/item_spec.js index 51c4cddef2f..750948cae3c 100644 --- a/spec/javascripts/ide/components/merge_requests/item_spec.js +++ b/spec/javascripts/ide/components/merge_requests/item_spec.js @@ -1,4 +1,5 @@ import Vue from 'vue'; +import router from '~/ide/ide_router'; import Item from '~/ide/components/merge_requests/item.vue'; import mountCompontent from '../../../helpers/vue_mount_component_helper'; @@ -27,6 +28,12 @@ describe('IDE merge request item', () => { expect(vm.$el.textContent).toContain('gitlab-org/gitlab-ce!1'); }); + it('renders link with href', () => { + const expectedHref = router.resolve(`/project/${vm.item.projectPathWithNamespace}/merge_requests/${vm.item.iid}`).href; + expect(vm.$el).toMatch('a'); + expect(vm.$el).toHaveAttr('href', expectedHref); + }); + it('renders icon if ID matches currentId', () => { expect(vm.$el.querySelector('.ic-mobile-issue-close')).not.toBe(null); }); @@ -50,12 +57,4 @@ describe('IDE merge request item', () => { done(); }); }); - - it('emits click event on click', () => { - spyOn(vm, '$emit'); - - vm.$el.click(); - - expect(vm.$emit).toHaveBeenCalledWith('click', vm.item); - }); }); diff --git a/spec/javascripts/ide/components/merge_requests/list_spec.js b/spec/javascripts/ide/components/merge_requests/list_spec.js index f4b393778dc..c761315444c 100644 --- a/spec/javascripts/ide/components/merge_requests/list_spec.js +++ b/spec/javascripts/ide/components/merge_requests/list_spec.js @@ -10,10 +10,7 @@ describe('IDE merge requests list', () => { let vm; beforeEach(() => { - vm = createComponentWithStore(Component, store, { - type: 'created', - emptyText: 'empty text', - }); + vm = createComponentWithStore(Component, store, {}); spyOn(vm, 'fetchMergeRequests'); @@ -28,13 +25,13 @@ describe('IDE merge requests list', () => { it('calls fetch on mounted', () => { expect(vm.fetchMergeRequests).toHaveBeenCalledWith({ - type: 'created', search: '', + type: '', }); }); it('renders loading icon', done => { - vm.$store.state.mergeRequests.created.isLoading = true; + vm.$store.state.mergeRequests.isLoading = true; vm.$nextTick(() => { expect(vm.$el.querySelector('.loading-container')).not.toBe(null); @@ -43,10 +40,6 @@ describe('IDE merge requests list', () => { }); }); - it('renders empty text when no merge requests exist', () => { - expect(vm.$el.textContent).toContain('empty text'); - }); - it('renders no search results text when search is not empty', done => { vm.search = 'testing'; @@ -57,9 +50,29 @@ describe('IDE merge requests list', () => { }); }); + it('clicking on search type, sets currentSearchType and loads merge requests', done => { + vm.onSearchFocus(); + + vm.$nextTick() + .then(() => { + vm.$el.querySelector('li button').click(); + + return vm.$nextTick(); + }) + .then(() => { + expect(vm.currentSearchType).toEqual(vm.$options.searchTypes[0]); + expect(vm.fetchMergeRequests).toHaveBeenCalledWith({ + type: vm.currentSearchType.type, + search: '', + }); + }) + .then(done) + .catch(done.fail); + }); + describe('with merge requests', () => { beforeEach(done => { - vm.$store.state.mergeRequests.created.mergeRequests.push({ + vm.$store.state.mergeRequests.mergeRequests.push({ ...mergeRequests[0], projectPathWithNamespace: 'gitlab-org/gitlab-ce', }); @@ -71,35 +84,6 @@ describe('IDE merge requests list', () => { expect(vm.$el.querySelectorAll('li').length).toBe(1); expect(vm.$el.querySelector('li').textContent).toContain(mergeRequests[0].title); }); - - it('calls openMergeRequest when clicking merge request', done => { - spyOn(vm, 'openMergeRequest'); - vm.$el.querySelector('li button').click(); - - vm.$nextTick(() => { - expect(vm.openMergeRequest).toHaveBeenCalledWith({ - projectPath: 'gitlab-org/gitlab-ce', - id: 1, - }); - - done(); - }); - }); - }); - - describe('focusSearch', () => { - it('focuses search input when loading is false', done => { - spyOn(vm.$refs.searchInput, 'focus'); - - vm.$store.state.mergeRequests.created.isLoading = false; - vm.focusSearch(); - - vm.$nextTick(() => { - expect(vm.$refs.searchInput.focus).toHaveBeenCalled(); - - done(); - }); - }); }); describe('searchMergeRequests', () => { @@ -123,4 +107,52 @@ describe('IDE merge requests list', () => { expect(vm.loadMergeRequests).toHaveBeenCalled(); }); }); + + describe('onSearchFocus', () => { + it('shows search types', done => { + vm.$el.querySelector('input').dispatchEvent(new Event('focus')); + + expect(vm.hasSearchFocus).toBe(true); + expect(vm.showSearchTypes).toBe(true); + + vm.$nextTick() + .then(() => { + const expectedSearchTypes = vm.$options.searchTypes.map(x => x.label); + const renderedSearchTypes = Array.from(vm.$el.querySelectorAll('li')) + .map(x => x.textContent.trim()); + + expect(renderedSearchTypes).toEqual(expectedSearchTypes); + }) + .then(done) + .catch(done.fail); + }); + + it('does not show search types, if already has search value', () => { + vm.search = 'lorem ipsum'; + vm.$el.querySelector('input').dispatchEvent(new Event('focus')); + + expect(vm.hasSearchFocus).toBe(true); + expect(vm.showSearchTypes).toBe(false); + }); + + it('does not show search types, if already has a search type', () => { + vm.currentSearchType = {}; + vm.$el.querySelector('input').dispatchEvent(new Event('focus')); + + expect(vm.hasSearchFocus).toBe(true); + expect(vm.showSearchTypes).toBe(false); + }); + + it('resets hasSearchFocus when search changes', done => { + vm.hasSearchFocus = true; + vm.search = 'something else'; + + vm.$nextTick() + .then(() => { + expect(vm.hasSearchFocus).toBe(false); + }) + .then(done) + .catch(done.fail); + }); + }); }); diff --git a/spec/javascripts/ide/components/nav_dropdown_button_spec.js b/spec/javascripts/ide/components/nav_dropdown_button_spec.js new file mode 100644 index 00000000000..0a58e260280 --- /dev/null +++ b/spec/javascripts/ide/components/nav_dropdown_button_spec.js @@ -0,0 +1,63 @@ +import Vue from 'vue'; +import NavDropdownButton from '~/ide/components/nav_dropdown_button.vue'; +import store from '~/ide/stores'; +import { trimText } from 'spec/helpers/vue_component_helper'; +import { mountComponentWithStore } from 'spec/helpers/vue_mount_component_helper'; +import { resetStore } from '../helpers'; + +describe('NavDropdown', () => { + const TEST_BRANCH_ID = 'lorem-ipsum-dolar'; + const TEST_MR_ID = '12345'; + const Component = Vue.extend(NavDropdownButton); + let vm; + + beforeEach(() => { + vm = mountComponentWithStore(Component, { store }); + + vm.$mount(); + }); + + afterEach(() => { + vm.$destroy(); + + resetStore(store); + }); + + it('renders empty placeholders, if state is falsey', () => { + expect(trimText(vm.$el.textContent)).toEqual('- -'); + }); + + it('renders branch name, if state has currentBranchId', done => { + vm.$store.state.currentBranchId = TEST_BRANCH_ID; + + vm.$nextTick() + .then(() => { + expect(trimText(vm.$el.textContent)).toEqual(`${TEST_BRANCH_ID} -`); + }) + .then(done) + .catch(done.fail); + }); + + it('renders mr id, if state has currentMergeRequestId', done => { + vm.$store.state.currentMergeRequestId = TEST_MR_ID; + + vm.$nextTick() + .then(() => { + expect(trimText(vm.$el.textContent)).toEqual(`- !${TEST_MR_ID}`); + }) + .then(done) + .catch(done.fail); + }); + + it('renders branch and mr, if state has both', done => { + vm.$store.state.currentBranchId = TEST_BRANCH_ID; + vm.$store.state.currentMergeRequestId = TEST_MR_ID; + + vm.$nextTick() + .then(() => { + expect(trimText(vm.$el.textContent)).toEqual(`${TEST_BRANCH_ID} !${TEST_MR_ID}`); + }) + .then(done) + .catch(done.fail); + }); +}); diff --git a/spec/javascripts/ide/components/nav_dropdown_spec.js b/spec/javascripts/ide/components/nav_dropdown_spec.js new file mode 100644 index 00000000000..af6665bcd62 --- /dev/null +++ b/spec/javascripts/ide/components/nav_dropdown_spec.js @@ -0,0 +1,50 @@ +import $ from 'jquery'; +import Vue from 'vue'; +import store from '~/ide/stores'; +import NavDropdown from '~/ide/components/nav_dropdown.vue'; +import { mountComponentWithStore } from 'spec/helpers/vue_mount_component_helper'; + +describe('IDE NavDropdown', () => { + const Component = Vue.extend(NavDropdown); + let vm; + let $dropdown; + + beforeEach(() => { + vm = mountComponentWithStore(Component, { store }); + $dropdown = $(vm.$el); + + // block dispatch from doing anything + spyOn(vm.$store, 'dispatch'); + }); + + afterEach(() => { + vm.$destroy(); + }); + + it('renders nothing initially', () => { + expect(vm.$el).not.toContainElement('.ide-nav-form'); + }); + + it('renders nav form when show.bs.dropdown', done => { + $dropdown.trigger('show.bs.dropdown'); + + vm.$nextTick() + .then(() => { + expect(vm.$el).toContainElement('.ide-nav-form'); + }) + .then(done) + .catch(done.fail); + }); + + it('destroys nav form when closed', done => { + $dropdown.trigger('show.bs.dropdown'); + $dropdown.trigger('hide.bs.dropdown'); + + vm.$nextTick() + .then(() => { + expect(vm.$el).not.toContainElement('.ide-nav-form'); + }) + .then(done) + .catch(done.fail); + }); +}); diff --git a/spec/javascripts/ide/components/shared/tokened_input_spec.js b/spec/javascripts/ide/components/shared/tokened_input_spec.js new file mode 100644 index 00000000000..09940fe8c6a --- /dev/null +++ b/spec/javascripts/ide/components/shared/tokened_input_spec.js @@ -0,0 +1,132 @@ +import Vue from 'vue'; +import TokenedInput from '~/ide/components/shared/tokened_input.vue'; +import mountComponent from 'spec/helpers/vue_mount_component_helper'; + +const TEST_PLACEHOLDER = 'Searching in test'; +const TEST_TOKENS = [ + { label: 'lorem', id: 1 }, + { label: 'ipsum', id: 2 }, + { label: 'dolar', id: 3 }, +]; +const TEST_VALUE = 'lorem'; + +function getTokenElements(vm) { + return Array.from(vm.$el.querySelectorAll('.filtered-search-token button')); +} + +function createBackspaceEvent() { + const e = new Event('keyup'); + e.keyCode = 8; + e.which = e.keyCode; + e.altKey = false; + e.ctrlKey = true; + e.shiftKey = false; + e.metaKey = false; + return e; +} + +describe('IDE shared/TokenedInput', () => { + const Component = Vue.extend(TokenedInput); + let vm; + + beforeEach(() => { + vm = mountComponent(Component, { + tokens: TEST_TOKENS, + placeholder: TEST_PLACEHOLDER, + value: TEST_VALUE, + }); + + spyOn(vm, '$emit'); + }); + + afterEach(() => { + vm.$destroy(); + }); + + it('renders tokens', () => { + const renderedTokens = getTokenElements(vm) + .map(x => x.textContent.trim()); + + expect(renderedTokens).toEqual(TEST_TOKENS.map(x => x.label)); + }); + + it('renders input', () => { + expect(vm.$refs.input).toBeTruthy(); + expect(vm.$refs.input).toHaveValue(TEST_VALUE); + }); + + it('renders placeholder, when tokens are empty', done => { + vm.tokens = []; + + vm.$nextTick() + .then(() => { + expect(vm.$refs.input).toHaveAttr('placeholder', TEST_PLACEHOLDER); + }) + .then(done) + .catch(done.fail); + }); + + it('triggers "removeToken" on token click', () => { + getTokenElements(vm)[0].click(); + + expect(vm.$emit).toHaveBeenCalledWith('removeToken', TEST_TOKENS[0]); + }); + + it('when input triggers backspace event, it calls "onBackspace"', () => { + spyOn(vm, 'onBackspace'); + + vm.$refs.input.dispatchEvent(createBackspaceEvent()); + vm.$refs.input.dispatchEvent(createBackspaceEvent()); + + expect(vm.onBackspace).toHaveBeenCalledTimes(2); + }); + + it('triggers "removeToken" on backspaces when value is empty', () => { + vm.value = ''; + + vm.onBackspace(); + expect(vm.$emit).not.toHaveBeenCalled(); + expect(vm.backspaceCount).toEqual(1); + + vm.onBackspace(); + expect(vm.$emit).toHaveBeenCalledWith('removeToken', TEST_TOKENS[TEST_TOKENS.length - 1]); + expect(vm.backspaceCount).toEqual(0); + }); + + it('does not trigger "removeToken" on backspaces when value is not empty', () => { + vm.onBackspace(); + vm.onBackspace(); + + expect(vm.backspaceCount).toEqual(0); + expect(vm.$emit).not.toHaveBeenCalled(); + }); + + it('does not trigger "removeToken" on backspaces when tokens are empty', () => { + vm.tokens = []; + + vm.onBackspace(); + vm.onBackspace(); + + expect(vm.backspaceCount).toEqual(0); + expect(vm.$emit).not.toHaveBeenCalled(); + }); + + it('triggers "focus" on input focus', () => { + vm.$refs.input.dispatchEvent(new Event('focus')); + + expect(vm.$emit).toHaveBeenCalledWith('focus'); + }); + + it('triggers "blur" on input blur', () => { + vm.$refs.input.dispatchEvent(new Event('blur')); + + expect(vm.$emit).toHaveBeenCalledWith('blur'); + }); + + it('triggers "input" with value on input change', () => { + vm.$refs.input.value = 'something-else'; + vm.$refs.input.dispatchEvent(new Event('input')); + + expect(vm.$emit).toHaveBeenCalledWith('input', 'something-else'); + }); +}); |