Commit 32ba2c17 authored by Giovanni's avatar Giovanni

we have the mergeRequests

parent a7e13f22
Pipeline #62042380 failed with stage
in 1 minute and 26 seconds
......@@ -11,4 +11,6 @@ export const getLoaderValue = state => !!_.get(getAppState(state), 'loaderValue'
export const isPopupVisible = state => !!_.get(getAppState(state), 'isPopupVisible');
export const getPopupContent = state => _.get(getAppState(state), 'popup');
export const getPagination = (state, entityName) => _.get(state, [entityName, 'pagination'], {});
export function getPagination(state, entityName) {
return _.get(state, [entityName, 'pagination'], _.get(state, [_.camelCase(entityName), 'pagination'], {}));
}
......@@ -12,7 +12,7 @@ function AvatarsComponent({assignee, author, navigate}) {
const authorImage = _.get(author, 'avatar_url');
const authorName = _.get(author, 'name');
const authorId = _.get(author, 'id');
const assigneeId = _.get(author, 'id');
const assigneeId = _.get(assignee, 'id');
const authorDifferentFromAssignee = (assignee || author) && (authorId !== assigneeId);
return (
......
......@@ -6,7 +6,17 @@ import _ from 'lodash';
import {setFooter, setHeaderLeftButton, MainFooter} from 'headerFooter';
import {getPreviousRouteName, navigate, registerRoute} from 'router';
import {getLastUpdatedTime} from 'utils';
import {Icon, FormattedText, ItemList, Tabs, ScrollableAppContainer, AppContainer, Tag, EmptyData} from 'elements';
import {
Icon,
FormattedText,
ItemList,
Tabs,
ScrollableAppContainer,
AppContainer,
Tag,
EmptyData,
LoadMore
} from 'elements';
import {getClosedMergeRequests, getMergedRequests, getOpenMergeRequests} from './selectors';
import {clearMergeRequests, requestMergeRequests} from './actions';
......@@ -44,7 +54,7 @@ class MergeRequestsComponent extends Component {
render() {
const {navigate, openMergeRequests, mergedMergeRequests, closedMergeRequests, requestMergeRequests, personal, projectId} = this.props;
const {selectedTab} = this.state;
let mergeRequests = [];
let mergeRequests;
if (selectedTab === 'Open') {
mergeRequests = openMergeRequests;
......@@ -57,17 +67,17 @@ class MergeRequestsComponent extends Component {
const tabs = [
{
id: 'Open',
text: `Open (${_.size(openMergeRequests) === 100 ? '99+' : _.size(openMergeRequests)})`,
text: `Open (${_.size(openMergeRequests) >= 100 ? '99+' : _.size(openMergeRequests)})`,
onPress: () => this.setState({selectedTab: 'Open'})
},
{
id: 'Merged',
text: `Merged (${_.size(mergedMergeRequests) === 100 ? '99+' : _.size(mergedMergeRequests)})`,
text: `Merged (${_.size(mergedMergeRequests) >= 100 ? '99+' : _.size(mergedMergeRequests)})`,
onPress: () => this.setState({selectedTab: 'Merged'})
},
{
id: 'Closed',
text: `Closed (${_.size(closedMergeRequests) === 100 ? '99+' : _.size(closedMergeRequests)})`,
text: `Closed (${_.size(closedMergeRequests) >= 100 ? '99+' : _.size(closedMergeRequests)})`,
onPress: () => this.setState({selectedTab: 'Closed'})
}
];
......@@ -78,10 +88,9 @@ class MergeRequestsComponent extends Component {
<ScrollableAppContainer refresh={() => requestMergeRequests(personal, true, projectId)}>
{!_.size(mergeRequests) && <EmptyData/>}
<FlatList
ListFooterComponent={<LoadMore entityName={'merge_requests'} onPress={() => requestMergeRequests(personal, true, projectId)}/>}
keyExtractor={item => `${item.id}`}
data={mergeRequests}
onEndReachedThreshold={5}
onEndReached={() => requestMergeRequests(personal, false, projectId)}
renderItem={({item}) => <SingleMergeRequest navigate={navigate} mr={item}/>}/>
</ScrollableAppContainer>
</AppContainer>
......
import React from 'react';
import {Provider} from "react-redux";
import {getRegisteredRoutes, getRouteName, navigate} from "router";
import _ from "lodash";
import {getMergedRequests} from "../selectors";
import {clearMergeRequests, requestMergeRequests} from "../actions";
import {getPagination, setPagination} from "utils";
const fakeMergeRequests = [
{
id: 1,
title: 'someTitle',
state: 'opened',
description: 'someDescription',
merge_status: 'can_be_merged',
web_url: 'someUrl',
created_at: '2019-05-11T14:30:23.406Z',
updated_at: '2019-05-11T14:30:23.406Z',
author: {id: 1},
assignee: {id: 2},
milestone: {title: 'someTitle', due_date: '2019-05-11T14:30:23.406Z'},
assignees: [],
labels: ['some', 'label']
},
{
id: 2,
title: 'someTitle2',
state: 'closed',
description: 'someDescription2',
merge_status: 'can_be_merged',
web_url: 'someUrl2',
created_at: '2019-05-11T14:30:23.406Z',
updated_at: '2019-05-11T14:30:23.406Z',
author: {id: 2},
assignee: {id: 2},
milestone: {title: 'someTitle2', due_date: '2019-05-11T14:30:23.406Z'},
assignees: [],
labels: ['some2', 'label']
},
{
id: 3,
title: 'someTitle3',
state: 'merged',
description: 'someDescription3',
merge_status: 'can_be_merged',
web_url: 'someUrl3',
created_at: '2019-05-11T14:30:23.406Z',
updated_at: '2019-05-11T14:30:23.406Z',
author: {id: 3},
assignee: {id: 2},
milestone: {title: 'someTitle3', due_date: '2019-05-11T14:30:23.406Z'},
assignees: [],
labels: ['some', 'label3']
}
];
describe('MergeRequests', () => {
let MergeRequests;
let store;
beforeAll(() => {
const {setStore} = require("api");
const {getStore} = require('store');
require('../index');
require('../../repository/reducers');
setStore(getStore());
});
beforeEach(() => {
MergeRequests = getRegisteredRoutes().MergeRequests;
store = require('store').getStore();
store.dispatch(clearMergeRequests());
// populate navigation's history
store.dispatch(navigate('Repository'));
store.dispatch(navigate('Glasnost'));
store.dispatch({type: 'REPOSITORY_RECEIVED', data: {id: 1}});
});
it('renders correctly the empty page', async () => {
const component = await asyncCreate(<Provider store={store}><MergeRequests/></Provider>);
expect(component.toJSON()).toMatchSnapshot();
});
it('should not render because there is no repository', async () => {
store.dispatch({type: 'REPOSITORY_RECEIVED', data: null});
const component = await asyncCreate(<Provider store={store}><MergeRequests/></Provider>);
expect(component.toJSON()).toMatchSnapshot();
});
it('should not render because there is no mergeRequests', async () => {
onGet({url: /merge_requests*/, data: null});
const component = await asyncCreate(<Provider store={store}><MergeRequests/></Provider>);
expect(component.toJSON()).toMatchSnapshot();
});
it('renders the list', async () => {
onGet({url: /merge_requests*/, data: fakeMergeRequests});
const component = await asyncCreate(<Provider store={store}><MergeRequests/></Provider>);
expect(component.toJSON()).toMatchSnapshot();
});
it('renders the personal list', async () => {
onGet({url: /merge_requests*/, data: fakeMergeRequests});
const component = await asyncCreate(<Provider store={store}><MergeRequests personal={true}/></Provider>);
expect(component.toJSON()).toMatchSnapshot();
});
it('refreshes the list', async () => {
onGet({url: /merge_requests*/, data: fakeMergeRequests});
const component = mount(<Provider store={store}><MergeRequests/></Provider>);
await waitForAsync();
component.update();
const ScrollableAppContainer = component.find('ScrollableAppContainerComponent').at(0);
ScrollableAppContainer.props().refresh();
await waitForAsync();
component.update();
expect(component.html()).toMatchSnapshot();
});
it('should ask for more merge_requests when clicking the button', async () => {
onGet({url: /merge_requests*/, data: fakeMergeRequests});
const component = mount(<Provider store={store}><MergeRequests/></Provider>);
await waitForAsync();
component.update();
const LoadMore = component.find('LoadMoreComponent');
LoadMore.props().onPress();
await waitForAsync();
component.update();
expect(component.html()).toMatchSnapshot();
});
it('should show the merged mergeRequests', async () => {
onGet({url: /merge_requests*/, data: fakeMergeRequests});
const component = mount(<Provider store={store}><MergeRequests/></Provider>);
await waitForAsync();
component.update();
const SingleTab = component.find('SingleTab').at(1);
SingleTab.props().onPress();
component.update();
expect(component.html()).toMatchSnapshot();
});
it('should show the closed mergeRequests', async () => {
onGet({url: /merge_requests*/, data: fakeMergeRequests});
const component = mount(<Provider store={store}><MergeRequests/></Provider>);
await waitForAsync();
component.update();
const SingleTab = component.find('SingleTab').at(2);
SingleTab.props().onPress();
component.update();
expect(component.html()).toMatchSnapshot();
});
it('should show the open mergeRequests', async () => {
onGet({url: /merge_requests*/, data: fakeMergeRequests});
const component = mount(<Provider store={store}><MergeRequests/></Provider>);
await waitForAsync();
component.update();
const SingleTab = component.find('SingleTab').at(0);
SingleTab.props().onPress();
component.update();
expect(component.html()).toMatchSnapshot();
});
it('should navigate to a single mergeRequest', async () => {
onGet({url: /merge_requests*/, data: fakeMergeRequests});
const component = mount(<Provider store={store}><MergeRequests/></Provider>);
await waitForAsync();
component.update();
const SingleMergeRequest = component.find('SingleMergeRequest').at(0).find('TouchableOpacity').at(0);
SingleMergeRequest.props().onPress();
component.update();
expect(getRouteName(store.getState())).toEqual('MergeRequest');
});
it('should navigate to the profile', async () => {
onGet({url: /merge_requests*/, data: fakeMergeRequests});
const component = mount(<Provider store={store}><MergeRequests/></Provider>);
await waitForAsync();
component.update();
const Avatar = component.find('SingleMergeRequest').at(0).find('CircledImageComponent').at(0);
Avatar.props().onPress();
component.update();
expect(getRouteName(store.getState())).toEqual('Profile');
});
it('should call the functions on componentWillUnmount', async () => {
onGet({url: /merge_requests*/, data: fakeMergeRequests});
const component = mount(<Provider store={store}><MergeRequests/></Provider>);
component.unmount();
expect(_.isEmpty(getMergedRequests(store.getState()))).toBeTruthy();
});
it('renders correctly a lot of open mergeRequests', async () => {
let mergeRequests = [];
for (let mrIdx = 0; mrIdx <= 100; mrIdx++) {mergeRequests.push({state: 'opened', id: mrIdx, iid: mrIdx});}
onGet({url: /merge_requests*/, data: mergeRequests});
const component = await asyncCreate(<Provider store={store}><MergeRequests/></Provider>);
expect(component.toJSON()).toMatchSnapshot();
});
it('renders correctly a lot of closed mergeRequests', async () => {
let mergeRequests = [];
for (let mrIdx = 0; mrIdx <= 100; mrIdx++) {mergeRequests.push({state: 'closed', id: mrIdx, iid: mrIdx});}
onGet({url: /merge_requests*/, data: mergeRequests});
const component = await asyncCreate(<Provider store={store}><MergeRequests/></Provider>);
expect(component.toJSON()).toMatchSnapshot();
});
it('renders correctly a lot of merged mergeRequests', async () => {
let mergeRequests = [];
for (let mrIdx = 0; mrIdx <= 100; mrIdx++) {mergeRequests.push({state: 'merged', id: mrIdx, iid: mrIdx});}
onGet({url: /merge_requests*/, data: mergeRequests});
const component = await asyncCreate(<Provider store={store}><MergeRequests/></Provider>);
expect(component.toJSON()).toMatchSnapshot();
});
it('requests the page0', async () => {
onGet({url: /merge_requests*/, data: fakeMergeRequests});
store.dispatch(requestMergeRequests(true, true, 1));
await waitForAsync();
expect(_.isEmpty(getMergedRequests(store.getState()))).toBeFalsy();
});
it('sets the pagination', async () => {
store.dispatch(setPagination('merge_requests', {'x-page': 1, 'x-per-page': 2}));
await waitForAsync();
expect(getPagination(store.getState(), 'merge_requests').pageToLoad).toEqual(1);
expect(getPagination(store.getState(), 'merge_requests').perPage).toEqual(2);
});
});
import {getRegisteredRoutes} from "router";
import {getReducers} from 'store';
import * as index from '../index';
describe('MergeRequests index', () => {
it('should export MergeRequests and reducers', () => {
expect(JSON.stringify(index)).toEqual('{}');
expect(getRegisteredRoutes().MergeRequests).toBeDefined();
expect(getReducers().mergeRequests).toBeDefined();
})
});
\ No newline at end of file
......@@ -2,20 +2,19 @@ import _ from 'lodash';
import {get} from 'api';
import {getRepository} from '../repository/selectors';
import {getMergeRequests} from './selectors';
import {getPagination, setPagination} from "utils";
function requestSatateMergeRequests({personal, state, page0, project_id}) {
return async (dispatch, getState) => {
let mergeRequests;
const mrInTheState = getMergeRequests(getState());
const lastPage = _.max(_.keys(mrInTheState));
const page = lastPage && !page0 ? parseInt(lastPage) + 1 : 0;
const {pageToLoad = 0} = getPagination(getState(), 'merge_requests');
if (personal) {
mergeRequests = await get({
path: 'merge_requests',
additionalParams: {per_page: 100, scope: 'assigned_to_me', state},
page
page: page0 ? 0 : pageToLoad,
});
} else {
const repo = getRepository(getState());
......@@ -28,27 +27,22 @@ function requestSatateMergeRequests({personal, state, page0, project_id}) {
mergeRequests = await get({
path: `projects/${repoId}/merge_requests`,
additionalParams: {per_page: 100, state},
page
page: page0 ? 0 : pageToLoad,
});
}
if (mergeRequests.data) {
dispatch({type: 'MERGE_REQUESTS_RECEIVED', data: mergeRequests.data, page});
dispatch({type: 'MERGE_REQUESTS_RECEIVED', data: mergeRequests.data});
dispatch(setPagination('merge_requests', mergeRequests.headers));
}
};
}
export function requestMergeRequests(personal, page0, project_id) {
return (dispatch) => {
dispatch(requestSatateMergeRequests({
personal, state: 'opened', page0, project_id
}));
dispatch(requestSatateMergeRequests({
personal, state: 'merged', page0, project_id
}));
dispatch(requestSatateMergeRequests({
personal, state: 'closed', page0, project_id
}));
dispatch(requestSatateMergeRequests({personal, state: 'opened', page0, project_id}));
dispatch(requestSatateMergeRequests({personal, state: 'merged', page0, project_id}));
dispatch(requestSatateMergeRequests({personal, state: 'closed', page0, project_id}));
};
}
......
import {registerReducer} from 'store';
import _ from 'lodash';
function mergeRequests() {
return {
MERGE_REQUESTS_RECEIVED: (state, {page, data}) => ({
...state,
mergeRequests: {
...state.mergeRequests,
[page]: _.uniqBy([..._.get(state, ['mergeRequests', page], []), ...data], 'iid')
}
}),
CLEAR_MERGE_REQUESTS: state => ({...state, mergeRequests: {0: []}})
};
}
const mergeRequests = {
MERGE_REQUESTS_RECEIVED: (state, {data}) => ({...state, mergeRequests: _.uniqBy([...(state.mergeRequests || []), ...data], 'id')}),
SET_MERGE_REQUESTS_PAGINATION: (state, {pagination}) => ({...state, pagination}),
CLEAR_MERGE_REQUESTS: state => ({...state, mergeRequests: []})
};
registerReducer('mergeRequests', mergeRequests());
registerReducer('mergeRequests', mergeRequests);
import _ from 'lodash';
export function getMergeRequests(state) {
const mrs = _.get(state, ['mergeRequests', 'mergeRequests'], []);
let allMrs = [];
_.map(mrs, (mrsPerPage) => {
if (!_.isEmpty(mrsPerPage)) {
allMrs = [...allMrs, ...mrsPerPage];
}
});
return allMrs;
}
export const getMergedRequests = state => _.filter(getMergeRequests(state), {state: 'merged'});
export const getOpenMergeRequests = state => _.filter(getMergeRequests(state), {state: 'opened'});
export const getClosedMergeRequests = state => _.filter(getMergeRequests(state), {state: 'closed'});
export const getMergedRequests = state => _.filter(_.get(state, ['mergeRequests', 'mergeRequests']), {state: 'merged'});
export const getOpenMergeRequests = state => _.filter(_.get(state, ['mergeRequests', 'mergeRequests']), {state: 'opened'});
export const getClosedMergeRequests = state => _.filter(_.get(state, ['mergeRequests', 'mergeRequests']), {state: 'closed'});
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