Add latest changes from gitlab-org/gitlab@master

This commit is contained in:
GitLab Bot 2021-05-17 06:10:18 +00:00
parent 1585480786
commit 58e2ed53aa
13 changed files with 198 additions and 369 deletions

View file

@ -1 +1 @@
2.10.0
2.11.0

View file

@ -1,24 +1,37 @@
<script>
import ReleasesPaginationGraphql from './releases_pagination_graphql.vue';
import ReleasesPaginationRest from './releases_pagination_rest.vue';
import { GlKeysetPagination } from '@gitlab/ui';
import { mapActions, mapState } from 'vuex';
import { historyPushState, buildUrlWithCurrentLocation } from '~/lib/utils/common_utils';
export default {
name: 'ReleasesPagination',
components: { ReleasesPaginationGraphql, ReleasesPaginationRest },
name: 'ReleasesPaginationGraphql',
components: { GlKeysetPagination },
computed: {
// TODO: Remove the ReleasesPaginationRest component since
// it is never shown.
// https://gitlab.com/gitlab-org/gitlab/-/issues/329267
useGraphQLEndpoint() {
return true;
...mapState('index', ['pageInfo']),
showPagination() {
return this.pageInfo.hasPreviousPage || this.pageInfo.hasNextPage;
},
},
methods: {
...mapActions('index', ['fetchReleases']),
onPrev(before) {
historyPushState(buildUrlWithCurrentLocation(`?before=${before}`));
this.fetchReleases({ before });
},
onNext(after) {
historyPushState(buildUrlWithCurrentLocation(`?after=${after}`));
this.fetchReleases({ after });
},
},
};
</script>
<template>
<div class="gl-display-flex gl-justify-content-center">
<releases-pagination-graphql v-if="useGraphQLEndpoint" />
<releases-pagination-rest v-else />
<gl-keyset-pagination
v-if="showPagination"
v-bind="pageInfo"
@prev="onPrev($event)"
@next="onNext($event)"
/>
</div>
</template>

View file

@ -1,35 +0,0 @@
<script>
import { GlKeysetPagination } from '@gitlab/ui';
import { mapActions, mapState } from 'vuex';
import { historyPushState, buildUrlWithCurrentLocation } from '~/lib/utils/common_utils';
export default {
name: 'ReleasesPaginationGraphql',
components: { GlKeysetPagination },
computed: {
...mapState('index', ['graphQlPageInfo']),
showPagination() {
return this.graphQlPageInfo.hasPreviousPage || this.graphQlPageInfo.hasNextPage;
},
},
methods: {
...mapActions('index', ['fetchReleases']),
onPrev(before) {
historyPushState(buildUrlWithCurrentLocation(`?before=${before}`));
this.fetchReleases({ before });
},
onNext(after) {
historyPushState(buildUrlWithCurrentLocation(`?after=${after}`));
this.fetchReleases({ after });
},
},
};
</script>
<template>
<gl-keyset-pagination
v-if="showPagination"
v-bind="graphQlPageInfo"
@prev="onPrev($event)"
@next="onNext($event)"
/>
</template>

View file

@ -1,24 +0,0 @@
<script>
import { mapActions, mapState } from 'vuex';
import { historyPushState, buildUrlWithCurrentLocation } from '~/lib/utils/common_utils';
import TablePagination from '~/vue_shared/components/pagination/table_pagination.vue';
export default {
name: 'ReleasesPaginationRest',
components: { TablePagination },
computed: {
...mapState('index', ['restPageInfo']),
},
methods: {
...mapActions('index', ['fetchReleases']),
onChangePage(page) {
historyPushState(buildUrlWithCurrentLocation(`?page=${page}`));
this.fetchReleases({ page });
},
},
};
</script>
<template>
<table-pagination :change="onChangePage" :page-info="restPageInfo" />
</template>

View file

@ -45,11 +45,11 @@ export const fetchReleases = ({ dispatch, commit, state }, { before, after }) =>
},
})
.then((response) => {
const { data, paginationInfo: graphQlPageInfo } = convertAllReleasesGraphQLResponse(response);
const { data, paginationInfo: pageInfo } = convertAllReleasesGraphQLResponse(response);
commit(types.RECEIVE_RELEASES_SUCCESS, {
data,
graphQlPageInfo,
pageInfo,
});
})
.catch(() => dispatch('receiveReleasesError'));

View file

@ -17,12 +17,11 @@ export default {
* @param {Object} state
* @param {Object} resp
*/
[types.RECEIVE_RELEASES_SUCCESS](state, { data, restPageInfo, graphQlPageInfo }) {
[types.RECEIVE_RELEASES_SUCCESS](state, { data, pageInfo }) {
state.hasError = false;
state.isLoading = false;
state.releases = data;
state.restPageInfo = restPageInfo;
state.graphQlPageInfo = graphQlPageInfo;
state.pageInfo = pageInfo;
},
/**
@ -36,8 +35,7 @@ export default {
state.isLoading = false;
state.releases = [];
state.hasError = true;
state.restPageInfo = {};
state.graphQlPageInfo = {};
state.pageInfo = {};
},
[types.SET_SORTING](state, sorting) {

View file

@ -16,8 +16,7 @@ export default ({
isLoading: false,
hasError: false,
releases: [],
restPageInfo: {},
graphQlPageInfo: {},
pageInfo: {},
sorting: {
sort: DESCENDING_ORDER,
orderBy: RELEASED_AT,

View file

@ -112,9 +112,6 @@ sudo apt-get install -y build-essential zlib1g-dev libyaml-dev libssl-dev libgdb
libcurl4-openssl-dev libicu-dev logrotate rsync python-docutils pkg-config cmake runit-systemd
```
Ubuntu 14.04 (Trusty Tahr) doesn't have the `libre2-dev` package available, but
you can [install re2 manually](https://github.com/google/re2/wiki/Install).
If you want to use Kerberos for user authentication, install `libkrb5-dev`
(if you don't know what Kerberos is, you can assume you don't need it):

View file

@ -1,175 +0,0 @@
import { mount, createLocalVue } from '@vue/test-utils';
import Vuex from 'vuex';
import { historyPushState } from '~/lib/utils/common_utils';
import ReleasesPaginationGraphql from '~/releases/components/releases_pagination_graphql.vue';
import createStore from '~/releases/stores';
import createIndexModule from '~/releases/stores/modules/index';
jest.mock('~/lib/utils/common_utils', () => ({
...jest.requireActual('~/lib/utils/common_utils'),
historyPushState: jest.fn(),
}));
const localVue = createLocalVue();
localVue.use(Vuex);
describe('~/releases/components/releases_pagination_graphql.vue', () => {
let wrapper;
let indexModule;
const cursors = {
startCursor: 'startCursor',
endCursor: 'endCursor',
};
const projectPath = 'my/project';
const createComponent = (pageInfo) => {
indexModule = createIndexModule({ projectPath });
indexModule.state.graphQlPageInfo = pageInfo;
indexModule.actions.fetchReleases = jest.fn();
wrapper = mount(ReleasesPaginationGraphql, {
store: createStore({
modules: {
index: indexModule,
},
featureFlags: {},
}),
localVue,
});
};
afterEach(() => {
wrapper.destroy();
wrapper = null;
});
const findPrevButton = () => wrapper.find('[data-testid="prevButton"]');
const findNextButton = () => wrapper.find('[data-testid="nextButton"]');
const expectDisabledPrev = () => {
expect(findPrevButton().attributes().disabled).toBe('disabled');
};
const expectEnabledPrev = () => {
expect(findPrevButton().attributes().disabled).toBe(undefined);
};
const expectDisabledNext = () => {
expect(findNextButton().attributes().disabled).toBe('disabled');
};
const expectEnabledNext = () => {
expect(findNextButton().attributes().disabled).toBe(undefined);
};
describe('when there is only one page of results', () => {
beforeEach(() => {
createComponent({
hasPreviousPage: false,
hasNextPage: false,
});
});
it('does not render anything', () => {
expect(wrapper.html()).toBe('');
});
});
describe('when there is a next page, but not a previous page', () => {
beforeEach(() => {
createComponent({
hasPreviousPage: false,
hasNextPage: true,
});
});
it('renders a disabled "Prev" button', () => {
expectDisabledPrev();
});
it('renders an enabled "Next" button', () => {
expectEnabledNext();
});
});
describe('when there is a previous page, but not a next page', () => {
beforeEach(() => {
createComponent({
hasPreviousPage: true,
hasNextPage: false,
});
});
it('renders a enabled "Prev" button', () => {
expectEnabledPrev();
});
it('renders an disabled "Next" button', () => {
expectDisabledNext();
});
});
describe('when there is both a previous page and a next page', () => {
beforeEach(() => {
createComponent({
hasPreviousPage: true,
hasNextPage: true,
});
});
it('renders a enabled "Prev" button', () => {
expectEnabledPrev();
});
it('renders an enabled "Next" button', () => {
expectEnabledNext();
});
});
describe('button behavior', () => {
beforeEach(() => {
createComponent({
hasPreviousPage: true,
hasNextPage: true,
...cursors,
});
});
describe('next button behavior', () => {
beforeEach(() => {
findNextButton().trigger('click');
});
it('calls fetchReleases with the correct after cursor', () => {
expect(indexModule.actions.fetchReleases.mock.calls).toEqual([
[expect.anything(), { after: cursors.endCursor }],
]);
});
it('calls historyPushState with the new URL', () => {
expect(historyPushState.mock.calls).toEqual([
[expect.stringContaining(`?after=${cursors.endCursor}`)],
]);
});
});
describe('previous button behavior', () => {
beforeEach(() => {
findPrevButton().trigger('click');
});
it('calls fetchReleases with the correct before cursor', () => {
expect(indexModule.actions.fetchReleases.mock.calls).toEqual([
[expect.anything(), { before: cursors.startCursor }],
]);
});
it('calls historyPushState with the new URL', () => {
expect(historyPushState.mock.calls).toEqual([
[expect.stringContaining(`?before=${cursors.startCursor}`)],
]);
});
});
});
});

View file

@ -1,72 +0,0 @@
import { GlPagination } from '@gitlab/ui';
import { mount, createLocalVue } from '@vue/test-utils';
import Vuex from 'vuex';
import * as commonUtils from '~/lib/utils/common_utils';
import ReleasesPaginationRest from '~/releases/components/releases_pagination_rest.vue';
import createStore from '~/releases/stores';
import createIndexModule from '~/releases/stores/modules/index';
commonUtils.historyPushState = jest.fn();
const localVue = createLocalVue();
localVue.use(Vuex);
describe('~/releases/components/releases_pagination_rest.vue', () => {
let wrapper;
let indexModule;
const projectId = 19;
const createComponent = (pageInfo) => {
indexModule = createIndexModule({ projectId });
indexModule.state.restPageInfo = pageInfo;
indexModule.actions.fetchReleases = jest.fn();
wrapper = mount(ReleasesPaginationRest, {
store: createStore({
modules: {
index: indexModule,
},
featureFlags: {},
}),
localVue,
});
};
const findGlPagination = () => wrapper.find(GlPagination);
afterEach(() => {
wrapper.destroy();
wrapper = null;
});
describe('when a page number is clicked', () => {
const newPage = 2;
beforeEach(() => {
createComponent({
perPage: 20,
page: 1,
total: 40,
totalPages: 2,
nextPage: 2,
});
findGlPagination().vm.$emit('input', newPage);
});
it('calls fetchReleases with the correct page', () => {
expect(indexModule.actions.fetchReleases.mock.calls).toEqual([
[expect.anything(), { page: newPage }],
]);
});
it('calls historyPushState with the new URL', () => {
expect(commonUtils.historyPushState.mock.calls).toEqual([
[expect.stringContaining(`?page=${newPage}`)],
]);
});
});
});

View file

@ -1,39 +1,177 @@
import { shallowMount, createLocalVue } from '@vue/test-utils';
import { GlKeysetPagination } from '@gitlab/ui';
import { mount, createLocalVue } from '@vue/test-utils';
import Vuex from 'vuex';
import { historyPushState } from '~/lib/utils/common_utils';
import ReleasesPagination from '~/releases/components/releases_pagination.vue';
import ReleasesPaginationGraphql from '~/releases/components/releases_pagination_graphql.vue';
import ReleasesPaginationRest from '~/releases/components/releases_pagination_rest.vue';
import createStore from '~/releases/stores';
import createIndexModule from '~/releases/stores/modules/index';
jest.mock('~/lib/utils/common_utils', () => ({
...jest.requireActual('~/lib/utils/common_utils'),
historyPushState: jest.fn(),
}));
const localVue = createLocalVue();
localVue.use(Vuex);
describe('~/releases/components/releases_pagination.vue', () => {
let wrapper;
let indexModule;
const createComponent = (useGraphQLEndpoint) => {
const store = new Vuex.Store({
getters: {
useGraphQLEndpoint: () => useGraphQLEndpoint,
},
});
wrapper = shallowMount(ReleasesPagination, { store, localVue });
const cursors = {
startCursor: 'startCursor',
endCursor: 'endCursor',
};
beforeEach(() => {
createComponent(true);
});
const projectPath = 'my/project';
const createComponent = (pageInfo) => {
indexModule = createIndexModule({ projectPath });
indexModule.state.pageInfo = pageInfo;
indexModule.actions.fetchReleases = jest.fn();
wrapper = mount(ReleasesPagination, {
store: createStore({
modules: {
index: indexModule,
},
featureFlags: {},
}),
localVue,
});
};
afterEach(() => {
wrapper.destroy();
wrapper = null;
});
const findRestPagination = () => wrapper.find(ReleasesPaginationRest);
const findGraphQlPagination = () => wrapper.find(ReleasesPaginationGraphql);
const findGlKeysetPagination = () => wrapper.findComponent(GlKeysetPagination);
const findPrevButton = () => findGlKeysetPagination().find('[data-testid="prevButton"]');
const findNextButton = () => findGlKeysetPagination().find('[data-testid="nextButton"]');
it('renders the GraphQL pagination component', () => {
expect(findGraphQlPagination().exists()).toBe(true);
expect(findRestPagination().exists()).toBe(false);
const expectDisabledPrev = () => {
expect(findPrevButton().attributes().disabled).toBe('disabled');
};
const expectEnabledPrev = () => {
expect(findPrevButton().attributes().disabled).toBe(undefined);
};
const expectDisabledNext = () => {
expect(findNextButton().attributes().disabled).toBe('disabled');
};
const expectEnabledNext = () => {
expect(findNextButton().attributes().disabled).toBe(undefined);
};
describe('when there is only one page of results', () => {
beforeEach(() => {
createComponent({
hasPreviousPage: false,
hasNextPage: false,
});
});
it('does not render a GlKeysetPagination', () => {
expect(findGlKeysetPagination().exists()).toBe(false);
});
});
describe('when there is a next page, but not a previous page', () => {
beforeEach(() => {
createComponent({
hasPreviousPage: false,
hasNextPage: true,
});
});
it('renders a disabled "Prev" button', () => {
expectDisabledPrev();
});
it('renders an enabled "Next" button', () => {
expectEnabledNext();
});
});
describe('when there is a previous page, but not a next page', () => {
beforeEach(() => {
createComponent({
hasPreviousPage: true,
hasNextPage: false,
});
});
it('renders a enabled "Prev" button', () => {
expectEnabledPrev();
});
it('renders an disabled "Next" button', () => {
expectDisabledNext();
});
});
describe('when there is both a previous page and a next page', () => {
beforeEach(() => {
createComponent({
hasPreviousPage: true,
hasNextPage: true,
});
});
it('renders a enabled "Prev" button', () => {
expectEnabledPrev();
});
it('renders an enabled "Next" button', () => {
expectEnabledNext();
});
});
describe('button behavior', () => {
beforeEach(() => {
createComponent({
hasPreviousPage: true,
hasNextPage: true,
...cursors,
});
});
describe('next button behavior', () => {
beforeEach(() => {
findNextButton().trigger('click');
});
it('calls fetchReleases with the correct after cursor', () => {
expect(indexModule.actions.fetchReleases.mock.calls).toEqual([
[expect.anything(), { after: cursors.endCursor }],
]);
});
it('calls historyPushState with the new URL', () => {
expect(historyPushState.mock.calls).toEqual([
[expect.stringContaining(`?after=${cursors.endCursor}`)],
]);
});
});
describe('previous button behavior', () => {
beforeEach(() => {
findPrevButton().trigger('click');
});
it('calls fetchReleases with the correct before cursor', () => {
expect(indexModule.actions.fetchReleases.mock.calls).toEqual([
[expect.anything(), { before: cursors.startCursor }],
]);
});
it('calls historyPushState with the new URL', () => {
expect(historyPushState.mock.calls).toEqual([
[expect.stringContaining(`?before=${cursors.startCursor}`)],
]);
});
});
});
});

View file

@ -141,7 +141,7 @@ describe('Releases State actions', () => {
type: types.RECEIVE_RELEASES_SUCCESS,
payload: {
data: convertedResponse.data,
graphQlPageInfo: convertedResponse.paginationInfo,
pageInfo: convertedResponse.paginationInfo,
},
},
],

View file

@ -1,10 +1,9 @@
import { getJSONFixture } from 'helpers/fixtures';
import { parseIntPagination, convertObjectPropsToCamelCase } from '~/lib/utils/common_utils';
import { convertObjectPropsToCamelCase } from '~/lib/utils/common_utils';
import * as types from '~/releases/stores/modules/index/mutation_types';
import mutations from '~/releases/stores/modules/index/mutations';
import createState from '~/releases/stores/modules/index/state';
import { convertAllReleasesGraphQLResponse } from '~/releases/util';
import { pageInfoHeadersWithoutPagination } from '../../../mock_data';
const originalRelease = getJSONFixture('api/releases/release.json');
const originalReleases = [originalRelease];
@ -15,14 +14,12 @@ const graphqlReleasesResponse = getJSONFixture(
describe('Releases Store Mutations', () => {
let stateCopy;
let restPageInfo;
let graphQlPageInfo;
let pageInfo;
let releases;
beforeEach(() => {
stateCopy = createState({});
restPageInfo = parseIntPagination(pageInfoHeadersWithoutPagination);
graphQlPageInfo = convertAllReleasesGraphQLResponse(graphqlReleasesResponse).paginationInfo;
pageInfo = convertAllReleasesGraphQLResponse(graphqlReleasesResponse).paginationInfo;
releases = convertObjectPropsToCamelCase(originalReleases, { deep: true });
});
@ -37,8 +34,7 @@ describe('Releases Store Mutations', () => {
describe('RECEIVE_RELEASES_SUCCESS', () => {
beforeEach(() => {
mutations[types.RECEIVE_RELEASES_SUCCESS](stateCopy, {
restPageInfo,
graphQlPageInfo,
pageInfo,
data: releases,
});
});
@ -55,20 +51,15 @@ describe('Releases Store Mutations', () => {
expect(stateCopy.releases).toEqual(releases);
});
it('sets restPageInfo', () => {
expect(stateCopy.restPageInfo).toEqual(restPageInfo);
});
it('sets graphQlPageInfo', () => {
expect(stateCopy.graphQlPageInfo).toEqual(graphQlPageInfo);
it('sets pageInfo', () => {
expect(stateCopy.pageInfo).toEqual(pageInfo);
});
});
describe('RECEIVE_RELEASES_ERROR', () => {
it('resets data', () => {
mutations[types.RECEIVE_RELEASES_SUCCESS](stateCopy, {
restPageInfo,
graphQlPageInfo,
pageInfo,
data: releases,
});
@ -76,8 +67,7 @@ describe('Releases Store Mutations', () => {
expect(stateCopy.isLoading).toEqual(false);
expect(stateCopy.releases).toEqual([]);
expect(stateCopy.restPageInfo).toEqual({});
expect(stateCopy.graphQlPageInfo).toEqual({});
expect(stateCopy.pageInfo).toEqual({});
});
});