From cfe4d2f29dcdcfad96ae7ba5a5eb822fbe46a9a7 Mon Sep 17 00:00:00 2001 From: Phil Hughes Date: Wed, 23 May 2018 11:44:47 +0100 Subject: added tab component --- .../vue_shared/components/tabs/tab_spec.js | 32 ++++++++++ .../vue_shared/components/tabs/tabs_spec.js | 68 ++++++++++++++++++++++ 2 files changed, 100 insertions(+) create mode 100644 spec/javascripts/vue_shared/components/tabs/tab_spec.js create mode 100644 spec/javascripts/vue_shared/components/tabs/tabs_spec.js (limited to 'spec/javascripts') diff --git a/spec/javascripts/vue_shared/components/tabs/tab_spec.js b/spec/javascripts/vue_shared/components/tabs/tab_spec.js new file mode 100644 index 00000000000..8437fe37738 --- /dev/null +++ b/spec/javascripts/vue_shared/components/tabs/tab_spec.js @@ -0,0 +1,32 @@ +import Vue from 'vue'; +import mountComponent from 'spec/helpers/vue_mount_component_helper'; +import Tab from '~/vue_shared/components/tabs/tab.vue'; + +describe('Tab component', () => { + const Component = Vue.extend(Tab); + let vm; + + beforeEach(() => { + vm = mountComponent(Component); + }); + + it('sets localActive to equal active', done => { + vm.active = true; + + vm.$nextTick(() => { + expect(vm.localActive).toBe(true); + + done(); + }); + }); + + it('sets active class', done => { + vm.active = true; + + vm.$nextTick(() => { + expect(vm.$el.classList).toContain('active'); + + done(); + }); + }); +}); diff --git a/spec/javascripts/vue_shared/components/tabs/tabs_spec.js b/spec/javascripts/vue_shared/components/tabs/tabs_spec.js new file mode 100644 index 00000000000..07752329965 --- /dev/null +++ b/spec/javascripts/vue_shared/components/tabs/tabs_spec.js @@ -0,0 +1,68 @@ +import Vue from 'vue'; +import Tabs from '~/vue_shared/components/tabs/tabs'; +import Tab from '~/vue_shared/components/tabs/tab.vue'; + +describe('Tabs component', () => { + let vm; + + beforeEach(done => { + vm = new Vue({ + components: { + Tabs, + Tab, + }, + template: ` +
+ + + First tab + + + + Second tab + + +
+ `, + }).$mount(); + + setTimeout(done); + }); + + describe('tab links', () => { + it('renders links for tabs', () => { + expect(vm.$el.querySelectorAll('a').length).toBe(2); + }); + + it('renders link titles from props', () => { + expect(vm.$el.querySelector('a').textContent).toContain('Testing'); + }); + + it('renders link titles from slot', () => { + expect(vm.$el.querySelectorAll('a')[1].textContent).toContain('Test slot'); + }); + + it('renders active class', () => { + expect(vm.$el.querySelector('li').classList).toContain('active'); + }); + + it('updates active class on click', done => { + vm.$el.querySelectorAll('a')[1].click(); + + setTimeout(() => { + expect(vm.$el.querySelector('li').classList).not.toContain('active'); + expect(vm.$el.querySelectorAll('li')[1].classList).toContain('active'); + + done(); + }); + }); + }); + + describe('content', () => { + it('renders content panes', () => { + expect(vm.$el.querySelectorAll('.tab-pane').length).toBe(2); + expect(vm.$el.querySelectorAll('.tab-pane')[0].textContent).toContain('First tab'); + expect(vm.$el.querySelectorAll('.tab-pane')[1].textContent).toContain('Second tab'); + }); + }); +}); -- cgit v1.2.1 From 8b1c43bde36c902d138b06dfb91ffab4bc1eb8ad Mon Sep 17 00:00:00 2001 From: Phil Hughes Date: Thu, 24 May 2018 15:07:09 +0100 Subject: spec fixes --- spec/javascripts/ide/mock_data.js | 21 +++++ .../ide/stores/modules/pipelines/actions_spec.js | 100 ++++++--------------- .../ide/stores/modules/pipelines/getters_spec.js | 31 ------- .../ide/stores/modules/pipelines/mutations_spec.js | 61 ++++--------- 4 files changed, 68 insertions(+), 145 deletions(-) (limited to 'spec/javascripts') diff --git a/spec/javascripts/ide/mock_data.js b/spec/javascripts/ide/mock_data.js index 7e641c7984b..a0cb8bae91c 100644 --- a/spec/javascripts/ide/mock_data.js +++ b/spec/javascripts/ide/mock_data.js @@ -29,6 +29,27 @@ export const pipelines = [ }, ]; +export const stages = [ + { + dropdown_path: 'testing', + name: 'build', + status: { + icon: 'status_failed', + group: 'failed', + text: 'Failed', + }, + }, + { + dropdown_path: 'testing', + name: 'test', + status: { + icon: 'status_failed', + group: 'failed', + text: 'Failed', + }, + }, +]; + export const jobs = [ { id: 1, diff --git a/spec/javascripts/ide/stores/modules/pipelines/actions_spec.js b/spec/javascripts/ide/stores/modules/pipelines/actions_spec.js index 85fbcf8084b..bcf9d9e1513 100644 --- a/spec/javascripts/ide/stores/modules/pipelines/actions_spec.js +++ b/spec/javascripts/ide/stores/modules/pipelines/actions_spec.js @@ -5,15 +5,15 @@ import actions, { receiveLatestPipelineError, receiveLatestPipelineSuccess, fetchLatestPipeline, - requestJobs, - receiveJobsError, - receiveJobsSuccess, - fetchJobs, + requestStages, + receiveStagesError, + receiveStagesSuccess, + fetchStages, } from '~/ide/stores/modules/pipelines/actions'; import state from '~/ide/stores/modules/pipelines/state'; import * as types from '~/ide/stores/modules/pipelines/mutation_types'; import testAction from '../../../../helpers/vuex_action_helper'; -import { pipelines, jobs } from '../../../mock_data'; +import { pipelines, stages } from '../../../mock_data'; describe('IDE pipelines actions', () => { let mockedState; @@ -141,19 +141,19 @@ describe('IDE pipelines actions', () => { }); }); - describe('requestJobs', () => { + describe('requestStages', () => { it('commits request', done => { - testAction(requestJobs, null, mockedState, [{ type: types.REQUEST_JOBS }], [], done); + testAction(requestStages, null, mockedState, [{ type: types.REQUEST_STAGES }], [], done); }); }); describe('receiveJobsError', () => { it('commits error', done => { testAction( - receiveJobsError, + receiveStagesError, null, mockedState, - [{ type: types.RECEIVE_JOBS_ERROR }], + [{ type: types.RECEIVE_STAGES_ERROR }], [], done, ); @@ -162,80 +162,53 @@ describe('IDE pipelines actions', () => { it('creates flash message', () => { const flashSpy = spyOnDependency(actions, 'flash'); - receiveJobsError({ commit() {} }); + receiveStagesError({ commit() {} }); expect(flashSpy).toHaveBeenCalled(); }); }); - describe('receiveJobsSuccess', () => { + describe('receiveStagesSuccess', () => { it('commits jobs', done => { testAction( - receiveJobsSuccess, - jobs, + receiveStagesSuccess, + stages, mockedState, - [{ type: types.RECEIVE_JOBS_SUCCESS, payload: jobs }], + [{ type: types.RECEIVE_STAGES_SUCCESS, payload: stages }], [], done, ); }); }); - describe('fetchJobs', () => { - let page = ''; - + describe('fetchStages', () => { beforeEach(() => { mockedState.latestPipeline = pipelines[0]; }); describe('success', () => { beforeEach(() => { - mock.onGet(/\/api\/v4\/projects\/(.*)\/pipelines\/(.*)\/jobs/).replyOnce(() => [ - 200, - jobs, - { - 'x-next-page': page, - }, - ]); + mock.onGet(/\/(.*)\/pipelines\/(.*)\/builds.json/).replyOnce(200, stages); }); it('dispatches request', done => { testAction( - fetchJobs, + fetchStages, null, mockedState, [], - [{ type: 'requestJobs' }, { type: 'receiveJobsSuccess' }], + [{ type: 'requestStages' }, { type: 'receiveStagesSuccess' }], done, ); }); it('dispatches success with latest pipeline', done => { testAction( - fetchJobs, + fetchStages, null, mockedState, [], - [{ type: 'requestJobs' }, { type: 'receiveJobsSuccess', payload: jobs }], - done, - ); - }); - - it('dispatches twice for both pages', done => { - page = '2'; - - testAction( - fetchJobs, - null, - mockedState, - [], - [ - { type: 'requestJobs' }, - { type: 'receiveJobsSuccess', payload: jobs }, - { type: 'fetchJobs', payload: '2' }, - { type: 'requestJobs' }, - { type: 'receiveJobsSuccess', payload: jobs }, - ], + [{ type: 'requestStages' }, { type: 'receiveStagesSuccess', payload: stages }], done, ); }); @@ -243,44 +216,27 @@ describe('IDE pipelines actions', () => { it('calls axios with correct URL', () => { const apiSpy = spyOn(axios, 'get').and.callThrough(); - fetchJobs({ dispatch() {}, state: mockedState, rootState: mockedState }); + fetchStages({ dispatch() {}, state: mockedState, rootState: mockedState }); - expect(apiSpy).toHaveBeenCalledWith('/api/v4/projects/test%2Fproject/pipelines/1/jobs', { - params: { page: '1' }, - }); - }); - - it('calls axios with page next page', () => { - const apiSpy = spyOn(axios, 'get').and.callThrough(); - - fetchJobs({ dispatch() {}, state: mockedState, rootState: mockedState }); - - expect(apiSpy).toHaveBeenCalledWith('/api/v4/projects/test%2Fproject/pipelines/1/jobs', { - params: { page: '1' }, - }); - - page = '2'; - - fetchJobs({ dispatch() {}, state: mockedState, rootState: mockedState }, page); - - expect(apiSpy).toHaveBeenCalledWith('/api/v4/projects/test%2Fproject/pipelines/1/jobs', { - params: { page: '2' }, - }); + expect(apiSpy).toHaveBeenCalledWith( + '/test/project/pipelines/1/builds.json', + jasmine.anything(), + ); }); }); describe('error', () => { beforeEach(() => { - mock.onGet(/\/api\/v4\/projects\/(.*)\/pipelines(.*)/).replyOnce(500); + mock.onGet(/\/(.*)\/pipelines\/(.*)\/builds.json/).replyOnce(500); }); it('dispatches error', done => { testAction( - fetchJobs, + fetchStages, null, mockedState, [], - [{ type: 'requestJobs' }, { type: 'receiveJobsError' }], + [{ type: 'requestStages' }, { type: 'receiveStagesError' }], done, ); }); diff --git a/spec/javascripts/ide/stores/modules/pipelines/getters_spec.js b/spec/javascripts/ide/stores/modules/pipelines/getters_spec.js index b2a7e8a9025..4514896b5ea 100644 --- a/spec/javascripts/ide/stores/modules/pipelines/getters_spec.js +++ b/spec/javascripts/ide/stores/modules/pipelines/getters_spec.js @@ -37,35 +37,4 @@ describe('IDE pipeline getters', () => { expect(getters.hasLatestPipeline(mockedState)).toBe(true); }); }); - - describe('failedJobs', () => { - it('returns array of failed jobs', () => { - mockedState.stages = [ - { - title: 'test', - jobs: [{ id: 1, status: 'failed' }, { id: 2, status: 'success' }], - }, - { - title: 'build', - jobs: [{ id: 3, status: 'failed' }, { id: 4, status: 'failed' }], - }, - ]; - - expect(getters.failedJobs(mockedState).length).toBe(3); - expect(getters.failedJobs(mockedState)).toEqual([ - { - id: 1, - status: jasmine.anything(), - }, - { - id: 3, - status: jasmine.anything(), - }, - { - id: 4, - status: jasmine.anything(), - }, - ]); - }); - }); }); diff --git a/spec/javascripts/ide/stores/modules/pipelines/mutations_spec.js b/spec/javascripts/ide/stores/modules/pipelines/mutations_spec.js index 8262e916243..d47ec33ad4d 100644 --- a/spec/javascripts/ide/stores/modules/pipelines/mutations_spec.js +++ b/spec/javascripts/ide/stores/modules/pipelines/mutations_spec.js @@ -1,7 +1,7 @@ import mutations from '~/ide/stores/modules/pipelines/mutations'; import state from '~/ide/stores/modules/pipelines/state'; import * as types from '~/ide/stores/modules/pipelines/mutation_types'; -import { pipelines, jobs } from '../../../mock_data'; +import { pipelines, stages } from '../../../mock_data'; describe('IDE pipelines mutations', () => { let mockedState; @@ -49,70 +49,47 @@ describe('IDE pipelines mutations', () => { }); }); - describe(types.REQUEST_JOBS, () => { - it('sets jobs loading to true', () => { - mutations[types.REQUEST_JOBS](mockedState); + describe(types.REQUEST_STAGES, () => { + it('sets stages loading to true', () => { + mutations[types.REQUEST_STAGES](mockedState); expect(mockedState.isLoadingJobs).toBe(true); }); }); - describe(types.RECEIVE_JOBS_ERROR, () => { + describe(types.RECEIVE_STAGES_ERROR, () => { it('sets jobs loading to false', () => { - mutations[types.RECEIVE_JOBS_ERROR](mockedState); + mutations[types.RECEIVE_STAGES_ERROR](mockedState); expect(mockedState.isLoadingJobs).toBe(false); }); }); - describe(types.RECEIVE_JOBS_SUCCESS, () => { + describe(types.RECEIVE_STAGES_SUCCESS, () => { it('sets jobs loading to false on success', () => { - mutations[types.RECEIVE_JOBS_SUCCESS](mockedState, jobs); + mutations[types.RECEIVE_STAGES_SUCCESS](mockedState, stages); expect(mockedState.isLoadingJobs).toBe(false); }); it('sets stages', () => { - mutations[types.RECEIVE_JOBS_SUCCESS](mockedState, jobs); + mutations[types.RECEIVE_STAGES_SUCCESS](mockedState, stages); expect(mockedState.stages.length).toBe(2); expect(mockedState.stages).toEqual([ { - title: 'test', - jobs: jasmine.anything(), + ...stages[0], + id: 0, + isCollapsed: false, + isLoading: false, + jobs: [], }, { - title: 'build', - jobs: jasmine.anything(), - }, - ]); - }); - - it('sets jobs in stages', () => { - mutations[types.RECEIVE_JOBS_SUCCESS](mockedState, jobs); - - expect(mockedState.stages[0].jobs.length).toBe(3); - expect(mockedState.stages[1].jobs.length).toBe(1); - expect(mockedState.stages).toEqual([ - { - title: jasmine.anything(), - jobs: jobs.filter(job => job.stage === 'test').map(job => ({ - id: job.id, - name: job.name, - status: job.status, - stage: job.stage, - duration: job.duration, - })), - }, - { - title: jasmine.anything(), - jobs: jobs.filter(job => job.stage === 'build').map(job => ({ - id: job.id, - name: job.name, - status: job.status, - stage: job.stage, - duration: job.duration, - })), + ...stages[1], + id: 1, + isCollapsed: false, + isLoading: false, + jobs: [], }, ]); }); -- cgit v1.2.1 From a57e43dd8cf29b7574fcab63948fa2aaca1c00b7 Mon Sep 17 00:00:00 2001 From: Phil Hughes Date: Fri, 11 May 2018 10:37:10 +0100 Subject: Show merge requests in web IDE Closes #45184 --- spec/javascripts/ide/mock_data.js | 8 ++ .../stores/modules/merge_requests/actions_spec.js | 144 +++++++++++++++++++++ .../modules/merge_requests/mutations_spec.js | 41 ++++++ 3 files changed, 193 insertions(+) create mode 100644 spec/javascripts/ide/stores/modules/merge_requests/actions_spec.js create mode 100644 spec/javascripts/ide/stores/modules/merge_requests/mutations_spec.js (limited to 'spec/javascripts') diff --git a/spec/javascripts/ide/mock_data.js b/spec/javascripts/ide/mock_data.js index c68ae050641..3c09ff36afa 100644 --- a/spec/javascripts/ide/mock_data.js +++ b/spec/javascripts/ide/mock_data.js @@ -93,3 +93,11 @@ export const fullPipelinesResponse = { ], }, }; + +export const mergeRequests = [ + { + iid: 1, + title: 'Test merge request', + project_id: 1, + }, +]; diff --git a/spec/javascripts/ide/stores/modules/merge_requests/actions_spec.js b/spec/javascripts/ide/stores/modules/merge_requests/actions_spec.js new file mode 100644 index 00000000000..5d076577753 --- /dev/null +++ b/spec/javascripts/ide/stores/modules/merge_requests/actions_spec.js @@ -0,0 +1,144 @@ +import MockAdapter from 'axios-mock-adapter'; +import axios from '~/lib/utils/axios_utils'; +import state from '~/ide/stores/modules/merge_requests/state'; +import * as types from '~/ide/stores/modules/merge_requests/mutation_types'; +import actions, { + requestMergeRequests, + receiveMergeRequestsError, + receiveMergeRequestsSuccess, + fetchMergeRequests, +} from '~/ide/stores/modules/merge_requests/actions'; +import { mergeRequests } from '../../../mock_data'; +import testAction from '../../../../helpers/vuex_action_helper'; + +describe('IDe merge requests actions', () => { + let mockedState; + let mock; + + beforeEach(() => { + mockedState = state(); + mock = new MockAdapter(axios); + }); + + afterEach(() => { + mock.restore(); + }); + + describe('requestMergeRequests', () => { + it('should should commit request', done => { + testAction( + requestMergeRequests, + null, + mockedState, + [{ type: types.REQUEST_MERGE_REQUESTS }], + [], + done, + ); + }); + }); + + describe('receiveMergeRequestsError', () => { + let flashSpy; + + beforeEach(() => { + flashSpy = spyOnDependency(actions, 'flash'); + }); + + it('should should commit error', done => { + testAction( + receiveMergeRequestsError, + null, + mockedState, + [{ type: types.RECEIVE_MERGE_REQUESTS_ERROR }], + [], + done, + ); + }); + + it('creates flash message', () => { + receiveMergeRequestsError({ commit() {} }); + + expect(flashSpy).toHaveBeenCalled(); + }); + }); + + describe('receiveMergeRequestsSuccess', () => { + it('should commit received data', done => { + testAction( + receiveMergeRequestsSuccess, + 'data', + mockedState, + [{ type: types.RECEIVE_MERGE_REQUESTS_SUCCESS, payload: 'data' }], + [], + done, + ); + }); + }); + + describe('fetchMergeRequests', () => { + beforeEach(() => { + gon.api_version = 'v4'; + }); + + describe('success', () => { + beforeEach(() => { + mock.onGet(/\/api\/v4\/merge_requests(.*)$/).replyOnce(200, mergeRequests); + }); + + it('calls API with params from state', () => { + const apiSpy = spyOn(axios, 'get').and.callThrough(); + + fetchMergeRequests({ dispatch() {}, state: mockedState }); + + expect(apiSpy).toHaveBeenCalledWith(jasmine.anything(), { + params: { + scope: 'assigned-to-me', + view: 'simple', + }, + }); + }); + + it('dispatches request', done => { + testAction( + fetchMergeRequests, + null, + mockedState, + [], + [{ type: 'requestMergeRequests' }, { type: 'receiveMergeRequestsSuccess' }], + done, + ); + }); + + it('dispatches success with received data', done => { + testAction( + fetchMergeRequests, + null, + mockedState, + [], + [ + { type: 'requestMergeRequests' }, + { type: 'receiveMergeRequestsSuccess', payload: mergeRequests }, + ], + done, + ); + }); + }); + + describe('error', () => { + beforeEach(() => { + mock.onGet(/\/api\/v4\/merge_requests(.*)$/).replyOnce(500); + }); + + it('dispatches error', done => { + testAction( + fetchMergeRequests, + null, + mockedState, + [], + [{ type: 'requestMergeRequests' }, { type: 'receiveMergeRequestsError' }], + done, + ); + }); + }); + }); +}); diff --git a/spec/javascripts/ide/stores/modules/merge_requests/mutations_spec.js b/spec/javascripts/ide/stores/modules/merge_requests/mutations_spec.js new file mode 100644 index 00000000000..8983bf65b31 --- /dev/null +++ b/spec/javascripts/ide/stores/modules/merge_requests/mutations_spec.js @@ -0,0 +1,41 @@ +import state from '~/ide/stores/modules/merge_requests/state'; +import mutations from '~/ide/stores/modules/merge_requests/mutations'; +import * as types from '~/ide/stores/modules/merge_requests/mutation_types'; +import { mergeRequests } from '../../../mock_data'; + +describe('IDE merge requests mutations', () => { + let mockedState; + + beforeEach(() => { + mockedState = state(); + }); + + describe(types.REQUEST_MERGE_REQUESTS, () => { + it('sets loading to true', () => { + mutations[types.REQUEST_MERGE_REQUESTS](mockedState); + + expect(mockedState.isLoading).toBe(true); + }); + }); + + describe(types.RECEIVE_MERGE_REQUESTS_ERROR, () => { + it('sets loading to false', () => { + mutations[types.RECEIVE_MERGE_REQUESTS_ERROR](mockedState); + + expect(mockedState.isLoading).toBe(false); + }); + }); + + describe(types.RECEIVE_MERGE_REQUESTS_SUCCESS, () => { + it('sets merge requests', () => { + mutations[types.RECEIVE_MERGE_REQUESTS_SUCCESS](mockedState, mergeRequests); + + expect(mockedState.mergeRequests).toEqual([ + { + id: 1, + title: 'Test merge request', + }, + ]); + }); + }); +}); -- cgit v1.2.1 From 323e402e21a643da22714bdede3cc6e248ba2cf4 Mon Sep 17 00:00:00 2001 From: Phil Hughes Date: Fri, 11 May 2018 11:36:34 +0100 Subject: fixed tests failing caused by rewire --- spec/javascripts/ide/helpers.js | 2 ++ 1 file changed, 2 insertions(+) (limited to 'spec/javascripts') diff --git a/spec/javascripts/ide/helpers.js b/spec/javascripts/ide/helpers.js index 98db6defc7a..bc9a7f97b37 100644 --- a/spec/javascripts/ide/helpers.js +++ b/spec/javascripts/ide/helpers.js @@ -1,11 +1,13 @@ import { decorateData } from '~/ide/stores/utils'; import state from '~/ide/stores/state'; import commitState from '~/ide/stores/modules/commit/state'; +import mergeRequestsState from '~/ide/stores/modules/merge_requests/state'; export const resetStore = store => { const newState = { ...state(), commit: commitState(), + mergeRequests: mergeRequestsState(), }; store.replaceState(newState); }; -- cgit v1.2.1 From cdc92d94e0c676bf86877a5794d72bd4dcf88c24 Mon Sep 17 00:00:00 2001 From: Phil Hughes Date: Fri, 25 May 2018 15:09:20 +0100 Subject: fixed pipeline actions spec --- .../javascripts/ide/stores/actions/project_spec.js | 111 +---------- .../ide/stores/modules/pipelines/actions_spec.js | 221 +++++++-------------- .../ide/stores/mutations/branch_spec.js | 36 ---- 3 files changed, 72 insertions(+), 296 deletions(-) (limited to 'spec/javascripts') diff --git a/spec/javascripts/ide/stores/actions/project_spec.js b/spec/javascripts/ide/stores/actions/project_spec.js index 8e078ae7138..d71fc0e035e 100644 --- a/spec/javascripts/ide/stores/actions/project_spec.js +++ b/spec/javascripts/ide/stores/actions/project_spec.js @@ -1,31 +1,10 @@ -import Visibility from 'visibilityjs'; -import MockAdapter from 'axios-mock-adapter'; -import { refreshLastCommitData, pollSuccessCallBack } from '~/ide/stores/actions'; +import { refreshLastCommitData } from '~/ide/stores/actions'; import store from '~/ide/stores'; import service from '~/ide/services'; -import axios from '~/lib/utils/axios_utils'; -import { fullPipelinesResponse } from '../../mock_data'; import { resetStore } from '../../helpers'; import testAction from '../../../helpers/vuex_action_helper'; describe('IDE store project actions', () => { - const setProjectState = () => { - store.state.currentProjectId = 'abc/def'; - store.state.currentBranchId = 'master'; - store.state.projects['abc/def'] = { - id: 4, - path_with_namespace: 'abc/def', - branches: { - master: { - commit: { - id: 'abc123def456ghi789jkl', - title: 'example', - }, - }, - }, - }; - }; - beforeEach(() => { store.state.projects['abc/def'] = {}; }); @@ -101,92 +80,4 @@ describe('IDE store project actions', () => { ); }); }); - - describe('pipelinePoll', () => { - let mock; - - beforeEach(() => { - setProjectState(); - jasmine.clock().install(); - mock = new MockAdapter(axios); - mock - .onGet('/abc/def/commit/abc123def456ghi789jkl/pipelines') - .reply(200, { data: { foo: 'bar' } }, { 'poll-interval': '10000' }); - }); - - afterEach(() => { - jasmine.clock().uninstall(); - mock.restore(); - store.dispatch('stopPipelinePolling'); - }); - - it('calls service periodically', done => { - spyOn(axios, 'get').and.callThrough(); - spyOn(Visibility, 'hidden').and.returnValue(false); - - store - .dispatch('pipelinePoll') - .then(() => { - jasmine.clock().tick(1000); - - expect(axios.get).toHaveBeenCalled(); - expect(axios.get.calls.count()).toBe(1); - }) - .then(() => new Promise(resolve => requestAnimationFrame(resolve))) - .then(() => { - jasmine.clock().tick(10000); - expect(axios.get.calls.count()).toBe(2); - }) - .then(() => new Promise(resolve => requestAnimationFrame(resolve))) - .then(() => { - jasmine.clock().tick(10000); - expect(axios.get.calls.count()).toBe(3); - }) - .then(() => new Promise(resolve => requestAnimationFrame(resolve))) - .then(() => { - jasmine.clock().tick(10000); - expect(axios.get.calls.count()).toBe(4); - }) - - .then(done) - .catch(done.fail); - }); - }); - - describe('pollSuccessCallBack', () => { - beforeEach(() => { - setProjectState(); - }); - - it('commits correct pipeline', done => { - testAction( - pollSuccessCallBack, - fullPipelinesResponse, - store.state, - [ - { - type: 'SET_LAST_COMMIT_PIPELINE', - payload: { - projectId: 'abc/def', - branchId: 'master', - pipeline: { - id: '50', - commit: { - id: 'abc123def456ghi789jkl', - }, - details: { - status: { - icon: 'status_passed', - text: 'passed', - }, - }, - }, - }, - }, - ], // mutations - [], // action - done, - ); - }); - }); }); diff --git a/spec/javascripts/ide/stores/modules/pipelines/actions_spec.js b/spec/javascripts/ide/stores/modules/pipelines/actions_spec.js index bcf9d9e1513..273e09f5d12 100644 --- a/spec/javascripts/ide/stores/modules/pipelines/actions_spec.js +++ b/spec/javascripts/ide/stores/modules/pipelines/actions_spec.js @@ -1,3 +1,4 @@ +import Visibility from 'visibilityjs'; import MockAdapter from 'axios-mock-adapter'; import axios from '~/lib/utils/axios_utils'; import actions, { @@ -5,15 +6,13 @@ import actions, { receiveLatestPipelineError, receiveLatestPipelineSuccess, fetchLatestPipeline, - requestStages, - receiveStagesError, - receiveStagesSuccess, - fetchStages, + stopPipelinePolling, + clearEtagPoll, } from '~/ide/stores/modules/pipelines/actions'; import state from '~/ide/stores/modules/pipelines/state'; import * as types from '~/ide/stores/modules/pipelines/mutation_types'; import testAction from '../../../../helpers/vuex_action_helper'; -import { pipelines, stages } from '../../../mock_data'; +import { pipelines } from '../../../mock_data'; describe('IDE pipelines actions', () => { let mockedState; @@ -51,7 +50,7 @@ describe('IDE pipelines actions', () => { null, mockedState, [{ type: types.RECEIVE_LASTEST_PIPELINE_ERROR }], - [], + [{ type: 'stopPipelinePolling' }], done, ); }); @@ -59,7 +58,7 @@ describe('IDE pipelines actions', () => { it('creates flash message', () => { const flashSpy = spyOnDependency(actions, 'flash'); - receiveLatestPipelineError({ commit() {} }); + receiveLatestPipelineError({ commit() {}, dispatch() {} }); expect(flashSpy).toHaveBeenCalled(); }); @@ -79,166 +78,88 @@ describe('IDE pipelines actions', () => { }); describe('fetchLatestPipeline', () => { - describe('success', () => { - beforeEach(() => { - mock.onGet(/\/api\/v4\/projects\/(.*)\/pipelines(.*)/).replyOnce(200, pipelines); - }); - - it('dispatches request', done => { - testAction( - fetchLatestPipeline, - '123', - mockedState, - [], - [{ type: 'requestLatestPipeline' }, { type: 'receiveLatestPipelineSuccess' }], - done, - ); - }); - - it('dispatches success with latest pipeline', done => { - testAction( - fetchLatestPipeline, - '123', - mockedState, - [], - [ - { type: 'requestLatestPipeline' }, - { type: 'receiveLatestPipelineSuccess', payload: pipelines[0] }, - ], - done, - ); - }); - - it('calls axios with correct params', () => { - const apiSpy = spyOn(axios, 'get').and.callThrough(); - - fetchLatestPipeline({ dispatch() {}, rootState: state }, '123'); - - expect(apiSpy).toHaveBeenCalledWith(jasmine.anything(), { - params: { - sha: '123', - per_page: '1', - }, - }); - }); - }); - - describe('error', () => { - beforeEach(() => { - mock.onGet(/\/api\/v4\/projects\/(.*)\/pipelines(.*)/).replyOnce(500); - }); - - it('dispatches error', done => { - testAction( - fetchLatestPipeline, - '123', - mockedState, - [], - [{ type: 'requestLatestPipeline' }, { type: 'receiveLatestPipelineError' }], - done, - ); - }); - }); - }); - - describe('requestStages', () => { - it('commits request', done => { - testAction(requestStages, null, mockedState, [{ type: types.REQUEST_STAGES }], [], done); - }); - }); - - describe('receiveJobsError', () => { - it('commits error', done => { - testAction( - receiveStagesError, - null, - mockedState, - [{ type: types.RECEIVE_STAGES_ERROR }], - [], - done, - ); - }); - - it('creates flash message', () => { - const flashSpy = spyOnDependency(actions, 'flash'); - - receiveStagesError({ commit() {} }); - - expect(flashSpy).toHaveBeenCalled(); - }); - }); - - describe('receiveStagesSuccess', () => { - it('commits jobs', done => { - testAction( - receiveStagesSuccess, - stages, - mockedState, - [{ type: types.RECEIVE_STAGES_SUCCESS, payload: stages }], - [], - done, - ); + beforeEach(() => { + jasmine.clock().install(); }); - }); - describe('fetchStages', () => { - beforeEach(() => { - mockedState.latestPipeline = pipelines[0]; + afterEach(() => { + jasmine.clock().uninstall(); + stopPipelinePolling(); + clearEtagPoll(); }); describe('success', () => { beforeEach(() => { - mock.onGet(/\/(.*)\/pipelines\/(.*)\/builds.json/).replyOnce(200, stages); + mock + .onGet('/abc/def/commit/abc123def456ghi789jkl/pipelines') + .reply(200, { data: { foo: 'bar' } }, { 'poll-interval': '10000' }); }); it('dispatches request', done => { - testAction( - fetchStages, - null, - mockedState, - [], - [{ type: 'requestStages' }, { type: 'receiveStagesSuccess' }], - done, - ); - }); - - it('dispatches success with latest pipeline', done => { - testAction( - fetchStages, - null, - mockedState, - [], - [{ type: 'requestStages' }, { type: 'receiveStagesSuccess', payload: stages }], - done, - ); - }); - - it('calls axios with correct URL', () => { - const apiSpy = spyOn(axios, 'get').and.callThrough(); - - fetchStages({ dispatch() {}, state: mockedState, rootState: mockedState }); - - expect(apiSpy).toHaveBeenCalledWith( - '/test/project/pipelines/1/builds.json', - jasmine.anything(), - ); + spyOn(axios, 'get').and.callThrough(); + spyOn(Visibility, 'hidden').and.returnValue(false); + + const dispatch = jasmine.createSpy('dispatch'); + const rootGetters = { + lastCommit: { id: 'abc123def456ghi789jkl' }, + currentProject: { path_with_namespace: 'abc/def' }, + }; + + fetchLatestPipeline({ dispatch, rootGetters }); + + expect(dispatch.calls.argsFor(0)).toEqual(['requestLatestPipeline']); + + jasmine.clock().tick(1000); + + new Promise(resolve => requestAnimationFrame(resolve)) + .then(() => { + expect(axios.get).toHaveBeenCalled(); + expect(axios.get.calls.count()).toBe(1); + + expect(dispatch.calls.argsFor(1)).toEqual([ + 'receiveLatestPipelineSuccess', + jasmine.anything(), + ]); + + jasmine.clock().tick(10000); + }) + .then(() => new Promise(resolve => requestAnimationFrame(resolve))) + .then(() => { + expect(axios.get).toHaveBeenCalled(); + expect(axios.get.calls.count()).toBe(2); + + expect(dispatch.calls.argsFor(2)).toEqual([ + 'receiveLatestPipelineSuccess', + jasmine.anything(), + ]); + }) + .then(done) + .catch(done.fail); }); }); describe('error', () => { beforeEach(() => { - mock.onGet(/\/(.*)\/pipelines\/(.*)\/builds.json/).replyOnce(500); + mock.onGet('/abc/def/commit/abc123def456ghi789jkl/pipelines').reply(500); }); it('dispatches error', done => { - testAction( - fetchStages, - null, - mockedState, - [], - [{ type: 'requestStages' }, { type: 'receiveStagesError' }], - done, - ); + const dispatch = jasmine.createSpy('dispatch'); + const rootGetters = { + lastCommit: { id: 'abc123def456ghi789jkl' }, + currentProject: { path_with_namespace: 'abc/def' }, + }; + + fetchLatestPipeline({ dispatch, rootGetters }); + + jasmine.clock().tick(1500); + + new Promise(resolve => requestAnimationFrame(resolve)) + .then(() => { + expect(dispatch.calls.argsFor(1)).toEqual(['receiveLatestPipelineError']); + }) + .then(done) + .catch(done.fail); }); }); }); diff --git a/spec/javascripts/ide/stores/mutations/branch_spec.js b/spec/javascripts/ide/stores/mutations/branch_spec.js index f2f1f2a9a2e..29eb859ddaf 100644 --- a/spec/javascripts/ide/stores/mutations/branch_spec.js +++ b/spec/javascripts/ide/stores/mutations/branch_spec.js @@ -37,40 +37,4 @@ describe('Multi-file store branch mutations', () => { expect(localState.projects.Example.branches.master.commit.title).toBe('Example commit'); }); }); - - describe('SET_LAST_COMMIT_PIPELINE', () => { - it('sets the pipeline for the last commit on current project', () => { - localState.projects = { - Example: { - branches: { - master: { - commit: {}, - }, - }, - }, - }; - - mutations.SET_LAST_COMMIT_PIPELINE(localState, { - projectId: 'Example', - branchId: 'master', - pipeline: { - id: '50', - details: { - status: { - icon: 'status_passed', - text: 'passed', - }, - }, - }, - }); - - expect(localState.projects.Example.branches.master.commit.pipeline.id).toBe('50'); - expect(localState.projects.Example.branches.master.commit.pipeline.details.status.text).toBe( - 'passed', - ); - expect(localState.projects.Example.branches.master.commit.pipeline.details.status.icon).toBe( - 'status_passed', - ); - }); - }); }); -- cgit v1.2.1 From e1d4deb2d2bc91c0730b57f81196b290fdfa86cf Mon Sep 17 00:00:00 2001 From: Filipa Lacerda Date: Thu, 24 May 2018 21:00:21 +0100 Subject: Disables jupyter install button while ingress is not installed Includes juptyer hostname in the post request Adds tests --- spec/javascripts/clusters/clusters_bundle_spec.js | 32 +++++++-- .../clusters/components/application_row_spec.js | 32 ++++++++- .../clusters/components/applications_spec.js | 77 ++++++++++++++++++++-- spec/javascripts/clusters/services/mock_data.js | 35 ++++++++++ .../clusters/stores/clusters_store_spec.js | 18 +++++ 5 files changed, 180 insertions(+), 14 deletions(-) (limited to 'spec/javascripts') diff --git a/spec/javascripts/clusters/clusters_bundle_spec.js b/spec/javascripts/clusters/clusters_bundle_spec.js index a5cd247b689..abe2954d506 100644 --- a/spec/javascripts/clusters/clusters_bundle_spec.js +++ b/spec/javascripts/clusters/clusters_bundle_spec.js @@ -207,11 +207,11 @@ describe('Clusters', () => { spyOn(cluster.service, 'installApplication').and.returnValue(Promise.resolve()); expect(cluster.store.state.applications.helm.requestStatus).toEqual(null); - cluster.installApplication('helm'); + cluster.installApplication({ id: 'helm' }); expect(cluster.store.state.applications.helm.requestStatus).toEqual(REQUEST_LOADING); expect(cluster.store.state.applications.helm.requestReason).toEqual(null); - expect(cluster.service.installApplication).toHaveBeenCalledWith('helm'); + expect(cluster.service.installApplication).toHaveBeenCalledWith('helm', undefined); getSetTimeoutPromise() .then(() => { @@ -226,11 +226,11 @@ describe('Clusters', () => { spyOn(cluster.service, 'installApplication').and.returnValue(Promise.resolve()); expect(cluster.store.state.applications.ingress.requestStatus).toEqual(null); - cluster.installApplication('ingress'); + cluster.installApplication({ id: 'ingress' }); expect(cluster.store.state.applications.ingress.requestStatus).toEqual(REQUEST_LOADING); expect(cluster.store.state.applications.ingress.requestReason).toEqual(null); - expect(cluster.service.installApplication).toHaveBeenCalledWith('ingress'); + expect(cluster.service.installApplication).toHaveBeenCalledWith('ingress', undefined); getSetTimeoutPromise() .then(() => { @@ -245,11 +245,11 @@ describe('Clusters', () => { spyOn(cluster.service, 'installApplication').and.returnValue(Promise.resolve()); expect(cluster.store.state.applications.runner.requestStatus).toEqual(null); - cluster.installApplication('runner'); + cluster.installApplication({ id: 'runner' }); expect(cluster.store.state.applications.runner.requestStatus).toEqual(REQUEST_LOADING); expect(cluster.store.state.applications.runner.requestReason).toEqual(null); - expect(cluster.service.installApplication).toHaveBeenCalledWith('runner'); + expect(cluster.service.installApplication).toHaveBeenCalledWith('runner', undefined); getSetTimeoutPromise() .then(() => { @@ -260,11 +260,29 @@ describe('Clusters', () => { .catch(done.fail); }); + it('tries to install jupyter', (done) => { + spyOn(cluster.service, 'installApplication').and.returnValue(Promise.resolve()); + expect(cluster.store.state.applications.jupyter.requestStatus).toEqual(null); + cluster.installApplication({ id: 'jupyter', params: { hostname: cluster.store.state.applications.jupyter.hostname } }); + + expect(cluster.store.state.applications.jupyter.requestStatus).toEqual(REQUEST_LOADING); + expect(cluster.store.state.applications.jupyter.requestReason).toEqual(null); + expect(cluster.service.installApplication).toHaveBeenCalledWith('jupyter', { hostname: cluster.store.state.applications.jupyter.hostname }); + + getSetTimeoutPromise() + .then(() => { + expect(cluster.store.state.applications.jupyter.requestStatus).toEqual(REQUEST_SUCCESS); + expect(cluster.store.state.applications.jupyter.requestReason).toEqual(null); + }) + .then(done) + .catch(done.fail); + }); + it('sets error request status when the request fails', (done) => { spyOn(cluster.service, 'installApplication').and.returnValue(Promise.reject(new Error('STUBBED ERROR'))); expect(cluster.store.state.applications.helm.requestStatus).toEqual(null); - cluster.installApplication('helm'); + cluster.installApplication({ id: 'helm' }); expect(cluster.store.state.applications.helm.requestStatus).toEqual(REQUEST_LOADING); expect(cluster.store.state.applications.helm.requestReason).toEqual(null); diff --git a/spec/javascripts/clusters/components/application_row_spec.js b/spec/javascripts/clusters/components/application_row_spec.js index 2c4707bb856..fed8f145ef1 100644 --- a/spec/javascripts/clusters/components/application_row_spec.js +++ b/spec/javascripts/clusters/components/application_row_spec.js @@ -174,7 +174,27 @@ describe('Application Row', () => { installButton.click(); - expect(eventHub.$emit).toHaveBeenCalledWith('installApplication', DEFAULT_APPLICATION_STATE.id); + expect(eventHub.$emit).toHaveBeenCalledWith('installApplication', { + id: DEFAULT_APPLICATION_STATE.id, + params: {}, + }); + }); + + it('clicking install button when installApplicationRequestParams are provided emits event', () => { + spyOn(eventHub, '$emit'); + vm = mountComponent(ApplicationRow, { + ...DEFAULT_APPLICATION_STATE, + status: APPLICATION_INSTALLABLE, + installApplicationRequestParams: { hostname: 'jupyter' }, + }); + const installButton = vm.$el.querySelector('.js-cluster-application-install-button'); + + installButton.click(); + + expect(eventHub.$emit).toHaveBeenCalledWith('installApplication', { + id: DEFAULT_APPLICATION_STATE.id, + params: { hostname: 'jupyter' }, + }); }); it('clicking disabled install button emits nothing', () => { @@ -191,6 +211,16 @@ describe('Application Row', () => { expect(eventHub.$emit).not.toHaveBeenCalled(); }); + + it('is disabled when disableInstallButton prop is provided', () => { + vm = mountComponent(ApplicationRow, { + ...DEFAULT_APPLICATION_STATE, + status: APPLICATION_INSTALLING, + disableInstallButton: true, + }); + + expect(vm.installButtonDisabled).toEqual(true); + }); }); describe('Error block', () => { diff --git a/spec/javascripts/clusters/components/applications_spec.js b/spec/javascripts/clusters/components/applications_spec.js index d546543d273..4619322bd4b 100644 --- a/spec/javascripts/clusters/components/applications_spec.js +++ b/spec/javascripts/clusters/components/applications_spec.js @@ -22,6 +22,7 @@ describe('Applications', () => { ingress: { title: 'Ingress' }, runner: { title: 'GitLab Runner' }, prometheus: { title: 'Prometheus' }, + jupyter: { title: 'JupyterHub' }, }, }); }); @@ -41,6 +42,10 @@ describe('Applications', () => { it('renders a row for GitLab Runner', () => { expect(vm.$el.querySelector('.js-cluster-application-row-runner')).toBeDefined(); }); + + it('renders a row for Jupyter', () => { + expect(vm.$el.querySelector('.js-cluster-application-row-jupyter')).not.toBe(null); + }); }); describe('Ingress application', () => { @@ -57,12 +62,11 @@ describe('Applications', () => { helm: { title: 'Helm Tiller' }, runner: { title: 'GitLab Runner' }, prometheus: { title: 'Prometheus' }, + jupyter: { title: 'JupyterHub', hostname: '' }, }, }); - expect( - vm.$el.querySelector('.js-ip-address').value, - ).toEqual('0.0.0.0'); + expect(vm.$el.querySelector('.js-ip-address').value).toEqual('0.0.0.0'); expect( vm.$el.querySelector('.js-clipboard-btn').getAttribute('data-clipboard-text'), @@ -81,12 +85,11 @@ describe('Applications', () => { helm: { title: 'Helm Tiller' }, runner: { title: 'GitLab Runner' }, prometheus: { title: 'Prometheus' }, + jupyter: { title: 'JupyterHub', hostname: '' }, }, }); - expect( - vm.$el.querySelector('.js-ip-address').value, - ).toEqual('?'); + expect(vm.$el.querySelector('.js-ip-address').value).toEqual('?'); expect(vm.$el.querySelector('.js-no-ip-message')).not.toBe(null); }); @@ -101,6 +104,7 @@ describe('Applications', () => { ingress: { title: 'Ingress' }, runner: { title: 'GitLab Runner' }, prometheus: { title: 'Prometheus' }, + jupyter: { title: 'JupyterHub', hostname: '' }, }, }); @@ -108,5 +112,66 @@ describe('Applications', () => { expect(vm.$el.querySelector('.js-ip-address')).toBe(null); }); }); + + describe('Jupyter application', () => { + describe('with ingress installed & jupyter not installed', () => { + it('renders hostname active input', () => { + vm = mountComponent(Applications, { + applications: { + helm: { title: 'Helm Tiller', status: 'installed' }, + ingress: { title: 'Ingress', status: 'installed', externalIp: '1.1.1.1' }, + runner: { title: 'GitLab Runner' }, + prometheus: { title: 'Prometheus' }, + jupyter: { title: 'JupyterHub', hostname: '' }, + }, + }); + + expect(vm.$el.querySelector('.js-hostname').getAttribute('readonly')).toEqual(null); + }); + describe('with ingress & jupyter installed', () => { + it('renders readonly input', () => { + vm = mountComponent(Applications, { + applications: { + helm: { title: 'Helm Tiller', status: 'installed' }, + ingress: { title: 'Ingress', status: 'installed', externalIp: '1.1.1.1' }, + runner: { title: 'GitLab Runner' }, + prometheus: { title: 'Prometheus' }, + jupyter: { title: 'JupyterHub', status: 'installed', hostname: '' }, + }, + }); + + expect(vm.$el.querySelector('.js-hostname').getAttribute('readonly')).toEqual('readonly'); + }); + }); + }); + + describe('without ingress installed', () => { + beforeEach(() => { + vm = mountComponent(Applications, { + applications: { + helm: { title: 'Helm Tiller' }, + ingress: { title: 'Ingress' }, + runner: { title: 'GitLab Runner' }, + prometheus: { title: 'Prometheus' }, + jupyter: { title: 'JupyterHub' }, + }, + }); + }); + + it('does not render input', () => { + expect(vm.$el.querySelector('.js-hostname')).toBe(null); + }); + + it('renders disabled install button', () => { + expect( + vm.$el + .querySelector( + '.js-cluster-application-row-jupyter .js-cluster-application-install-button', + ) + .getAttribute('disabled'), + ).toEqual('disabled'); + }); + }); + }); }); }); diff --git a/spec/javascripts/clusters/services/mock_data.js b/spec/javascripts/clusters/services/mock_data.js index 6ae7a792329..b2b0ebf840b 100644 --- a/spec/javascripts/clusters/services/mock_data.js +++ b/spec/javascripts/clusters/services/mock_data.js @@ -1,4 +1,5 @@ import { + APPLICATION_INSTALLED, APPLICATION_INSTALLABLE, APPLICATION_INSTALLING, APPLICATION_ERROR, @@ -28,6 +29,39 @@ const CLUSTERS_MOCK_DATA = { name: 'prometheus', status: APPLICATION_ERROR, status_reason: 'Cannot connect', + }, { + name: 'jupyter', + status: APPLICATION_INSTALLING, + status_reason: 'Cannot connect', + }], + }, + }, + '/gitlab-org/gitlab-shell/clusters/2/status.json': { + data: { + status: 'errored', + status_reason: 'Failed to request to CloudPlatform.', + applications: [{ + name: 'helm', + status: APPLICATION_INSTALLED, + status_reason: null, + }, { + name: 'ingress', + status: APPLICATION_INSTALLED, + status_reason: 'Cannot connect', + external_ip: '1.1.1.1', + }, { + name: 'runner', + status: APPLICATION_INSTALLING, + status_reason: null, + }, + { + name: 'prometheus', + status: APPLICATION_ERROR, + status_reason: 'Cannot connect', + }, { + name: 'jupyter', + status: APPLICATION_INSTALLABLE, + status_reason: 'Cannot connect', }], }, }, @@ -37,6 +71,7 @@ const CLUSTERS_MOCK_DATA = { '/gitlab-org/gitlab-shell/clusters/1/applications/ingress': { }, '/gitlab-org/gitlab-shell/clusters/1/applications/runner': { }, '/gitlab-org/gitlab-shell/clusters/1/applications/prometheus': { }, + '/gitlab-org/gitlab-shell/clusters/1/applications/jupyter': { }, }, }; diff --git a/spec/javascripts/clusters/stores/clusters_store_spec.js b/spec/javascripts/clusters/stores/clusters_store_spec.js index 8028faf2f02..6854b016852 100644 --- a/spec/javascripts/clusters/stores/clusters_store_spec.js +++ b/spec/javascripts/clusters/stores/clusters_store_spec.js @@ -91,8 +91,26 @@ describe('Clusters Store', () => { requestStatus: null, requestReason: null, }, + jupyter: { + title: 'JupyterHub', + status: mockResponseData.applications[4].status, + statusReason: mockResponseData.applications[4].status_reason, + requestStatus: null, + requestReason: null, + hostname: '', + }, }, }); }); + + it('sets default hostname for jupyter when ingress has a ip address', () => { + const mockResponseData = CLUSTERS_MOCK_DATA.GET['/gitlab-org/gitlab-shell/clusters/2/status.json'].data; + + store.updateStateFromServer(mockResponseData); + + expect( + store.state.applications.jupyter.hostname, + ).toEqual(`jupyter.${store.state.applications.ingress.externalIp}.xip.io`); + }); }); }); -- cgit v1.2.1 From 1d6adc27c5c2cec7d62fa4e659e6d5b34888e6a1 Mon Sep 17 00:00:00 2001 From: Filipa Lacerda Date: Fri, 25 May 2018 15:45:08 +0200 Subject: Handle disabled button state in backend --- spec/javascripts/clusters/components/application_row_spec.js | 10 ---------- spec/javascripts/clusters/components/applications_spec.js | 7 ++++--- 2 files changed, 4 insertions(+), 13 deletions(-) (limited to 'spec/javascripts') diff --git a/spec/javascripts/clusters/components/application_row_spec.js b/spec/javascripts/clusters/components/application_row_spec.js index fed8f145ef1..c83cbe90a57 100644 --- a/spec/javascripts/clusters/components/application_row_spec.js +++ b/spec/javascripts/clusters/components/application_row_spec.js @@ -211,16 +211,6 @@ describe('Application Row', () => { expect(eventHub.$emit).not.toHaveBeenCalled(); }); - - it('is disabled when disableInstallButton prop is provided', () => { - vm = mountComponent(ApplicationRow, { - ...DEFAULT_APPLICATION_STATE, - status: APPLICATION_INSTALLING, - disableInstallButton: true, - }); - - expect(vm.installButtonDisabled).toEqual(true); - }); }); describe('Error block', () => { diff --git a/spec/javascripts/clusters/components/applications_spec.js b/spec/javascripts/clusters/components/applications_spec.js index 4619322bd4b..fa228f5927f 100644 --- a/spec/javascripts/clusters/components/applications_spec.js +++ b/spec/javascripts/clusters/components/applications_spec.js @@ -114,7 +114,7 @@ describe('Applications', () => { }); describe('Jupyter application', () => { - describe('with ingress installed & jupyter not installed', () => { + describe('with ingress installed with ip & jupyter installable', () => { it('renders hostname active input', () => { vm = mountComponent(Applications, { applications: { @@ -122,12 +122,13 @@ describe('Applications', () => { ingress: { title: 'Ingress', status: 'installed', externalIp: '1.1.1.1' }, runner: { title: 'GitLab Runner' }, prometheus: { title: 'Prometheus' }, - jupyter: { title: 'JupyterHub', hostname: '' }, + jupyter: { title: 'JupyterHub', hostname: '', status: 'installable' }, }, }); expect(vm.$el.querySelector('.js-hostname').getAttribute('readonly')).toEqual(null); }); + describe('with ingress & jupyter installed', () => { it('renders readonly input', () => { vm = mountComponent(Applications, { @@ -153,7 +154,7 @@ describe('Applications', () => { ingress: { title: 'Ingress' }, runner: { title: 'GitLab Runner' }, prometheus: { title: 'Prometheus' }, - jupyter: { title: 'JupyterHub' }, + jupyter: { title: 'JupyterHub', status: 'not_installable' }, }, }); }); -- cgit v1.2.1 From aca0d610fd1beb7e9e93daa8e59977417e9f8d2d Mon Sep 17 00:00:00 2001 From: Phil Hughes Date: Fri, 25 May 2018 16:00:00 +0100 Subject: fixed mutations spec --- spec/javascripts/ide/mock_data.js | 5 ++- .../ide/stores/modules/pipelines/mutations_spec.js | 47 ++++++++-------------- 2 files changed, 21 insertions(+), 31 deletions(-) (limited to 'spec/javascripts') diff --git a/spec/javascripts/ide/mock_data.js b/spec/javascripts/ide/mock_data.js index 23c75b964d8..3359c829c6e 100644 --- a/spec/javascripts/ide/mock_data.js +++ b/spec/javascripts/ide/mock_data.js @@ -89,14 +89,16 @@ export const fullPipelinesResponse = { pipelines: [ { id: '51', + path: 'test', commit: { - id: 'xxxxxxxxxxxxxxxxxxxx', + id: '123', }, details: { status: { icon: 'status_failed', text: 'failed', }, + stages: [...stages], }, }, { @@ -109,6 +111,7 @@ export const fullPipelinesResponse = { icon: 'status_passed', text: 'passed', }, + stages: [...stages], }, }, ], diff --git a/spec/javascripts/ide/stores/modules/pipelines/mutations_spec.js b/spec/javascripts/ide/stores/modules/pipelines/mutations_spec.js index d47ec33ad4d..653af1128ad 100644 --- a/spec/javascripts/ide/stores/modules/pipelines/mutations_spec.js +++ b/spec/javascripts/ide/stores/modules/pipelines/mutations_spec.js @@ -1,7 +1,7 @@ import mutations from '~/ide/stores/modules/pipelines/mutations'; import state from '~/ide/stores/modules/pipelines/state'; import * as types from '~/ide/stores/modules/pipelines/mutation_types'; -import { pipelines, stages } from '../../../mock_data'; +import { fullPipelinesResponse, stages } from '../../../mock_data'; describe('IDE pipelines mutations', () => { let mockedState; @@ -28,17 +28,25 @@ describe('IDE pipelines mutations', () => { describe(types.RECEIVE_LASTEST_PIPELINE_SUCCESS, () => { it('sets loading to false on success', () => { - mutations[types.RECEIVE_LASTEST_PIPELINE_SUCCESS](mockedState, pipelines[0]); + mutations[types.RECEIVE_LASTEST_PIPELINE_SUCCESS]( + mockedState, + fullPipelinesResponse.data.pipelines[0], + ); expect(mockedState.isLoadingPipeline).toBe(false); }); it('sets latestPipeline', () => { - mutations[types.RECEIVE_LASTEST_PIPELINE_SUCCESS](mockedState, pipelines[0]); + mutations[types.RECEIVE_LASTEST_PIPELINE_SUCCESS]( + mockedState, + fullPipelinesResponse.data.pipelines[0], + ); expect(mockedState.latestPipeline).toEqual({ - id: pipelines[0].id, - status: pipelines[0].status, + id: '51', + path: 'test', + commit: { id: '123' }, + details: { status: jasmine.any(Object) }, }); }); @@ -47,33 +55,12 @@ describe('IDE pipelines mutations', () => { expect(mockedState.latestPipeline).toEqual(null); }); - }); - - describe(types.REQUEST_STAGES, () => { - it('sets stages loading to true', () => { - mutations[types.REQUEST_STAGES](mockedState); - - expect(mockedState.isLoadingJobs).toBe(true); - }); - }); - - describe(types.RECEIVE_STAGES_ERROR, () => { - it('sets jobs loading to false', () => { - mutations[types.RECEIVE_STAGES_ERROR](mockedState); - - expect(mockedState.isLoadingJobs).toBe(false); - }); - }); - - describe(types.RECEIVE_STAGES_SUCCESS, () => { - it('sets jobs loading to false on success', () => { - mutations[types.RECEIVE_STAGES_SUCCESS](mockedState, stages); - - expect(mockedState.isLoadingJobs).toBe(false); - }); it('sets stages', () => { - mutations[types.RECEIVE_STAGES_SUCCESS](mockedState, stages); + mutations[types.RECEIVE_LASTEST_PIPELINE_SUCCESS]( + mockedState, + fullPipelinesResponse.data.pipelines[0], + ); expect(mockedState.stages.length).toBe(2); expect(mockedState.stages).toEqual([ -- cgit v1.2.1 From 32498cd70d84f8e08d62d80330ca73dd703be1e9 Mon Sep 17 00:00:00 2001 From: Phil Hughes Date: Fri, 25 May 2018 17:31:59 +0100 Subject: fixed mutations spec --- spec/javascripts/ide/stores/modules/pipelines/mutations_spec.js | 2 ++ 1 file changed, 2 insertions(+) (limited to 'spec/javascripts') diff --git a/spec/javascripts/ide/stores/modules/pipelines/mutations_spec.js b/spec/javascripts/ide/stores/modules/pipelines/mutations_spec.js index 653af1128ad..1425f4d0b71 100644 --- a/spec/javascripts/ide/stores/modules/pipelines/mutations_spec.js +++ b/spec/javascripts/ide/stores/modules/pipelines/mutations_spec.js @@ -66,6 +66,7 @@ describe('IDE pipelines mutations', () => { expect(mockedState.stages).toEqual([ { ...stages[0], + dropdownPath: stages[0].dropdownPath, id: 0, isCollapsed: false, isLoading: false, @@ -73,6 +74,7 @@ describe('IDE pipelines mutations', () => { }, { ...stages[1], + dropdownPath: stages[1].dropdownPath, id: 1, isCollapsed: false, isLoading: false, -- cgit v1.2.1 From 6c9027f8f6d1eced3ff067b837a1fda3961ba70d Mon Sep 17 00:00:00 2001 From: Phil Hughes Date: Fri, 25 May 2018 23:10:04 +0100 Subject: fixed up after bootstrap upgrade --- .../javascripts/ide/stores/modules/pipelines/mutations_spec.js | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) (limited to 'spec/javascripts') diff --git a/spec/javascripts/ide/stores/modules/pipelines/mutations_spec.js b/spec/javascripts/ide/stores/modules/pipelines/mutations_spec.js index 1425f4d0b71..3e572a66acf 100644 --- a/spec/javascripts/ide/stores/modules/pipelines/mutations_spec.js +++ b/spec/javascripts/ide/stores/modules/pipelines/mutations_spec.js @@ -65,17 +65,19 @@ describe('IDE pipelines mutations', () => { expect(mockedState.stages.length).toBe(2); expect(mockedState.stages).toEqual([ { - ...stages[0], - dropdownPath: stages[0].dropdownPath, id: 0, + dropdownPath: stages[0].dropdown_path, + name: stages[0].name, + status: stages[0].status, isCollapsed: false, isLoading: false, jobs: [], }, { - ...stages[1], - dropdownPath: stages[1].dropdownPath, id: 1, + dropdownPath: stages[1].dropdown_path, + name: stages[1].name, + status: stages[1].status, isCollapsed: false, isLoading: false, jobs: [], -- cgit v1.2.1 From 08216d2e58edea388d334077ccfce7d842292319 Mon Sep 17 00:00:00 2001 From: Filipa Lacerda Date: Mon, 28 May 2018 10:17:17 +0100 Subject: Changes after review --- .../clusters/components/applications_spec.js | 40 +++++++++++++++------- 1 file changed, 28 insertions(+), 12 deletions(-) (limited to 'spec/javascripts') diff --git a/spec/javascripts/clusters/components/applications_spec.js b/spec/javascripts/clusters/components/applications_spec.js index fa228f5927f..a70138c7eee 100644 --- a/spec/javascripts/clusters/components/applications_spec.js +++ b/spec/javascripts/clusters/components/applications_spec.js @@ -128,21 +128,37 @@ describe('Applications', () => { expect(vm.$el.querySelector('.js-hostname').getAttribute('readonly')).toEqual(null); }); + }); - describe('with ingress & jupyter installed', () => { - it('renders readonly input', () => { - vm = mountComponent(Applications, { - applications: { - helm: { title: 'Helm Tiller', status: 'installed' }, - ingress: { title: 'Ingress', status: 'installed', externalIp: '1.1.1.1' }, - runner: { title: 'GitLab Runner' }, - prometheus: { title: 'Prometheus' }, - jupyter: { title: 'JupyterHub', status: 'installed', hostname: '' }, - }, - }); + describe('with ingress installed without external ip', () => { + it('does not render hostname input', () => { + vm = mountComponent(Applications, { + applications: { + helm: { title: 'Helm Tiller', status: 'installed' }, + ingress: { title: 'Ingress', status: 'installed' }, + runner: { title: 'GitLab Runner' }, + prometheus: { title: 'Prometheus' }, + jupyter: { title: 'JupyterHub', hostname: '', status: 'installable' }, + }, + }); + + expect(vm.$el.querySelector('.js-hostname')).toBe(null); + }); + }); - expect(vm.$el.querySelector('.js-hostname').getAttribute('readonly')).toEqual('readonly'); + describe('with ingress & jupyter installed', () => { + it('renders readonly input', () => { + vm = mountComponent(Applications, { + applications: { + helm: { title: 'Helm Tiller', status: 'installed' }, + ingress: { title: 'Ingress', status: 'installed', externalIp: '1.1.1.1' }, + runner: { title: 'GitLab Runner' }, + prometheus: { title: 'Prometheus' }, + jupyter: { title: 'JupyterHub', status: 'installed', hostname: '' }, + }, }); + + expect(vm.$el.querySelector('.js-hostname').getAttribute('readonly')).toEqual('readonly'); }); }); -- cgit v1.2.1 From 782c31a494faeda9e30ed453953b2be2f80cd5c0 Mon Sep 17 00:00:00 2001 From: Phil Hughes Date: Tue, 29 May 2018 10:35:13 +0100 Subject: added empty state & YAML error state --- spec/javascripts/ide/mock_data.js | 2 ++ .../ide/stores/modules/pipelines/actions_spec.js | 29 ++++++++++++++++------ .../ide/stores/modules/pipelines/mutations_spec.js | 3 ++- 3 files changed, 25 insertions(+), 9 deletions(-) (limited to 'spec/javascripts') diff --git a/spec/javascripts/ide/mock_data.js b/spec/javascripts/ide/mock_data.js index 3359c829c6e..f94c5f19d83 100644 --- a/spec/javascripts/ide/mock_data.js +++ b/spec/javascripts/ide/mock_data.js @@ -20,12 +20,14 @@ export const pipelines = [ ref: 'master', sha: '123', status: 'failed', + commit: { id: '123' }, }, { id: 2, ref: 'master', sha: '213', status: 'success', + commit: { id: '213' }, }, ]; diff --git a/spec/javascripts/ide/stores/modules/pipelines/actions_spec.js b/spec/javascripts/ide/stores/modules/pipelines/actions_spec.js index 273e09f5d12..ee8cae9c199 100644 --- a/spec/javascripts/ide/stores/modules/pipelines/actions_spec.js +++ b/spec/javascripts/ide/stores/modules/pipelines/actions_spec.js @@ -65,15 +65,28 @@ describe('IDE pipelines actions', () => { }); describe('receiveLatestPipelineSuccess', () => { - it('commits pipeline', done => { - testAction( - receiveLatestPipelineSuccess, + const rootGetters = { + lastCommit: { id: '123' }, + }; + let commit; + + beforeEach(() => { + commit = jasmine.createSpy('commit'); + }); + + it('commits pipeline', () => { + receiveLatestPipelineSuccess({ rootGetters, commit }, { pipelines }); + + expect(commit.calls.argsFor(0)).toEqual([ + types.RECEIVE_LASTEST_PIPELINE_SUCCESS, pipelines[0], - mockedState, - [{ type: types.RECEIVE_LASTEST_PIPELINE_SUCCESS, payload: pipelines[0] }], - [], - done, - ); + ]); + }); + + it('commits false when there are no pipelines', () => { + receiveLatestPipelineSuccess({ rootGetters, commit }, { pipelines: [] }); + + expect(commit.calls.argsFor(0)).toEqual([types.RECEIVE_LASTEST_PIPELINE_SUCCESS, false]); }); }); diff --git a/spec/javascripts/ide/stores/modules/pipelines/mutations_spec.js b/spec/javascripts/ide/stores/modules/pipelines/mutations_spec.js index 3e572a66acf..c43d2249af5 100644 --- a/spec/javascripts/ide/stores/modules/pipelines/mutations_spec.js +++ b/spec/javascripts/ide/stores/modules/pipelines/mutations_spec.js @@ -47,13 +47,14 @@ describe('IDE pipelines mutations', () => { path: 'test', commit: { id: '123' }, details: { status: jasmine.any(Object) }, + yamlError: undefined, }); }); it('does not set latest pipeline if pipeline is null', () => { mutations[types.RECEIVE_LASTEST_PIPELINE_SUCCESS](mockedState, null); - expect(mockedState.latestPipeline).toEqual(null); + expect(mockedState.latestPipeline).toEqual(false); }); it('sets stages', () => { -- cgit v1.2.1 From 66bf2de830c421c06bef571a87f8d3d48edcad76 Mon Sep 17 00:00:00 2001 From: Phil Hughes Date: Tue, 29 May 2018 13:09:19 +0100 Subject: added component specs --- spec/javascripts/ide/components/jobs/item_spec.js | 29 ++++++ spec/javascripts/ide/components/jobs/list_spec.js | 45 ++++++++ spec/javascripts/ide/components/jobs/stage_spec.js | 95 +++++++++++++++++ .../ide/components/pipelines/list_spec.js | 114 +++++++++++++++++++++ spec/javascripts/ide/helpers.js | 2 + spec/javascripts/ide/mock_data.js | 48 +++++++-- 6 files changed, 323 insertions(+), 10 deletions(-) create mode 100644 spec/javascripts/ide/components/jobs/item_spec.js create mode 100644 spec/javascripts/ide/components/jobs/list_spec.js create mode 100644 spec/javascripts/ide/components/jobs/stage_spec.js create mode 100644 spec/javascripts/ide/components/pipelines/list_spec.js (limited to 'spec/javascripts') diff --git a/spec/javascripts/ide/components/jobs/item_spec.js b/spec/javascripts/ide/components/jobs/item_spec.js new file mode 100644 index 00000000000..7c1dd4e475c --- /dev/null +++ b/spec/javascripts/ide/components/jobs/item_spec.js @@ -0,0 +1,29 @@ +import Vue from 'vue'; +import JobItem from '~/ide/components/jobs/item.vue'; +import mountComponent from '../../../helpers/vue_mount_component_helper'; +import { jobs } from '../../mock_data'; + +describe('IDE jobs item', () => { + const Component = Vue.extend(JobItem); + const job = jobs[0]; + let vm; + + beforeEach(() => { + vm = mountComponent(Component, { + job, + }); + }); + + afterEach(() => { + vm.$destroy(); + }); + + it('renders job details', () => { + expect(vm.$el.textContent).toContain(job.name); + expect(vm.$el.textContent).toContain(`#${job.id}`); + }); + + it('renders CI icon', () => { + expect(vm.$el.querySelector('.ic-status_passed_borderless')).not.toBe(null); + }); +}); diff --git a/spec/javascripts/ide/components/jobs/list_spec.js b/spec/javascripts/ide/components/jobs/list_spec.js new file mode 100644 index 00000000000..484cab3c135 --- /dev/null +++ b/spec/javascripts/ide/components/jobs/list_spec.js @@ -0,0 +1,45 @@ +import Vue from 'vue'; +import StageList from '~/ide/components/jobs/list.vue'; +import { createStore } from '~/ide/stores'; +import { createComponentWithStore } from '../../../helpers/vue_mount_component_helper'; +import { stages, jobs } from '../../mock_data'; + +describe('IDE stages list', () => { + const Component = Vue.extend(StageList); + let vm; + + beforeEach(() => { + const store = createStore(); + + vm = createComponentWithStore(Component, store, { + stages: stages.map((mappedState, i) => ({ + ...mappedState, + id: i, + dropdownPath: mappedState.dropdown_path, + jobs: [...jobs], + isLoading: false, + isCollapsed: false, + })), + loading: false, + }).$mount(); + }); + + afterEach(() => { + vm.$destroy(); + }); + + it('renders list of stages', () => { + expect(vm.$el.querySelectorAll('.card').length).toBe(2); + }); + + it('renders loading icon when no stages & is loading', done => { + vm.stages = []; + vm.loading = true; + + vm.$nextTick(() => { + expect(vm.$el.querySelector('.loading-container')).not.toBe(null); + + done(); + }); + }); +}); diff --git a/spec/javascripts/ide/components/jobs/stage_spec.js b/spec/javascripts/ide/components/jobs/stage_spec.js new file mode 100644 index 00000000000..66b62d90e84 --- /dev/null +++ b/spec/javascripts/ide/components/jobs/stage_spec.js @@ -0,0 +1,95 @@ +import Vue from 'vue'; +import MockAdapter from 'axios-mock-adapter'; +import axios from '~/lib/utils/axios_utils'; +import { createStore } from '~/ide/stores'; +import Stage from '~/ide/components/jobs/stage.vue'; +import { createComponentWithStore } from '../../../helpers/vue_mount_component_helper'; +import { stages, jobs } from '../../mock_data'; + +describe('IDE pipeline stage', () => { + const Component = Vue.extend(Stage); + let vm; + let mock; + let stage; + + beforeEach(done => { + const store = createStore(); + mock = new MockAdapter(axios); + + store.state.pipelines.stages = stages.map((mappedState, i) => ({ + ...mappedState, + id: i, + dropdownPath: mappedState.dropdown_path, + jobs: [], + isLoading: false, + isCollapsed: false, + })); + + stage = store.state.pipelines.stages[0]; + + mock.onGet(stage.dropdownPath).reply(200, { + latest_statuses: jobs, + }); + + vm = createComponentWithStore(Component, store, { + stage, + }).$mount(); + + setTimeout(done); + }); + + afterEach(() => { + vm.$destroy(); + + mock.restore(); + }); + + it('renders stages details', () => { + expect(vm.$el.textContent).toContain(vm.stage.name); + }); + + it('renders CI icon', () => { + expect(vm.$el.querySelector('.ic-status_failed')).not.toBe(null); + }); + + describe('collapsed', () => { + it('toggles collapse status when clicking header', done => { + vm.$el.querySelector('.card-header').click(); + + vm.$nextTick(() => { + expect(vm.$el.querySelector('.card-body').style.display).toBe('none'); + + done(); + }); + }); + + it('sets border bottom class when collapsed', done => { + vm.$el.querySelector('.card-header').click(); + + vm.$nextTick(() => { + expect(vm.$el.querySelector('.card-header').classList).toContain('border-bottom-0'); + + done(); + }); + }); + }); + + it('renders jobs count', () => { + expect(vm.$el.querySelector('.badge').textContent).toContain('4'); + }); + + it('renders loading icon when no jobs and isLoading is true', done => { + vm.stage.isLoading = true; + vm.stage.jobs = []; + + vm.$nextTick(() => { + expect(vm.$el.querySelector('.loading-container')).not.toBe(null); + + done(); + }); + }); + + it('renders list of jobs', () => { + expect(vm.$el.querySelectorAll('.ide-job-item').length).toBe(4); + }); +}); diff --git a/spec/javascripts/ide/components/pipelines/list_spec.js b/spec/javascripts/ide/components/pipelines/list_spec.js new file mode 100644 index 00000000000..d727dbdc1da --- /dev/null +++ b/spec/javascripts/ide/components/pipelines/list_spec.js @@ -0,0 +1,114 @@ +import Vue from 'vue'; +import MockAdapter from 'axios-mock-adapter'; +import axios from '~/lib/utils/axios_utils'; +import { createStore } from '~/ide/stores'; +import List from '~/ide/components/pipelines/list.vue'; +import { createComponentWithStore } from '../../../helpers/vue_mount_component_helper'; +import { pipelines, projectData, stages, jobs } from '../../mock_data'; + +describe('IDE pipelines list', () => { + const Component = Vue.extend(List); + let vm; + let mock; + + beforeEach(done => { + const store = createStore(); + + mock = new MockAdapter(axios); + + store.state.currentProjectId = 'abc/def'; + store.state.currentBranchId = 'master'; + store.state.projects['abc/def'] = { + ...projectData, + path_with_namespace: 'abc/def', + branches: { + master: { commit: { id: '123' } }, + }, + }; + store.state.links = { ciHelpPagePath: gl.TEST_HOST }; + store.state.pipelinesEmptyStateSvgPath = gl.TEST_HOST; + store.state.pipelines.stages = stages.map((mappedState, i) => ({ + ...mappedState, + id: i, + dropdownPath: mappedState.dropdown_path, + jobs: [...jobs], + isLoading: false, + isCollapsed: false, + })); + + mock + .onGet('/abc/def/commit/123/pipelines') + .reply(200, { pipelines: [...pipelines] }, { 'poll-interval': '-1' }); + + vm = createComponentWithStore(Component, store).$mount(); + + setTimeout(done); + }); + + afterEach(() => { + vm.$destroy(); + mock.restore(); + }); + + it('renders pipeline data', () => { + expect(vm.$el.textContent).toContain('#1'); + }); + + it('renders CI icon', () => { + expect(vm.$el.querySelector('.ci-status-icon-failed')).not.toBe(null); + }); + + it('renders list of jobs', () => { + expect(vm.$el.querySelectorAll('.tab-pane:first-child .ide-job-item').length).toBe( + jobs.length * stages.length, + ); + }); + + it('renders list of failed jobs on failed jobs tab', done => { + vm.$el.querySelectorAll('.tab-links a')[1].click(); + + vm.$nextTick(() => { + expect(vm.$el.querySelectorAll('.tab-pane.active .ide-job-item').length).toBe(2); + + done(); + }); + }); + + describe('YAML error', () => { + it('renders YAML error', done => { + vm.$store.state.pipelines.latestPipeline.yamlError = 'test yaml error'; + + vm.$nextTick(() => { + expect(vm.$el.textContent).toContain('Found errors in your .gitlab-ci.yml:'); + expect(vm.$el.textContent).toContain('test yaml error'); + + done(); + }); + }); + }); + + describe('empty state', () => { + it('renders pipelines empty state', done => { + vm.$store.state.pipelines.latestPipeline = false; + + vm.$nextTick(() => { + expect(vm.$el.querySelector('.empty-state')).not.toBe(null); + + done(); + }); + }); + }); + + describe('loading state', () => { + it('renders loading state when there is no latest pipeline', done => { + vm.$store.state.pipelines.latestPipeline = null; + vm.$store.state.pipelines.isLoadingPipeline = true; + + vm.$nextTick(() => { + expect(vm.$el.querySelector('.loading-container')).not.toBe(null); + + done(); + }); + }); + }); +}); diff --git a/spec/javascripts/ide/helpers.js b/spec/javascripts/ide/helpers.js index 98db6defc7a..5c7e2db0e96 100644 --- a/spec/javascripts/ide/helpers.js +++ b/spec/javascripts/ide/helpers.js @@ -1,11 +1,13 @@ import { decorateData } from '~/ide/stores/utils'; import state from '~/ide/stores/state'; import commitState from '~/ide/stores/modules/commit/state'; +import pipelinesState from '~/ide/stores/modules/pipelines/state'; export const resetStore = store => { const newState = { ...state(), commit: commitState(), + pipelines: pipelinesState(), }; store.replaceState(newState); }; diff --git a/spec/javascripts/ide/mock_data.js b/spec/javascripts/ide/mock_data.js index f94c5f19d83..8ad51e122b6 100644 --- a/spec/javascripts/ide/mock_data.js +++ b/spec/javascripts/ide/mock_data.js @@ -19,26 +19,38 @@ export const pipelines = [ id: 1, ref: 'master', sha: '123', - status: 'failed', + details: { + status: { + icon: 'status_failed', + group: 'failed', + text: 'Failed', + }, + }, commit: { id: '123' }, }, { id: 2, ref: 'master', sha: '213', - status: 'success', + details: { + status: { + icon: 'status_failed', + group: 'failed', + text: 'Failed', + }, + }, commit: { id: '213' }, }, ]; export const stages = [ { - dropdown_path: 'testing', + dropdown_path: `${gl.TEST_HOST}/testing`, name: 'build', status: { icon: 'status_failed', group: 'failed', - text: 'Failed', + text: 'failed', }, }, { @@ -47,7 +59,7 @@ export const stages = [ status: { icon: 'status_failed', group: 'failed', - text: 'Failed', + text: 'failed', }, }, ]; @@ -56,28 +68,44 @@ export const jobs = [ { id: 1, name: 'test', - status: 'failed', + path: 'testing', + status: { + icon: 'status_passed', + text: 'passed', + }, stage: 'test', duration: 1, }, { id: 2, name: 'test 2', - status: 'failed', + path: 'testing2', + status: { + icon: 'status_passed', + text: 'passed', + }, stage: 'test', duration: 1, }, { id: 3, name: 'test 3', - status: 'failed', + path: 'testing3', + status: { + icon: 'status_passed', + text: 'passed', + }, stage: 'test', duration: 1, }, { id: 4, - name: 'test 3', - status: 'failed', + name: 'test 4', + path: 'testing4', + status: { + icon: 'status_failed', + text: 'failed', + }, stage: 'build', duration: 1, }, -- cgit v1.2.1 From aab713132e00be10d86ebcc50824365d0ff7a0c8 Mon Sep 17 00:00:00 2001 From: Phil Hughes Date: Tue, 29 May 2018 14:26:46 +0100 Subject: karma fixes --- spec/javascripts/ide/components/pipelines/list_spec.js | 4 +++- spec/javascripts/vue_shared/components/tabs/tabs_spec.js | 6 +++--- 2 files changed, 6 insertions(+), 4 deletions(-) (limited to 'spec/javascripts') diff --git a/spec/javascripts/ide/components/pipelines/list_spec.js b/spec/javascripts/ide/components/pipelines/list_spec.js index d727dbdc1da..81fd00c7153 100644 --- a/spec/javascripts/ide/components/pipelines/list_spec.js +++ b/spec/javascripts/ide/components/pipelines/list_spec.js @@ -38,7 +38,7 @@ describe('IDE pipelines list', () => { mock .onGet('/abc/def/commit/123/pipelines') - .reply(200, { pipelines: [...pipelines] }, { 'poll-interval': '-1' }); + .replyOnce(200, { pipelines: [...pipelines] }, { 'poll-interval': '-1' }); vm = createComponentWithStore(Component, store).$mount(); @@ -48,6 +48,8 @@ describe('IDE pipelines list', () => { afterEach(() => { vm.$destroy(); mock.restore(); + vm.$store.dispatch('stopPipelinePolling'); + vm.$store.dispatch('clearEtagPoll'); }); it('renders pipeline data', () => { diff --git a/spec/javascripts/vue_shared/components/tabs/tabs_spec.js b/spec/javascripts/vue_shared/components/tabs/tabs_spec.js index 07752329965..50ba18cd338 100644 --- a/spec/javascripts/vue_shared/components/tabs/tabs_spec.js +++ b/spec/javascripts/vue_shared/components/tabs/tabs_spec.js @@ -43,15 +43,15 @@ describe('Tabs component', () => { }); it('renders active class', () => { - expect(vm.$el.querySelector('li').classList).toContain('active'); + expect(vm.$el.querySelector('a').classList).toContain('active'); }); it('updates active class on click', done => { vm.$el.querySelectorAll('a')[1].click(); setTimeout(() => { - expect(vm.$el.querySelector('li').classList).not.toContain('active'); - expect(vm.$el.querySelectorAll('li')[1].classList).toContain('active'); + expect(vm.$el.querySelector('a').classList).not.toContain('active'); + expect(vm.$el.querySelectorAll('a')[1].classList).toContain('active'); done(); }); -- cgit v1.2.1 From 552c0c99bb8216d137d4c0cd295d28a800a63502 Mon Sep 17 00:00:00 2001 From: Phil Hughes Date: Tue, 29 May 2018 14:59:52 +0100 Subject: more store tests --- .../ide/stores/modules/pipelines/actions_spec.js | 107 ++++++++++++++++++++- .../ide/stores/modules/pipelines/mutations_spec.js | 89 ++++++++++++++++- 2 files changed, 194 insertions(+), 2 deletions(-) (limited to 'spec/javascripts') diff --git a/spec/javascripts/ide/stores/modules/pipelines/actions_spec.js b/spec/javascripts/ide/stores/modules/pipelines/actions_spec.js index ee8cae9c199..f26eaf9c81f 100644 --- a/spec/javascripts/ide/stores/modules/pipelines/actions_spec.js +++ b/spec/javascripts/ide/stores/modules/pipelines/actions_spec.js @@ -8,11 +8,16 @@ import actions, { fetchLatestPipeline, stopPipelinePolling, clearEtagPoll, + requestJobs, + receiveJobsError, + receiveJobsSuccess, + fetchJobs, + toggleStageCollapsed, } from '~/ide/stores/modules/pipelines/actions'; import state from '~/ide/stores/modules/pipelines/state'; import * as types from '~/ide/stores/modules/pipelines/mutation_types'; import testAction from '../../../../helpers/vuex_action_helper'; -import { pipelines } from '../../../mock_data'; +import { pipelines, jobs } from '../../../mock_data'; describe('IDE pipelines actions', () => { let mockedState; @@ -176,4 +181,104 @@ describe('IDE pipelines actions', () => { }); }); }); + + describe('requestJobs', () => { + it('commits request', done => { + testAction(requestJobs, 1, mockedState, [{ type: types.REQUEST_JOBS, payload: 1 }], [], done); + }); + }); + + describe('receiveJobsError', () => { + it('commits error', done => { + testAction( + receiveJobsError, + 1, + mockedState, + [{ type: types.RECEIVE_JOBS_ERROR, payload: 1 }], + [], + done, + ); + }); + + it('creates flash message', () => { + const flashSpy = spyOnDependency(actions, 'flash'); + + receiveJobsError({ commit() {} }, 1); + + expect(flashSpy).toHaveBeenCalled(); + }); + }); + + describe('receiveJobsSuccess', () => { + it('commits data', done => { + testAction( + receiveJobsSuccess, + { id: 1, data: jobs }, + mockedState, + [{ type: types.RECEIVE_JOBS_SUCCESS, payload: { id: 1, data: jobs } }], + [], + done, + ); + }); + }); + + describe('fetchJobs', () => { + const stage = { + id: 1, + dropdownPath: `${gl.TEST_HOST}/jobs`, + }; + + describe('success', () => { + beforeEach(() => { + mock.onGet(stage.dropdownPath).replyOnce(200, jobs); + }); + + it('dispatches request', done => { + testAction( + fetchJobs, + stage, + mockedState, + [], + [ + { type: 'requestJobs', payload: stage.id }, + { type: 'receiveJobsSuccess', payload: { id: stage.id, data: jobs } }, + ], + done, + ); + }); + }); + + describe('error', () => { + beforeEach(() => { + mock.onGet(stage.dropdownPath).replyOnce(500); + }); + + it('dispatches error', done => { + testAction( + fetchJobs, + stage, + mockedState, + [], + [ + { type: 'requestJobs', payload: stage.id }, + { type: 'receiveJobsError', payload: stage.id }, + ], + done, + ); + }); + }); + }); + + describe('toggleStageCollapsed', () => { + it('commits collapse', done => { + testAction( + toggleStageCollapsed, + 1, + mockedState, + [{ type: types.TOGGLE_STAGE_COLLAPSE, payload: 1 }], + [], + done, + ); + }); + }); }); diff --git a/spec/javascripts/ide/stores/modules/pipelines/mutations_spec.js b/spec/javascripts/ide/stores/modules/pipelines/mutations_spec.js index c43d2249af5..1bd7bde7c4d 100644 --- a/spec/javascripts/ide/stores/modules/pipelines/mutations_spec.js +++ b/spec/javascripts/ide/stores/modules/pipelines/mutations_spec.js @@ -1,7 +1,7 @@ import mutations from '~/ide/stores/modules/pipelines/mutations'; import state from '~/ide/stores/modules/pipelines/state'; import * as types from '~/ide/stores/modules/pipelines/mutation_types'; -import { fullPipelinesResponse, stages } from '../../../mock_data'; +import { fullPipelinesResponse, stages, jobs } from '../../../mock_data'; describe('IDE pipelines mutations', () => { let mockedState; @@ -86,4 +86,91 @@ describe('IDE pipelines mutations', () => { ]); }); }); + + describe(types.REQUEST_JOBS, () => { + beforeEach(() => { + mockedState.stages = stages.map((stage, i) => ({ + ...stage, + id: i, + })); + }); + + it('sets isLoading on stage', () => { + mutations[types.REQUEST_JOBS](mockedState, mockedState.stages[0].id); + + expect(mockedState.stages[0].isLoading).toBe(true); + }); + }); + + describe(types.RECEIVE_JOBS_ERROR, () => { + beforeEach(() => { + mockedState.stages = stages.map((stage, i) => ({ + ...stage, + id: i, + })); + }); + + it('sets isLoading on stage after error', () => { + mutations[types.RECEIVE_JOBS_ERROR](mockedState, mockedState.stages[0].id); + + expect(mockedState.stages[0].isLoading).toBe(false); + }); + }); + + describe(types.RECEIVE_JOBS_SUCCESS, () => { + let data; + + beforeEach(() => { + mockedState.stages = stages.map((stage, i) => ({ + ...stage, + id: i, + })); + + data = { + latest_statuses: [...jobs], + }; + }); + + it('updates loading', () => { + mutations[types.RECEIVE_JOBS_SUCCESS](mockedState, { id: mockedState.stages[0].id, data }); + + expect(mockedState.stages[0].isLoading).toBe(false); + }); + + it('sets jobs on stage', () => { + mutations[types.RECEIVE_JOBS_SUCCESS](mockedState, { id: mockedState.stages[0].id, data }); + + expect(mockedState.stages[0].jobs.length).toBe(jobs.length); + expect(mockedState.stages[0].jobs).toEqual( + jobs.map(job => ({ + id: job.id, + name: job.name, + status: job.status, + path: job.build_path, + })), + ); + }); + }); + + describe(types.TOGGLE_STAGE_COLLAPSE, () => { + beforeEach(() => { + mockedState.stages = stages.map((stage, i) => ({ + ...stage, + id: i, + isCollapsed: false, + })); + }); + + it('toggles collapsed state', () => { + const stage = mockedState.stages[0]; + + mutations[types.TOGGLE_STAGE_COLLAPSE](mockedState, stage.id); + + expect(stage.isCollapsed).toBe(true); + + mutations[types.TOGGLE_STAGE_COLLAPSE](mockedState, stage.id); + + expect(stage.isCollapsed).toBe(false); + }); + }); }); -- cgit v1.2.1 From e6e9706e5308b366ce20ff592d0685fa02e4b404 Mon Sep 17 00:00:00 2001 From: Phil Hughes Date: Wed, 30 May 2018 09:40:04 +0100 Subject: fixed failing specs fixed merge requests not loading --- spec/javascripts/ide/components/pipelines/list_spec.js | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) (limited to 'spec/javascripts') diff --git a/spec/javascripts/ide/components/pipelines/list_spec.js b/spec/javascripts/ide/components/pipelines/list_spec.js index 81fd00c7153..2bb5aa08c3b 100644 --- a/spec/javascripts/ide/components/pipelines/list_spec.js +++ b/spec/javascripts/ide/components/pipelines/list_spec.js @@ -46,10 +46,11 @@ describe('IDE pipelines list', () => { }); afterEach(() => { + vm.$store.dispatch('pipelines/stopPipelinePolling'); + vm.$store.dispatch('pipelines/clearEtagPoll'); + vm.$destroy(); mock.restore(); - vm.$store.dispatch('stopPipelinePolling'); - vm.$store.dispatch('clearEtagPoll'); }); it('renders pipeline data', () => { -- cgit v1.2.1 From 8f646faf3dc2ded677a9a5fbca16b5da9b146d19 Mon Sep 17 00:00:00 2001 From: Phil Hughes Date: Wed, 30 May 2018 10:59:29 +0100 Subject: improved stored data created reset action to reset stored merge requests --- spec/javascripts/ide/mock_data.js | 2 ++ .../ide/stores/modules/merge_requests/actions_spec.js | 16 +++++++++++++++- .../ide/stores/modules/merge_requests/mutations_spec.js | 14 ++++++++++++++ 3 files changed, 31 insertions(+), 1 deletion(-) (limited to 'spec/javascripts') diff --git a/spec/javascripts/ide/mock_data.js b/spec/javascripts/ide/mock_data.js index 3c09ff36afa..8f24114de26 100644 --- a/spec/javascripts/ide/mock_data.js +++ b/spec/javascripts/ide/mock_data.js @@ -96,8 +96,10 @@ export const fullPipelinesResponse = { export const mergeRequests = [ { + id: 1, iid: 1, title: 'Test merge request', project_id: 1, + web_url: `${gl.TEST_HOST}/namespace/project-path/merge_requests/1`, }, ]; diff --git a/spec/javascripts/ide/stores/modules/merge_requests/actions_spec.js b/spec/javascripts/ide/stores/modules/merge_requests/actions_spec.js index 5d076577753..3b88ac36683 100644 --- a/spec/javascripts/ide/stores/modules/merge_requests/actions_spec.js +++ b/spec/javascripts/ide/stores/modules/merge_requests/actions_spec.js @@ -7,6 +7,7 @@ import actions, { receiveMergeRequestsError, receiveMergeRequestsSuccess, fetchMergeRequests, + resetMergeRequests, } from '~/ide/stores/modules/merge_requests/actions'; import { mergeRequests } from '../../../mock_data'; import testAction from '../../../../helpers/vuex_action_helper'; @@ -93,7 +94,7 @@ describe('IDe merge requests actions', () => { expect(apiSpy).toHaveBeenCalledWith(jasmine.anything(), { params: { scope: 'assigned-to-me', - view: 'simple', + state: 'opened', }, }); }); @@ -141,4 +142,17 @@ describe('IDe merge requests actions', () => { }); }); }); + + describe('resetMergeRequests', () => { + it('commits reset', done => { + testAction( + resetMergeRequests, + null, + mockedState, + [{ type: types.RESET_MERGE_REQUESTS }], + [], + done, + ); + }); + }); }); diff --git a/spec/javascripts/ide/stores/modules/merge_requests/mutations_spec.js b/spec/javascripts/ide/stores/modules/merge_requests/mutations_spec.js index 8983bf65b31..664d3914564 100644 --- a/spec/javascripts/ide/stores/modules/merge_requests/mutations_spec.js +++ b/spec/javascripts/ide/stores/modules/merge_requests/mutations_spec.js @@ -28,14 +28,28 @@ describe('IDE merge requests mutations', () => { describe(types.RECEIVE_MERGE_REQUESTS_SUCCESS, () => { it('sets merge requests', () => { + gon.gitlab_url = gl.TEST_HOST; mutations[types.RECEIVE_MERGE_REQUESTS_SUCCESS](mockedState, mergeRequests); expect(mockedState.mergeRequests).toEqual([ { id: 1, + iid: 1, title: 'Test merge request', + projectId: 1, + projectPathWithNamespace: 'namespace/project-path', }, ]); }); }); + + describe(types.RESET_MERGE_REQUESTS, () => { + it('clears merge request array', () => { + mockedState.mergeRequests = ['test']; + + mutations[types.RESET_MERGE_REQUESTS](mockedState); + + expect(mockedState.mergeRequests).toEqual([]); + }); + }); }); -- cgit v1.2.1 From 3bffbb159eebf80dabfa2476a040a620e051be0b Mon Sep 17 00:00:00 2001 From: Phil Hughes Date: Wed, 30 May 2018 11:10:32 +0100 Subject: ability to search merge requests reset merge requests before fetch --- .../stores/modules/merge_requests/actions_spec.js | 30 +++++++++++++++++++--- 1 file changed, 27 insertions(+), 3 deletions(-) (limited to 'spec/javascripts') diff --git a/spec/javascripts/ide/stores/modules/merge_requests/actions_spec.js b/spec/javascripts/ide/stores/modules/merge_requests/actions_spec.js index 3b88ac36683..b571cfb963a 100644 --- a/spec/javascripts/ide/stores/modules/merge_requests/actions_spec.js +++ b/spec/javascripts/ide/stores/modules/merge_requests/actions_spec.js @@ -12,7 +12,7 @@ import actions, { import { mergeRequests } from '../../../mock_data'; import testAction from '../../../../helpers/vuex_action_helper'; -describe('IDe merge requests actions', () => { +describe('IDE merge requests actions', () => { let mockedState; let mock; @@ -95,6 +95,21 @@ describe('IDe merge requests actions', () => { params: { scope: 'assigned-to-me', state: 'opened', + search: '', + }, + }); + }); + + it('calls API with search', () => { + const apiSpy = spyOn(axios, 'get').and.callThrough(); + + fetchMergeRequests({ dispatch() {}, state: mockedState }, 'testing search'); + + expect(apiSpy).toHaveBeenCalledWith(jasmine.anything(), { + params: { + scope: 'assigned-to-me', + state: 'opened', + search: 'testing search', }, }); }); @@ -105,7 +120,11 @@ describe('IDe merge requests actions', () => { null, mockedState, [], - [{ type: 'requestMergeRequests' }, { type: 'receiveMergeRequestsSuccess' }], + [ + { type: 'requestMergeRequests' }, + { type: 'resetMergeRequests' }, + { type: 'receiveMergeRequestsSuccess' }, + ], done, ); }); @@ -118,6 +137,7 @@ describe('IDe merge requests actions', () => { [], [ { type: 'requestMergeRequests' }, + { type: 'resetMergeRequests' }, { type: 'receiveMergeRequestsSuccess', payload: mergeRequests }, ], done, @@ -136,7 +156,11 @@ describe('IDe merge requests actions', () => { null, mockedState, [], - [{ type: 'requestMergeRequests' }, { type: 'receiveMergeRequestsError' }], + [ + { type: 'requestMergeRequests' }, + { type: 'resetMergeRequests' }, + { type: 'receiveMergeRequestsError' }, + ], done, ); }); -- cgit v1.2.1 From c9d676c1069e6e88b4eeeea2246cc5706b56f2ab Mon Sep 17 00:00:00 2001 From: Phil Hughes Date: Wed, 30 May 2018 12:56:06 +0100 Subject: changed mutations, update single object instead of returning new array bunch of i18n stuff :see_no_evil: --- spec/javascripts/ide/components/jobs/stage_spec.js | 22 +++++++++++++--------- 1 file changed, 13 insertions(+), 9 deletions(-) (limited to 'spec/javascripts') diff --git a/spec/javascripts/ide/components/jobs/stage_spec.js b/spec/javascripts/ide/components/jobs/stage_spec.js index 66b62d90e84..5496c57bbb7 100644 --- a/spec/javascripts/ide/components/jobs/stage_spec.js +++ b/spec/javascripts/ide/components/jobs/stage_spec.js @@ -16,14 +16,18 @@ describe('IDE pipeline stage', () => { const store = createStore(); mock = new MockAdapter(axios); - store.state.pipelines.stages = stages.map((mappedState, i) => ({ - ...mappedState, - id: i, - dropdownPath: mappedState.dropdown_path, - jobs: [], - isLoading: false, - isCollapsed: false, - })); + Vue.set( + store.state.pipelines, + 'stages', + stages.map((mappedState, i) => ({ + ...mappedState, + id: i, + dropdownPath: mappedState.dropdown_path, + jobs: [], + isLoading: false, + isCollapsed: false, + })), + ); stage = store.state.pipelines.stages[0]; @@ -35,7 +39,7 @@ describe('IDE pipeline stage', () => { stage, }).$mount(); - setTimeout(done); + setTimeout(done, 500); }); afterEach(() => { -- cgit v1.2.1 From 3270e140772319f74ede743c61a94af84a850b97 Mon Sep 17 00:00:00 2001 From: Phil Hughes Date: Wed, 30 May 2018 13:31:15 +0100 Subject: changed mutation to return new array this makes the component completly unaware of the store, instead it emits events to the parent which knows about the store --- spec/javascripts/ide/components/jobs/list_spec.js | 24 ++++++++- spec/javascripts/ide/components/jobs/stage_spec.js | 62 ++++++++++------------ 2 files changed, 52 insertions(+), 34 deletions(-) (limited to 'spec/javascripts') diff --git a/spec/javascripts/ide/components/jobs/list_spec.js b/spec/javascripts/ide/components/jobs/list_spec.js index 484cab3c135..b24853c56fa 100644 --- a/spec/javascripts/ide/components/jobs/list_spec.js +++ b/spec/javascripts/ide/components/jobs/list_spec.js @@ -21,7 +21,12 @@ describe('IDE stages list', () => { isCollapsed: false, })), loading: false, - }).$mount(); + }); + + spyOn(vm, 'fetchJobs'); + spyOn(vm, 'toggleStageCollapsed'); + + vm.$mount(); }); afterEach(() => { @@ -42,4 +47,21 @@ describe('IDE stages list', () => { done(); }); }); + + it('calls toggleStageCollapsed when clicking stage header', done => { + vm.$el.querySelector('.card-header').click(); + + vm.$nextTick(() => { + expect(vm.toggleStageCollapsed).toHaveBeenCalledWith(0); + + done(); + }); + }); + + it('calls fetchJobs when stage is mounted', () => { + expect(vm.fetchJobs.calls.count()).toBe(stages.length); + + expect(vm.fetchJobs.calls.argsFor(0)).toEqual([vm.stages[0]]); + expect(vm.fetchJobs.calls.argsFor(1)).toEqual([vm.stages[1]]); + }); }); diff --git a/spec/javascripts/ide/components/jobs/stage_spec.js b/spec/javascripts/ide/components/jobs/stage_spec.js index 5496c57bbb7..fc3831f2d05 100644 --- a/spec/javascripts/ide/components/jobs/stage_spec.js +++ b/spec/javascripts/ide/components/jobs/stage_spec.js @@ -1,51 +1,37 @@ import Vue from 'vue'; -import MockAdapter from 'axios-mock-adapter'; -import axios from '~/lib/utils/axios_utils'; -import { createStore } from '~/ide/stores'; import Stage from '~/ide/components/jobs/stage.vue'; -import { createComponentWithStore } from '../../../helpers/vue_mount_component_helper'; import { stages, jobs } from '../../mock_data'; describe('IDE pipeline stage', () => { const Component = Vue.extend(Stage); let vm; - let mock; let stage; - beforeEach(done => { - const store = createStore(); - mock = new MockAdapter(axios); - - Vue.set( - store.state.pipelines, - 'stages', - stages.map((mappedState, i) => ({ - ...mappedState, - id: i, - dropdownPath: mappedState.dropdown_path, - jobs: [], - isLoading: false, - isCollapsed: false, - })), - ); - - stage = store.state.pipelines.stages[0]; - - mock.onGet(stage.dropdownPath).reply(200, { - latest_statuses: jobs, + beforeEach(() => { + stage = { + ...stages[0], + id: 0, + dropdownPath: stages[0].dropdown_path, + jobs: [...jobs], + isLoading: false, + isCollapsed: false, + }; + + vm = new Component({ + propsData: { stage }, }); - vm = createComponentWithStore(Component, store, { - stage, - }).$mount(); + spyOn(vm, '$emit'); - setTimeout(done, 500); + vm.$mount(); }); afterEach(() => { vm.$destroy(); + }); - mock.restore(); + it('emits fetch event when mounted', () => { + expect(vm.$emit).toHaveBeenCalledWith('fetch', vm.stage); }); it('renders stages details', () => { @@ -57,9 +43,19 @@ describe('IDE pipeline stage', () => { }); describe('collapsed', () => { - it('toggles collapse status when clicking header', done => { + it('emits event when clicking header', done => { vm.$el.querySelector('.card-header').click(); + vm.$nextTick(() => { + expect(vm.$emit).toHaveBeenCalledWith('toggleCollapsed', vm.stage.id); + + done(); + }); + }); + + it('toggles collapse status when collapsed', done => { + vm.stage.isCollapsed = true; + vm.$nextTick(() => { expect(vm.$el.querySelector('.card-body').style.display).toBe('none'); @@ -68,7 +64,7 @@ describe('IDE pipeline stage', () => { }); it('sets border bottom class when collapsed', done => { - vm.$el.querySelector('.card-header').click(); + vm.stage.isCollapsed = true; vm.$nextTick(() => { expect(vm.$el.querySelector('.card-header').classList).toContain('border-bottom-0'); -- cgit v1.2.1 From 9797905bd422a540954949d0bb2995b60d01a219 Mon Sep 17 00:00:00 2001 From: Phil Hughes Date: Wed, 30 May 2018 14:04:43 +0100 Subject: fixed mutations spec --- .../javascripts/ide/stores/modules/pipelines/mutations_spec.js | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) (limited to 'spec/javascripts') diff --git a/spec/javascripts/ide/stores/modules/pipelines/mutations_spec.js b/spec/javascripts/ide/stores/modules/pipelines/mutations_spec.js index 1bd7bde7c4d..6285c01d483 100644 --- a/spec/javascripts/ide/stores/modules/pipelines/mutations_spec.js +++ b/spec/javascripts/ide/stores/modules/pipelines/mutations_spec.js @@ -162,15 +162,13 @@ describe('IDE pipelines mutations', () => { }); it('toggles collapsed state', () => { - const stage = mockedState.stages[0]; + mutations[types.TOGGLE_STAGE_COLLAPSE](mockedState, mockedState.stages[0].id); - mutations[types.TOGGLE_STAGE_COLLAPSE](mockedState, stage.id); + expect(mockedState.stages[0].isCollapsed).toBe(true); - expect(stage.isCollapsed).toBe(true); + mutations[types.TOGGLE_STAGE_COLLAPSE](mockedState, mockedState.stages[0].id); - mutations[types.TOGGLE_STAGE_COLLAPSE](mockedState, stage.id); - - expect(stage.isCollapsed).toBe(false); + expect(mockedState.stages[0].isCollapsed).toBe(false); }); }); }); -- cgit v1.2.1 From 6a4b4255a16c0c63e1ff2fc9f76ff61cbc5e158c Mon Sep 17 00:00:00 2001 From: Kushal Pandya Date: Thu, 31 May 2018 17:40:41 +0530 Subject: Update Sortable package path --- spec/javascripts/boards/board_list_spec.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'spec/javascripts') diff --git a/spec/javascripts/boards/board_list_spec.js b/spec/javascripts/boards/board_list_spec.js index a49b190d36a..de261d36c61 100644 --- a/spec/javascripts/boards/board_list_spec.js +++ b/spec/javascripts/boards/board_list_spec.js @@ -3,7 +3,7 @@ import Vue from 'vue'; import MockAdapter from 'axios-mock-adapter'; import axios from '~/lib/utils/axios_utils'; -import Sortable from 'vendor/Sortable'; +import Sortable from 'sortablejs'; import BoardList from '~/boards/components/board_list.vue'; import eventHub from '~/boards/eventhub'; import '~/boards/mixins/sortable_default_options'; -- cgit v1.2.1 From 840f80d48b7d8363f171f6137cd9f1fbafb52bfc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Francisco=20Javier=20L=C3=B3pez?= Date: Fri, 1 Jun 2018 11:43:53 +0000 Subject: Add validation to webhook and service URLs to ensure they are not blocked because of SSRF --- .../integrations/integration_settings_form_spec.js | 23 ++++++++++++++++++++++ 1 file changed, 23 insertions(+) (limited to 'spec/javascripts') diff --git a/spec/javascripts/integrations/integration_settings_form_spec.js b/spec/javascripts/integrations/integration_settings_form_spec.js index 050b1f2074e..e07343810d2 100644 --- a/spec/javascripts/integrations/integration_settings_form_spec.js +++ b/spec/javascripts/integrations/integration_settings_form_spec.js @@ -143,6 +143,7 @@ describe('IntegrationSettingsForm', () => { error: true, message: errorMessage, service_response: 'some error', + test_failed: true, }); integrationSettingsForm.testSettings(formData) @@ -157,6 +158,27 @@ describe('IntegrationSettingsForm', () => { .catch(done.fail); }); + it('should not show error Flash with `Save anyway` action if ajax request responds with error in validation', (done) => { + const errorMessage = 'Validations failed.'; + mock.onPut(integrationSettingsForm.testEndPoint).reply(200, { + error: true, + message: errorMessage, + service_response: 'some error', + test_failed: false, + }); + + integrationSettingsForm.testSettings(formData) + .then(() => { + const $flashContainer = $('.flash-container'); + expect($flashContainer.find('.flash-text').text().trim()).toEqual('Validations failed. some error'); + expect($flashContainer.find('.flash-action')).toBeDefined(); + expect($flashContainer.find('.flash-action').text().trim()).toEqual(''); + + done(); + }) + .catch(done.fail); + }); + it('should submit form if ajax request responds without any error in test', (done) => { spyOn(integrationSettingsForm.$form, 'submit'); @@ -180,6 +202,7 @@ describe('IntegrationSettingsForm', () => { mock.onPut(integrationSettingsForm.testEndPoint).reply(200, { error: true, message: errorMessage, + test_failed: true, }); integrationSettingsForm.testSettings(formData) -- cgit v1.2.1 From 399e1e83c038685f15bf3049382a16c6ccb8fa37 Mon Sep 17 00:00:00 2001 From: Paul Slaughter Date: Fri, 1 Jun 2018 16:24:56 +0000 Subject: Update position sticky polyfill --- spec/javascripts/lib/utils/sticky_spec.js | 79 ------------------------------- 1 file changed, 79 deletions(-) delete mode 100644 spec/javascripts/lib/utils/sticky_spec.js (limited to 'spec/javascripts') diff --git a/spec/javascripts/lib/utils/sticky_spec.js b/spec/javascripts/lib/utils/sticky_spec.js deleted file mode 100644 index b87c836654d..00000000000 --- a/spec/javascripts/lib/utils/sticky_spec.js +++ /dev/null @@ -1,79 +0,0 @@ -import { isSticky } from '~/lib/utils/sticky'; - -describe('sticky', () => { - let el; - - beforeEach(() => { - document.body.innerHTML += ` -
-
-
- `; - - el = document.getElementById('js-sticky'); - }); - - afterEach(() => { - el.parentNode.remove(); - }); - - describe('when stuck', () => { - it('does not remove is-stuck class', () => { - isSticky(el, 0, el.offsetTop); - isSticky(el, 0, el.offsetTop); - - expect( - el.classList.contains('is-stuck'), - ).toBeTruthy(); - }); - - it('adds is-stuck class', () => { - isSticky(el, 0, el.offsetTop); - - expect( - el.classList.contains('is-stuck'), - ).toBeTruthy(); - }); - - it('inserts placeholder element', () => { - isSticky(el, 0, el.offsetTop, true); - - expect( - document.querySelector('.sticky-placeholder'), - ).not.toBeNull(); - }); - }); - - describe('when not stuck', () => { - it('removes is-stuck class', () => { - spyOn(el.classList, 'remove').and.callThrough(); - - isSticky(el, 0, el.offsetTop); - isSticky(el, 0, 0); - - expect( - el.classList.remove, - ).toHaveBeenCalledWith('is-stuck'); - expect( - el.classList.contains('is-stuck'), - ).toBeFalsy(); - }); - - it('does not add is-stuck class', () => { - isSticky(el, 0, 0); - - expect( - el.classList.contains('is-stuck'), - ).toBeFalsy(); - }); - - it('removes placeholder', () => { - isSticky(el, 0, el.offsetTop, true); - isSticky(el, 0, 0, true); - - expect( - document.querySelector('.sticky-placeholder'), - ).toBeNull(); - }); - }); -}); -- cgit v1.2.1 From 30bc82f68b6de69a2e456286888824fa0221d4a7 Mon Sep 17 00:00:00 2001 From: Clement Ho Date: Fri, 1 Jun 2018 16:41:50 +0000 Subject: Revert "Merge branch '46833-sticky-polyfill' into 'master'" This reverts merge request !19304 --- spec/javascripts/lib/utils/sticky_spec.js | 79 +++++++++++++++++++++++++++++++ 1 file changed, 79 insertions(+) create mode 100644 spec/javascripts/lib/utils/sticky_spec.js (limited to 'spec/javascripts') diff --git a/spec/javascripts/lib/utils/sticky_spec.js b/spec/javascripts/lib/utils/sticky_spec.js new file mode 100644 index 00000000000..b87c836654d --- /dev/null +++ b/spec/javascripts/lib/utils/sticky_spec.js @@ -0,0 +1,79 @@ +import { isSticky } from '~/lib/utils/sticky'; + +describe('sticky', () => { + let el; + + beforeEach(() => { + document.body.innerHTML += ` +
+
+
+ `; + + el = document.getElementById('js-sticky'); + }); + + afterEach(() => { + el.parentNode.remove(); + }); + + describe('when stuck', () => { + it('does not remove is-stuck class', () => { + isSticky(el, 0, el.offsetTop); + isSticky(el, 0, el.offsetTop); + + expect( + el.classList.contains('is-stuck'), + ).toBeTruthy(); + }); + + it('adds is-stuck class', () => { + isSticky(el, 0, el.offsetTop); + + expect( + el.classList.contains('is-stuck'), + ).toBeTruthy(); + }); + + it('inserts placeholder element', () => { + isSticky(el, 0, el.offsetTop, true); + + expect( + document.querySelector('.sticky-placeholder'), + ).not.toBeNull(); + }); + }); + + describe('when not stuck', () => { + it('removes is-stuck class', () => { + spyOn(el.classList, 'remove').and.callThrough(); + + isSticky(el, 0, el.offsetTop); + isSticky(el, 0, 0); + + expect( + el.classList.remove, + ).toHaveBeenCalledWith('is-stuck'); + expect( + el.classList.contains('is-stuck'), + ).toBeFalsy(); + }); + + it('does not add is-stuck class', () => { + isSticky(el, 0, 0); + + expect( + el.classList.contains('is-stuck'), + ).toBeFalsy(); + }); + + it('removes placeholder', () => { + isSticky(el, 0, el.offsetTop, true); + isSticky(el, 0, 0, true); + + expect( + document.querySelector('.sticky-placeholder'), + ).toBeNull(); + }); + }); +}); -- cgit v1.2.1