diff options
author | Phil Hughes <me@iamphill.com> | 2018-05-29 13:09:19 +0100 |
---|---|---|
committer | Phil Hughes <me@iamphill.com> | 2018-05-29 13:09:19 +0100 |
commit | 66bf2de830c421c06bef571a87f8d3d48edcad76 (patch) | |
tree | 50235dec4a3595e76c5bebe43a64aecdfc20f93c | |
parent | dd17a4840659467628d9390139e4430a05349175 (diff) | |
download | gitlab-ce-66bf2de830c421c06bef571a87f8d3d48edcad76.tar.gz |
added component specs
-rw-r--r-- | app/assets/javascripts/ide/stores/index.js | 23 | ||||
-rw-r--r-- | app/assets/javascripts/ide/stores/modules/pipelines/getters.js | 4 | ||||
-rw-r--r-- | app/assets/javascripts/ide/stores/modules/pipelines/mutations.js | 38 | ||||
-rw-r--r-- | app/assets/javascripts/ide/stores/state.js | 1 | ||||
-rw-r--r-- | spec/javascripts/ide/components/jobs/item_spec.js | 29 | ||||
-rw-r--r-- | spec/javascripts/ide/components/jobs/list_spec.js | 45 | ||||
-rw-r--r-- | spec/javascripts/ide/components/jobs/stage_spec.js | 95 | ||||
-rw-r--r-- | spec/javascripts/ide/components/pipelines/list_spec.js | 114 | ||||
-rw-r--r-- | spec/javascripts/ide/helpers.js | 2 | ||||
-rw-r--r-- | spec/javascripts/ide/mock_data.js | 48 |
10 files changed, 360 insertions, 39 deletions
diff --git a/app/assets/javascripts/ide/stores/index.js b/app/assets/javascripts/ide/stores/index.js index 699710055e3..5d26cb8d626 100644 --- a/app/assets/javascripts/ide/stores/index.js +++ b/app/assets/javascripts/ide/stores/index.js @@ -9,13 +9,16 @@ import pipelines from './modules/pipelines'; Vue.use(Vuex); -export default new Vuex.Store({ - state: state(), - actions, - mutations, - getters, - modules: { - commit: commitModule, - pipelines, - }, -}); +export const createStore = () => + new Vuex.Store({ + state: state(), + actions, + mutations, + getters, + modules: { + commit: commitModule, + pipelines, + }, + }); + +export default createStore(); diff --git a/app/assets/javascripts/ide/stores/modules/pipelines/getters.js b/app/assets/javascripts/ide/stores/modules/pipelines/getters.js index fea77b661b5..d6f10e0d71e 100644 --- a/app/assets/javascripts/ide/stores/modules/pipelines/getters.js +++ b/app/assets/javascripts/ide/stores/modules/pipelines/getters.js @@ -4,9 +4,9 @@ export const pipelineFailed = state => state.latestPipeline && state.latestPipeline.details.status.text === 'failed'; export const failedStages = state => - state.stages.filter(stage => stage.status.text === 'failed').map(stage => ({ + state.stages.filter(stage => stage.status.text.toLowerCase() === 'failed').map(stage => ({ ...stage, - jobs: stage.jobs.filter(job => job.status.text === 'failed'), + jobs: stage.jobs.filter(job => job.status.text.toLowerCase() === 'failed'), })); export const failedJobsCount = state => diff --git a/app/assets/javascripts/ide/stores/modules/pipelines/mutations.js b/app/assets/javascripts/ide/stores/modules/pipelines/mutations.js index 419be989901..39552c548ea 100644 --- a/app/assets/javascripts/ide/stores/modules/pipelines/mutations.js +++ b/app/assets/javascripts/ide/stores/modules/pipelines/mutations.js @@ -38,16 +38,18 @@ export default { } }, [types.REQUEST_JOBS](state, id) { - state.stages = state.stages.map(stage => ({ - ...stage, - isLoading: id === stage.id ? true : stage.isLoading, - })); + state.stages = state.stages.map(stage => + Object.assign(stage, { + isLoading: id === stage.id ? true : stage.isLoading, + }), + ); }, [types.RECEIVE_JOBS_ERROR](state, id) { - state.stages = state.stages.map(stage => ({ - ...stage, - isLoading: id === stage.id ? true : stage.isLoading, - })); + state.stages = state.stages.map(stage => + Object.assign(stage, { + isLoading: id === stage.id ? true : stage.isLoading, + }), + ); }, [types.RECEIVE_JOBS_SUCCESS](state, { id, data }) { const normalizeData = job => ({ @@ -57,16 +59,18 @@ export default { path: job.build_path, }); - state.stages = state.stages.map(stage => ({ - ...stage, - isLoading: id === stage.id ? false : stage.isLoading, - jobs: id === stage.id ? data.latest_statuses.map(normalizeData) : stage.jobs, - })); + state.stages = state.stages.map(stage => + Object.assign(stage, { + isLoading: id === stage.id ? false : stage.isLoading, + jobs: id === stage.id ? data.latest_statuses.map(normalizeData) : stage.jobs, + }), + ); }, [types.TOGGLE_STAGE_COLLAPSE](state, id) { - state.stages = state.stages.map(stage => ({ - ...stage, - isCollapsed: stage.id === id ? !stage.isCollapsed : stage.isCollapsed, - })); + state.stages = state.stages.map(stage => + Object.assign(stage, { + isCollapsed: stage.id === id ? !stage.isCollapsed : stage.isCollapsed, + }), + ); }, }; diff --git a/app/assets/javascripts/ide/stores/state.js b/app/assets/javascripts/ide/stores/state.js index ef8d678dd43..4aac4696075 100644 --- a/app/assets/javascripts/ide/stores/state.js +++ b/app/assets/javascripts/ide/stores/state.js @@ -24,4 +24,5 @@ export default () => ({ unusedSeal: true, fileFindVisible: false, rightPane: null, + links: {}, }); 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, }, |