diff options
author | GitLab Bot <gitlab-bot@gitlab.com> | 2019-09-24 00:06:29 +0300 |
---|---|---|
committer | GitLab Bot <gitlab-bot@gitlab.com> | 2019-09-24 00:06:29 +0300 |
commit | b9254657872c4db441ab268154686f5476fb4bc6 (patch) | |
tree | ab045c623296a049d1246ba2d66800456a1077aa /spec | |
parent | c792263edfaf826c58f4aa41d26904464a17a3e7 (diff) |
Add latest changes from gitlab-org/gitlab@master
Diffstat (limited to 'spec')
10 files changed, 241 insertions, 151 deletions
diff --git a/spec/controllers/boards/lists_controller_spec.rb b/spec/controllers/boards/lists_controller_spec.rb index 802fc1770a4..77b5872fcb3 100644 --- a/spec/controllers/boards/lists_controller_spec.rb +++ b/spec/controllers/boards/lists_controller_spec.rb @@ -162,10 +162,10 @@ describe Boards::ListsController do end context 'with unauthorized user' do - it 'returns a forbidden 403 response' do + it 'returns a 422 unprocessable entity response' do move user: guest, board: board, list: planning, position: 6 - expect(response).to have_gitlab_http_status(403) + expect(response).to have_gitlab_http_status(422) end end diff --git a/spec/frontend/ide/components/error_message_spec.js b/spec/frontend/ide/components/error_message_spec.js new file mode 100644 index 00000000000..fd6d1a494d4 --- /dev/null +++ b/spec/frontend/ide/components/error_message_spec.js @@ -0,0 +1,122 @@ +import { shallowMount, createLocalVue } from '@vue/test-utils'; +import Vuex from 'vuex'; +import { GlLoadingIcon } from '@gitlab/ui'; +import ErrorMessage from '~/ide/components/error_message.vue'; + +const localVue = createLocalVue(); +localVue.use(Vuex); + +describe('IDE error message component', () => { + let wrapper; + + const setErrorMessageMock = jest.fn(); + const createComponent = messageProps => { + const fakeStore = new Vuex.Store({ + actions: { setErrorMessage: setErrorMessageMock }, + }); + + wrapper = shallowMount(ErrorMessage, { + propsData: { + message: { + text: 'some text', + actionText: 'test action', + actionPayload: 'testActionPayload', + ...messageProps, + }, + }, + store: fakeStore, + localVue, + sync: false, + }); + }; + + beforeEach(() => { + setErrorMessageMock.mockReset(); + }); + + afterEach(() => { + wrapper.destroy(); + wrapper = null; + }); + + it('renders error message', () => { + const text = 'error message'; + createComponent({ text }); + expect(wrapper.text()).toContain(text); + }); + + it('clears error message on click', () => { + createComponent(); + wrapper.trigger('click'); + + expect(setErrorMessageMock).toHaveBeenCalledWith(expect.any(Object), null, undefined); + }); + + describe('with action', () => { + let actionMock; + + const message = { + actionText: 'test action', + actionPayload: 'testActionPayload', + }; + + beforeEach(() => { + actionMock = jest.fn().mockResolvedValue(); + createComponent({ + ...message, + action: actionMock, + }); + }); + + it('renders action button', () => { + const button = wrapper.find('button'); + + expect(button.exists()).toBe(true); + expect(button.text()).toContain(message.actionText); + }); + + it('does not clear error message on click', () => { + wrapper.trigger('click'); + + expect(setErrorMessageMock).not.toHaveBeenCalled(); + }); + + it('dispatches action', () => { + wrapper.find('button').trigger('click'); + + expect(actionMock).toHaveBeenCalledWith(message.actionPayload); + }); + + it('does not dispatch action when already loading', () => { + wrapper.find('button').trigger('click'); + actionMock.mockReset(); + wrapper.find('button').trigger('click'); + expect(actionMock).not.toHaveBeenCalled(); + }); + + it('shows loading icon when loading', () => { + let resolve; + actionMock.mockImplementation( + () => + new Promise(ok => { + resolve = ok; + }), + ); + wrapper.find('button').trigger('click'); + + return wrapper.vm.$nextTick(() => { + expect(wrapper.find(GlLoadingIcon).isVisible()).toBe(true); + resolve(); + }); + }); + + it('hides loading icon when operation finishes', () => { + wrapper.find('button').trigger('click'); + return actionMock() + .then(() => wrapper.vm.$nextTick()) + .then(() => { + expect(wrapper.find(GlLoadingIcon).isVisible()).toBe(false); + }); + }); + }); +}); diff --git a/spec/frontend/lib/utils/datetime_utility_spec.js b/spec/frontend/lib/utils/datetime_utility_spec.js index 9f1700bb243..8cbf170b3e6 100644 --- a/spec/frontend/lib/utils/datetime_utility_spec.js +++ b/spec/frontend/lib/utils/datetime_utility_spec.js @@ -388,20 +388,6 @@ describe('prettyTime methods', () => { expect(datetimeUtility.stringifyTime(timeObject, true)).toEqual('1 week 1 hour'); }); }); - - describe('abbreviateTime', () => { - it('should abbreviate stringified times for weeks', () => { - const fullTimeString = '1w 3d 4h 5m'; - - expect(datetimeUtility.abbreviateTime(fullTimeString)).toBe('1w'); - }); - - it('should abbreviate stringified times for non-weeks', () => { - const fullTimeString = '0w 3d 4h 5m'; - - expect(datetimeUtility.abbreviateTime(fullTimeString)).toBe('3d'); - }); - }); }); describe('calculateRemainingMilliseconds', () => { diff --git a/spec/frontend/vue_shared/components/recaptcha_eventhub_spec.js b/spec/frontend/vue_shared/components/recaptcha_eventhub_spec.js new file mode 100644 index 00000000000..d86d627886f --- /dev/null +++ b/spec/frontend/vue_shared/components/recaptcha_eventhub_spec.js @@ -0,0 +1,21 @@ +import { eventHub, callbackName } from '~/vue_shared/components/recaptcha_eventhub'; + +describe('reCAPTCHA event hub', () => { + // the following test case currently crashes + // see https://gitlab.com/gitlab-org/gitlab/issues/29192#note_217840035 + // eslint-disable-next-line jest/no-disabled-tests + it.skip('throws an error for overriding the callback', () => { + expect(() => { + window[callbackName] = 'something'; + }).toThrow(); + }); + + it('triggering callback emits a submit event', () => { + const eventHandler = jest.fn(); + eventHub.$once('submit', eventHandler); + + window[callbackName](); + + expect(eventHandler).toHaveBeenCalled(); + }); +}); diff --git a/spec/frontend/vue_shared/components/recaptcha_modal_spec.js b/spec/frontend/vue_shared/components/recaptcha_modal_spec.js new file mode 100644 index 00000000000..e509fe09d94 --- /dev/null +++ b/spec/frontend/vue_shared/components/recaptcha_modal_spec.js @@ -0,0 +1,36 @@ +import { shallowMount } from '@vue/test-utils'; + +import { eventHub } from '~/vue_shared/components/recaptcha_eventhub'; + +import RecaptchaModal from '~/vue_shared/components/recaptcha_modal'; + +describe('RecaptchaModal', () => { + const recaptchaFormId = 'recaptcha-form'; + const recaptchaHtml = `<form id="${recaptchaFormId}"></form>`; + + let wrapper; + + const findRecaptchaForm = () => wrapper.find(`#${recaptchaFormId}`).element; + + beforeEach(() => { + wrapper = shallowMount(RecaptchaModal, { + sync: false, + propsData: { + html: recaptchaHtml, + }, + }); + }); + + afterEach(() => { + wrapper.destroy(); + }); + + it('submits the form if event hub emits submit event', () => { + const form = findRecaptchaForm(); + jest.spyOn(form, 'submit').mockImplementation(); + + eventHub.$emit('submit'); + + expect(form.submit).toHaveBeenCalled(); + }); +}); diff --git a/spec/javascripts/ide/components/error_message_spec.js b/spec/javascripts/ide/components/error_message_spec.js deleted file mode 100644 index 80d6c7fd564..00000000000 --- a/spec/javascripts/ide/components/error_message_spec.js +++ /dev/null @@ -1,106 +0,0 @@ -import Vue from 'vue'; -import store from '~/ide/stores'; -import ErrorMessage from '~/ide/components/error_message.vue'; -import { createComponentWithStore } from '../../helpers/vue_mount_component_helper'; -import { resetStore } from '../helpers'; - -describe('IDE error message component', () => { - const Component = Vue.extend(ErrorMessage); - let vm; - - beforeEach(() => { - vm = createComponentWithStore(Component, store, { - message: { - text: 'error message', - action: null, - actionText: null, - }, - }).$mount(); - }); - - afterEach(() => { - vm.$destroy(); - resetStore(vm.$store); - }); - - it('renders error message', () => { - expect(vm.$el.textContent).toContain('error message'); - }); - - it('clears error message on click', () => { - spyOn(vm, 'setErrorMessage'); - - vm.$el.click(); - - expect(vm.setErrorMessage).toHaveBeenCalledWith(null); - }); - - describe('with action', () => { - let actionSpy; - - beforeEach(done => { - actionSpy = jasmine.createSpy('action').and.returnValue(Promise.resolve()); - - vm.message.action = actionSpy; - vm.message.actionText = 'test action'; - vm.message.actionPayload = 'testActionPayload'; - - vm.$nextTick(done); - }); - - it('renders action button', () => { - expect(vm.$el.querySelector('.flash-action')).not.toBe(null); - expect(vm.$el.textContent).toContain('test action'); - }); - - it('does not clear error message on click', () => { - spyOn(vm, 'setErrorMessage'); - - vm.$el.click(); - - expect(vm.setErrorMessage).not.toHaveBeenCalled(); - }); - - it('dispatches action', done => { - vm.$el.querySelector('.flash-action').click(); - - vm.$nextTick(() => { - expect(actionSpy).toHaveBeenCalledWith('testActionPayload'); - - done(); - }); - }); - - it('does not dispatch action when already loading', () => { - vm.isLoading = true; - - vm.$el.querySelector('.flash-action').click(); - - expect(actionSpy).not.toHaveBeenCalledWith(); - }); - - it('resets isLoading after click', done => { - vm.$el.querySelector('.flash-action').click(); - - expect(vm.isLoading).toBe(true); - - setTimeout(() => { - expect(vm.isLoading).toBe(false); - - done(); - }); - }); - - it('shows loading icon when isLoading is true', done => { - expect(vm.$el.querySelector('.loading-container').style.display).not.toBe(''); - - vm.isLoading = true; - - vm.$nextTick(() => { - expect(vm.$el.querySelector('.loading-container').style.display).toBe(''); - - done(); - }); - }); - }); -}); diff --git a/spec/javascripts/sidebar/components/time_tracking/time_tracker_spec.js b/spec/javascripts/sidebar/components/time_tracking/time_tracker_spec.js index 2e1863cff86..ab28190ae64 100644 --- a/spec/javascripts/sidebar/components/time_tracking/time_tracker_spec.js +++ b/spec/javascripts/sidebar/components/time_tracking/time_tracker_spec.js @@ -83,8 +83,8 @@ describe('Issuable Time Tracker', () => { initTimeTrackingComponent({ timeEstimate: 100000, // 1d 3h timeSpent: 5000, // 1h 23m - timeEstimateHumanReadable: '', - timeSpentHumanReadable: '', + timeEstimateHumanReadable: '1d 3h', + timeSpentHumanReadable: '1h 23m', }); }); @@ -98,6 +98,16 @@ describe('Issuable Time Tracker', () => { }); }); + it('should show full times when the sidebar is collapsed', done => { + Vue.nextTick(() => { + const timeTrackingText = vm.$el.querySelector('.time-tracking-collapsed-summary span') + .innerText; + + expect(timeTrackingText).toBe('1h 23m / 1d 3h'); + done(); + }); + }); + describe('Remaining meter', () => { it('should display the remaining meter with the correct width', done => { Vue.nextTick(() => { diff --git a/spec/routing/project_routing_spec.rb b/spec/routing/project_routing_spec.rb index 7e2d70d6eb5..acdbf064a73 100644 --- a/spec/routing/project_routing_spec.rb +++ b/spec/routing/project_routing_spec.rb @@ -276,6 +276,11 @@ describe 'project routing' do expect(get('/gitlab/gitlabhq/refs/feature%2B45/logs_tree/foo/bar/baz')).to route_to('projects/refs#logs_tree', namespace_id: 'gitlab', project_id: 'gitlabhq', id: 'feature+45', path: 'foo/bar/baz') expect(get('/gitlab/gitlabhq/refs/feature@45/logs_tree/foo/bar/baz')).to route_to('projects/refs#logs_tree', namespace_id: 'gitlab', project_id: 'gitlabhq', id: 'feature@45', path: 'foo/bar/baz') expect(get('/gitlab/gitlabhq/refs/stable/logs_tree/files.scss')).to route_to('projects/refs#logs_tree', namespace_id: 'gitlab', project_id: 'gitlabhq', id: 'stable', path: 'files.scss') + assert_routing({ path: "/gitlab/gitlabhq/refs/stable/logs_tree/new%0A%0Aline.txt", + method: :get }, + { controller: 'projects/refs', action: 'logs_tree', + namespace_id: 'gitlab', project_id: 'gitlabhq', + id: "stable", path: "new\n\nline.txt" }) end end diff --git a/spec/services/boards/lists/update_service_spec.rb b/spec/services/boards/lists/update_service_spec.rb index f28bbab941a..a5411a2fb3a 100644 --- a/spec/services/boards/lists/update_service_spec.rb +++ b/spec/services/boards/lists/update_service_spec.rb @@ -10,9 +10,8 @@ describe Boards::Lists::UpdateService do context 'when user can admin list' do it 'calls Lists::MoveService to update list position' do board.parent.add_developer(user) - service = described_class.new(board.parent, user, position: 1) - expect(Boards::Lists::MoveService).to receive(:new).with(board.parent, user, { position: 1 }).and_call_original + expect(Boards::Lists::MoveService).to receive(:new).with(board.parent, user, params).and_call_original expect_any_instance_of(Boards::Lists::MoveService).to receive(:execute).with(list) service.execute(list) @@ -21,8 +20,6 @@ describe Boards::Lists::UpdateService do context 'when user cannot admin list' do it 'does not call Lists::MoveService to update list position' do - service = described_class.new(board.parent, user, position: 1) - expect(Boards::Lists::MoveService).not_to receive(:new) service.execute(list) @@ -34,7 +31,6 @@ describe Boards::Lists::UpdateService do context 'when user can read list' do it 'updates list preference for user' do board.parent.add_guest(user) - service = described_class.new(board.parent, user, collapsed: true) service.execute(list) @@ -44,8 +40,6 @@ describe Boards::Lists::UpdateService do context 'when user cannot read list' do it 'does not update list preference for user' do - service = described_class.new(board.parent, user, collapsed: true) - service.execute(list) expect(list.preferences_for(user).collapsed).to be_nil @@ -54,35 +48,61 @@ describe Boards::Lists::UpdateService do end describe '#execute' do + let(:service) { described_class.new(board.parent, user, params) } + context 'when position parameter is present' do + let(:params) { { position: 1 } } + context 'for projects' do - it_behaves_like 'moving list' do - let(:project) { create(:project, :private) } - let(:board) { create(:board, project: project) } - end + let(:project) { create(:project, :private) } + let(:board) { create(:board, project: project) } + + it_behaves_like 'moving list' end context 'for groups' do - it_behaves_like 'moving list' do - let(:group) { create(:group, :private) } - let(:board) { create(:board, group: group) } - end + let(:group) { create(:group, :private) } + let(:board) { create(:board, group: group) } + + it_behaves_like 'moving list' end end context 'when collapsed parameter is present' do + let(:params) { { collapsed: true } } + context 'for projects' do - it_behaves_like 'updating list preferences' do - let(:project) { create(:project, :private) } - let(:board) { create(:board, project: project) } - end + let(:project) { create(:project, :private) } + let(:board) { create(:board, project: project) } + + it_behaves_like 'updating list preferences' end context 'for groups' do - it_behaves_like 'updating list preferences' do - let(:group) { create(:group, :private) } - let(:board) { create(:board, group: group) } - end + let(:project) { create(:project, :private) } + let(:board) { create(:board, project: project) } + + it_behaves_like 'updating list preferences' + end + end + + context 'when position and collapsed are both present' do + let(:params) { { collapsed: true, position: 1 } } + + context 'for projects' do + let(:project) { create(:project, :private) } + let(:board) { create(:board, project: project) } + + it_behaves_like 'moving list' + it_behaves_like 'updating list preferences' + end + + context 'for groups' do + let(:group) { create(:group, :private) } + let(:board) { create(:board, group: group) } + + it_behaves_like 'moving list' + it_behaves_like 'updating list preferences' end end end diff --git a/spec/support/shared_examples/boards/multiple_issue_boards_shared_examples.rb b/spec/support/shared_examples/boards/multiple_issue_boards_shared_examples.rb index 07c541ec189..f2f31e1b7f2 100644 --- a/spec/support/shared_examples/boards/multiple_issue_boards_shared_examples.rb +++ b/spec/support/shared_examples/boards/multiple_issue_boards_shared_examples.rb @@ -11,10 +11,6 @@ shared_examples_for 'multiple issue boards' do wait_for_requests end - it 'shows board switcher' do - expect(page).to have_css('.boards-switcher') - end - it 'shows current board name' do page.within('.boards-switcher') do expect(page).to have_content(board.name) |