diff options
author | Phil Hughes <me@iamphill.com> | 2018-05-14 16:30:52 +0100 |
---|---|---|
committer | Phil Hughes <me@iamphill.com> | 2018-05-22 11:11:36 +0100 |
commit | ba90742631a5e1a0d1edc546d5d49b59210642bf (patch) | |
tree | 67b8e75ae82003cf502ba7303bf6d586224949cb | |
parent | 50985f54baf9a4d7e840e22ce8914f0bc919f5d8 (diff) | |
download | gitlab-ce-ba90742631a5e1a0d1edc546d5d49b59210642bf.tar.gz |
group jobs into stageside-jobs-list
9 files changed, 171 insertions, 27 deletions
diff --git a/app/assets/javascripts/api.js b/app/assets/javascripts/api.js index e16d520024b..eb919241318 100644 --- a/app/assets/javascripts/api.js +++ b/app/assets/javascripts/api.js @@ -230,12 +230,12 @@ const Api = { return axios.get(url, { params }); }, - pipelineJobs(projectPath, pipelineId) { + pipelineJobs(projectPath, pipelineId, params = {}) { const url = Api.buildUrl(this.pipelineJobsPath) .replace(':id', encodeURIComponent(projectPath)) .replace(':pipeline_id', pipelineId); - return axios.get(url); + return axios.get(url, { params }); }, buildUrl(url) { diff --git a/app/assets/javascripts/ide/stores/modules/pipelines/actions.js b/app/assets/javascripts/ide/stores/modules/pipelines/actions.js index 146a0e72354..07f7b201f2e 100644 --- a/app/assets/javascripts/ide/stores/modules/pipelines/actions.js +++ b/app/assets/javascripts/ide/stores/modules/pipelines/actions.js @@ -28,12 +28,20 @@ export const receiveJobsError = ({ commit }) => { }; export const receiveJobsSuccess = ({ commit }, data) => commit(types.RECEIVE_JOBS_SUCCESS, data); -export const fetchJobs = ({ dispatch, state, rootState }) => { +export const fetchJobs = ({ dispatch, state, rootState }, page = '1') => { dispatch('requestJobs'); - Api.pipelineJobs(rootState.currentProjectId, state.latestPipeline.id) - .then(({ data }) => { + Api.pipelineJobs(rootState.currentProjectId, state.latestPipeline.id, { + page, + }) + .then(({ data, headers }) => { + const nextPage = headers && headers['x-next-page']; + dispatch('receiveJobsSuccess', data); + + if (nextPage) { + dispatch('fetchJobs', nextPage); + } }) .catch(() => dispatch('receiveJobsError')); }; diff --git a/app/assets/javascripts/ide/stores/modules/pipelines/getters.js b/app/assets/javascripts/ide/stores/modules/pipelines/getters.js index 8344bb1b34e..d6c91f5b64d 100644 --- a/app/assets/javascripts/ide/stores/modules/pipelines/getters.js +++ b/app/assets/javascripts/ide/stores/modules/pipelines/getters.js @@ -1,2 +1,7 @@ -// eslint-disable-next-line import/prefer-default-export export const hasLatestPipeline = state => !state.isLoadingPipeline && !!state.latestPipeline; + +export const failedJobs = state => + state.stages.reduce( + (acc, stage) => acc.concat(stage.jobs.filter(job => job.status === 'failed')), + [], + ); diff --git a/app/assets/javascripts/ide/stores/modules/pipelines/mutations.js b/app/assets/javascripts/ide/stores/modules/pipelines/mutations.js index 382775de10a..2b16e57b386 100644 --- a/app/assets/javascripts/ide/stores/modules/pipelines/mutations.js +++ b/app/assets/javascripts/ide/stores/modules/pipelines/mutations.js @@ -26,12 +26,28 @@ export default { }, [types.RECEIVE_JOBS_SUCCESS](state, jobs) { state.isLoadingJobs = false; - state.jobs = jobs.map(job => ({ - id: job.id, - name: job.name, - status: job.status, - stage: job.stage, - duration: job.duration, - })); + + state.stages = jobs.reduce((acc, job) => { + let stage = acc.find(s => s.title === job.stage); + + if (!stage) { + stage = { + title: job.stage, + jobs: [], + }; + + acc.push(stage); + } + + stage.jobs = stage.jobs.concat({ + id: job.id, + name: job.name, + status: job.status, + stage: job.stage, + duration: job.duration, + }); + + return acc; + }, state.stages); }, }; diff --git a/app/assets/javascripts/ide/stores/modules/pipelines/state.js b/app/assets/javascripts/ide/stores/modules/pipelines/state.js index deb376f07d6..6f22542aaea 100644 --- a/app/assets/javascripts/ide/stores/modules/pipelines/state.js +++ b/app/assets/javascripts/ide/stores/modules/pipelines/state.js @@ -2,5 +2,5 @@ export default () => ({ isLoadingPipeline: false, isLoadingJobs: false, latestPipeline: null, - jobs: [], + stages: [], }); diff --git a/spec/javascripts/ide/mock_data.js b/spec/javascripts/ide/mock_data.js index 4a565ec57ce..7e641c7984b 100644 --- a/spec/javascripts/ide/mock_data.js +++ b/spec/javascripts/ide/mock_data.js @@ -51,4 +51,11 @@ export const jobs = [ stage: 'test', duration: 1, }, + { + id: 4, + name: 'test 3', + status: 'failed', + stage: 'build', + duration: 1, + }, ]; diff --git a/spec/javascripts/ide/stores/modules/pipelines/actions_spec.js b/spec/javascripts/ide/stores/modules/pipelines/actions_spec.js index b7f04642dcd..85fbcf8084b 100644 --- a/spec/javascripts/ide/stores/modules/pipelines/actions_spec.js +++ b/spec/javascripts/ide/stores/modules/pipelines/actions_spec.js @@ -182,13 +182,21 @@ describe('IDE pipelines actions', () => { }); describe('fetchJobs', () => { + let page = ''; + beforeEach(() => { mockedState.latestPipeline = pipelines[0]; }); describe('success', () => { beforeEach(() => { - mock.onGet(/\/api\/v4\/projects\/(.*)\/pipelines\/(.*)\/jobs/).replyOnce(200, jobs); + mock.onGet(/\/api\/v4\/projects\/(.*)\/pipelines\/(.*)\/jobs/).replyOnce(() => [ + 200, + jobs, + { + 'x-next-page': page, + }, + ]); }); it('dispatches request', done => { @@ -213,12 +221,51 @@ describe('IDE pipelines actions', () => { ); }); + 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 }, + ], + done, + ); + }); + it('calls axios with correct URL', () => { const apiSpy = spyOn(axios, 'get').and.callThrough(); fetchJobs({ dispatch() {}, state: mockedState, rootState: mockedState }); - expect(apiSpy).toHaveBeenCalledWith('/api/v4/projects/test%2Fproject/pipelines/1/jobs'); + 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' }, + }); }); }); diff --git a/spec/javascripts/ide/stores/modules/pipelines/getters_spec.js b/spec/javascripts/ide/stores/modules/pipelines/getters_spec.js index 4514896b5ea..b2a7e8a9025 100644 --- a/spec/javascripts/ide/stores/modules/pipelines/getters_spec.js +++ b/spec/javascripts/ide/stores/modules/pipelines/getters_spec.js @@ -37,4 +37,35 @@ 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 59e218ef0a6..8262e916243 100644 --- a/spec/javascripts/ide/stores/modules/pipelines/mutations_spec.js +++ b/spec/javascripts/ide/stores/modules/pipelines/mutations_spec.js @@ -72,19 +72,49 @@ describe('IDE pipelines mutations', () => { expect(mockedState.isLoadingJobs).toBe(false); }); - it('sets jobs', () => { + it('sets stages', () => { mutations[types.RECEIVE_JOBS_SUCCESS](mockedState, jobs); - expect(mockedState.jobs.length).toBe(3); - expect(mockedState.jobs).toEqual( - jobs.map(job => ({ - id: job.id, - name: job.name, - status: job.status, - stage: job.stage, - duration: job.duration, - })), - ); + expect(mockedState.stages.length).toBe(2); + expect(mockedState.stages).toEqual([ + { + title: 'test', + jobs: jasmine.anything(), + }, + { + 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, + })), + }, + ]); }); }); }); |