2017-08-17 22:00:37 +05:30
|
|
|
|
/* eslint no-param-reassign: "off" */
|
|
|
|
|
|
2018-05-09 12:01:36 +05:30
|
|
|
|
import $ from 'jquery';
|
2020-01-01 13:55:28 +05:30
|
|
|
|
import GfmAutoComplete, { membersBeforeSave } from 'ee_else_ce/gfm_auto_complete';
|
2017-08-17 22:00:37 +05:30
|
|
|
|
|
2019-07-07 11:18:12 +05:30
|
|
|
|
import 'jquery.caret';
|
2020-03-09 13:42:32 +05:30
|
|
|
|
import '@gitlab/at.js';
|
2017-08-17 22:00:37 +05:30
|
|
|
|
|
2019-07-07 11:18:12 +05:30
|
|
|
|
import { TEST_HOST } from 'helpers/test_constants';
|
|
|
|
|
import { getJSONFixture } from 'helpers/fixtures';
|
|
|
|
|
|
|
|
|
|
const labelsFixture = getJSONFixture('autocomplete_sources/labels.json');
|
|
|
|
|
|
|
|
|
|
describe('GfmAutoComplete', () => {
|
2017-09-10 17:25:29 +05:30
|
|
|
|
const gfmAutoCompleteCallbacks = GfmAutoComplete.prototype.getDefaultCallbacks.call({
|
|
|
|
|
fetchData: () => {},
|
|
|
|
|
});
|
|
|
|
|
|
2019-07-07 11:18:12 +05:30
|
|
|
|
let atwhoInstance;
|
|
|
|
|
let sorterValue;
|
|
|
|
|
|
|
|
|
|
describe('DefaultOptions.sorter', () => {
|
|
|
|
|
describe('assets loading', () => {
|
|
|
|
|
let items;
|
2017-08-17 22:00:37 +05:30
|
|
|
|
|
2019-07-07 11:18:12 +05:30
|
|
|
|
beforeEach(() => {
|
|
|
|
|
jest.spyOn(GfmAutoComplete, 'isLoading').mockReturnValue(true);
|
2017-08-17 22:00:37 +05:30
|
|
|
|
|
2019-07-07 11:18:12 +05:30
|
|
|
|
atwhoInstance = { setting: {} };
|
|
|
|
|
items = [];
|
|
|
|
|
|
|
|
|
|
sorterValue = gfmAutoCompleteCallbacks.sorter.call(atwhoInstance, '', items);
|
2017-08-17 22:00:37 +05:30
|
|
|
|
});
|
|
|
|
|
|
2019-07-07 11:18:12 +05:30
|
|
|
|
it('should disable highlightFirst', () => {
|
|
|
|
|
expect(atwhoInstance.setting.highlightFirst).toBe(false);
|
2017-08-17 22:00:37 +05:30
|
|
|
|
});
|
|
|
|
|
|
2019-07-07 11:18:12 +05:30
|
|
|
|
it('should return the passed unfiltered items', () => {
|
|
|
|
|
expect(sorterValue).toEqual(items);
|
2017-08-17 22:00:37 +05:30
|
|
|
|
});
|
|
|
|
|
});
|
|
|
|
|
|
2019-07-07 11:18:12 +05:30
|
|
|
|
describe('assets finished loading', () => {
|
|
|
|
|
beforeEach(() => {
|
|
|
|
|
jest.spyOn(GfmAutoComplete, 'isLoading').mockReturnValue(false);
|
|
|
|
|
jest.spyOn($.fn.atwho.default.callbacks, 'sorter').mockImplementation(() => {});
|
2017-08-17 22:00:37 +05:30
|
|
|
|
});
|
|
|
|
|
|
2019-07-07 11:18:12 +05:30
|
|
|
|
it('should enable highlightFirst if alwaysHighlightFirst is set', () => {
|
|
|
|
|
atwhoInstance = { setting: { alwaysHighlightFirst: true } };
|
2017-08-17 22:00:37 +05:30
|
|
|
|
|
2017-09-10 17:25:29 +05:30
|
|
|
|
gfmAutoCompleteCallbacks.sorter.call(atwhoInstance);
|
2017-08-17 22:00:37 +05:30
|
|
|
|
|
|
|
|
|
expect(atwhoInstance.setting.highlightFirst).toBe(true);
|
|
|
|
|
});
|
|
|
|
|
|
2019-07-07 11:18:12 +05:30
|
|
|
|
it('should enable highlightFirst if a query is present', () => {
|
|
|
|
|
atwhoInstance = { setting: {} };
|
2017-08-17 22:00:37 +05:30
|
|
|
|
|
2017-09-10 17:25:29 +05:30
|
|
|
|
gfmAutoCompleteCallbacks.sorter.call(atwhoInstance, 'query');
|
2017-08-17 22:00:37 +05:30
|
|
|
|
|
|
|
|
|
expect(atwhoInstance.setting.highlightFirst).toBe(true);
|
|
|
|
|
});
|
|
|
|
|
|
2019-07-07 11:18:12 +05:30
|
|
|
|
it('should call the default atwho sorter', () => {
|
|
|
|
|
atwhoInstance = { setting: {} };
|
2017-08-17 22:00:37 +05:30
|
|
|
|
|
|
|
|
|
const query = 'query';
|
|
|
|
|
const items = [];
|
|
|
|
|
const searchKey = 'searchKey';
|
|
|
|
|
|
2017-09-10 17:25:29 +05:30
|
|
|
|
gfmAutoCompleteCallbacks.sorter.call(atwhoInstance, query, items, searchKey);
|
2017-08-17 22:00:37 +05:30
|
|
|
|
|
|
|
|
|
expect($.fn.atwho.default.callbacks.sorter).toHaveBeenCalledWith(query, items, searchKey);
|
|
|
|
|
});
|
|
|
|
|
});
|
|
|
|
|
});
|
|
|
|
|
|
2018-03-17 18:26:18 +05:30
|
|
|
|
describe('DefaultOptions.beforeInsert', () => {
|
2018-12-13 13:39:08 +05:30
|
|
|
|
const beforeInsert = (context, value) =>
|
|
|
|
|
gfmAutoCompleteCallbacks.beforeInsert.call(context, value);
|
2018-03-17 18:26:18 +05:30
|
|
|
|
|
2019-07-07 11:18:12 +05:30
|
|
|
|
beforeEach(() => {
|
|
|
|
|
atwhoInstance = { setting: { skipSpecialCharacterTest: false } };
|
|
|
|
|
});
|
2018-03-17 18:26:18 +05:30
|
|
|
|
|
|
|
|
|
it('should not quote if value only contains alphanumeric charecters', () => {
|
|
|
|
|
expect(beforeInsert(atwhoInstance, '@user1')).toBe('@user1');
|
|
|
|
|
expect(beforeInsert(atwhoInstance, '~label1')).toBe('~label1');
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
it('should quote if value contains any non-alphanumeric characters', () => {
|
2019-07-31 22:56:46 +05:30
|
|
|
|
expect(beforeInsert(atwhoInstance, '~label-20')).toBe('~"label-20"');
|
2018-03-17 18:26:18 +05:30
|
|
|
|
expect(beforeInsert(atwhoInstance, '~label 20')).toBe('~"label 20"');
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
it('should quote integer labels', () => {
|
|
|
|
|
expect(beforeInsert(atwhoInstance, '~1234')).toBe('~"1234"');
|
|
|
|
|
});
|
2018-05-09 12:01:36 +05:30
|
|
|
|
|
2019-07-31 22:56:46 +05:30
|
|
|
|
it('escapes Markdown strikethroughs when needed', () => {
|
|
|
|
|
expect(beforeInsert(atwhoInstance, '~a~bug')).toEqual('~"a~bug"');
|
|
|
|
|
expect(beforeInsert(atwhoInstance, '~a~~bug~~')).toEqual('~"a\\~~bug\\~~"');
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
it('escapes Markdown emphasis when needed', () => {
|
|
|
|
|
expect(beforeInsert(atwhoInstance, '~a_bug_')).toEqual('~a_bug\\_');
|
|
|
|
|
expect(beforeInsert(atwhoInstance, '~a _bug_')).toEqual('~"a \\_bug\\_"');
|
|
|
|
|
expect(beforeInsert(atwhoInstance, '~a*bug*')).toEqual('~"a\\*bug\\*"');
|
|
|
|
|
expect(beforeInsert(atwhoInstance, '~a *bug*')).toEqual('~"a \\*bug\\*"');
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
it('escapes Markdown code spans when needed', () => {
|
|
|
|
|
expect(beforeInsert(atwhoInstance, '~a`bug`')).toEqual('~"a\\`bug\\`"');
|
2018-05-09 12:01:36 +05:30
|
|
|
|
expect(beforeInsert(atwhoInstance, '~a `bug`')).toEqual('~"a \\`bug\\`"');
|
|
|
|
|
});
|
2018-03-17 18:26:18 +05:30
|
|
|
|
});
|
|
|
|
|
|
2019-07-07 11:18:12 +05:30
|
|
|
|
describe('DefaultOptions.matcher', () => {
|
2018-12-13 13:39:08 +05:30
|
|
|
|
const defaultMatcher = (context, flag, subtext) =>
|
|
|
|
|
gfmAutoCompleteCallbacks.matcher.call(context, flag, subtext);
|
2017-08-17 22:00:37 +05:30
|
|
|
|
|
2018-12-05 23:21:45 +05:30
|
|
|
|
const flagsUseDefaultMatcher = ['@', '#', '!', '~', '%', '$'];
|
2017-08-17 22:00:37 +05:30
|
|
|
|
const otherFlags = ['/', ':'];
|
|
|
|
|
const flags = flagsUseDefaultMatcher.concat(otherFlags);
|
|
|
|
|
|
2018-12-13 13:39:08 +05:30
|
|
|
|
const flagsHash = flags.reduce((hash, el) => {
|
|
|
|
|
hash[el] = null;
|
|
|
|
|
return hash;
|
|
|
|
|
}, {});
|
2019-07-07 11:18:12 +05:30
|
|
|
|
|
|
|
|
|
beforeEach(() => {
|
|
|
|
|
atwhoInstance = { setting: {}, app: { controllers: flagsHash } };
|
|
|
|
|
});
|
2017-08-17 22:00:37 +05:30
|
|
|
|
|
|
|
|
|
const minLen = 1;
|
|
|
|
|
const maxLen = 20;
|
|
|
|
|
const argumentSize = [minLen, maxLen / 2, maxLen];
|
|
|
|
|
|
2018-12-13 13:39:08 +05:30
|
|
|
|
const allowedSymbols = [
|
|
|
|
|
'',
|
|
|
|
|
'a',
|
|
|
|
|
'n',
|
|
|
|
|
'z',
|
|
|
|
|
'A',
|
|
|
|
|
'Z',
|
|
|
|
|
'N',
|
|
|
|
|
'0',
|
|
|
|
|
'5',
|
|
|
|
|
'9',
|
|
|
|
|
'А',
|
|
|
|
|
'а',
|
|
|
|
|
'Я',
|
|
|
|
|
'я',
|
|
|
|
|
'.',
|
|
|
|
|
"'",
|
|
|
|
|
'+',
|
|
|
|
|
'-',
|
|
|
|
|
'_',
|
|
|
|
|
];
|
2017-08-17 22:00:37 +05:30
|
|
|
|
const jointAllowedSymbols = allowedSymbols.join('');
|
|
|
|
|
|
|
|
|
|
describe('should match regular symbols', () => {
|
2018-12-13 13:39:08 +05:30
|
|
|
|
flagsUseDefaultMatcher.forEach(flag => {
|
|
|
|
|
allowedSymbols.forEach(symbol => {
|
|
|
|
|
argumentSize.forEach(size => {
|
2017-08-17 22:00:37 +05:30
|
|
|
|
const query = new Array(size + 1).join(symbol);
|
|
|
|
|
const subtext = flag + query;
|
|
|
|
|
|
|
|
|
|
it(`matches argument "${flag}" with query "${subtext}"`, () => {
|
|
|
|
|
expect(defaultMatcher(atwhoInstance, flag, subtext)).toBe(query);
|
|
|
|
|
});
|
|
|
|
|
});
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
it(`matches combination of allowed symbols for flag "${flag}"`, () => {
|
|
|
|
|
const subtext = flag + jointAllowedSymbols;
|
|
|
|
|
|
|
|
|
|
expect(defaultMatcher(atwhoInstance, flag, subtext)).toBe(jointAllowedSymbols);
|
|
|
|
|
});
|
|
|
|
|
});
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
describe('should not match special sequences', () => {
|
2018-03-17 18:26:18 +05:30
|
|
|
|
const shouldNotBeFollowedBy = flags.concat(['\x00', '\x10', '\x3f', '\n', ' ']);
|
|
|
|
|
const shouldNotBePrependedBy = ['`'];
|
2017-08-17 22:00:37 +05:30
|
|
|
|
|
2018-12-13 13:39:08 +05:30
|
|
|
|
flagsUseDefaultMatcher.forEach(atSign => {
|
|
|
|
|
shouldNotBeFollowedBy.forEach(followedSymbol => {
|
2017-08-17 22:00:37 +05:30
|
|
|
|
const seq = atSign + followedSymbol;
|
2018-03-17 18:26:18 +05:30
|
|
|
|
|
2018-12-05 23:21:45 +05:30
|
|
|
|
it(`should not match ${JSON.stringify(seq)}`, () => {
|
2018-03-17 18:26:18 +05:30
|
|
|
|
expect(defaultMatcher(atwhoInstance, atSign, seq)).toBe(null);
|
|
|
|
|
});
|
|
|
|
|
});
|
|
|
|
|
|
2018-12-13 13:39:08 +05:30
|
|
|
|
shouldNotBePrependedBy.forEach(prependedSymbol => {
|
2018-03-17 18:26:18 +05:30
|
|
|
|
const seq = prependedSymbol + atSign;
|
2017-08-17 22:00:37 +05:30
|
|
|
|
|
|
|
|
|
it(`should not match "${seq}"`, () => {
|
|
|
|
|
expect(defaultMatcher(atwhoInstance, atSign, seq)).toBe(null);
|
|
|
|
|
});
|
|
|
|
|
});
|
|
|
|
|
});
|
|
|
|
|
});
|
|
|
|
|
});
|
|
|
|
|
|
2019-07-31 22:56:46 +05:30
|
|
|
|
describe('DefaultOptions.highlighter', () => {
|
|
|
|
|
beforeEach(() => {
|
|
|
|
|
atwhoInstance = { setting: {} };
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
it('should return li if no query is given', () => {
|
|
|
|
|
const liTag = '<li></li>';
|
|
|
|
|
|
|
|
|
|
const highlightedTag = gfmAutoCompleteCallbacks.highlighter.call(atwhoInstance, liTag);
|
|
|
|
|
|
|
|
|
|
expect(highlightedTag).toEqual(liTag);
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
it('should highlight search query in li element', () => {
|
|
|
|
|
const liTag = '<li><img src="" />string</li>';
|
|
|
|
|
const query = 's';
|
|
|
|
|
|
|
|
|
|
const highlightedTag = gfmAutoCompleteCallbacks.highlighter.call(atwhoInstance, liTag, query);
|
|
|
|
|
|
|
|
|
|
expect(highlightedTag).toEqual('<li><img src="" /> <strong>s</strong>tring </li>');
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
it('should highlight search query with special char in li element', () => {
|
|
|
|
|
const liTag = '<li><img src="" />te.st</li>';
|
|
|
|
|
const query = '.';
|
|
|
|
|
|
|
|
|
|
const highlightedTag = gfmAutoCompleteCallbacks.highlighter.call(atwhoInstance, liTag, query);
|
|
|
|
|
|
|
|
|
|
expect(highlightedTag).toEqual('<li><img src="" /> te<strong>.</strong>st </li>');
|
|
|
|
|
});
|
|
|
|
|
});
|
|
|
|
|
|
2019-07-07 11:18:12 +05:30
|
|
|
|
describe('isLoading', () => {
|
|
|
|
|
it('should be true with loading data object item', () => {
|
2017-08-17 22:00:37 +05:30
|
|
|
|
expect(GfmAutoComplete.isLoading({ name: 'loading' })).toBe(true);
|
|
|
|
|
});
|
|
|
|
|
|
2019-07-07 11:18:12 +05:30
|
|
|
|
it('should be true with loading data array', () => {
|
2017-08-17 22:00:37 +05:30
|
|
|
|
expect(GfmAutoComplete.isLoading(['loading'])).toBe(true);
|
|
|
|
|
});
|
|
|
|
|
|
2019-07-07 11:18:12 +05:30
|
|
|
|
it('should be true with loading data object array', () => {
|
2017-08-17 22:00:37 +05:30
|
|
|
|
expect(GfmAutoComplete.isLoading([{ name: 'loading' }])).toBe(true);
|
|
|
|
|
});
|
|
|
|
|
|
2019-07-07 11:18:12 +05:30
|
|
|
|
it('should be false with actual array data', () => {
|
2018-12-13 13:39:08 +05:30
|
|
|
|
expect(
|
|
|
|
|
GfmAutoComplete.isLoading([{ title: 'Foo' }, { title: 'Bar' }, { title: 'Qux' }]),
|
|
|
|
|
).toBe(false);
|
2017-08-17 22:00:37 +05:30
|
|
|
|
});
|
|
|
|
|
|
2019-07-07 11:18:12 +05:30
|
|
|
|
it('should be false with actual data item', () => {
|
2017-08-17 22:00:37 +05:30
|
|
|
|
expect(GfmAutoComplete.isLoading({ title: 'Foo' })).toBe(false);
|
|
|
|
|
});
|
|
|
|
|
});
|
2019-02-15 15:39:39 +05:30
|
|
|
|
|
2020-01-01 13:55:28 +05:30
|
|
|
|
describe('membersBeforeSave', () => {
|
|
|
|
|
const mockGroup = {
|
|
|
|
|
username: 'my-group',
|
|
|
|
|
name: 'My Group',
|
|
|
|
|
count: 2,
|
|
|
|
|
avatar_url: './group.jpg',
|
|
|
|
|
type: 'Group',
|
|
|
|
|
mentionsDisabled: false,
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
it('should return the original object when username is null', () => {
|
|
|
|
|
expect(membersBeforeSave([{ ...mockGroup, username: null }])).toEqual([
|
|
|
|
|
{ ...mockGroup, username: null },
|
|
|
|
|
]);
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
it('should set the text avatar if avatar_url is null', () => {
|
|
|
|
|
expect(membersBeforeSave([{ ...mockGroup, avatar_url: null }])).toEqual([
|
|
|
|
|
{
|
|
|
|
|
username: 'my-group',
|
|
|
|
|
avatarTag: '<div class="avatar rect-avatar center avatar-inline s26">M</div>',
|
|
|
|
|
title: 'My Group (2)',
|
|
|
|
|
search: 'my-group My Group',
|
|
|
|
|
icon: '',
|
|
|
|
|
},
|
|
|
|
|
]);
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
it('should set the image avatar if avatar_url is given', () => {
|
|
|
|
|
expect(membersBeforeSave([mockGroup])).toEqual([
|
|
|
|
|
{
|
|
|
|
|
username: 'my-group',
|
|
|
|
|
avatarTag:
|
|
|
|
|
'<img src="./group.jpg" alt="my-group" class="avatar rect-avatar avatar-inline center s26"/>',
|
|
|
|
|
title: 'My Group (2)',
|
|
|
|
|
search: 'my-group My Group',
|
|
|
|
|
icon: '',
|
|
|
|
|
},
|
|
|
|
|
]);
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
it('should set mentions disabled icon if mentionsDisabled is set', () => {
|
|
|
|
|
expect(membersBeforeSave([{ ...mockGroup, mentionsDisabled: true }])).toEqual([
|
|
|
|
|
{
|
|
|
|
|
username: 'my-group',
|
|
|
|
|
avatarTag:
|
|
|
|
|
'<img src="./group.jpg" alt="my-group" class="avatar rect-avatar avatar-inline center s26"/>',
|
|
|
|
|
title: 'My Group',
|
|
|
|
|
search: 'my-group My Group',
|
|
|
|
|
icon:
|
|
|
|
|
'<svg class="s16 vertical-align-middle prepend-left-5"><use xlink:href="undefined#notifications-off" /></svg>',
|
|
|
|
|
},
|
|
|
|
|
]);
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
it('should set the right image classes for User type members', () => {
|
|
|
|
|
expect(
|
|
|
|
|
membersBeforeSave([
|
|
|
|
|
{ username: 'my-user', name: 'My User', avatar_url: './users.jpg', type: 'User' },
|
|
|
|
|
]),
|
|
|
|
|
).toEqual([
|
|
|
|
|
{
|
|
|
|
|
username: 'my-user',
|
|
|
|
|
avatarTag:
|
|
|
|
|
'<img src="./users.jpg" alt="my-user" class="avatar avatar-inline center s26"/>',
|
|
|
|
|
title: 'My User',
|
|
|
|
|
search: 'my-user My User',
|
|
|
|
|
icon: '',
|
|
|
|
|
},
|
|
|
|
|
]);
|
|
|
|
|
});
|
|
|
|
|
});
|
|
|
|
|
|
2019-07-07 11:18:12 +05:30
|
|
|
|
describe('Issues.insertTemplateFunction', () => {
|
|
|
|
|
it('should return default template', () => {
|
2019-02-15 15:39:39 +05:30
|
|
|
|
expect(GfmAutoComplete.Issues.insertTemplateFunction({ id: 5, title: 'Some Issue' })).toBe(
|
|
|
|
|
'${atwho-at}${id}', // eslint-disable-line no-template-curly-in-string
|
|
|
|
|
);
|
|
|
|
|
});
|
|
|
|
|
|
2019-07-07 11:18:12 +05:30
|
|
|
|
it('should return reference when reference is set', () => {
|
2019-02-15 15:39:39 +05:30
|
|
|
|
expect(
|
|
|
|
|
GfmAutoComplete.Issues.insertTemplateFunction({
|
|
|
|
|
id: 5,
|
|
|
|
|
title: 'Some Issue',
|
|
|
|
|
reference: 'grp/proj#5',
|
|
|
|
|
}),
|
|
|
|
|
).toBe('grp/proj#5');
|
|
|
|
|
});
|
|
|
|
|
});
|
|
|
|
|
|
2019-07-07 11:18:12 +05:30
|
|
|
|
describe('Issues.templateFunction', () => {
|
|
|
|
|
it('should return html with id and title', () => {
|
2019-02-15 15:39:39 +05:30
|
|
|
|
expect(GfmAutoComplete.Issues.templateFunction({ id: 5, title: 'Some Issue' })).toBe(
|
|
|
|
|
'<li><small>5</small> Some Issue</li>',
|
|
|
|
|
);
|
|
|
|
|
});
|
|
|
|
|
|
2019-07-07 11:18:12 +05:30
|
|
|
|
it('should replace id with reference if reference is set', () => {
|
2019-02-15 15:39:39 +05:30
|
|
|
|
expect(
|
|
|
|
|
GfmAutoComplete.Issues.templateFunction({
|
|
|
|
|
id: 5,
|
|
|
|
|
title: 'Some Issue',
|
|
|
|
|
reference: 'grp/proj#5',
|
|
|
|
|
}),
|
|
|
|
|
).toBe('<li><small>grp/proj#5</small> Some Issue</li>');
|
|
|
|
|
});
|
|
|
|
|
});
|
2019-07-07 11:18:12 +05:30
|
|
|
|
|
2020-01-01 13:55:28 +05:30
|
|
|
|
describe('Members.templateFunction', () => {
|
|
|
|
|
it('should return html with avatarTag and username', () => {
|
|
|
|
|
expect(
|
|
|
|
|
GfmAutoComplete.Members.templateFunction({
|
|
|
|
|
avatarTag: 'IMG',
|
|
|
|
|
username: 'my-group',
|
|
|
|
|
title: '',
|
|
|
|
|
icon: '',
|
|
|
|
|
}),
|
|
|
|
|
).toBe('<li>IMG my-group <small></small> </li>');
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
it('should add icon if icon is set', () => {
|
|
|
|
|
expect(
|
|
|
|
|
GfmAutoComplete.Members.templateFunction({
|
|
|
|
|
avatarTag: 'IMG',
|
|
|
|
|
username: 'my-group',
|
|
|
|
|
title: '',
|
|
|
|
|
icon: '<i class="icon"/>',
|
|
|
|
|
}),
|
|
|
|
|
).toBe('<li>IMG my-group <small></small> <i class="icon"/></li>');
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
it('should add escaped title if title is set', () => {
|
|
|
|
|
expect(
|
|
|
|
|
GfmAutoComplete.Members.templateFunction({
|
|
|
|
|
avatarTag: 'IMG',
|
|
|
|
|
username: 'my-group',
|
|
|
|
|
title: 'MyGroup+',
|
|
|
|
|
icon: '<i class="icon"/>',
|
|
|
|
|
}),
|
|
|
|
|
).toBe('<li>IMG my-group <small>MyGroup+</small> <i class="icon"/></li>');
|
|
|
|
|
});
|
|
|
|
|
});
|
|
|
|
|
|
2019-07-07 11:18:12 +05:30
|
|
|
|
describe('labels', () => {
|
|
|
|
|
const dataSources = {
|
|
|
|
|
labels: `${TEST_HOST}/autocomplete_sources/labels`,
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
const allLabels = labelsFixture;
|
|
|
|
|
const assignedLabels = allLabels.filter(label => label.set);
|
|
|
|
|
const unassignedLabels = allLabels.filter(label => !label.set);
|
|
|
|
|
|
|
|
|
|
let autocomplete;
|
|
|
|
|
let $textarea;
|
|
|
|
|
|
|
|
|
|
beforeEach(() => {
|
|
|
|
|
autocomplete = new GfmAutoComplete(dataSources);
|
|
|
|
|
$textarea = $('<textarea></textarea>');
|
|
|
|
|
autocomplete.setup($textarea, { labels: true });
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
afterEach(() => {
|
|
|
|
|
autocomplete.destroy();
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
const triggerDropdown = text => {
|
|
|
|
|
$textarea
|
|
|
|
|
.trigger('focus')
|
|
|
|
|
.val(text)
|
|
|
|
|
.caret('pos', -1);
|
|
|
|
|
$textarea.trigger('keyup');
|
|
|
|
|
|
|
|
|
|
return new Promise(window.requestAnimationFrame);
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
const getDropdownItems = () => {
|
|
|
|
|
const dropdown = document.getElementById('at-view-labels');
|
|
|
|
|
const items = dropdown.getElementsByTagName('li');
|
|
|
|
|
return [].map.call(items, item => item.textContent.trim());
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
const expectLabels = ({ input, output }) =>
|
|
|
|
|
triggerDropdown(input).then(() => {
|
|
|
|
|
expect(getDropdownItems()).toEqual(output.map(label => label.title));
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
describe('with no labels assigned', () => {
|
|
|
|
|
beforeEach(() => {
|
|
|
|
|
autocomplete.cachedData['~'] = [...unassignedLabels];
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
it.each`
|
|
|
|
|
input | output
|
|
|
|
|
${'~'} | ${unassignedLabels}
|
|
|
|
|
${'/label ~'} | ${unassignedLabels}
|
|
|
|
|
${'/relabel ~'} | ${unassignedLabels}
|
|
|
|
|
${'/unlabel ~'} | ${[]}
|
|
|
|
|
`('$input shows $output.length labels', expectLabels);
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
describe('with some labels assigned', () => {
|
|
|
|
|
beforeEach(() => {
|
|
|
|
|
autocomplete.cachedData['~'] = allLabels;
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
it.each`
|
|
|
|
|
input | output
|
|
|
|
|
${'~'} | ${allLabels}
|
|
|
|
|
${'/label ~'} | ${unassignedLabels}
|
|
|
|
|
${'/relabel ~'} | ${allLabels}
|
|
|
|
|
${'/unlabel ~'} | ${assignedLabels}
|
|
|
|
|
`('$input shows $output.length labels', expectLabels);
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
describe('with all labels assigned', () => {
|
|
|
|
|
beforeEach(() => {
|
|
|
|
|
autocomplete.cachedData['~'] = [...assignedLabels];
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
it.each`
|
|
|
|
|
input | output
|
|
|
|
|
${'~'} | ${assignedLabels}
|
|
|
|
|
${'/label ~'} | ${[]}
|
|
|
|
|
${'/relabel ~'} | ${assignedLabels}
|
|
|
|
|
${'/unlabel ~'} | ${assignedLabels}
|
|
|
|
|
`('$input shows $output.length labels', expectLabels);
|
|
|
|
|
});
|
|
|
|
|
});
|
2017-08-17 22:00:37 +05:30
|
|
|
|
});
|