Add latest changes from gitlab-org/gitlab@master
This commit is contained in:
parent
b82d691107
commit
b86ad5f488
18 changed files with 191 additions and 58 deletions
|
@ -1579,7 +1579,7 @@
|
||||||
changes: *code-qa-patterns
|
changes: *code-qa-patterns
|
||||||
when: manual
|
when: manual
|
||||||
allow_failure: true
|
allow_failure: true
|
||||||
- <<: *if-dot-com-gitlab-org-schedule-child-pipeline
|
- <<: *if-dot-com-gitlab-org-schedule
|
||||||
allow_failure: true
|
allow_failure: true
|
||||||
|
|
||||||
.review:rules:review-stop:
|
.review:rules:review-stop:
|
||||||
|
|
|
@ -13,7 +13,7 @@ export default {
|
||||||
ConfidentialityFilter,
|
ConfidentialityFilter,
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
...mapState(['query']),
|
...mapState(['query', 'sidebarDirty']),
|
||||||
showReset() {
|
showReset() {
|
||||||
return this.query.state || this.query.confidential;
|
return this.query.state || this.query.confidential;
|
||||||
},
|
},
|
||||||
|
@ -32,7 +32,7 @@ export default {
|
||||||
<status-filter />
|
<status-filter />
|
||||||
<confidentiality-filter />
|
<confidentiality-filter />
|
||||||
<div class="gl-display-flex gl-align-items-center gl-mt-3">
|
<div class="gl-display-flex gl-align-items-center gl-mt-3">
|
||||||
<gl-button category="primary" variant="confirm" size="small" type="submit">
|
<gl-button category="primary" variant="confirm" type="submit" :disabled="!sidebarDirty">
|
||||||
{{ __('Apply') }}
|
{{ __('Apply') }}
|
||||||
</gl-button>
|
</gl-button>
|
||||||
<gl-link v-if="showReset" class="gl-ml-auto" @click="resetQuery">{{
|
<gl-link v-if="showReset" class="gl-ml-auto" @click="resetQuery">{{
|
||||||
|
|
|
@ -5,7 +5,7 @@ const header = __('Status');
|
||||||
const filters = {
|
const filters = {
|
||||||
ANY: {
|
ANY: {
|
||||||
label: __('Any'),
|
label: __('Any'),
|
||||||
value: 'all',
|
value: null,
|
||||||
},
|
},
|
||||||
OPEN: {
|
OPEN: {
|
||||||
label: __('Open'),
|
label: __('Open'),
|
||||||
|
|
|
@ -2,9 +2,9 @@ import Api from '~/api';
|
||||||
import createFlash from '~/flash';
|
import createFlash from '~/flash';
|
||||||
import { visitUrl, setUrlParams } from '~/lib/utils/url_utility';
|
import { visitUrl, setUrlParams } from '~/lib/utils/url_utility';
|
||||||
import { __ } from '~/locale';
|
import { __ } from '~/locale';
|
||||||
import { GROUPS_LOCAL_STORAGE_KEY, PROJECTS_LOCAL_STORAGE_KEY } from './constants';
|
import { GROUPS_LOCAL_STORAGE_KEY, PROJECTS_LOCAL_STORAGE_KEY, SIDEBAR_PARAMS } from './constants';
|
||||||
import * as types from './mutation_types';
|
import * as types from './mutation_types';
|
||||||
import { loadDataFromLS, setFrequentItemToLS, mergeById } from './utils';
|
import { loadDataFromLS, setFrequentItemToLS, mergeById, isSidebarDirty } from './utils';
|
||||||
|
|
||||||
export const fetchGroups = ({ commit }, search) => {
|
export const fetchGroups = ({ commit }, search) => {
|
||||||
commit(types.REQUEST_GROUPS);
|
commit(types.REQUEST_GROUPS);
|
||||||
|
@ -86,8 +86,12 @@ export const setFrequentProject = ({ state, commit }, item) => {
|
||||||
commit(types.LOAD_FREQUENT_ITEMS, { key: PROJECTS_LOCAL_STORAGE_KEY, data: frequentItems });
|
commit(types.LOAD_FREQUENT_ITEMS, { key: PROJECTS_LOCAL_STORAGE_KEY, data: frequentItems });
|
||||||
};
|
};
|
||||||
|
|
||||||
export const setQuery = ({ commit }, { key, value }) => {
|
export const setQuery = ({ state, commit }, { key, value }) => {
|
||||||
commit(types.SET_QUERY, { key, value });
|
commit(types.SET_QUERY, { key, value });
|
||||||
|
|
||||||
|
if (SIDEBAR_PARAMS.includes(key)) {
|
||||||
|
commit(types.SET_SIDEBAR_DIRTY, isSidebarDirty(state.query, state.urlQuery));
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
export const applyQuery = ({ state }) => {
|
export const applyQuery = ({ state }) => {
|
||||||
|
|
|
@ -1,3 +1,6 @@
|
||||||
|
import { stateFilterData } from '~/search/sidebar/constants/state_filter_data';
|
||||||
|
import { confidentialFilterData } from '~/search/sidebar/constants/confidential_filter_data';
|
||||||
|
|
||||||
export const MAX_FREQUENT_ITEMS = 5;
|
export const MAX_FREQUENT_ITEMS = 5;
|
||||||
|
|
||||||
export const MAX_FREQUENCY = 5;
|
export const MAX_FREQUENCY = 5;
|
||||||
|
@ -5,3 +8,5 @@ export const MAX_FREQUENCY = 5;
|
||||||
export const GROUPS_LOCAL_STORAGE_KEY = 'global-search-frequent-groups';
|
export const GROUPS_LOCAL_STORAGE_KEY = 'global-search-frequent-groups';
|
||||||
|
|
||||||
export const PROJECTS_LOCAL_STORAGE_KEY = 'global-search-frequent-projects';
|
export const PROJECTS_LOCAL_STORAGE_KEY = 'global-search-frequent-projects';
|
||||||
|
|
||||||
|
export const SIDEBAR_PARAMS = [stateFilterData.filterParam, confidentialFilterData.filterParam];
|
||||||
|
|
|
@ -7,5 +7,6 @@ export const RECEIVE_PROJECTS_SUCCESS = 'RECEIVE_PROJECTS_SUCCESS';
|
||||||
export const RECEIVE_PROJECTS_ERROR = 'RECEIVE_PROJECTS_ERROR';
|
export const RECEIVE_PROJECTS_ERROR = 'RECEIVE_PROJECTS_ERROR';
|
||||||
|
|
||||||
export const SET_QUERY = 'SET_QUERY';
|
export const SET_QUERY = 'SET_QUERY';
|
||||||
|
export const SET_SIDEBAR_DIRTY = 'SET_SIDEBAR_DIRTY';
|
||||||
|
|
||||||
export const LOAD_FREQUENT_ITEMS = 'LOAD_FREQUENT_ITEMS';
|
export const LOAD_FREQUENT_ITEMS = 'LOAD_FREQUENT_ITEMS';
|
||||||
|
|
|
@ -26,6 +26,9 @@ export default {
|
||||||
[types.SET_QUERY](state, { key, value }) {
|
[types.SET_QUERY](state, { key, value }) {
|
||||||
state.query[key] = value;
|
state.query[key] = value;
|
||||||
},
|
},
|
||||||
|
[types.SET_SIDEBAR_DIRTY](state, value) {
|
||||||
|
state.sidebarDirty = value;
|
||||||
|
},
|
||||||
[types.LOAD_FREQUENT_ITEMS](state, { key, data }) {
|
[types.LOAD_FREQUENT_ITEMS](state, { key, data }) {
|
||||||
state.frequentItems[key] = data;
|
state.frequentItems[key] = data;
|
||||||
},
|
},
|
||||||
|
|
|
@ -1,6 +1,8 @@
|
||||||
|
import { cloneDeep } from 'lodash';
|
||||||
import { GROUPS_LOCAL_STORAGE_KEY, PROJECTS_LOCAL_STORAGE_KEY } from './constants';
|
import { GROUPS_LOCAL_STORAGE_KEY, PROJECTS_LOCAL_STORAGE_KEY } from './constants';
|
||||||
|
|
||||||
const createState = ({ query }) => ({
|
const createState = ({ query }) => ({
|
||||||
|
urlQuery: cloneDeep(query),
|
||||||
query,
|
query,
|
||||||
groups: [],
|
groups: [],
|
||||||
fetchingGroups: false,
|
fetchingGroups: false,
|
||||||
|
@ -10,5 +12,6 @@ const createState = ({ query }) => ({
|
||||||
[GROUPS_LOCAL_STORAGE_KEY]: [],
|
[GROUPS_LOCAL_STORAGE_KEY]: [],
|
||||||
[PROJECTS_LOCAL_STORAGE_KEY]: [],
|
[PROJECTS_LOCAL_STORAGE_KEY]: [],
|
||||||
},
|
},
|
||||||
|
sidebarDirty: false,
|
||||||
});
|
});
|
||||||
export default createState;
|
export default createState;
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
import AccessorUtilities from '../../lib/utils/accessor';
|
import AccessorUtilities from '../../lib/utils/accessor';
|
||||||
import { MAX_FREQUENT_ITEMS, MAX_FREQUENCY } from './constants';
|
import { MAX_FREQUENT_ITEMS, MAX_FREQUENCY, SIDEBAR_PARAMS } from './constants';
|
||||||
|
|
||||||
function extractKeys(object, keyList) {
|
function extractKeys(object, keyList) {
|
||||||
return Object.fromEntries(keyList.map((key) => [key, object[key]]));
|
return Object.fromEntries(keyList.map((key) => [key, object[key]]));
|
||||||
|
@ -80,3 +80,13 @@ export const mergeById = (inflatedData, storedData) => {
|
||||||
return { ...stored, ...data };
|
return { ...stored, ...data };
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const isSidebarDirty = (currentQuery, urlQuery) => {
|
||||||
|
return SIDEBAR_PARAMS.some((param) => {
|
||||||
|
// userAddParam ensures we don't get a false dirty from null !== undefined
|
||||||
|
const userAddedParam = !urlQuery[param] && currentQuery[param];
|
||||||
|
const userChangedExistingParam = urlQuery[param] && urlQuery[param] !== currentQuery[param];
|
||||||
|
|
||||||
|
return userAddedParam || userChangedExistingParam;
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
|
@ -341,9 +341,8 @@ Ensure your SAML identity provider sends an attribute statement named `Groups` o
|
||||||
```
|
```
|
||||||
|
|
||||||
NOTE:
|
NOTE:
|
||||||
|
The value for `Groups` or `groups` in the SAML response can be either the group name or the group ID.
|
||||||
To inspect the SAML response, you can use one of these [SAML debugging tools](#saml-debugging-tools).
|
To inspect the SAML response, you can use one of these [SAML debugging tools](#saml-debugging-tools).
|
||||||
Also note that the value for `Groups` or `groups` in the SAML response can be either the group name or
|
|
||||||
the group ID depending what the IdP sends to GitLab.
|
|
||||||
|
|
||||||
When SAML SSO is enabled for the top-level group, `Maintainer` and `Owner` level users
|
When SAML SSO is enabled for the top-level group, `Maintainer` and `Owner` level users
|
||||||
see a new menu item in group **Settings > SAML Group Links**. You can configure one or more **SAML Group Links** to map
|
see a new menu item in group **Settings > SAML Group Links**. You can configure one or more **SAML Group Links** to map
|
||||||
|
|
|
@ -78,10 +78,7 @@ the author's:
|
||||||
- `slug`
|
- `slug`
|
||||||
- `displayName`
|
- `displayName`
|
||||||
|
|
||||||
If the user is not found by any of these properties, the search falls back to the author's
|
If the user is not found by any of these properties, the project creator is set as the author.
|
||||||
`email` address.
|
|
||||||
|
|
||||||
Alternatively, if there is also no email address, the project creator is set as the author.
|
|
||||||
|
|
||||||
##### Enable or disable User assignment by username
|
##### Enable or disable User assignment by username
|
||||||
|
|
||||||
|
|
|
@ -135,11 +135,11 @@ To learn more, see [Coverage check approval rule](../../../../ci/pipelines/setti
|
||||||
|
|
||||||
## Merge request approval settings cascading
|
## Merge request approval settings cascading
|
||||||
|
|
||||||
> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/285410) in GitLab 14.4. [Deployed behind the `group_merge_request_approval_settings_feature_flag` flag](../../../../administration/feature_flags.md), disabled by default.
|
> - [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/285410) in GitLab 14.4. [Deployed behind the `group_merge_request_approval_settings_feature_flag` flag](../../../../administration/feature_flags.md), disabled by default.
|
||||||
|
> - [Enabled by default](https://gitlab.com/gitlab-org/gitlab/-/issues/285410) in GitLab 14.5.
|
||||||
|
|
||||||
FLAG:
|
FLAG:
|
||||||
On self-managed GitLab, by default this feature is not available. To make it available per group, ask an administrator to [enable the `group_merge_request_approval_settings_feature_flag` flag](../../../../administration/feature_flags.md). On GitLab.com, this feature is not available.
|
On self-managed GitLab, by default this feature is available. To hide the feature per group, ask an administrator to [disable the feature flag](../../../../administration/feature_flags.md) named `group_merge_request_approval_settings_feature_flag`. On GitLab.com, this feature is available.
|
||||||
You should not use this feature for production environments
|
|
||||||
|
|
||||||
You can also enforce merge request approval settings:
|
You can also enforce merge request approval settings:
|
||||||
|
|
||||||
|
|
|
@ -461,11 +461,15 @@ module Gitlab
|
||||||
end
|
end
|
||||||
|
|
||||||
def uid(rep_object)
|
def uid(rep_object)
|
||||||
find_user_id(by: :email, value: rep_object.author_email) unless Feature.enabled?(:bitbucket_server_user_mapping_by_username)
|
# We want this explicit to only be username on the FF
|
||||||
|
# Otherwise, match email.
|
||||||
find_user_id(by: :username, value: rep_object.author_username) ||
|
# There should be no default fall-through on username. Fall-through to import user
|
||||||
|
if Feature.enabled?(:bitbucket_server_user_mapping_by_username)
|
||||||
|
find_user_id(by: :username, value: rep_object.author_username)
|
||||||
|
else
|
||||||
find_user_id(by: :email, value: rep_object.author_email)
|
find_user_id(by: :email, value: rep_object.author_email)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
end
|
||||||
|
|
|
@ -1,13 +1,13 @@
|
||||||
import { GlButton, GlLink } from '@gitlab/ui';
|
import { GlButton, GlLink } from '@gitlab/ui';
|
||||||
import { createLocalVue, shallowMount } from '@vue/test-utils';
|
import { shallowMount } from '@vue/test-utils';
|
||||||
|
import Vue from 'vue';
|
||||||
import Vuex from 'vuex';
|
import Vuex from 'vuex';
|
||||||
import { MOCK_QUERY } from 'jest/search/mock_data';
|
import { MOCK_QUERY } from 'jest/search/mock_data';
|
||||||
import GlobalSearchSidebar from '~/search/sidebar/components/app.vue';
|
import GlobalSearchSidebar from '~/search/sidebar/components/app.vue';
|
||||||
import ConfidentialityFilter from '~/search/sidebar/components/confidentiality_filter.vue';
|
import ConfidentialityFilter from '~/search/sidebar/components/confidentiality_filter.vue';
|
||||||
import StatusFilter from '~/search/sidebar/components/status_filter.vue';
|
import StatusFilter from '~/search/sidebar/components/status_filter.vue';
|
||||||
|
|
||||||
const localVue = createLocalVue();
|
Vue.use(Vuex);
|
||||||
localVue.use(Vuex);
|
|
||||||
|
|
||||||
describe('GlobalSearchSidebar', () => {
|
describe('GlobalSearchSidebar', () => {
|
||||||
let wrapper;
|
let wrapper;
|
||||||
|
@ -27,21 +27,19 @@ describe('GlobalSearchSidebar', () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
wrapper = shallowMount(GlobalSearchSidebar, {
|
wrapper = shallowMount(GlobalSearchSidebar, {
|
||||||
localVue,
|
|
||||||
store,
|
store,
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
afterEach(() => {
|
afterEach(() => {
|
||||||
wrapper.destroy();
|
wrapper.destroy();
|
||||||
wrapper = null;
|
|
||||||
});
|
});
|
||||||
|
|
||||||
const findSidebarForm = () => wrapper.find('form');
|
const findSidebarForm = () => wrapper.find('form');
|
||||||
const findStatusFilter = () => wrapper.find(StatusFilter);
|
const findStatusFilter = () => wrapper.findComponent(StatusFilter);
|
||||||
const findConfidentialityFilter = () => wrapper.find(ConfidentialityFilter);
|
const findConfidentialityFilter = () => wrapper.findComponent(ConfidentialityFilter);
|
||||||
const findApplyButton = () => wrapper.find(GlButton);
|
const findApplyButton = () => wrapper.findComponent(GlButton);
|
||||||
const findResetLinkButton = () => wrapper.find(GlLink);
|
const findResetLinkButton = () => wrapper.findComponent(GlLink);
|
||||||
|
|
||||||
describe('template', () => {
|
describe('template', () => {
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
|
@ -61,6 +59,28 @@ describe('GlobalSearchSidebar', () => {
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe('ApplyButton', () => {
|
||||||
|
describe('when sidebarDirty is false', () => {
|
||||||
|
beforeEach(() => {
|
||||||
|
createComponent({ sidebarDirty: false });
|
||||||
|
});
|
||||||
|
|
||||||
|
it('disables the button', () => {
|
||||||
|
expect(findApplyButton().attributes('disabled')).toBe('true');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('when sidebarDirty is true', () => {
|
||||||
|
beforeEach(() => {
|
||||||
|
createComponent({ sidebarDirty: true });
|
||||||
|
});
|
||||||
|
|
||||||
|
it('enables the button', () => {
|
||||||
|
expect(findApplyButton().attributes('disabled')).toBe(undefined);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
describe('ResetLinkButton', () => {
|
describe('ResetLinkButton', () => {
|
||||||
describe('with no filter selected', () => {
|
describe('with no filter selected', () => {
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
|
|
|
@ -5,7 +5,11 @@ import createFlash from '~/flash';
|
||||||
import axios from '~/lib/utils/axios_utils';
|
import axios from '~/lib/utils/axios_utils';
|
||||||
import * as urlUtils from '~/lib/utils/url_utility';
|
import * as urlUtils from '~/lib/utils/url_utility';
|
||||||
import * as actions from '~/search/store/actions';
|
import * as actions from '~/search/store/actions';
|
||||||
import { GROUPS_LOCAL_STORAGE_KEY, PROJECTS_LOCAL_STORAGE_KEY } from '~/search/store/constants';
|
import {
|
||||||
|
GROUPS_LOCAL_STORAGE_KEY,
|
||||||
|
PROJECTS_LOCAL_STORAGE_KEY,
|
||||||
|
SIDEBAR_PARAMS,
|
||||||
|
} from '~/search/store/constants';
|
||||||
import * as types from '~/search/store/mutation_types';
|
import * as types from '~/search/store/mutation_types';
|
||||||
import createState from '~/search/store/state';
|
import createState from '~/search/store/state';
|
||||||
import * as storeUtils from '~/search/store/utils';
|
import * as storeUtils from '~/search/store/utils';
|
||||||
|
@ -153,15 +157,24 @@ describe('Global Search Store Actions', () => {
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('setQuery', () => {
|
describe.each`
|
||||||
const payload = { key: 'key1', value: 'value1' };
|
payload | isDirty | isDirtyMutation
|
||||||
|
${{ key: SIDEBAR_PARAMS[0], value: 'test' }} | ${false} | ${[{ type: types.SET_SIDEBAR_DIRTY, payload: false }]}
|
||||||
|
${{ key: SIDEBAR_PARAMS[0], value: 'test' }} | ${true} | ${[{ type: types.SET_SIDEBAR_DIRTY, payload: true }]}
|
||||||
|
${{ key: SIDEBAR_PARAMS[1], value: 'test' }} | ${false} | ${[{ type: types.SET_SIDEBAR_DIRTY, payload: false }]}
|
||||||
|
${{ key: SIDEBAR_PARAMS[1], value: 'test' }} | ${true} | ${[{ type: types.SET_SIDEBAR_DIRTY, payload: true }]}
|
||||||
|
${{ key: 'non-sidebar', value: 'test' }} | ${false} | ${[]}
|
||||||
|
${{ key: 'non-sidebar', value: 'test' }} | ${true} | ${[]}
|
||||||
|
`('setQuery', ({ payload, isDirty, isDirtyMutation }) => {
|
||||||
|
describe(`when filter param is ${payload.key} and utils.isSidebarDirty returns ${isDirty}`, () => {
|
||||||
|
const expectedMutations = [{ type: types.SET_QUERY, payload }].concat(isDirtyMutation);
|
||||||
|
|
||||||
it('calls the SET_QUERY mutation', () => {
|
beforeEach(() => {
|
||||||
return testAction({
|
storeUtils.isSidebarDirty = jest.fn().mockReturnValue(isDirty);
|
||||||
action: actions.setQuery,
|
});
|
||||||
payload,
|
|
||||||
state,
|
it(`should dispatch the correct mutations`, () => {
|
||||||
expectedMutations: [{ type: types.SET_QUERY, payload }],
|
return testAction({ action: actions.setQuery, payload, state, expectedMutations });
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
|
@ -72,6 +72,16 @@ describe('Global Search Store Mutations', () => {
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe('SET_SIDEBAR_DIRTY', () => {
|
||||||
|
const value = true;
|
||||||
|
|
||||||
|
it('sets sidebarDirty to the value', () => {
|
||||||
|
mutations[types.SET_SIDEBAR_DIRTY](state, value);
|
||||||
|
|
||||||
|
expect(state.sidebarDirty).toBe(value);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
describe('LOAD_FREQUENT_ITEMS', () => {
|
describe('LOAD_FREQUENT_ITEMS', () => {
|
||||||
it('sets frequentItems[key] to data', () => {
|
it('sets frequentItems[key] to data', () => {
|
||||||
const payload = { key: 'test-key', data: [1, 2, 3] };
|
const payload = { key: 'test-key', data: [1, 2, 3] };
|
||||||
|
|
|
@ -1,6 +1,11 @@
|
||||||
import { useLocalStorageSpy } from 'helpers/local_storage_helper';
|
import { useLocalStorageSpy } from 'helpers/local_storage_helper';
|
||||||
import { MAX_FREQUENCY } from '~/search/store/constants';
|
import { MAX_FREQUENCY, SIDEBAR_PARAMS } from '~/search/store/constants';
|
||||||
import { loadDataFromLS, setFrequentItemToLS, mergeById } from '~/search/store/utils';
|
import {
|
||||||
|
loadDataFromLS,
|
||||||
|
setFrequentItemToLS,
|
||||||
|
mergeById,
|
||||||
|
isSidebarDirty,
|
||||||
|
} from '~/search/store/utils';
|
||||||
import {
|
import {
|
||||||
MOCK_LS_KEY,
|
MOCK_LS_KEY,
|
||||||
MOCK_GROUPS,
|
MOCK_GROUPS,
|
||||||
|
@ -216,4 +221,24 @@ describe('Global Search Store Utils', () => {
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe.each`
|
||||||
|
description | currentQuery | urlQuery | isDirty
|
||||||
|
${'identical'} | ${{ [SIDEBAR_PARAMS[0]]: 'default', [SIDEBAR_PARAMS[1]]: 'default' }} | ${{ [SIDEBAR_PARAMS[0]]: 'default', [SIDEBAR_PARAMS[1]]: 'default' }} | ${false}
|
||||||
|
${'different'} | ${{ [SIDEBAR_PARAMS[0]]: 'default', [SIDEBAR_PARAMS[1]]: 'new' }} | ${{ [SIDEBAR_PARAMS[0]]: 'default', [SIDEBAR_PARAMS[1]]: 'default' }} | ${true}
|
||||||
|
${'null/undefined'} | ${{ [SIDEBAR_PARAMS[0]]: null, [SIDEBAR_PARAMS[1]]: null }} | ${{ [SIDEBAR_PARAMS[0]]: undefined, [SIDEBAR_PARAMS[1]]: undefined }} | ${false}
|
||||||
|
${'updated/undefined'} | ${{ [SIDEBAR_PARAMS[0]]: 'new', [SIDEBAR_PARAMS[1]]: 'new' }} | ${{ [SIDEBAR_PARAMS[0]]: undefined, [SIDEBAR_PARAMS[1]]: undefined }} | ${true}
|
||||||
|
`('isSidebarDirty', ({ description, currentQuery, urlQuery, isDirty }) => {
|
||||||
|
describe(`with ${description} sidebar query data`, () => {
|
||||||
|
let res;
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
res = isSidebarDirty(currentQuery, urlQuery);
|
||||||
|
});
|
||||||
|
|
||||||
|
it(`returns ${isDirty}`, () => {
|
||||||
|
expect(res).toStrictEqual(isDirty);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
|
@ -142,7 +142,7 @@ RSpec.describe Gitlab::BitbucketServerImport::Importer do
|
||||||
expect { subject.execute }.to change { MergeRequest.count }.by(1)
|
expect { subject.execute }.to change { MergeRequest.count }.by(1)
|
||||||
|
|
||||||
merge_request = MergeRequest.first
|
merge_request = MergeRequest.first
|
||||||
expect(merge_request.author).to eq(pull_request_author)
|
expect(merge_request.author).to eq(expected_author)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -151,27 +151,50 @@ RSpec.describe Gitlab::BitbucketServerImport::Importer do
|
||||||
stub_feature_flags(bitbucket_server_user_mapping_by_username: false)
|
stub_feature_flags(bitbucket_server_user_mapping_by_username: false)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
context 'when email is not present' do
|
||||||
|
before do
|
||||||
|
allow(pull_request).to receive(:author_email).and_return(nil)
|
||||||
|
end
|
||||||
|
|
||||||
|
let(:expected_author) { project_creator }
|
||||||
|
|
||||||
include_examples 'imports pull requests'
|
include_examples 'imports pull requests'
|
||||||
end
|
end
|
||||||
|
|
||||||
|
context 'when email is present' do
|
||||||
|
before do
|
||||||
|
allow(pull_request).to receive(:author_email).and_return(pull_request_author.email)
|
||||||
|
end
|
||||||
|
|
||||||
|
let(:expected_author) { pull_request_author }
|
||||||
|
|
||||||
|
include_examples 'imports pull requests'
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
context 'when bitbucket_server_user_mapping_by_username feature flag is enabled' do
|
context 'when bitbucket_server_user_mapping_by_username feature flag is enabled' do
|
||||||
before do
|
before do
|
||||||
stub_feature_flags(bitbucket_server_user_mapping_by_username: true)
|
stub_feature_flags(bitbucket_server_user_mapping_by_username: true)
|
||||||
end
|
end
|
||||||
|
|
||||||
include_examples 'imports pull requests' do
|
|
||||||
context 'when username is not present' do
|
context 'when username is not present' do
|
||||||
before do
|
before do
|
||||||
allow(pull_request).to receive(:author_username).and_return(nil)
|
allow(pull_request).to receive(:author_username).and_return(nil)
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'maps by email' do
|
let(:expected_author) { project_creator }
|
||||||
expect { subject.execute }.to change { MergeRequest.count }.by(1)
|
|
||||||
|
|
||||||
merge_request = MergeRequest.first
|
include_examples 'imports pull requests'
|
||||||
expect(merge_request.author).to eq(pull_request_author)
|
|
||||||
end
|
end
|
||||||
|
|
||||||
|
context 'when username is present' do
|
||||||
|
before do
|
||||||
|
allow(pull_request).to receive(:author_username).and_return(pull_request_author.username)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
let(:expected_author) { pull_request_author }
|
||||||
|
|
||||||
|
include_examples 'imports pull requests'
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -228,7 +251,23 @@ RSpec.describe Gitlab::BitbucketServerImport::Importer do
|
||||||
allow(subject.client).to receive(:activities).and_return([pr_comment])
|
allow(subject.client).to receive(:activities).and_return([pr_comment])
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'maps by email' do
|
it 'defaults to import user' do
|
||||||
|
expect { subject.execute }.to change { MergeRequest.count }.by(1)
|
||||||
|
|
||||||
|
merge_request = MergeRequest.first
|
||||||
|
expect(merge_request.notes.count).to eq(1)
|
||||||
|
note = merge_request.notes.first
|
||||||
|
expect(note.author).to eq(project_creator)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'when username is present' do
|
||||||
|
before do
|
||||||
|
allow(pr_note).to receive(:author_username).and_return(note_author.username)
|
||||||
|
allow(subject.client).to receive(:activities).and_return([pr_comment])
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'maps by username' do
|
||||||
expect { subject.execute }.to change { MergeRequest.count }.by(1)
|
expect { subject.execute }.to change { MergeRequest.count }.by(1)
|
||||||
|
|
||||||
merge_request = MergeRequest.first
|
merge_request = MergeRequest.first
|
||||||
|
@ -384,13 +423,13 @@ RSpec.describe Gitlab::BitbucketServerImport::Importer do
|
||||||
allow(inline_note).to receive(:author_username).and_return(nil)
|
allow(inline_note).to receive(:author_username).and_return(nil)
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'maps by email' do
|
it 'defaults to import user' do
|
||||||
expect { subject.execute }.to change { MergeRequest.count }.by(1)
|
expect { subject.execute }.to change { MergeRequest.count }.by(1)
|
||||||
|
|
||||||
notes = MergeRequest.first.notes.order(:id).to_a
|
notes = MergeRequest.first.notes.order(:id).to_a
|
||||||
|
|
||||||
expect(notes.first.author).to eq(inline_note_author)
|
expect(notes.first.author).to eq(project_creator)
|
||||||
expect(notes.last.author).to eq(reply_author)
|
expect(notes.last.author).to eq(project_creator)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
Loading…
Reference in a new issue