Add latest changes from gitlab-org/gitlab@master
This commit is contained in:
parent
ca98ae2df5
commit
5b20366d04
|
@ -1 +1 @@
|
|||
14.3.1
|
||||
14.4.0
|
||||
|
|
|
@ -1,7 +1,8 @@
|
|||
<script>
|
||||
import { GlModal, GlAlert } from '@gitlab/ui';
|
||||
import { mapGetters, mapActions, mapState } from 'vuex';
|
||||
import { getParameterByName, visitUrl } from '~/lib/utils/url_utility';
|
||||
import { getIdFromGraphQLId } from '~/graphql_shared/utils';
|
||||
import { visitUrl, updateHistory, getParameterByName } from '~/lib/utils/url_utility';
|
||||
import { __, s__ } from '~/locale';
|
||||
import glFeatureFlagMixin from '~/vue_shared/mixins/gl_feature_flags_mixin';
|
||||
import { formType } from '../constants';
|
||||
|
@ -170,17 +171,7 @@ export default {
|
|||
}
|
||||
},
|
||||
methods: {
|
||||
...mapActions(['setError', 'unsetError']),
|
||||
boardCreateResponse(data) {
|
||||
return data.createBoard.board.webPath;
|
||||
},
|
||||
boardUpdateResponse(data) {
|
||||
const path = data.updateBoard.board.webPath;
|
||||
const param = getParameterByName('group_by')
|
||||
? `?group_by=${getParameterByName('group_by')}`
|
||||
: '';
|
||||
return `${path}${param}`;
|
||||
},
|
||||
...mapActions(['setError', 'unsetError', 'setBoard']),
|
||||
cancel() {
|
||||
this.$emit('cancel');
|
||||
},
|
||||
|
@ -191,10 +182,10 @@ export default {
|
|||
});
|
||||
|
||||
if (!this.board.id) {
|
||||
return this.boardCreateResponse(response.data);
|
||||
return response.data.createBoard.board;
|
||||
}
|
||||
|
||||
return this.boardUpdateResponse(response.data);
|
||||
return response.data.updateBoard.board;
|
||||
},
|
||||
async deleteBoard() {
|
||||
await this.$apollo.mutate({
|
||||
|
@ -218,8 +209,14 @@ export default {
|
|||
}
|
||||
} else {
|
||||
try {
|
||||
const url = await this.createOrUpdateBoard();
|
||||
visitUrl(url);
|
||||
const board = await this.createOrUpdateBoard();
|
||||
this.setBoard(board);
|
||||
this.cancel();
|
||||
|
||||
const param = getParameterByName('group_by')
|
||||
? `?group_by=${getParameterByName('group_by')}`
|
||||
: '';
|
||||
updateHistory({ url: `${this.boardBaseUrl}/${getIdFromGraphQLId(board.id)}${param}` });
|
||||
} catch {
|
||||
this.setError({ message: this.$options.i18n.saveErrorMessage });
|
||||
} finally {
|
||||
|
|
|
@ -45,9 +45,6 @@ export default {
|
|||
},
|
||||
mixins: [Tracking.mixin(), glFeatureFlagMixin()],
|
||||
inject: {
|
||||
boardId: {
|
||||
default: '',
|
||||
},
|
||||
weightFeatureAvailable: {
|
||||
default: false,
|
||||
},
|
||||
|
@ -78,7 +75,7 @@ export default {
|
|||
},
|
||||
},
|
||||
computed: {
|
||||
...mapState(['activeId', 'filterParams']),
|
||||
...mapState(['activeId', 'filterParams', 'boardId']),
|
||||
...mapGetters(['isEpicBoard', 'isSwimlanesOn']),
|
||||
isLoggedIn() {
|
||||
return Boolean(this.currentUserId);
|
||||
|
|
|
@ -19,8 +19,6 @@ import { s__ } from '~/locale';
|
|||
import eventHub from '../eventhub';
|
||||
import groupBoardsQuery from '../graphql/group_boards.query.graphql';
|
||||
import projectBoardsQuery from '../graphql/project_boards.query.graphql';
|
||||
import groupBoardQuery from '../graphql/group_board.query.graphql';
|
||||
import projectBoardQuery from '../graphql/project_board.query.graphql';
|
||||
import groupRecentBoardsQuery from '../graphql/group_recent_boards.query.graphql';
|
||||
import projectRecentBoardsQuery from '../graphql/project_recent_boards.query.graphql';
|
||||
|
||||
|
@ -69,48 +67,15 @@ export default {
|
|||
maxPosition: 0,
|
||||
filterTerm: '',
|
||||
currentPage: '',
|
||||
board: {},
|
||||
};
|
||||
},
|
||||
apollo: {
|
||||
board: {
|
||||
query() {
|
||||
return this.currentBoardQuery;
|
||||
},
|
||||
variables() {
|
||||
return {
|
||||
fullPath: this.fullPath,
|
||||
boardId: this.fullBoardId,
|
||||
};
|
||||
},
|
||||
update(data) {
|
||||
const board = data.workspace?.board;
|
||||
this.setBoardConfig(board);
|
||||
return {
|
||||
...board,
|
||||
labels: board?.labels?.nodes,
|
||||
};
|
||||
},
|
||||
error() {
|
||||
this.setError({ message: this.$options.i18n.errorFetchingBoard });
|
||||
},
|
||||
},
|
||||
},
|
||||
|
||||
computed: {
|
||||
...mapState(['boardType', 'fullBoardId']),
|
||||
...mapState(['boardType', 'board', 'isBoardLoading']),
|
||||
...mapGetters(['isGroupBoard', 'isProjectBoard']),
|
||||
parentType() {
|
||||
return this.boardType;
|
||||
},
|
||||
currentBoardQueryCE() {
|
||||
return this.isGroupBoard ? groupBoardQuery : projectBoardQuery;
|
||||
},
|
||||
currentBoardQuery() {
|
||||
return this.currentBoardQueryCE;
|
||||
},
|
||||
isBoardLoading() {
|
||||
return this.$apollo.queries.board.loading;
|
||||
},
|
||||
loading() {
|
||||
return this.loadingRecentBoards || Boolean(this.loadingBoards);
|
||||
},
|
||||
|
|
|
@ -1,8 +1,9 @@
|
|||
#import "ee_else_ce/boards/graphql/board_scope.fragment.graphql"
|
||||
|
||||
mutation createBoard($input: CreateBoardInput!) {
|
||||
createBoard(input: $input) {
|
||||
board {
|
||||
id
|
||||
webPath
|
||||
...BoardScopeFragment
|
||||
}
|
||||
errors
|
||||
}
|
||||
|
|
|
@ -1,8 +1,9 @@
|
|||
#import "ee_else_ce/boards/graphql/board_scope.fragment.graphql"
|
||||
|
||||
mutation UpdateBoard($input: UpdateBoardInput!) {
|
||||
updateBoard(input: $input) {
|
||||
board {
|
||||
id
|
||||
webPath
|
||||
...BoardScopeFragment
|
||||
}
|
||||
errors
|
||||
}
|
||||
|
|
|
@ -54,7 +54,6 @@ function mountBoardApp(el) {
|
|||
apolloProvider,
|
||||
provide: {
|
||||
disabled: parseBoolean(el.dataset.disabled),
|
||||
boardId,
|
||||
groupId: Number(groupId),
|
||||
rootPath,
|
||||
fullPath,
|
||||
|
|
|
@ -31,10 +31,12 @@ import {
|
|||
import createBoardListMutation from 'ee_else_ce/boards/graphql/board_list_create.mutation.graphql';
|
||||
import issueMoveListMutation from 'ee_else_ce/boards/graphql/issue_move_list.mutation.graphql';
|
||||
import totalCountAndWeightQuery from 'ee_else_ce/boards/graphql/board_lists_deferred.query.graphql';
|
||||
import { fetchPolicies } from '~/lib/graphql';
|
||||
import { getIdFromGraphQLId } from '~/graphql_shared/utils';
|
||||
import { convertObjectPropsToCamelCase } from '~/lib/utils/common_utils';
|
||||
import { queryToObject } from '~/lib/utils/url_utility';
|
||||
import { s__ } from '~/locale';
|
||||
import eventHub from '../eventhub';
|
||||
import { gqlClient } from '../graphql';
|
||||
import projectBoardQuery from '../graphql/project_board.query.graphql';
|
||||
import groupBoardQuery from '../graphql/group_board.query.graphql';
|
||||
|
@ -49,6 +51,8 @@ import * as types from './mutation_types';
|
|||
|
||||
export default {
|
||||
fetchBoard: ({ commit, dispatch }, { fullPath, fullBoardId, boardType }) => {
|
||||
commit(types.REQUEST_CURRENT_BOARD);
|
||||
|
||||
const variables = {
|
||||
fullPath,
|
||||
boardId: fullBoardId,
|
||||
|
@ -60,9 +64,13 @@ export default {
|
|||
variables,
|
||||
})
|
||||
.then(({ data }) => {
|
||||
const board = data.workspace?.board;
|
||||
commit(types.RECEIVE_BOARD_SUCCESS, board);
|
||||
dispatch('setBoardConfig', board);
|
||||
if (data.workspace?.errors) {
|
||||
commit(types.RECEIVE_BOARD_FAILURE);
|
||||
} else {
|
||||
const board = data.workspace?.board;
|
||||
commit(types.RECEIVE_BOARD_SUCCESS, board);
|
||||
dispatch('setBoardConfig', board);
|
||||
}
|
||||
})
|
||||
.catch(() => commit(types.RECEIVE_BOARD_FAILURE));
|
||||
},
|
||||
|
@ -87,6 +95,13 @@ export default {
|
|||
commit(types.SET_BOARD_CONFIG, config);
|
||||
},
|
||||
|
||||
setBoard: async ({ commit, dispatch }, board) => {
|
||||
commit(types.RECEIVE_BOARD_SUCCESS, board);
|
||||
await dispatch('setBoardConfig', board);
|
||||
dispatch('performSearch', { resetLists: true });
|
||||
eventHub.$emit('updateTokens');
|
||||
},
|
||||
|
||||
setActiveId({ commit }, { id, sidebarType }) {
|
||||
commit(types.SET_ACTIVE_ID, { id, sidebarType });
|
||||
},
|
||||
|
@ -107,16 +122,16 @@ export default {
|
|||
);
|
||||
},
|
||||
|
||||
performSearch({ dispatch }) {
|
||||
performSearch({ dispatch }, { resetLists = false } = {}) {
|
||||
dispatch(
|
||||
'setFilters',
|
||||
convertObjectPropsToCamelCase(queryToObject(window.location.search, { gatherArrays: true })),
|
||||
);
|
||||
dispatch('fetchLists');
|
||||
dispatch('fetchLists', { resetLists });
|
||||
dispatch('resetIssues');
|
||||
},
|
||||
|
||||
fetchLists: ({ commit, state, dispatch }) => {
|
||||
fetchLists: ({ commit, state, dispatch }, { resetLists = false } = {}) => {
|
||||
const { boardType, filterParams, fullPath, fullBoardId, issuableType } = state;
|
||||
|
||||
const variables = {
|
||||
|
@ -133,6 +148,7 @@ export default {
|
|||
.query({
|
||||
query: listsQuery[issuableType].query,
|
||||
variables,
|
||||
...(resetLists ? { fetchPolicy: fetchPolicies.NO_CACHE } : {}),
|
||||
})
|
||||
.then(({ data }) => {
|
||||
const { lists, hideBacklogList } = data[boardType].board;
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
export const REQUEST_CURRENT_BOARD = 'REQUEST_CURRENT_BOARD';
|
||||
export const RECEIVE_BOARD_SUCCESS = 'RECEIVE_BOARD_SUCCESS';
|
||||
export const RECEIVE_BOARD_FAILURE = 'RECEIVE_BOARD_FAILURE';
|
||||
export const SET_INITIAL_BOARD_DATA = 'SET_INITIAL_BOARD_DATA';
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
import { cloneDeep, pull, union } from 'lodash';
|
||||
import Vue from 'vue';
|
||||
import { getIdFromGraphQLId } from '~/graphql_shared/utils';
|
||||
import { s__, __ } from '~/locale';
|
||||
import { formatIssue } from '../boards_util';
|
||||
import { issuableTypes } from '../constants';
|
||||
|
@ -33,15 +34,23 @@ export const addItemToList = ({ state, listId, itemId, moveBeforeId, moveAfterId
|
|||
};
|
||||
|
||||
export default {
|
||||
[mutationTypes.REQUEST_CURRENT_BOARD]: (state) => {
|
||||
state.isBoardLoading = true;
|
||||
},
|
||||
|
||||
[mutationTypes.RECEIVE_BOARD_SUCCESS]: (state, board) => {
|
||||
state.board = {
|
||||
...board,
|
||||
labels: board?.labels?.nodes || [],
|
||||
};
|
||||
state.fullBoardId = board.id;
|
||||
state.boardId = getIdFromGraphQLId(board.id);
|
||||
state.isBoardLoading = false;
|
||||
},
|
||||
|
||||
[mutationTypes.RECEIVE_BOARD_FAILURE]: (state) => {
|
||||
state.error = s__('Boards|An error occurred while fetching the board. Please reload the page.');
|
||||
state.isBoardLoading = false;
|
||||
},
|
||||
|
||||
[mutationTypes.SET_INITIAL_BOARD_DATA](state, data) {
|
||||
|
|
|
@ -2,6 +2,7 @@ import { inactiveId, ListType } from '~/boards/constants';
|
|||
|
||||
export default () => ({
|
||||
board: {},
|
||||
isBoardLoading: false,
|
||||
boardType: null,
|
||||
issuableType: null,
|
||||
fullPath: null,
|
||||
|
|
|
@ -75,25 +75,25 @@ export default {
|
|||
<gl-button-group class="gl-ml-3">
|
||||
<gl-button
|
||||
v-gl-tooltip.hover
|
||||
:title="__('Jump to previous unresolved thread')"
|
||||
:aria-label="__('Jump to previous unresolved thread')"
|
||||
:title="__('Go to previous unresolved thread')"
|
||||
:aria-label="__('Go to previous unresolved thread')"
|
||||
class="discussion-previous-btn gl-rounded-base! gl-px-2!"
|
||||
data-track-action="click_button"
|
||||
data-track-label="mr_previous_unresolved_thread"
|
||||
data-track-property="click_previous_unresolved_thread_top"
|
||||
icon="angle-up"
|
||||
icon="chevron-lg-up"
|
||||
category="tertiary"
|
||||
@click="jumpToPreviousDiscussion"
|
||||
/>
|
||||
<gl-button
|
||||
v-gl-tooltip.hover
|
||||
:title="__('Jump to next unresolved thread')"
|
||||
:aria-label="__('Jump to next unresolved thread')"
|
||||
:title="__('Go to next unresolved thread')"
|
||||
:aria-label="__('Go to next unresolved thread')"
|
||||
class="discussion-next-btn gl-rounded-base! gl-px-2!"
|
||||
data-track-action="click_button"
|
||||
data-track-label="mr_next_unresolved_thread"
|
||||
data-track-property="click_next_unresolved_thread_top"
|
||||
icon="angle-down"
|
||||
icon="chevron-lg-down"
|
||||
category="tertiary"
|
||||
@click="jumpToNextDiscussion"
|
||||
/>
|
||||
|
|
|
@ -1,12 +1,19 @@
|
|||
<script>
|
||||
import { GlLink, GlSprintf } from '@gitlab/ui';
|
||||
import { first } from 'lodash';
|
||||
import createFlash from '~/flash';
|
||||
import { getIdFromGraphQLId } from '~/graphql_shared/utils';
|
||||
import { truncateSha } from '~/lib/utils/text_utility';
|
||||
import { s__, n__ } from '~/locale';
|
||||
import { HISTORY_PIPELINES_LIMIT } from '~/packages_and_registries/shared/constants';
|
||||
import HistoryItem from '~/vue_shared/components/registry/history_item.vue';
|
||||
import TimeAgoTooltip from '~/vue_shared/components/time_ago_tooltip.vue';
|
||||
import {
|
||||
GRAPHQL_PACKAGE_PIPELINES_PAGE_SIZE,
|
||||
FETCH_PACKAGE_PIPELINES_ERROR_MESSAGE,
|
||||
} from '../../constants';
|
||||
import getPackagePipelinesQuery from '../../graphql/queries/get_package_pipelines.query.graphql';
|
||||
import PackageHistoryLoader from './package_history_loader.vue';
|
||||
|
||||
export default {
|
||||
name: 'PackageHistory',
|
||||
|
@ -25,6 +32,7 @@ export default {
|
|||
GlLink,
|
||||
GlSprintf,
|
||||
HistoryItem,
|
||||
PackageHistoryLoader,
|
||||
TimeAgoTooltip,
|
||||
},
|
||||
props: {
|
||||
|
@ -37,15 +45,27 @@ export default {
|
|||
required: true,
|
||||
},
|
||||
},
|
||||
apollo: {
|
||||
pipelines: {
|
||||
query: getPackagePipelinesQuery,
|
||||
variables() {
|
||||
return this.queryVariables;
|
||||
},
|
||||
update(data) {
|
||||
return data.package?.pipelines?.nodes || [];
|
||||
},
|
||||
error() {
|
||||
createFlash({ message: FETCH_PACKAGE_PIPELINES_ERROR_MESSAGE });
|
||||
},
|
||||
},
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
pipelines: [],
|
||||
showDescription: false,
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
pipelines() {
|
||||
return this.packageEntity?.pipelines?.nodes || [];
|
||||
},
|
||||
firstPipeline() {
|
||||
return first(this.pipelines);
|
||||
},
|
||||
|
@ -65,6 +85,15 @@ export default {
|
|||
this.archivedLines,
|
||||
);
|
||||
},
|
||||
isLoading() {
|
||||
return this.$apollo.queries.pipelines.loading;
|
||||
},
|
||||
queryVariables() {
|
||||
return {
|
||||
id: this.packageEntity.id,
|
||||
first: GRAPHQL_PACKAGE_PIPELINES_PAGE_SIZE,
|
||||
};
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
truncate(value) {
|
||||
|
@ -80,7 +109,8 @@ export default {
|
|||
<template>
|
||||
<div class="issuable-discussion">
|
||||
<h3 class="gl-font-lg" data-testid="title">{{ __('History') }}</h3>
|
||||
<ul class="timeline main-notes-list notes gl-mb-4" data-testid="timeline">
|
||||
<package-history-loader v-if="isLoading" />
|
||||
<ul v-else class="timeline main-notes-list notes gl-mb-4" data-testid="timeline">
|
||||
<history-item icon="clock" data-testid="created-on">
|
||||
<gl-sprintf :message="$options.i18n.createdOn">
|
||||
<template #name>
|
||||
|
|
|
@ -0,0 +1,24 @@
|
|||
<script>
|
||||
import { GlSkeletonLoader } from '@gitlab/ui';
|
||||
|
||||
export default {
|
||||
components: {
|
||||
GlSkeletonLoader,
|
||||
},
|
||||
loader: {
|
||||
width: 580,
|
||||
height: 80,
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="gl-ml-5 gl-md-max-w-70p">
|
||||
<gl-skeleton-loader :width="$options.loader.width" :height="$options.loader.height">
|
||||
<rect x="49" y="9" width="531" height="16" rx="4" />
|
||||
<circle cx="16" cy="16" r="16" />
|
||||
<rect x="49" y="57" width="302" height="16" rx="4" />
|
||||
<circle cx="16" cy="64" r="16" />
|
||||
</gl-skeleton-loader>
|
||||
</div>
|
||||
</template>
|
|
@ -72,6 +72,9 @@ export const DELETE_PACKAGE_FILE_SUCCESS_MESSAGE = s__(
|
|||
export const FETCH_PACKAGE_DETAILS_ERROR_MESSAGE = s__(
|
||||
'PackageRegistry|Failed to load the package data',
|
||||
);
|
||||
export const FETCH_PACKAGE_PIPELINES_ERROR_MESSAGE = s__(
|
||||
'PackageRegistry|Something went wrong while fetching the package history.',
|
||||
);
|
||||
|
||||
export const DELETE_PACKAGE_SUCCESS_MESSAGE = s__('PackageRegistry|Package deleted successfully');
|
||||
export const PACKAGE_REGISTRY_TITLE = __('Package Registry');
|
||||
|
@ -149,3 +152,5 @@ export const CONAN_HELP_PATH = helpPagePath('user/packages/conan_repository/inde
|
|||
export const NUGET_HELP_PATH = helpPagePath('user/packages/nuget_repository/index');
|
||||
export const PYPI_HELP_PATH = helpPagePath('user/packages/pypi_repository/index');
|
||||
export const COMPOSER_HELP_PATH = helpPagePath('user/packages/composer_repository/index');
|
||||
|
||||
export const GRAPHQL_PACKAGE_PIPELINES_PAGE_SIZE = 10;
|
||||
|
|
|
@ -27,25 +27,6 @@ query getPackageDetails($id: PackagesPackageID!) {
|
|||
name
|
||||
}
|
||||
}
|
||||
pipelines(first: 10) {
|
||||
nodes {
|
||||
ref
|
||||
id
|
||||
sha
|
||||
createdAt
|
||||
commitPath
|
||||
path
|
||||
user {
|
||||
id
|
||||
name
|
||||
}
|
||||
project {
|
||||
id
|
||||
name
|
||||
webUrl
|
||||
}
|
||||
}
|
||||
}
|
||||
packageFiles(first: 100) {
|
||||
nodes {
|
||||
id
|
||||
|
|
|
@ -0,0 +1,24 @@
|
|||
query getPackagePipelines($id: PackagesPackageID!, $first: Int) {
|
||||
package(id: $id) {
|
||||
id
|
||||
pipelines(first: $first) {
|
||||
nodes {
|
||||
ref
|
||||
id
|
||||
sha
|
||||
createdAt
|
||||
commitPath
|
||||
path
|
||||
user {
|
||||
id
|
||||
name
|
||||
}
|
||||
project {
|
||||
id
|
||||
name
|
||||
webUrl
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,8 @@
|
|||
---
|
||||
name: remove_mergeable_state_check
|
||||
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/86612
|
||||
rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/362555
|
||||
milestone: '15.1'
|
||||
type: development
|
||||
group: group::code review
|
||||
default_enabled: false
|
|
@ -313,6 +313,10 @@ This creates two jobs:
|
|||
|
||||
The `covfuzz-ci.yml` is the same as that in the [original synchronous example](https://gitlab.com/gitlab-org/security-products/demos/coverage-fuzzing/go-fuzzing-example#running-go-fuzz-from-ci).
|
||||
|
||||
## FIPS-enabled binary
|
||||
|
||||
[Starting in GitLab 15.0](https://gitlab.com/gitlab-org/gitlab/-/issues/352549) the coverage fuzzing binary is compiled with `golang-fips` on Linux x86 and uses OpenSSL as the cryptographic backend. For more details, see [FIPS compliance at GitLab with Go](../../../development/fips_compliance.md#go).
|
||||
|
||||
## Offline environment
|
||||
|
||||
To use coverage fuzzing in an offline environment:
|
||||
|
|
|
@ -17516,6 +17516,9 @@ msgstr ""
|
|||
msgid "Go to next page"
|
||||
msgstr ""
|
||||
|
||||
msgid "Go to next unresolved thread"
|
||||
msgstr ""
|
||||
|
||||
msgid "Go to page %{page}"
|
||||
msgstr ""
|
||||
|
||||
|
@ -17528,6 +17531,9 @@ msgstr ""
|
|||
msgid "Go to previous page"
|
||||
msgstr ""
|
||||
|
||||
msgid "Go to previous unresolved thread"
|
||||
msgstr ""
|
||||
|
||||
msgid "Go to primary site"
|
||||
msgstr ""
|
||||
|
||||
|
@ -22065,12 +22071,6 @@ msgstr ""
|
|||
msgid "July"
|
||||
msgstr ""
|
||||
|
||||
msgid "Jump to next unresolved thread"
|
||||
msgstr ""
|
||||
|
||||
msgid "Jump to previous unresolved thread"
|
||||
msgstr ""
|
||||
|
||||
msgid "Jun"
|
||||
msgstr ""
|
||||
|
||||
|
@ -27154,6 +27154,9 @@ msgstr ""
|
|||
msgid "PackageRegistry|Something went wrong while deleting the package."
|
||||
msgstr ""
|
||||
|
||||
msgid "PackageRegistry|Something went wrong while fetching the package history."
|
||||
msgstr ""
|
||||
|
||||
msgid "PackageRegistry|Sorry, your filter produced no results"
|
||||
msgstr ""
|
||||
|
||||
|
|
|
@ -20,8 +20,6 @@ describe('Board Column Component', () => {
|
|||
};
|
||||
|
||||
const createComponent = ({ listType = ListType.backlog, collapsed = false } = {}) => {
|
||||
const boardId = '1';
|
||||
|
||||
const listMock = {
|
||||
...listObj,
|
||||
listType,
|
||||
|
@ -39,9 +37,6 @@ describe('Board Column Component', () => {
|
|||
disabled: false,
|
||||
list: listMock,
|
||||
},
|
||||
provide: {
|
||||
boardId,
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
|
|
|
@ -1,4 +1,6 @@
|
|||
import { GlModal } from '@gitlab/ui';
|
||||
import Vue from 'vue';
|
||||
import Vuex from 'vuex';
|
||||
import setWindowLocation from 'helpers/set_window_location_helper';
|
||||
import { shallowMountExtended } from 'helpers/vue_test_utils_helper';
|
||||
import waitForPromises from 'helpers/wait_for_promises';
|
||||
|
@ -8,7 +10,6 @@ import { formType } from '~/boards/constants';
|
|||
import createBoardMutation from '~/boards/graphql/board_create.mutation.graphql';
|
||||
import destroyBoardMutation from '~/boards/graphql/board_destroy.mutation.graphql';
|
||||
import updateBoardMutation from '~/boards/graphql/board_update.mutation.graphql';
|
||||
import { createStore } from '~/boards/stores';
|
||||
import { visitUrl } from '~/lib/utils/url_utility';
|
||||
|
||||
jest.mock('~/lib/utils/url_utility', () => ({
|
||||
|
@ -16,6 +17,8 @@ jest.mock('~/lib/utils/url_utility', () => ({
|
|||
visitUrl: jest.fn().mockName('visitUrlMock'),
|
||||
}));
|
||||
|
||||
Vue.use(Vuex);
|
||||
|
||||
const currentBoard = {
|
||||
id: 'gid://gitlab/Board/1',
|
||||
name: 'test',
|
||||
|
@ -46,11 +49,18 @@ describe('BoardForm', () => {
|
|||
const findDeleteConfirmation = () => wrapper.findByTestId('delete-confirmation-message');
|
||||
const findInput = () => wrapper.find('#board-new-name');
|
||||
|
||||
const store = createStore({
|
||||
const setBoardMock = jest.fn();
|
||||
const setErrorMock = jest.fn();
|
||||
|
||||
const store = new Vuex.Store({
|
||||
getters: {
|
||||
isGroupBoard: () => true,
|
||||
isProjectBoard: () => false,
|
||||
},
|
||||
actions: {
|
||||
setBoard: setBoardMock,
|
||||
setError: setErrorMock,
|
||||
},
|
||||
});
|
||||
|
||||
const createComponent = (props, data) => {
|
||||
|
@ -168,7 +178,7 @@ describe('BoardForm', () => {
|
|||
expect(mutate).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('calls a correct GraphQL mutation and redirects to correct page from existing board', async () => {
|
||||
it('calls a correct GraphQL mutation and sets board in state', async () => {
|
||||
createComponent({ canAdminBoard: true, currentPage: formType.new });
|
||||
fillForm();
|
||||
|
||||
|
@ -184,13 +194,12 @@ describe('BoardForm', () => {
|
|||
});
|
||||
|
||||
await waitForPromises();
|
||||
expect(visitUrl).toHaveBeenCalledWith('test-path');
|
||||
expect(setBoardMock).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
|
||||
it('shows a GlAlert if GraphQL mutation fails', async () => {
|
||||
it('sets error in state if GraphQL mutation fails', async () => {
|
||||
mutate = jest.fn().mockRejectedValue('Houston, we have a problem');
|
||||
createComponent({ canAdminBoard: true, currentPage: formType.new });
|
||||
jest.spyOn(wrapper.vm, 'setError').mockImplementation(() => {});
|
||||
|
||||
fillForm();
|
||||
|
||||
|
@ -199,8 +208,8 @@ describe('BoardForm', () => {
|
|||
expect(mutate).toHaveBeenCalled();
|
||||
|
||||
await waitForPromises();
|
||||
expect(visitUrl).not.toHaveBeenCalled();
|
||||
expect(wrapper.vm.setError).toHaveBeenCalled();
|
||||
expect(setBoardMock).not.toHaveBeenCalled();
|
||||
expect(setErrorMock).toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
@ -256,7 +265,8 @@ describe('BoardForm', () => {
|
|||
});
|
||||
|
||||
await waitForPromises();
|
||||
expect(visitUrl).toHaveBeenCalledWith('test-path');
|
||||
expect(setBoardMock).toHaveBeenCalledTimes(1);
|
||||
expect(global.window.location.href).not.toContain('?group_by=epic');
|
||||
});
|
||||
|
||||
it('calls GraphQL mutation with correct parameters when issues are grouped by epic', async () => {
|
||||
|
@ -282,13 +292,13 @@ describe('BoardForm', () => {
|
|||
});
|
||||
|
||||
await waitForPromises();
|
||||
expect(visitUrl).toHaveBeenCalledWith('test-path?group_by=epic');
|
||||
expect(setBoardMock).toHaveBeenCalledTimes(1);
|
||||
expect(global.window.location.href).toContain('?group_by=epic');
|
||||
});
|
||||
|
||||
it('shows a GlAlert if GraphQL mutation fails', async () => {
|
||||
it('sets error in state if GraphQL mutation fails', async () => {
|
||||
mutate = jest.fn().mockRejectedValue('Houston, we have a problem');
|
||||
createComponent({ canAdminBoard: true, currentPage: formType.edit });
|
||||
jest.spyOn(wrapper.vm, 'setError').mockImplementation(() => {});
|
||||
|
||||
findInput().trigger('keyup.enter', { metaKey: true });
|
||||
|
||||
|
@ -297,8 +307,8 @@ describe('BoardForm', () => {
|
|||
expect(mutate).toHaveBeenCalled();
|
||||
|
||||
await waitForPromises();
|
||||
expect(visitUrl).not.toHaveBeenCalled();
|
||||
expect(wrapper.vm.setError).toHaveBeenCalled();
|
||||
expect(setBoardMock).not.toHaveBeenCalled();
|
||||
expect(setErrorMock).toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
|
||||
|
|
|
@ -2,11 +2,10 @@ import { GlDropdown, GlLoadingIcon, GlDropdownSectionHeader } from '@gitlab/ui';
|
|||
import Vue, { nextTick } from 'vue';
|
||||
import VueApollo from 'vue-apollo';
|
||||
import Vuex from 'vuex';
|
||||
import waitForPromises from 'helpers/wait_for_promises';
|
||||
import { TEST_HOST } from 'spec/test_constants';
|
||||
import BoardsSelector from '~/boards/components/boards_selector.vue';
|
||||
import { BoardType } from '~/boards/constants';
|
||||
import groupBoardQuery from '~/boards/graphql/group_board.query.graphql';
|
||||
import projectBoardQuery from '~/boards/graphql/project_board.query.graphql';
|
||||
import groupBoardsQuery from '~/boards/graphql/group_boards.query.graphql';
|
||||
import projectBoardsQuery from '~/boards/graphql/project_boards.query.graphql';
|
||||
import groupRecentBoardsQuery from '~/boards/graphql/group_recent_boards.query.graphql';
|
||||
|
@ -15,8 +14,7 @@ import defaultStore from '~/boards/stores';
|
|||
import createMockApollo from 'helpers/mock_apollo_helper';
|
||||
import { mountExtended } from 'helpers/vue_test_utils_helper';
|
||||
import {
|
||||
mockGroupBoardResponse,
|
||||
mockProjectBoardResponse,
|
||||
mockBoard,
|
||||
mockGroupAllBoardsResponse,
|
||||
mockProjectAllBoardsResponse,
|
||||
mockGroupRecentBoardsResponse,
|
||||
|
@ -49,6 +47,7 @@ describe('BoardsSelector', () => {
|
|||
},
|
||||
state: {
|
||||
boardType: isGroupBoard ? 'group' : 'project',
|
||||
board: mockBoard,
|
||||
},
|
||||
});
|
||||
};
|
||||
|
@ -65,9 +64,6 @@ describe('BoardsSelector', () => {
|
|||
const getLoadingIcon = () => wrapper.findComponent(GlLoadingIcon);
|
||||
const findDropdown = () => wrapper.findComponent(GlDropdown);
|
||||
|
||||
const projectBoardQueryHandlerSuccess = jest.fn().mockResolvedValue(mockProjectBoardResponse);
|
||||
const groupBoardQueryHandlerSuccess = jest.fn().mockResolvedValue(mockGroupBoardResponse);
|
||||
|
||||
const projectBoardsQueryHandlerSuccess = jest
|
||||
.fn()
|
||||
.mockResolvedValue(mockProjectAllBoardsResponse);
|
||||
|
@ -92,8 +88,6 @@ describe('BoardsSelector', () => {
|
|||
projectRecentBoardsQueryHandler = projectRecentBoardsQueryHandlerSuccess,
|
||||
} = {}) => {
|
||||
fakeApollo = createMockApollo([
|
||||
[projectBoardQuery, projectBoardQueryHandlerSuccess],
|
||||
[groupBoardQuery, groupBoardQueryHandlerSuccess],
|
||||
[projectBoardsQuery, projectBoardsQueryHandler],
|
||||
[groupBoardsQuery, groupBoardsQueryHandlerSuccess],
|
||||
[projectRecentBoardsQuery, projectRecentBoardsQueryHandler],
|
||||
|
@ -133,12 +127,13 @@ describe('BoardsSelector', () => {
|
|||
describe('loading', () => {
|
||||
// we are testing loading state, so don't resolve responses until after the tests
|
||||
afterEach(async () => {
|
||||
await nextTick();
|
||||
await waitForPromises();
|
||||
});
|
||||
|
||||
it('shows loading spinner', () => {
|
||||
it('shows loading spinner', async () => {
|
||||
// Emits gl-dropdown show event to simulate the dropdown is opened at initialization time
|
||||
findDropdown().vm.$emit('show');
|
||||
await nextTick();
|
||||
|
||||
expect(getLoadingIcon().exists()).toBe(true);
|
||||
expect(getDropdownHeaders()).toHaveLength(0);
|
||||
|
@ -251,23 +246,4 @@ describe('BoardsSelector', () => {
|
|||
expect(notCalledHandler).not.toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
|
||||
describe('fetching current board', () => {
|
||||
it.each`
|
||||
boardType | queryHandler | notCalledHandler
|
||||
${BoardType.group} | ${groupBoardQueryHandlerSuccess} | ${projectBoardQueryHandlerSuccess}
|
||||
${BoardType.project} | ${projectBoardQueryHandlerSuccess} | ${groupBoardQueryHandlerSuccess}
|
||||
`('fetches $boardType board', async ({ boardType, queryHandler, notCalledHandler }) => {
|
||||
createStore({
|
||||
isProjectBoard: boardType === BoardType.project,
|
||||
isGroupBoard: boardType === BoardType.group,
|
||||
});
|
||||
createComponent();
|
||||
|
||||
await nextTick();
|
||||
|
||||
expect(queryHandler).toHaveBeenCalled();
|
||||
expect(notCalledHandler).not.toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
@ -144,30 +144,6 @@ export const mockProjectRecentBoardsResponse = {
|
|||
},
|
||||
};
|
||||
|
||||
export const mockGroupBoardResponse = {
|
||||
data: {
|
||||
workspace: {
|
||||
board: {
|
||||
id: 'gid://gitlab/Board/1',
|
||||
name: 'Development',
|
||||
},
|
||||
__typename: 'Group',
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
export const mockProjectBoardResponse = {
|
||||
data: {
|
||||
workspace: {
|
||||
board: {
|
||||
id: 'gid://gitlab/Board/2',
|
||||
name: 'Development',
|
||||
},
|
||||
__typename: 'Project',
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
export const mockAssigneesList = [
|
||||
{
|
||||
id: 2,
|
||||
|
|
|
@ -84,6 +84,9 @@ describe('fetchBoard', () => {
|
|||
action: actions.fetchBoard,
|
||||
payload,
|
||||
expectedMutations: [
|
||||
{
|
||||
type: types.REQUEST_CURRENT_BOARD,
|
||||
},
|
||||
{
|
||||
type: types.RECEIVE_BOARD_SUCCESS,
|
||||
payload: mockBoard,
|
||||
|
@ -100,6 +103,9 @@ describe('fetchBoard', () => {
|
|||
action: actions.fetchBoard,
|
||||
payload,
|
||||
expectedMutations: [
|
||||
{
|
||||
type: types.REQUEST_CURRENT_BOARD,
|
||||
},
|
||||
{
|
||||
type: types.RECEIVE_BOARD_FAILURE,
|
||||
},
|
||||
|
@ -133,6 +139,20 @@ describe('setBoardConfig', () => {
|
|||
});
|
||||
});
|
||||
|
||||
describe('setBoard', () => {
|
||||
it('dispatches setBoardConfig', () => {
|
||||
return testAction({
|
||||
action: actions.setBoard,
|
||||
payload: mockBoard,
|
||||
expectedMutations: [{ type: types.RECEIVE_BOARD_SUCCESS, payload: mockBoard }],
|
||||
expectedActions: [
|
||||
{ type: 'setBoardConfig', payload: mockBoard },
|
||||
{ type: 'performSearch', payload: { resetLists: true } },
|
||||
],
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('setFilters', () => {
|
||||
it.each([
|
||||
[
|
||||
|
@ -172,7 +192,11 @@ describe('performSearch', () => {
|
|||
{},
|
||||
{},
|
||||
[],
|
||||
[{ type: 'setFilters', payload: {} }, { type: 'fetchLists' }, { type: 'resetIssues' }],
|
||||
[
|
||||
{ type: 'setFilters', payload: {} },
|
||||
{ type: 'fetchLists', payload: { resetLists: false } },
|
||||
{ type: 'resetIssues' },
|
||||
],
|
||||
);
|
||||
});
|
||||
});
|
||||
|
|
|
@ -34,6 +34,14 @@ describe('Board Store Mutations', () => {
|
|||
state = defaultState();
|
||||
});
|
||||
|
||||
describe('REQUEST_CURRENT_BOARD', () => {
|
||||
it('Should set isBoardLoading state to true', () => {
|
||||
mutations[types.REQUEST_CURRENT_BOARD](state);
|
||||
|
||||
expect(state.isBoardLoading).toBe(true);
|
||||
});
|
||||
});
|
||||
|
||||
describe('RECEIVE_BOARD_SUCCESS', () => {
|
||||
it('Should set board to state', () => {
|
||||
mutations[types.RECEIVE_BOARD_SUCCESS](state, mockBoard);
|
||||
|
|
|
@ -1,17 +1,29 @@
|
|||
import Vue from 'vue';
|
||||
import VueApollo from 'vue-apollo';
|
||||
import { GlLink, GlSprintf } from '@gitlab/ui';
|
||||
import createFlash from '~/flash';
|
||||
import { stubComponent } from 'helpers/stub_component';
|
||||
import createMockApollo from 'helpers/mock_apollo_helper';
|
||||
import { shallowMountExtended } from 'helpers/vue_test_utils_helper';
|
||||
import {
|
||||
packageData,
|
||||
packagePipelines,
|
||||
packagePipelinesQuery,
|
||||
} from 'jest/packages_and_registries/package_registry/mock_data';
|
||||
import { HISTORY_PIPELINES_LIMIT } from '~/packages_and_registries/shared/constants';
|
||||
import { FETCH_PACKAGE_PIPELINES_ERROR_MESSAGE } from '~/packages_and_registries/package_registry/constants';
|
||||
import component from '~/packages_and_registries/package_registry/components/details/package_history.vue';
|
||||
import PackageHistoryLoader from '~/packages_and_registries/package_registry/components/details/package_history_loader.vue';
|
||||
import HistoryItem from '~/vue_shared/components/registry/history_item.vue';
|
||||
import TimeAgoTooltip from '~/vue_shared/components/time_ago_tooltip.vue';
|
||||
import waitForPromises from 'helpers/wait_for_promises';
|
||||
import getPackagePipelines from '~/packages_and_registries/package_registry/graphql/queries/get_package_pipelines.query.graphql';
|
||||
|
||||
jest.mock('~/flash');
|
||||
describe('Package History', () => {
|
||||
let wrapper;
|
||||
let apolloProvider;
|
||||
|
||||
const defaultProps = {
|
||||
projectName: 'baz project',
|
||||
packageEntity: { ...packageData() },
|
||||
|
@ -22,8 +34,17 @@ describe('Package History', () => {
|
|||
const createPipelines = (amount) =>
|
||||
[...Array(amount)].map((x, index) => packagePipelines({ id: index + 1 })[0]);
|
||||
|
||||
const mountComponent = (props) => {
|
||||
const mountComponent = (
|
||||
props,
|
||||
resolver = jest.fn().mockResolvedValue(packagePipelinesQuery()),
|
||||
) => {
|
||||
Vue.use(VueApollo);
|
||||
|
||||
const requestHandlers = [[getPackagePipelines, resolver]];
|
||||
apolloProvider = createMockApollo(requestHandlers);
|
||||
|
||||
wrapper = shallowMountExtended(component, {
|
||||
apolloProvider,
|
||||
propsData: { ...defaultProps, ...props },
|
||||
stubs: {
|
||||
HistoryItem: stubComponent(HistoryItem, {
|
||||
|
@ -38,23 +59,39 @@ describe('Package History', () => {
|
|||
wrapper.destroy();
|
||||
});
|
||||
|
||||
const findPackageHistoryLoader = () => wrapper.findComponent(PackageHistoryLoader);
|
||||
const findHistoryElement = (testId) => wrapper.findByTestId(testId);
|
||||
const findElementLink = (container) => container.findComponent(GlLink);
|
||||
const findElementTimeAgo = (container) => container.findComponent(TimeAgoTooltip);
|
||||
const findTitle = () => wrapper.findByTestId('title');
|
||||
const findTimeline = () => wrapper.findByTestId('timeline');
|
||||
|
||||
it('has the correct title', () => {
|
||||
it('renders the loading container when loading', () => {
|
||||
mountComponent();
|
||||
|
||||
expect(findPackageHistoryLoader().exists()).toBe(true);
|
||||
});
|
||||
|
||||
it('does not render the loading container once resolved', async () => {
|
||||
mountComponent();
|
||||
await waitForPromises();
|
||||
|
||||
expect(findPackageHistoryLoader().exists()).toBe(false);
|
||||
});
|
||||
|
||||
it('has the correct title', async () => {
|
||||
mountComponent();
|
||||
await waitForPromises();
|
||||
|
||||
const title = findTitle();
|
||||
|
||||
expect(title.exists()).toBe(true);
|
||||
expect(title.text()).toBe('History');
|
||||
});
|
||||
|
||||
it('has a timeline container', () => {
|
||||
it('has a timeline container', async () => {
|
||||
mountComponent();
|
||||
await waitForPromises();
|
||||
|
||||
const title = findTimeline();
|
||||
|
||||
|
@ -64,6 +101,18 @@ describe('Package History', () => {
|
|||
);
|
||||
});
|
||||
|
||||
it('calls createFlash function if load fails', async () => {
|
||||
mountComponent({}, jest.fn().mockRejectedValue());
|
||||
|
||||
await waitForPromises();
|
||||
|
||||
expect(createFlash).toHaveBeenCalledWith(
|
||||
expect.objectContaining({
|
||||
message: FETCH_PACKAGE_PIPELINES_ERROR_MESSAGE,
|
||||
}),
|
||||
);
|
||||
});
|
||||
|
||||
describe.each`
|
||||
name | amount | icon | text | timeAgoTooltip | link
|
||||
${'created-on'} | ${HISTORY_PIPELINES_LIMIT + 2} | ${'clock'} | ${'@gitlab-org/package-15 version 1.0.0 was first created'} | ${packageData().createdAt} | ${null}
|
||||
|
@ -78,11 +127,18 @@ describe('Package History', () => {
|
|||
({ name, icon, text, timeAgoTooltip, link, amount }) => {
|
||||
let element;
|
||||
|
||||
beforeEach(() => {
|
||||
const packageEntity = { ...packageData(), pipelines: { nodes: createPipelines(amount) } };
|
||||
mountComponent({
|
||||
packageEntity,
|
||||
});
|
||||
beforeEach(async () => {
|
||||
const packageEntity = { ...packageData() };
|
||||
const pipelinesResolver = jest
|
||||
.fn()
|
||||
.mockResolvedValue(packagePipelinesQuery(createPipelines(amount)));
|
||||
mountComponent(
|
||||
{
|
||||
packageEntity,
|
||||
},
|
||||
pipelinesResolver,
|
||||
);
|
||||
await waitForPromises();
|
||||
element = findHistoryElement(name);
|
||||
});
|
||||
|
||||
|
|
|
@ -202,10 +202,6 @@ export const packageDetailsQuery = (extendPackage) => ({
|
|||
nodes: packageTags(),
|
||||
__typename: 'PackageTagConnection',
|
||||
},
|
||||
pipelines: {
|
||||
nodes: packagePipelines(),
|
||||
__typename: 'PipelineConnection',
|
||||
},
|
||||
packageFiles: {
|
||||
nodes: packageFiles(),
|
||||
__typename: 'PackageFileConnection',
|
||||
|
@ -223,6 +219,19 @@ export const packageDetailsQuery = (extendPackage) => ({
|
|||
},
|
||||
});
|
||||
|
||||
export const packagePipelinesQuery = (pipelines = packagePipelines()) => ({
|
||||
data: {
|
||||
package: {
|
||||
id: 'gid://gitlab/Packages::Package/111',
|
||||
pipelines: {
|
||||
nodes: pipelines,
|
||||
__typename: 'PipelineConnection',
|
||||
},
|
||||
__typename: 'PackageDetailsType',
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
export const emptyPackageDetailsQuery = () => ({
|
||||
data: {
|
||||
package: {
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
require 'spec_helper'
|
||||
|
||||
RSpec.describe Emails::ConfirmService do
|
||||
let(:user) { create(:user) }
|
||||
let_it_be(:user) { create(:user) }
|
||||
|
||||
subject(:service) { described_class.new(user) }
|
||||
|
||||
|
@ -11,7 +11,9 @@ RSpec.describe Emails::ConfirmService do
|
|||
it 'enqueues a background job to send confirmation email again' do
|
||||
email = user.emails.create!(email: 'new@email.com')
|
||||
|
||||
expect { service.execute(email) }.to have_enqueued_job.on_queue('mailers')
|
||||
travel_to(10.minutes.from_now) do
|
||||
expect { service.execute(email) }.to have_enqueued_job.on_queue('mailers')
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
Loading…
Reference in New Issue