Commit 4b0ff7c7 authored by Filipa Lacerda's avatar Filipa Lacerda

Merge branch 'ide-list-merge-requests' into 'master'

Show merge requests in web IDE

Closes #45184

See merge request gitlab-org/gitlab-ce!18898
parents ccf05290 61bf5ede
......@@ -11,6 +11,7 @@ const Api = {
projectPath: '/api/:version/projects/:id',
projectLabelsPath: '/:namespace_path/:project_path/labels',
mergeRequestPath: '/api/:version/projects/:id/merge_requests/:mrid',
mergeRequestsPath: '/api/:version/merge_requests',
mergeRequestChangesPath: '/api/:version/projects/:id/merge_requests/:mrid/changes',
mergeRequestVersionsPath: '/api/:version/projects/:id/merge_requests/:mrid/versions',
groupLabelsPath: '/groups/:namespace_path/-/labels',
......@@ -107,6 +108,12 @@ const Api = {
return axios.get(url);
},
mergeRequests(params = {}) {
const url = Api.buildUrl(Api.mergeRequestsPath);
return axios.get(url, { params });
},
mergeRequestChanges(projectPath, mergeRequestId) {
const url = Api.buildUrl(Api.mergeRequestChangesPath)
.replace(':id', encodeURIComponent(projectPath))
......
......@@ -6,6 +6,7 @@ import * as getters from './getters';
import mutations from './mutations';
import commitModule from './modules/commit';
import pipelines from './modules/pipelines';
import mergeRequests from './modules/merge_requests';
Vue.use(Vuex);
......@@ -18,6 +19,7 @@ export const createStore = () =>
modules: {
commit: commitModule,
pipelines,
mergeRequests,
},
});
......
import { __ } from '../../../../locale';
import Api from '../../../../api';
import flash from '../../../../flash';
import * as types from './mutation_types';
export const requestMergeRequests = ({ commit }) => commit(types.REQUEST_MERGE_REQUESTS);
export const receiveMergeRequestsError = ({ commit }) => {
flash(__('Error loading merge requests.'));
commit(types.RECEIVE_MERGE_REQUESTS_ERROR);
};
export const receiveMergeRequestsSuccess = ({ commit }, data) =>
commit(types.RECEIVE_MERGE_REQUESTS_SUCCESS, data);
export const fetchMergeRequests = ({ dispatch, state: { scope, state } }, search = '') => {
dispatch('requestMergeRequests');
dispatch('resetMergeRequests');
Api.mergeRequests({ scope, state, search })
.then(({ data }) => dispatch('receiveMergeRequestsSuccess', data))
.catch(() => dispatch('receiveMergeRequestsError'));
};
export const resetMergeRequests = ({ commit }) => commit(types.RESET_MERGE_REQUESTS);
export default () => {};
export const scopes = {
assignedToMe: 'assigned-to-me',
createdByMe: 'created-by-me',
};
export const states = {
opened: 'opened',
closed: 'closed',
merged: 'merged',
};
import state from './state';
import * as actions from './actions';
import mutations from './mutations';
export default {
namespaced: true,
state: state(),
actions,
mutations,
};
export const REQUEST_MERGE_REQUESTS = 'REQUEST_MERGE_REQUESTS';
export const RECEIVE_MERGE_REQUESTS_ERROR = 'RECEIVE_MERGE_REQUESTS_ERROR';
export const RECEIVE_MERGE_REQUESTS_SUCCESS = 'RECEIVE_MERGE_REQUESTS_SUCCESS';
export const RESET_MERGE_REQUESTS = 'RESET_MERGE_REQUESTS';
/* eslint-disable no-param-reassign */
import * as types from './mutation_types';
export default {
[types.REQUEST_MERGE_REQUESTS](state) {
state.isLoading = true;
},
[types.RECEIVE_MERGE_REQUESTS_ERROR](state) {
state.isLoading = false;
},
[types.RECEIVE_MERGE_REQUESTS_SUCCESS](state, data) {
state.isLoading = false;
state.mergeRequests = data.map(mergeRequest => ({
id: mergeRequest.id,
iid: mergeRequest.iid,
title: mergeRequest.title,
projectId: mergeRequest.project_id,
projectPathWithNamespace: mergeRequest.web_url
.replace(`${gon.gitlab_url}/`, '')
.replace(`/merge_requests/${mergeRequest.iid}`, ''),
}));
},
[types.RESET_MERGE_REQUESTS](state) {
state.mergeRequests = [];
},
};
import { scopes, states } from './constants';
export default () => ({
isLoading: false,
mergeRequests: [],
scope: scopes.assignedToMe,
state: states.opened,
});
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';
import pipelinesState from '~/ide/stores/modules/pipelines/state';
export const resetStore = store => {
const newState = {
...state(),
commit: commitState(),
mergeRequests: mergeRequestsState(),
pipelines: pipelinesState(),
};
store.replaceState(newState);
......
......@@ -147,3 +147,13 @@ 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`,
},
];
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,
resetMergeRequests,
} 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',
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',
},
});
});
it('dispatches request', done => {
testAction(
fetchMergeRequests,
null,
mockedState,
[],
[
{ type: 'requestMergeRequests' },
{ type: 'resetMergeRequests' },
{ type: 'receiveMergeRequestsSuccess' },
],
done,
);
});
it('dispatches success with received data', done => {
testAction(
fetchMergeRequests,
null,
mockedState,
[],
[
{ type: 'requestMergeRequests' },
{ type: 'resetMergeRequests' },
{ 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: 'resetMergeRequests' },
{ type: 'receiveMergeRequestsError' },
],
done,
);
});
});
});
describe('resetMergeRequests', () => {
it('commits reset', done => {
testAction(
resetMergeRequests,
null,
mockedState,
[{ type: types.RESET_MERGE_REQUESTS }],
[],
done,
);
});
});
});
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', () => {
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([]);
});
});
});
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment