Add latest changes from gitlab-org/gitlab@master
This commit is contained in:
parent
f3f9b9fe66
commit
ad3b511ba3
|
@ -98,6 +98,6 @@ export default {
|
|||
class="board-card gl-p-5 gl-rounded-base"
|
||||
@click="toggleIssue($event)"
|
||||
>
|
||||
<board-card-inner :list="list" :item="item" :update-filters="true" />
|
||||
<board-card-inner :list="list" :item="item" :update-filters="true" :index="index" />
|
||||
</li>
|
||||
</template>
|
||||
|
|
|
@ -15,6 +15,7 @@ import { updateHistory } from '~/lib/utils/url_utility';
|
|||
import { sprintf, __, n__ } from '~/locale';
|
||||
import TooltipOnTruncate from '~/vue_shared/components/tooltip_on_truncate/tooltip_on_truncate.vue';
|
||||
import UserAvatarLink from '~/vue_shared/components/user_avatar/user_avatar_link.vue';
|
||||
import BoardCardMoveToPosition from '~/boards/components/board_card_move_to_position.vue';
|
||||
import { ListType } from '../constants';
|
||||
import eventHub from '../eventhub';
|
||||
import BoardBlockedIcon from './board_blocked_icon.vue';
|
||||
|
@ -34,6 +35,7 @@ export default {
|
|||
IssueCardWeight: () => import('ee_component/boards/components/issue_card_weight.vue'),
|
||||
BoardBlockedIcon,
|
||||
GlSprintf,
|
||||
BoardCardMoveToPosition,
|
||||
},
|
||||
directives: {
|
||||
GlTooltip: GlTooltipDirective,
|
||||
|
@ -55,6 +57,10 @@ export default {
|
|||
required: false,
|
||||
default: false,
|
||||
},
|
||||
index: {
|
||||
type: Number,
|
||||
required: true,
|
||||
},
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
|
@ -202,7 +208,7 @@ export default {
|
|||
<template>
|
||||
<div>
|
||||
<div class="gl-display-flex" dir="auto">
|
||||
<h4 class="board-card-title gl-mb-0 gl-mt-0">
|
||||
<h4 class="board-card-title gl-mb-0 gl-mt-0 gl-mr-3">
|
||||
<board-blocked-icon
|
||||
v-if="item.blocked"
|
||||
:item="item"
|
||||
|
@ -235,6 +241,7 @@ export default {
|
|||
>{{ item.title }}</a
|
||||
>
|
||||
</h4>
|
||||
<board-card-move-to-position :item="item" :list="list" :index="index" />
|
||||
</div>
|
||||
<div v-if="showLabelFooter" class="board-card-labels gl-mt-2 gl-display-flex gl-flex-wrap">
|
||||
<template v-for="label in orderedLabels">
|
||||
|
|
|
@ -0,0 +1,140 @@
|
|||
<script>
|
||||
import { GlDropdown, GlDropdownItem } from '@gitlab/ui';
|
||||
import { mapActions, mapGetters } from 'vuex';
|
||||
import { s__ } from '~/locale';
|
||||
import { DEFAULT_BOARD_LIST_ITEMS_SIZE } from 'ee_else_ce/boards/constants';
|
||||
|
||||
import Tracking from '~/tracking';
|
||||
|
||||
export default {
|
||||
i18n: {
|
||||
moveToStartText: s__('Boards|Move to start of list'),
|
||||
moveToEndText: s__('Boards|Move to end of list'),
|
||||
},
|
||||
name: 'BoardCardMoveToPosition',
|
||||
components: {
|
||||
GlDropdown,
|
||||
GlDropdownItem,
|
||||
},
|
||||
mixins: [Tracking.mixin()],
|
||||
props: {
|
||||
item: {
|
||||
type: Object,
|
||||
required: true,
|
||||
validator: (item) => ['id', 'iid', 'referencePath'].every((key) => item[key]),
|
||||
},
|
||||
list: {
|
||||
type: Object,
|
||||
required: false,
|
||||
default: () => ({}),
|
||||
},
|
||||
index: {
|
||||
type: Number,
|
||||
required: true,
|
||||
},
|
||||
},
|
||||
computed: {
|
||||
...mapGetters(['getBoardItemsByList']),
|
||||
tracking() {
|
||||
return {
|
||||
category: 'boards:list',
|
||||
label: 'move_to_position',
|
||||
property: `type_card`,
|
||||
};
|
||||
},
|
||||
listItems() {
|
||||
return this.getBoardItemsByList(this.list.id);
|
||||
},
|
||||
firstItemInListId() {
|
||||
return this.listItems[0]?.id;
|
||||
},
|
||||
lengthOfListItemsInBoard() {
|
||||
return this.listItems?.length;
|
||||
},
|
||||
lastItemInTheListId() {
|
||||
return this.listItems[this.lengthOfListItemsInBoard - 1]?.id;
|
||||
},
|
||||
itemIdentifier() {
|
||||
return `${this.item.id}-${this.item.iid}-${this.index}`;
|
||||
},
|
||||
showMoveToEndOfList() {
|
||||
return this.lengthOfListItemsInBoard <= DEFAULT_BOARD_LIST_ITEMS_SIZE;
|
||||
},
|
||||
isFirstItemInList() {
|
||||
return this.index === 0;
|
||||
},
|
||||
isLastItemInList() {
|
||||
return this.index === this.lengthOfListItemsInBoard - 1;
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
...mapActions(['moveItem']),
|
||||
moveToStart() {
|
||||
this.track('click_toggle_button', {
|
||||
label: 'move_to_start',
|
||||
});
|
||||
/** in case it is the first in the list don't call any action/mutation * */
|
||||
if (this.isFirstItemInList) {
|
||||
return;
|
||||
}
|
||||
const moveAfterId = this.firstItemInListId;
|
||||
this.moveToPosition({
|
||||
moveAfterId,
|
||||
});
|
||||
},
|
||||
moveToEnd() {
|
||||
this.track('click_toggle_button', {
|
||||
label: 'move_to_end',
|
||||
});
|
||||
/** in case it is the last in the list don't call any action/mutation * */
|
||||
if (this.isLastItemInList) {
|
||||
return;
|
||||
}
|
||||
const moveBeforeId = this.lastItemInTheListId;
|
||||
this.moveToPosition({
|
||||
moveBeforeId,
|
||||
});
|
||||
},
|
||||
moveToPosition({ moveAfterId, moveBeforeId }) {
|
||||
this.moveItem({
|
||||
itemId: this.item.id,
|
||||
itemIid: this.item.iid,
|
||||
itemPath: this.item.referencePath,
|
||||
fromListId: this.list.id,
|
||||
toListId: this.list.id,
|
||||
moveAfterId,
|
||||
moveBeforeId,
|
||||
});
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<gl-dropdown
|
||||
ref="dropdown"
|
||||
:key="itemIdentifier"
|
||||
data-testid="move-card-dropdown"
|
||||
icon="ellipsis_v"
|
||||
:text="s__('Boards|Move card')"
|
||||
:text-sr-only="true"
|
||||
class="move-to-position gl-display-block gl-mb-2 gl-ml-2 gl-mt-n3 gl-mr-n3"
|
||||
category="tertiary"
|
||||
:tabindex="index"
|
||||
no-caret
|
||||
@keydown.esc.native="$emit('hide')"
|
||||
>
|
||||
<div>
|
||||
<gl-dropdown-item data-testid="action-move-to-first" @click.stop="moveToStart">
|
||||
{{ $options.i18n.moveToStartText }}
|
||||
</gl-dropdown-item>
|
||||
<gl-dropdown-item
|
||||
v-if="showMoveToEndOfList"
|
||||
data-testid="action-move-to-end"
|
||||
@click.stop="moveToEnd"
|
||||
>
|
||||
{{ $options.i18n.moveToEndText }}
|
||||
</gl-dropdown-item>
|
||||
</div>
|
||||
</gl-dropdown>
|
||||
</template>
|
|
@ -146,3 +146,5 @@ export default {
|
|||
BoardType,
|
||||
ListType,
|
||||
};
|
||||
|
||||
export const DEFAULT_BOARD_LIST_ITEMS_SIZE = 10;
|
||||
|
|
|
@ -15,6 +15,7 @@ import {
|
|||
FilterFields,
|
||||
ListTypeTitles,
|
||||
DraggableItemTypes,
|
||||
DEFAULT_BOARD_LIST_ITEMS_SIZE,
|
||||
} from 'ee_else_ce/boards/constants';
|
||||
import {
|
||||
formatIssueInput,
|
||||
|
@ -429,7 +430,7 @@ export default {
|
|||
filters: filterParams,
|
||||
isGroup: boardType === BoardType.group,
|
||||
isProject: boardType === BoardType.project,
|
||||
first: 10,
|
||||
first: DEFAULT_BOARD_LIST_ITEMS_SIZE,
|
||||
after: fetchNext ? state.pageInfoByListId[listId].endCursor : undefined,
|
||||
};
|
||||
|
||||
|
|
|
@ -207,6 +207,14 @@
|
|||
background-color: var(--blue-50, $blue-50);
|
||||
}
|
||||
|
||||
.move-to-position {
|
||||
visibility: hidden;
|
||||
}
|
||||
|
||||
&:hover .move-to-position {
|
||||
visibility: visible;
|
||||
}
|
||||
|
||||
&.multi-select {
|
||||
border-color: var(--blue-200, $blue-200);
|
||||
background-color: var(--blue-50, $blue-50);
|
||||
|
@ -234,11 +242,18 @@
|
|||
@include media-breakpoint-down(md) {
|
||||
padding: $gl-padding-8;
|
||||
}
|
||||
|
||||
@include media-breakpoint-down(sm) {
|
||||
.move-to-position {
|
||||
visibility: visible;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.board-card-title {
|
||||
@include overflow-break-word();
|
||||
font-size: 1em;
|
||||
width: 95%;
|
||||
|
||||
a {
|
||||
color: var(--gray-900, $gray-900);
|
||||
|
|
|
@ -28,6 +28,10 @@ module Mutations
|
|||
description: 'Location of the emoji file.'
|
||||
|
||||
def resolve(group_path:, **args)
|
||||
if Feature.disabled?(:custom_emoji)
|
||||
raise Gitlab::Graphql::Errors::ResourceNotAvailable, 'Custom emoji feature is disabled'
|
||||
end
|
||||
|
||||
group = authorized_find!(group_path: group_path)
|
||||
# See https://gitlab.com/gitlab-org/gitlab/-/merge_requests/37911#note_444682238
|
||||
args[:external] = true
|
||||
|
|
|
@ -17,6 +17,10 @@ module Mutations
|
|||
description: 'Global ID of the custom emoji to destroy.'
|
||||
|
||||
def resolve(id:)
|
||||
if Feature.disabled?(:custom_emoji)
|
||||
raise Gitlab::Graphql::Errors::ResourceNotAvailable, 'Custom emoji feature is disabled'
|
||||
end
|
||||
|
||||
custom_emoji = authorized_find!(id: id)
|
||||
|
||||
custom_emoji.destroy!
|
||||
|
|
|
@ -22,7 +22,7 @@ module Types
|
|||
type: Types::CustomEmojiType.connection_type,
|
||||
null: true,
|
||||
description: 'Custom emoji within this namespace.',
|
||||
_deprecated_feature_flag: :custom_emoji
|
||||
alpha: { milestone: '13.6' }
|
||||
|
||||
field :share_with_group_lock,
|
||||
type: GraphQL::Types::Boolean,
|
||||
|
@ -278,6 +278,10 @@ module Types
|
|||
group.dependency_proxy_setting || group.create_dependency_proxy_setting
|
||||
end
|
||||
|
||||
def custom_emoji
|
||||
object.custom_emoji if Feature.enabled?(:custom_emoji)
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def group
|
||||
|
|
|
@ -37,8 +37,8 @@ module Types
|
|||
mount_mutation Mutations::Clusters::AgentTokens::Create
|
||||
mount_mutation Mutations::Clusters::AgentTokens::Revoke
|
||||
mount_mutation Mutations::Commits::Create, calls_gitaly: true
|
||||
mount_mutation Mutations::CustomEmoji::Create, _deprecated_feature_flag: :custom_emoji
|
||||
mount_mutation Mutations::CustomEmoji::Destroy, _deprecated_feature_flag: :custom_emoji
|
||||
mount_mutation Mutations::CustomEmoji::Create, alpha: { milestone: '13.6' }
|
||||
mount_mutation Mutations::CustomEmoji::Destroy, alpha: { milestone: '13.6' }
|
||||
mount_mutation Mutations::CustomerRelations::Contacts::Create
|
||||
mount_mutation Mutations::CustomerRelations::Contacts::Update
|
||||
mount_mutation Mutations::CustomerRelations::Organizations::Create
|
||||
|
|
|
@ -1409,7 +1409,9 @@ Input type: `CreateComplianceFrameworkInput`
|
|||
|
||||
### `Mutation.createCustomEmoji`
|
||||
|
||||
Available only when feature flag `custom_emoji` is enabled. This flag is disabled by default, because the feature is experimental and is subject to change without notice.
|
||||
WARNING:
|
||||
**Introduced** in 13.6.
|
||||
This feature is in Alpha. It can be changed or removed at any time.
|
||||
|
||||
Input type: `CreateCustomEmojiInput`
|
||||
|
||||
|
@ -2271,7 +2273,9 @@ Input type: `DestroyContainerRepositoryTagsInput`
|
|||
|
||||
### `Mutation.destroyCustomEmoji`
|
||||
|
||||
Available only when feature flag `custom_emoji` is enabled. This flag is disabled by default, because the feature is experimental and is subject to change without notice.
|
||||
WARNING:
|
||||
**Introduced** in 13.6.
|
||||
This feature is in Alpha. It can be changed or removed at any time.
|
||||
|
||||
Input type: `DestroyCustomEmojiInput`
|
||||
|
||||
|
@ -12144,7 +12148,7 @@ four standard [pagination arguments](#connection-pagination-arguments):
|
|||
| <a id="groupcontainerrepositoriescount"></a>`containerRepositoriesCount` | [`Int!`](#int) | Number of container repositories in the group. |
|
||||
| <a id="groupcontainslockedprojects"></a>`containsLockedProjects` | [`Boolean!`](#boolean) | Includes at least one project where the repository size exceeds the limit. |
|
||||
| <a id="groupcrossprojectpipelineavailable"></a>`crossProjectPipelineAvailable` | [`Boolean!`](#boolean) | Indicates if the cross_project_pipeline feature is available for the namespace. |
|
||||
| <a id="groupcustomemoji"></a>`customEmoji` | [`CustomEmojiConnection`](#customemojiconnection) | Custom emoji within this namespace. Available only when feature flag `custom_emoji` is enabled. This flag is disabled by default, because the feature is experimental and is subject to change without notice. (see [Connections](#connections)) |
|
||||
| <a id="groupcustomemoji"></a>`customEmoji` **{warning-solid}** | [`CustomEmojiConnection`](#customemojiconnection) | **Introduced** in 13.6. This feature is in Alpha. It can be changed or removed at any time. Custom emoji within this namespace. |
|
||||
| <a id="groupdependencyproxyblobcount"></a>`dependencyProxyBlobCount` | [`Int!`](#int) | Number of dependency proxy blobs cached in the group. |
|
||||
| <a id="groupdependencyproxyblobs"></a>`dependencyProxyBlobs` | [`DependencyProxyBlobConnection`](#dependencyproxyblobconnection) | Dependency Proxy blobs. (see [Connections](#connections)) |
|
||||
| <a id="groupdependencyproxyimagecount"></a>`dependencyProxyImageCount` | [`Int!`](#int) | Number of dependency proxy images cached in the group. |
|
||||
|
|
|
@ -497,6 +497,11 @@ Feedback is welcome on our vision for [unifying the user experience for these tw
|
|||
-->
|
||||
### Secure job failing with exit code 1
|
||||
|
||||
WARNING:
|
||||
Debug logging can be a serious security risk. The output may contain the content of
|
||||
environment variables and other secrets available to the job. The output is uploaded
|
||||
to the GitLab server and visible in job logs.
|
||||
|
||||
If a Secure job is failing and it's unclear why, add `SECURE_LOG_LEVEL: "debug"` as a global CI/CD variable for
|
||||
more verbose output that is helpful for troubleshooting.
|
||||
|
||||
|
@ -534,6 +539,11 @@ Select **new pipeline** to run a new pipeline.
|
|||
|
||||
### Getting warning messages `… report.json: no matching files`
|
||||
|
||||
WARNING:
|
||||
Debug logging can be a serious security risk. The output may contain the content of
|
||||
environment variables and other secrets available to the job. The output is uploaded
|
||||
to the GitLab server and visible in job logs.
|
||||
|
||||
This message is often followed by the [error `No files to upload`](../../ci/pipelines/job_artifacts.md#error-message-no-files-to-upload),
|
||||
and preceded by other errors or warnings that indicate why the JSON report wasn't generated. Check
|
||||
the entire job log for such messages. If you don't find these messages, retry the failed job after
|
||||
|
|
|
@ -419,6 +419,9 @@ as both have a different home directory:
|
|||
|
||||
You can either copy over the `.ssh/` directory to use the same key, or generate a key in each environment.
|
||||
|
||||
If you're running Windows 11 and using [OpenSSH for Windows](https://docs.microsoft.com/en-us/windows-server/administration/openssh/openssh_overview), ensure the `HOME`
|
||||
environment variable is set correctly. Otherwise, your private SSH key might not be found.
|
||||
|
||||
Alternative tools include:
|
||||
|
||||
- [Cygwin](https://www.cygwin.com)
|
||||
|
@ -468,6 +471,7 @@ This indicates that something is wrong with your SSH setup.
|
|||
- Try to manually register your private SSH key by using `ssh-agent`.
|
||||
- Try to debug the connection by running `ssh -Tv git@example.com`.
|
||||
Replace `example.com` with your GitLab URL.
|
||||
- Ensure you followed all the instructions in [Use SSH on Microsoft Windows](#use-ssh-on-microsoft-windows).
|
||||
|
||||
### `Could not resolve hostname` error
|
||||
|
||||
|
|
|
@ -6581,6 +6581,15 @@ msgstr ""
|
|||
msgid "Boards|Failed to fetch blocking %{issuableType}s"
|
||||
msgstr ""
|
||||
|
||||
msgid "Boards|Move card"
|
||||
msgstr ""
|
||||
|
||||
msgid "Boards|Move to end of list"
|
||||
msgstr ""
|
||||
|
||||
msgid "Boards|Move to start of list"
|
||||
msgstr ""
|
||||
|
||||
msgid "Boards|New board"
|
||||
msgstr ""
|
||||
|
||||
|
|
|
@ -7,6 +7,7 @@ import { createMockDirective, getBinding } from 'helpers/vue_mock_directive';
|
|||
import { mountExtended } from 'helpers/vue_test_utils_helper';
|
||||
import BoardBlockedIcon from '~/boards/components/board_blocked_icon.vue';
|
||||
import BoardCardInner from '~/boards/components/board_card_inner.vue';
|
||||
import BoardCardMoveToPosition from '~/boards/components/board_card_move_to_position.vue';
|
||||
import { issuableTypes } from '~/boards/constants';
|
||||
import eventHub from '~/boards/eventhub';
|
||||
import defaultStore from '~/boards/stores';
|
||||
|
@ -47,6 +48,7 @@ describe('Board card component', () => {
|
|||
const findEpicCountablesTotalWeight = () => wrapper.findByTestId('epic-countables-total-weight');
|
||||
const findEpicProgressTooltip = () => wrapper.findByTestId('epic-progress-tooltip-content');
|
||||
const findHiddenIssueIcon = () => wrapper.findByTestId('hidden-icon');
|
||||
const findMoveToPositionComponent = () => wrapper.findComponent(BoardCardMoveToPosition);
|
||||
|
||||
const performSearchMock = jest.fn();
|
||||
|
||||
|
@ -75,10 +77,12 @@ describe('Board card component', () => {
|
|||
propsData: {
|
||||
list,
|
||||
item: issue,
|
||||
index: 0,
|
||||
...props,
|
||||
},
|
||||
stubs: {
|
||||
GlLoadingIcon: true,
|
||||
BoardCardMoveToPosition: true,
|
||||
},
|
||||
directives: {
|
||||
GlTooltip: createMockDirective(),
|
||||
|
@ -137,6 +141,10 @@ describe('Board card component', () => {
|
|||
expect(findHiddenIssueIcon().exists()).toBe(false);
|
||||
});
|
||||
|
||||
it('renders the move to position icon', () => {
|
||||
expect(findMoveToPositionComponent().exists()).toBe(true);
|
||||
});
|
||||
|
||||
it('renders issue ID with #', () => {
|
||||
expect(wrapper.find('.board-card-number').text()).toContain(`#${issue.iid}`);
|
||||
});
|
||||
|
|
|
@ -75,6 +75,7 @@ export default function createComponent({
|
|||
id: 1,
|
||||
iid: 1,
|
||||
confidential: false,
|
||||
referencePath: 'gitlab-org/test-subgroup/gitlab-test#1',
|
||||
labels: [],
|
||||
assignees: [],
|
||||
...listIssueProps,
|
||||
|
|
|
@ -0,0 +1,117 @@
|
|||
import Vue, { nextTick } from 'vue';
|
||||
import Vuex from 'vuex';
|
||||
import { GlDropdown, GlDropdownItem } from '@gitlab/ui';
|
||||
|
||||
import { shallowMountExtended } from 'helpers/vue_test_utils_helper';
|
||||
import BoardCardMoveToPosition from '~/boards/components/board_card_move_to_position.vue';
|
||||
import { createStore } from '~/boards/stores';
|
||||
import { mockList, mockIssue2 } from 'jest/boards/mock_data';
|
||||
import { mockTracking, unmockTracking } from 'helpers/tracking_helper';
|
||||
|
||||
Vue.use(Vuex);
|
||||
|
||||
const dropdownOptions = [
|
||||
BoardCardMoveToPosition.i18n.moveToStartText,
|
||||
BoardCardMoveToPosition.i18n.moveToEndText,
|
||||
];
|
||||
|
||||
describe('Board Card Move to position', () => {
|
||||
let wrapper;
|
||||
let trackingSpy;
|
||||
let store;
|
||||
let dispatch;
|
||||
|
||||
store = new Vuex.Store();
|
||||
|
||||
const createComponent = (propsData) => {
|
||||
wrapper = shallowMountExtended(BoardCardMoveToPosition, {
|
||||
store,
|
||||
propsData: {
|
||||
item: mockIssue2,
|
||||
list: mockList,
|
||||
index: 0,
|
||||
...propsData,
|
||||
},
|
||||
stubs: {
|
||||
GlDropdown,
|
||||
GlDropdownItem,
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
beforeEach(() => {
|
||||
store = createStore();
|
||||
createComponent();
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
wrapper.destroy();
|
||||
});
|
||||
|
||||
const findEllipsesButton = () => wrapper.findByTestId('move-card-dropdown');
|
||||
const findMoveToPositionDropdown = () => wrapper.findComponent(GlDropdown);
|
||||
const findDropdownItems = () => findMoveToPositionDropdown().findAllComponents(GlDropdownItem);
|
||||
const findDropdownItemAtIndex = (index) => findDropdownItems().at(index);
|
||||
|
||||
describe('Dropdown', () => {
|
||||
describe('Dropdown button', () => {
|
||||
it('has an icon with vertical ellipsis', () => {
|
||||
expect(findEllipsesButton().exists()).toBe(true);
|
||||
expect(findMoveToPositionDropdown().props('icon')).toBe('ellipsis_v');
|
||||
});
|
||||
|
||||
it('is opened on the click of vertical ellipsis and has 2 dropdown items when number of list items < 10', () => {
|
||||
findMoveToPositionDropdown().vm.$emit('click');
|
||||
|
||||
expect(findDropdownItems()).toHaveLength(dropdownOptions.length);
|
||||
});
|
||||
});
|
||||
|
||||
describe('Dropdown options', () => {
|
||||
beforeEach(() => {
|
||||
trackingSpy = mockTracking(undefined, wrapper.element, jest.spyOn);
|
||||
dispatch = jest.spyOn(store, 'dispatch').mockImplementation(() => {});
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
unmockTracking();
|
||||
});
|
||||
|
||||
it.each`
|
||||
dropdownIndex | dropdownLabel | startActionCalledTimes | trackLabel
|
||||
${0} | ${BoardCardMoveToPosition.i18n.moveToStartText} | ${0} | ${'move_to_start'}
|
||||
${1} | ${BoardCardMoveToPosition.i18n.moveToEndText} | ${1} | ${'move_to_end'}
|
||||
`(
|
||||
'on click of dropdown index $dropdownIndex with label $dropdownLabel should call moveItem action with tracking label $trackLabel',
|
||||
async ({ dropdownIndex, startActionCalledTimes, dropdownLabel, trackLabel }) => {
|
||||
await findEllipsesButton().vm.$emit('click');
|
||||
|
||||
expect(findDropdownItemAtIndex(dropdownIndex).text()).toBe(dropdownLabel);
|
||||
await findDropdownItemAtIndex(dropdownIndex).vm.$emit('click', {
|
||||
stopPropagation: () => {},
|
||||
});
|
||||
|
||||
await nextTick();
|
||||
|
||||
expect(trackingSpy).toHaveBeenCalledWith('boards:list', 'click_toggle_button', {
|
||||
category: 'boards:list',
|
||||
label: trackLabel,
|
||||
property: 'type_card',
|
||||
});
|
||||
expect(dispatch).toHaveBeenCalledTimes(startActionCalledTimes);
|
||||
if (startActionCalledTimes) {
|
||||
expect(dispatch).toHaveBeenCalledWith('moveItem', {
|
||||
fromListId: mockList.id,
|
||||
itemId: mockIssue2.id,
|
||||
itemIid: mockIssue2.iid,
|
||||
itemPath: mockIssue2.referencePath,
|
||||
moveBeforeId: undefined,
|
||||
moveAfterId: undefined,
|
||||
toListId: mockList.id,
|
||||
});
|
||||
}
|
||||
},
|
||||
);
|
||||
});
|
||||
});
|
||||
});
|
|
@ -1,5 +1,5 @@
|
|||
import { GlLabel } from '@gitlab/ui';
|
||||
import { shallowMount, mount } from '@vue/test-utils';
|
||||
import { shallowMount } from '@vue/test-utils';
|
||||
import Vue, { nextTick } from 'vue';
|
||||
import Vuex from 'vuex';
|
||||
|
||||
|
@ -45,7 +45,10 @@ describe('Board card', () => {
|
|||
item = mockIssue,
|
||||
} = {}) => {
|
||||
wrapper = mountFn(BoardCard, {
|
||||
stubs,
|
||||
stubs: {
|
||||
...stubs,
|
||||
BoardCardInner,
|
||||
},
|
||||
store,
|
||||
propsData: {
|
||||
list: mockLabelList,
|
||||
|
@ -86,7 +89,7 @@ describe('Board card', () => {
|
|||
describe('when GlLabel is clicked in BoardCardInner', () => {
|
||||
it('doesnt call toggleBoardItem', () => {
|
||||
createStore({ initialState: { isShowingLabels: true } });
|
||||
mountComponent({ mountFn: mount, stubs: {} });
|
||||
mountComponent();
|
||||
|
||||
wrapper.findComponent(GlLabel).trigger('mouseup');
|
||||
|
||||
|
|
|
@ -35,7 +35,17 @@ RSpec.describe 'getting custom emoji within namespace' do
|
|||
expect(graphql_data['group']['customEmoji']['nodes'].first['name']).to eq(custom_emoji.name)
|
||||
end
|
||||
|
||||
it 'returns nil when unauthorised' do
|
||||
it 'returns nil custom emoji when the custom_emoji feature flag is disabled' do
|
||||
stub_feature_flags(custom_emoji: false)
|
||||
|
||||
post_graphql(custom_emoji_query(group), current_user: current_user)
|
||||
|
||||
expect(response).to have_gitlab_http_status(:ok)
|
||||
expect(graphql_data['group']).to be_present
|
||||
expect(graphql_data['group']['customEmoji']).to be_nil
|
||||
end
|
||||
|
||||
it 'returns nil group when unauthorised' do
|
||||
user = create(:user)
|
||||
post_graphql(custom_emoji_query(group), current_user: user)
|
||||
|
||||
|
|
|
@ -39,5 +39,19 @@ RSpec.describe 'Creation of a new Custom Emoji' do
|
|||
expect(gql_response['customEmoji']['name']).to eq(attributes[:name])
|
||||
expect(gql_response['customEmoji']['url']).to eq(attributes[:url])
|
||||
end
|
||||
|
||||
context 'when the custom_emoji feature flag is disabled' do
|
||||
before do
|
||||
stub_feature_flags(custom_emoji: false)
|
||||
end
|
||||
|
||||
it 'does nothing and returns and error' do
|
||||
expect do
|
||||
post_graphql_mutation(mutation, current_user: current_user)
|
||||
end.to not_change(CustomEmoji, :count)
|
||||
|
||||
expect_graphql_errors_to_include('Custom emoji feature is disabled')
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -68,6 +68,20 @@ RSpec.describe 'Deletion of custom emoji' do
|
|||
end
|
||||
|
||||
it_behaves_like 'deletes custom emoji'
|
||||
|
||||
context 'when the custom_emoji feature flag is disabled' do
|
||||
before do
|
||||
stub_feature_flags(custom_emoji: false)
|
||||
end
|
||||
|
||||
it_behaves_like 'does not delete custom emoji'
|
||||
|
||||
it 'returns an error' do
|
||||
post_graphql_mutation(mutation, current_user: current_user)
|
||||
|
||||
expect_graphql_errors_to_include('Custom emoji feature is disabled')
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
Loading…
Reference in New Issue