Commit c1e09d30 authored by Aleksei Lipniagov's avatar Aleksei Lipniagov

Merge branch '292804-delete-tributejs' into 'master'

Delete tribute_autocomplete feature flag

See merge request gitlab-org/gitlab!78564
parents 3a2b1276 b2115644
<script>
import markdownField from '~/vue_shared/components/markdown/field.vue';
import glFeatureFlagsMixin from '~/vue_shared/mixins/gl_feature_flags_mixin';
import updateMixin from '../../mixins/update';
export default {
components: {
markdownField,
},
mixins: [glFeatureFlagsMixin(), updateMixin],
mixins: [updateMixin],
props: {
formState: {
type: Object,
......@@ -56,7 +55,7 @@ export default {
v-model="formState.description"
class="note-textarea js-gfm-input js-autosize markdown-area qa-description-textarea"
dir="auto"
:data-supports-quick-actions="!glFeatures.tributeAutocomplete"
data-supports-quick-actions="true"
:aria-label="__('Description')"
:placeholder="__('Write a comment or drag your files here…')"
@keydown.meta.enter="updateIssuable"
......
......@@ -369,7 +369,7 @@ export default {
class="note-textarea js-vue-comment-form js-note-text js-gfm-input js-autosize markdown-area"
data-qa-selector="comment_field"
data-testid="comment-field"
:data-supports-quick-actions="!glFeatures.tributeAutocomplete"
data-supports-quick-actions="true"
:aria-label="$options.i18n.comment"
:placeholder="$options.i18n.bodyPlaceholder"
@keydown.up="editCurrentUserLastNote()"
......
......@@ -5,7 +5,6 @@ import { getDraft, updateDraft } from '~/lib/utils/autosave';
import { mergeUrlParams } from '~/lib/utils/url_utility';
import { __ } from '~/locale';
import markdownField from '~/vue_shared/components/markdown/field.vue';
import glFeatureFlagsMixin from '~/vue_shared/mixins/gl_feature_flags_mixin';
import eventHub from '../event_hub';
import issuableStateMixin from '../mixins/issuable_state';
import resolvable from '../mixins/resolvable';
......@@ -20,7 +19,7 @@ export default {
GlSprintf,
GlLink,
},
mixins: [glFeatureFlagsMixin(), issuableStateMixin, resolvable],
mixins: [issuableStateMixin, resolvable],
props: {
noteBody: {
type: String,
......@@ -349,7 +348,7 @@ export default {
ref="textarea"
v-model="updatedNoteBody"
:disabled="isSubmitting"
:data-supports-quick-actions="!isEditing && !glFeatures.tributeAutocomplete"
:data-supports-quick-actions="!isEditing"
name="note[note]"
class="note-textarea js-gfm-input js-note-text js-autosize markdown-area js-vue-issue-note-form"
data-qa-selector="reply_field"
......
<script>
import Tribute from '@gitlab/tributejs';
import {
GfmAutocompleteType,
tributeConfig,
} from 'ee_else_ce/vue_shared/components/gfm_autocomplete/utils';
import * as Emoji from '~/emoji';
import createFlash from '~/flash';
import axios from '~/lib/utils/axios_utils';
import { __ } from '~/locale';
import SidebarMediator from '~/sidebar/sidebar_mediator';
export default {
errorMessage: __(
'An error occurred while getting autocomplete data. Please refresh the page and try again.',
),
props: {
autocompleteTypes: {
type: Array,
required: false,
default: () => Object.values(GfmAutocompleteType),
},
dataSources: {
type: Object,
required: false,
default: () => gl.GfmAutoComplete?.dataSources || {},
},
},
computed: {
config() {
return this.autocompleteTypes.map((type) => ({
...tributeConfig[type].config,
loadingItemTemplate: `<span class="gl-spinner gl-vertical-align-text-bottom gl-ml-3 gl-mr-2"></span>${__(
'Loading',
)}`,
requireLeadingSpace: true,
values: this.getValues(type),
}));
},
},
mounted() {
this.cache = {};
this.tribute = new Tribute({ collection: this.config });
const input = this.$slots.default?.[0]?.elm;
this.tribute.attach(input);
},
beforeDestroy() {
const input = this.$slots.default?.[0]?.elm;
this.tribute.detach(input);
},
methods: {
cacheAssignees() {
const isAssigneesLengthSame =
this.assignees?.length === SidebarMediator.singleton?.store?.assignees?.length;
if (!this.assignees || !isAssigneesLengthSame) {
this.assignees =
SidebarMediator.singleton?.store?.assignees?.map((assignee) => assignee.username) || [];
}
},
filterValues(type) {
// The assignees AJAX response can come after the user first invokes autocomplete
// so we need to check more than once if we need to update the assignee cache
this.cacheAssignees();
return tributeConfig[type].filterValues
? tributeConfig[type].filterValues({
assignees: this.assignees,
collection: this.cache[type],
fullText: this.$slots.default?.[0]?.elm?.value,
selectionStart: this.$slots.default?.[0]?.elm?.selectionStart,
})
: this.cache[type];
},
getValues(type) {
return (inputText, processValues) => {
if (this.cache[type]) {
processValues(this.filterValues(type));
} else if (type === GfmAutocompleteType.Emojis) {
Emoji.initEmojiMap()
.then(() => {
const emojis = Emoji.getValidEmojiNames();
this.cache[type] = emojis;
processValues(emojis);
})
.catch(() => createFlash({ message: this.$options.errorMessage }));
} else if (this.dataSources[type]) {
axios
.get(this.dataSources[type])
.then((response) => {
this.cache[type] = response.data;
processValues(this.filterValues(type));
})
.catch(() => createFlash({ message: this.$options.errorMessage }));
} else {
processValues([]);
}
};
},
},
render(createElement) {
return createElement('div', this.$slots.default);
},
};
</script>
import { escape, last } from 'lodash';
import * as Emoji from '~/emoji';
import { spriteIcon } from '~/lib/utils/common_utils';
const groupType = 'Group'; // eslint-disable-line @gitlab/require-i18n-strings
// Number of users to show in the autocomplete menu to avoid doing a mass fetch of 100+ avatars
const memberLimit = 10;
const nonWordOrInteger = /\W|^\d+$/;
export const menuItemLimit = 100;
export const GfmAutocompleteType = {
Emojis: 'emojis',
Issues: 'issues',
Labels: 'labels',
Members: 'members',
MergeRequests: 'mergeRequests',
Milestones: 'milestones',
QuickActions: 'commands',
Snippets: 'snippets',
};
function doesCurrentLineStartWith(searchString, fullText, selectionStart) {
const currentLineNumber = fullText.slice(0, selectionStart).split('\n').length;
const currentLine = fullText.split('\n')[currentLineNumber - 1];
return currentLine.startsWith(searchString);
}
export const tributeConfig = {
[GfmAutocompleteType.Emojis]: {
config: {
trigger: ':',
lookup: (value) => value,
menuItemLimit,
menuItemTemplate: ({ original }) => `${original} ${Emoji.glEmojiTag(original)}`,
selectTemplate: ({ original }) => `:${original}:`,
},
},
[GfmAutocompleteType.Issues]: {
config: {
trigger: '#',
lookup: (value) => `${value.iid}${value.title}`,
menuItemLimit,
menuItemTemplate: ({ original }) =>
`<small>${original.reference || original.iid}</small> ${escape(original.title)}`,
selectTemplate: ({ original }) => original.reference || `#${original.iid}`,
},
},
[GfmAutocompleteType.Labels]: {
config: {
trigger: '~',
lookup: 'title',
menuItemLimit,
menuItemTemplate: ({ original }) => `
<span class="dropdown-label-box" style="background: ${escape(original.color)};"></span>
${escape(original.title)}`,
selectTemplate: ({ original }) =>
nonWordOrInteger.test(original.title)
? `~"${escape(original.title)}"`
: `~${escape(original.title)}`,
},
filterValues({ collection, fullText, selectionStart }) {
if (doesCurrentLineStartWith('/label', fullText, selectionStart)) {
return collection.filter((label) => !label.set);
}
if (doesCurrentLineStartWith('/unlabel', fullText, selectionStart)) {
return collection.filter((label) => label.set);
}
return collection;
},
},
[GfmAutocompleteType.Members]: {
config: {
trigger: '@',
fillAttr: 'username',
lookup: (value) =>
value.type === groupType ? last(value.name.split(' / ')) : `${value.name}${value.username}`,
menuItemLimit: memberLimit,
menuItemTemplate: ({ original }) => {
const commonClasses = 'gl-avatar gl-avatar-s32 gl-flex-shrink-0';
const noAvatarClasses = `${commonClasses} gl-rounded-small
gl-display-flex gl-align-items-center gl-justify-content-center`;
const avatar = original.avatar_url
? `<img class="${commonClasses} gl-avatar-circle" src="${original.avatar_url}" alt="" />`
: `<div class="${noAvatarClasses}" aria-hidden="true">
${original.username.charAt(0).toUpperCase()}</div>`;
let displayName = original.name;
let parentGroupOrUsername = `@${original.username}`;
if (original.type === groupType) {
const splitName = original.name.split(' / ');
displayName = splitName.pop();
parentGroupOrUsername = splitName.pop();
}
const count = original.count && !original.mentionsDisabled ? ` (${original.count})` : '';
const disabledMentionsIcon = original.mentionsDisabled
? spriteIcon('notifications-off', 's16 gl-ml-3')
: '';
return `
<div class="gl-display-flex gl-align-items-center">
${avatar}
<div class="gl-line-height-normal gl-ml-4">
<div>${escape(displayName)}${count}</div>
<div class="gl-text-gray-700">${escape(parentGroupOrUsername)}</div>
</div>
${disabledMentionsIcon}
</div>
`;
},
},
filterValues({ assignees, collection, fullText, selectionStart }) {
if (doesCurrentLineStartWith('/assign', fullText, selectionStart)) {
return collection.filter((member) => !assignees.includes(member.username));
}
if (doesCurrentLineStartWith('/unassign', fullText, selectionStart)) {
return collection.filter((member) => assignees.includes(member.username));
}
return collection;
},
},
[GfmAutocompleteType.MergeRequests]: {
config: {
trigger: '!',
lookup: (value) => `${value.iid}${value.title}`,
menuItemLimit,
menuItemTemplate: ({ original }) =>
`<small>${original.reference || original.iid}</small> ${escape(original.title)}`,
selectTemplate: ({ original }) => original.reference || `!${original.iid}`,
},
},
[GfmAutocompleteType.Milestones]: {
config: {
trigger: '%',
lookup: 'title',
menuItemLimit,
menuItemTemplate: ({ original }) => escape(original.title),
selectTemplate: ({ original }) => `%"${escape(original.title)}"`,
},
},
[GfmAutocompleteType.QuickActions]: {
config: {
trigger: '/',
fillAttr: 'name',
lookup: (value) => `${value.name}${value.aliases.join()}`,
menuItemLimit,
menuItemTemplate: ({ original }) => {
const aliases = original.aliases.length
? `<small>(or /${original.aliases.join(', /')})</small>`
: '';
const params = original.params.length ? `<small>${original.params.join(' ')}</small>` : '';
let description = '';
if (original.warning) {
const confidentialIcon =
original.icon === 'confidential' ? spriteIcon('eye-slash', 's16 gl-mr-2') : '';
description = `<small>${confidentialIcon}<em>${original.warning}</em></small>`;
} else if (original.description) {
description = `<small><em>${original.description}</em></small>`;
}
return `<div>/${original.name} ${aliases} ${params}</div>
<div>${description}</div>`;
},
},
},
[GfmAutocompleteType.Snippets]: {
config: {
trigger: '$',
fillAttr: 'id',
lookup: (value) => `${value.id}${value.title}`,
menuItemLimit,
menuItemTemplate: ({ original }) => `<small>${original.id}</small> ${escape(original.title)}`,
},
},
};
......@@ -8,9 +8,7 @@ import GLForm from '~/gl_form';
import axios from '~/lib/utils/axios_utils';
import { stripHtml } from '~/lib/utils/text_utility';
import { __, sprintf } from '~/locale';
import GfmAutocomplete from '~/vue_shared/components/gfm_autocomplete/gfm_autocomplete.vue';
import Suggestions from '~/vue_shared/components/markdown/suggestions.vue';
import glFeatureFlagsMixin from '~/vue_shared/mixins/gl_feature_flags_mixin';
import MarkdownHeader from './header.vue';
import MarkdownToolbar from './toolbar.vue';
......@@ -20,13 +18,11 @@ function cleanUpLine(content) {
export default {
components: {
GfmAutocomplete,
MarkdownHeader,
MarkdownToolbar,
GlIcon,
Suggestions,
},
mixins: [glFeatureFlagsMixin()],
props: {
/**
* This prop should be bound to the value of the `<textarea>` element
......@@ -212,14 +208,14 @@ export default {
return new GLForm(
$(this.$refs['gl-form']),
{
emojis: this.enableAutocomplete && !this.glFeatures.tributeAutocomplete,
members: this.enableAutocomplete && !this.glFeatures.tributeAutocomplete,
issues: this.enableAutocomplete && !this.glFeatures.tributeAutocomplete,
mergeRequests: this.enableAutocomplete && !this.glFeatures.tributeAutocomplete,
epics: this.enableAutocomplete && !this.glFeatures.tributeAutocomplete,
milestones: this.enableAutocomplete && !this.glFeatures.tributeAutocomplete,
labels: this.enableAutocomplete && !this.glFeatures.tributeAutocomplete,
snippets: this.enableAutocomplete && !this.glFeatures.tributeAutocomplete,
emojis: this.enableAutocomplete,
members: this.enableAutocomplete,
issues: this.enableAutocomplete,
mergeRequests: this.enableAutocomplete,
epics: this.enableAutocomplete,
milestones: this.enableAutocomplete,
labels: this.enableAutocomplete,
snippets: this.enableAutocomplete,
vulnerabilities: this.enableAutocomplete,
},
true,
......@@ -311,10 +307,7 @@ export default {
/>
<div v-show="!previewMarkdown" class="md-write-holder">
<div class="zen-backdrop">
<gfm-autocomplete v-if="glFeatures.tributeAutocomplete">
<slot name="textarea"></slot>
</gfm-autocomplete>
<slot v-else name="textarea"></slot>
<a
class="zen-control zen-control-leave js-zen-leave gl-text-gray-500"
href="#"
......
.tribute-container {
background: $white;
border: 1px solid $gray-100;
border-radius: $border-radius-base;
box-shadow: 0 0 5px $issue-boards-card-shadow;
color: $black;
margin-top: $gl-padding-12;
max-height: 200px;
min-width: 120px;
overflow-y: auto;
z-index: 11110 !important;
ul {
list-style: none;
margin-bottom: 0;
padding: $gl-padding-8 1px;
}
li {
cursor: pointer;
padding: $gl-padding-8 $gl-padding;
white-space: nowrap;
small {
color: $gray-500;
}
&.highlight {
background-color: $gray-darker;
.avatar {
@include disable-all-animation;
border: 1px solid $white;
}
small {
color: inherit;
}
}
}
}
......@@ -42,7 +42,6 @@ class Projects::IssuesController < Projects::ApplicationController
if: -> { Feature.disabled?('rate_limited_service_issues_create', project, default_enabled: :yaml) }
before_action do
push_frontend_feature_flag(:tribute_autocomplete, @project)
push_frontend_feature_flag(:improved_emoji_picker, project, default_enabled: :yaml)
push_frontend_feature_flag(:vue_issues_list, project&.group, default_enabled: :yaml)
push_frontend_feature_flag(:iteration_cadences, project&.group, default_enabled: :yaml)
......
---
name: tribute_autocomplete
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/32671
rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/292804
milestone: '13.2'
type: development
group: group::project management
default_enabled: false
......@@ -260,8 +260,7 @@ module.exports = {
{
test: /\.js$/,
exclude: (modulePath) =>
/node_modules\/(?!tributejs)|node_modules|vendor[\\/]assets/.test(modulePath) &&
!/\.vue\.js/.test(modulePath),
/node_modules|vendor[\\/]assets/.test(modulePath) && !/\.vue\.js/.test(modulePath),
loader: 'babel-loader',
options: {
cacheDirectory: path.join(CACHE_PATH, 'babel-loader'),
......
import { escape } from 'lodash';
import {
GfmAutocompleteType as GfmAutocompleteTypeFoss,
menuItemLimit,
tributeConfig as tributeConfigFoss,
} from '~/vue_shared/components/gfm_autocomplete/utils';
export const GfmAutocompleteType = {
...GfmAutocompleteTypeFoss,
Epics: 'epics',
};
export const tributeConfig = {
...tributeConfigFoss,
[GfmAutocompleteType.Epics]: {
config: {
trigger: '&',
fillAttr: 'iid',
lookup: (value) => `${value.iid}${value.title}`,
menuItemLimit,
menuItemTemplate: ({ original }) =>
`<small>${original.iid}</small> ${escape(original.title)}`,
selectTemplate: ({ original }) => original.reference || `&${original.iid}`,
},
},
};
......@@ -13,8 +13,6 @@ RSpec.describe 'Issue promotion', :js do
let(:user) { create(:user) }
before do
stub_feature_flags(tribute_autocomplete: false)
sign_in(user)
end
......
......@@ -5,9 +5,7 @@ require 'spec_helper'
RSpec.describe 'GFM autocomplete EE', :js do
let_it_be(:user) { create(:user, name: '💃speciąl someone💃', username: 'someone.special') }
let_it_be(:another_user) { create(:user, name: 'another user', username: 'another.user') }
let_it_be(:group) { create(:group) }
let_it_be(:epic) { create(:epic, group: group) }
let_it_be(:project) { create(:project, group: group) }
let_it_be(:project) { create(:project) }
let_it_be(:issue) { create(:issue, project: project, assignees: [user]) }
before do
......@@ -15,10 +13,7 @@ RSpec.describe 'GFM autocomplete EE', :js do
end
context 'assignees' do
describe 'when tribute_autocomplete feature flag is off' do
before do
stub_feature_flags(tribute_autocomplete: false)
sign_in(user)
visit project_issue_path(project, issue)
......@@ -36,39 +31,6 @@ RSpec.describe 'GFM autocomplete EE', :js do
end
end
describe 'when tribute_autocomplete feature flag is on' do
before do
stub_licensed_features(epics: true)
stub_feature_flags(tribute_autocomplete: true)
sign_in(user)
visit project_issue_path(project, issue)
end
it 'only lists users who are currently assigned to the issue when using /unassign' do
note = find_field('Comment')
note.native.send_keys('/unassign ')
# The `/unassign` ajax response might replace the one by `@` below causing a failed test
# so we need to wait for the `/assign` ajax request to finish first
wait_for_requests
note.native.send_keys('@')
wait_for_requests
expect(find_tribute_autocomplete_menu).to have_text(user.username)
expect(find_tribute_autocomplete_menu).not_to have_text(another_user.username)
end
it 'shows epics' do
fill_in 'Comment', with: '&'
wait_for_requests
expect(find_tribute_autocomplete_menu).to have_text(epic.title)
end
end
end
private
def find_autocomplete_menu
......@@ -78,8 +40,4 @@ RSpec.describe 'GFM autocomplete EE', :js do
def find_highlighted_autocomplete_item
find('.atwho-view li.cur', visible: true)
end
def find_tribute_autocomplete_menu
find('.tribute-container ul', visible: true)
end
end
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`ee gfm_autocomplete/utils epics config shows the iid and title in the menu item 1`] = `"<small>123456</small> Epic title &lt;script&gt;alert(&#39;hi&#39;)&lt;/script&gt;"`;
import {
GfmAutocompleteType,
tributeConfig,
} from 'ee/vue_shared/components/gfm_autocomplete/utils';
describe('ee gfm_autocomplete/utils', () => {
describe('epics config', () => {
const epicsConfig = tributeConfig[GfmAutocompleteType.Epics].config;
const epic = {
id: null,
iid: 123456,
title: "Epic title <script>alert('hi')</script>",
};
const subgroupEpic = {
iid: 987654,
reference: 'gitlab&987654',
title: "Subgroup context epic title <script>alert('hi')</script>",
};
it('uses & as the trigger', () => {
expect(epicsConfig.trigger).toBe('&');
});
it('inserts the iid on autocomplete selection', () => {
expect(epicsConfig.fillAttr).toBe('iid');
});
it('searches using both the iid and title', () => {
expect(epicsConfig.lookup(epic)).toBe(`${epic.iid}${epic.title}`);
});
it('limits the number of rendered items to 100', () => {
expect(epicsConfig.menuItemLimit).toBe(100);
});
it('shows the iid and title in the menu item', () => {
expect(epicsConfig.menuItemTemplate({ original: epic })).toMatchSnapshot();
});
it('inserts the iid on autocomplete selection within a top level group context', () => {
expect(epicsConfig.selectTemplate({ original: epic })).toBe(`&${epic.iid}`);
});
it('inserts the reference on autocomplete selection within a group context', () => {
expect(epicsConfig.selectTemplate({ original: subgroupEpic })).toBe(subgroupEpic.reference);
});
});
});
......@@ -3818,9 +3818,6 @@ msgstr ""
msgid "An error occurred while fetching this tab."
msgstr ""
msgid "An error occurred while getting autocomplete data. Please refresh the page and try again."
msgstr ""
msgid "An error occurred while getting files for - %{branchId}"
msgstr ""
......
......@@ -10,7 +10,6 @@ RSpec.describe "User comments on issue", :js do
let(:user) { create(:user) }
before do
stub_feature_flags(tribute_autocomplete: false)
stub_feature_flags(sandboxed_mermaid: false)
project.add_guest(user)
sign_in(user)
......
......@@ -33,31 +33,12 @@ RSpec.describe 'Member autocomplete', :js do
let(:noteable) { create(:issue, author: author, project: project) }
before do
stub_feature_flags(tribute_autocomplete: false)
visit project_issue_path(project, noteable)
end
include_examples "open suggestions when typing @", 'issue'
end
describe 'when tribute_autocomplete feature flag is on' do
context 'adding a new note on a Issue' do
let(:noteable) { create(:issue, author: author, project: project) }
before do
stub_feature_flags(tribute_autocomplete: true)
visit project_issue_path(project, noteable)
fill_in 'Comment', with: '@'
end
it 'suggests noteable author and note author' do
expect(find_tribute_autocomplete_menu).to have_content(author.username)
expect(find_tribute_autocomplete_menu).to have_content(note.author.username)
end
end
end
context 'adding a new note on a Merge Request' do
let(:noteable) do
create(:merge_request, source_project: project,
......@@ -91,8 +72,4 @@ RSpec.describe 'Member autocomplete', :js do
def find_autocomplete_menu
find('.atwho-view ul', visible: true)
end
def find_tribute_autocomplete_menu
find('.tribute-container ul', visible: true)
end
end
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`gfm_autocomplete/utils emojis config shows the emoji name and icon in the menu item 1`] = `"raised_hands <gl-emoji data-name=\\"raised_hands\\"></gl-emoji>"`;
exports[`gfm_autocomplete/utils issues config shows the iid and title in the menu item within a project context 1`] = `"<small>123456</small> Project context issue title &lt;script&gt;alert(&#39;hi&#39;)&lt;/script&gt;"`;
exports[`gfm_autocomplete/utils issues config shows the reference and title in the menu item within a group context 1`] = `"<small>gitlab#987654</small> Group context issue title &lt;script&gt;alert(&#39;hi&#39;)&lt;/script&gt;"`;
exports[`gfm_autocomplete/utils labels config shows the title in the menu item 1`] = `
"
<span class=\\"dropdown-label-box\\" style=\\"background: #123456;\\"></span>
bug &lt;script&gt;alert(&#39;hi&#39;)&lt;/script&gt;"
`;
exports[`gfm_autocomplete/utils members config shows an avatar character, name, parent name, and count in the menu item for a group 1`] = `
"
<div class=\\"gl-display-flex gl-align-items-center\\">
<div class=\\"gl-avatar gl-avatar-s32 gl-flex-shrink-0 gl-rounded-small
gl-display-flex gl-align-items-center gl-justify-content-center\\" aria-hidden=\\"true\\">
G</div>
<div class=\\"gl-line-height-normal gl-ml-4\\">
<div>1-1s &lt;script&gt;alert(&#39;hi&#39;)&lt;/script&gt; (2)</div>
<div class=\\"gl-text-gray-700\\">GitLab Support Team</div>
</div>
</div>
"
`;
exports[`gfm_autocomplete/utils members config shows the avatar, name and username in the menu item for a user 1`] = `
"
<div class=\\"gl-display-flex gl-align-items-center\\">
<img class=\\"gl-avatar gl-avatar-s32 gl-flex-shrink-0 gl-avatar-circle\\" src=\\"/uploads/-/system/user/avatar/123456/avatar.png\\" alt=\\"\\" />
<div class=\\"gl-line-height-normal gl-ml-4\\">
<div>My Name &lt;script&gt;alert(&#39;hi&#39;)&lt;/script&gt;</div>
<div class=\\"gl-text-gray-700\\">@myusername</div>
</div>
</div>
"
`;
exports[`gfm_autocomplete/utils merge requests config shows the iid and title in the menu item within a project context 1`] = `"<small>123456</small> Project context merge request title &lt;script&gt;alert(&#39;hi&#39;)&lt;/script&gt;"`;
exports[`gfm_autocomplete/utils merge requests config shows the reference and title in the menu item within a group context 1`] = `"<small>gitlab!456789</small> Group context merge request title &lt;script&gt;alert(&#39;hi&#39;)&lt;/script&gt;"`;
exports[`gfm_autocomplete/utils milestones config shows the title in the menu item 1`] = `"13.2 &lt;script&gt;alert(&#39;hi&#39;)&lt;/script&gt;"`;
exports[`gfm_autocomplete/utils quick actions config shows the name, aliases, params and description in the menu item 1`] = `
"<div>/unlabel <small>(or /remove_label)</small> <small>~label1 ~\\"label 2\\"</small></div>
<div><small><em>Remove all or specific label(s)</em></small></div>"
`;
exports[`gfm_autocomplete/utils snippets config shows the id and title in the menu item 1`] = `"<small>123456</small> Snippet title &lt;script&gt;alert(&#39;hi&#39;)&lt;/script&gt;"`;
import Tribute from '@gitlab/tributejs';
import { shallowMount } from '@vue/test-utils';
import GfmAutocomplete from '~/vue_shared/components/gfm_autocomplete/gfm_autocomplete.vue';
describe('GfmAutocomplete', () => {
let wrapper;
describe('tribute', () => {
const mentions = '/gitlab-org/gitlab-test/-/autocomplete_sources/members?type=Issue&type_id=1';
beforeEach(() => {
wrapper = shallowMount(GfmAutocomplete, {
propsData: {
dataSources: {
mentions,
},
},
slots: {
default: ['<input/>'],
},
});
});
it('is set to tribute instance variable', () => {
expect(wrapper.vm.tribute instanceof Tribute).toBe(true);
});
it('contains the slot input element', () => {
wrapper.find('input').setValue('@');
expect(wrapper.vm.tribute.current.element).toBe(wrapper.find('input').element);
});
});
});
......@@ -961,11 +961,6 @@
resolved "https://registry.yarnpkg.com/@gitlab/svgs/-/svgs-2.2.0.tgz#95cf58d6ae634d535145159f08f5cff6241d4013"
integrity sha512-mCwR3KfNPsxRoojtTjMIZwdd4FFlBh5DlR9AeodP+7+k8rILdWGYxTZbJMPNXoPbZx16R94nG8c5bR7toD4QBw==
"@gitlab/tributejs@1.0.0":
version "1.0.0"
resolved "https://registry.yarnpkg.com/@gitlab/tributejs/-/tributejs-1.0.0.tgz#672befa222aeffc83e7d799b0500a7a4418e59b8"
integrity sha512-nmKw1+hB6MHvlmPz63yPwVs1qQkycHwsKgxpEbzmky16Y6mL4EJMk3w1b8QlOAF/AIAzjCERPhe/R4MJiohbZw==
"@gitlab/ui@33.1.0":
version "33.1.0"
resolved "https://registry.yarnpkg.com/@gitlab/ui/-/ui-33.1.0.tgz#45ac2e6362546530b5756b1973f97f74a9c920da"
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment