summaryrefslogtreecommitdiff
path: root/spec/frontend/vue_shared/components/filtered_search_bar
diff options
context:
space:
mode:
Diffstat (limited to 'spec/frontend/vue_shared/components/filtered_search_bar')
-rw-r--r--spec/frontend/vue_shared/components/filtered_search_bar/filtered_search_bar_root_spec.js129
-rw-r--r--spec/frontend/vue_shared/components/filtered_search_bar/filtered_search_utils_spec.js19
-rw-r--r--spec/frontend/vue_shared/components/filtered_search_bar/mock_data.js56
-rw-r--r--spec/frontend/vue_shared/components/filtered_search_bar/tokens/author_token_spec.js2
-rw-r--r--spec/frontend/vue_shared/components/filtered_search_bar/tokens/label_token_spec.js170
-rw-r--r--spec/frontend/vue_shared/components/filtered_search_bar/tokens/milestone_token_spec.js152
6 files changed, 517 insertions, 11 deletions
diff --git a/spec/frontend/vue_shared/components/filtered_search_bar/filtered_search_bar_root_spec.js b/spec/frontend/vue_shared/components/filtered_search_bar/filtered_search_bar_root_spec.js
index 05508d14209..73dbecadd89 100644
--- a/spec/frontend/vue_shared/components/filtered_search_bar/filtered_search_bar_root_spec.js
+++ b/spec/frontend/vue_shared/components/filtered_search_bar/filtered_search_bar_root_spec.js
@@ -1,4 +1,4 @@
-import { shallowMount } from '@vue/test-utils';
+import { shallowMount, mount } from '@vue/test-utils';
import {
GlFilteredSearch,
GlButtonGroup,
@@ -16,13 +16,16 @@ import RecentSearchesService from '~/filtered_search/services/recent_searches_se
import { mockAvailableTokens, mockSortOptions, mockHistoryItems } from './mock_data';
const createComponent = ({
+ shallow = true,
namespace = 'gitlab-org/gitlab-test',
recentSearchesStorageKey = 'requirements',
tokens = mockAvailableTokens,
- sortOptions = mockSortOptions,
+ sortOptions,
searchInputPlaceholder = 'Filter requirements',
-} = {}) =>
- shallowMount(FilteredSearchBarRoot, {
+} = {}) => {
+ const mountMethod = shallow ? shallowMount : mount;
+
+ return mountMethod(FilteredSearchBarRoot, {
propsData: {
namespace,
recentSearchesStorageKey,
@@ -31,12 +34,13 @@ const createComponent = ({
searchInputPlaceholder,
},
});
+};
describe('FilteredSearchBarRoot', () => {
let wrapper;
beforeEach(() => {
- wrapper = createComponent();
+ wrapper = createComponent({ sortOptions: mockSortOptions });
});
afterEach(() => {
@@ -44,23 +48,38 @@ describe('FilteredSearchBarRoot', () => {
});
describe('data', () => {
- it('initializes `filterValue`, `selectedSortOption` and `selectedSortDirection` data props', () => {
+ it('initializes `filterValue`, `selectedSortOption` and `selectedSortDirection` data props and displays the sort dropdown', () => {
expect(wrapper.vm.filterValue).toEqual([]);
expect(wrapper.vm.selectedSortOption).toBe(mockSortOptions[0].sortDirection.descending);
expect(wrapper.vm.selectedSortDirection).toBe(SortDirection.descending);
+ expect(wrapper.contains(GlButtonGroup)).toBe(true);
+ expect(wrapper.contains(GlButton)).toBe(true);
+ expect(wrapper.contains(GlDropdown)).toBe(true);
+ expect(wrapper.contains(GlDropdownItem)).toBe(true);
+ });
+
+ it('does not initialize `selectedSortOption` and `selectedSortDirection` when `sortOptions` is not applied and hides the sort dropdown', () => {
+ const wrapperNoSort = createComponent();
+
+ expect(wrapperNoSort.vm.filterValue).toEqual([]);
+ expect(wrapperNoSort.vm.selectedSortOption).toBe(undefined);
+ expect(wrapperNoSort.contains(GlButtonGroup)).toBe(false);
+ expect(wrapperNoSort.contains(GlButton)).toBe(false);
+ expect(wrapperNoSort.contains(GlDropdown)).toBe(false);
+ expect(wrapperNoSort.contains(GlDropdownItem)).toBe(false);
});
});
describe('computed', () => {
describe('tokenSymbols', () => {
it('returns a map containing type and symbols from `tokens` prop', () => {
- expect(wrapper.vm.tokenSymbols).toEqual({ author_username: '@' });
+ expect(wrapper.vm.tokenSymbols).toEqual({ author_username: '@', label_name: '~' });
});
});
describe('tokenTitles', () => {
it('returns a map containing type and title from `tokens` prop', () => {
- expect(wrapper.vm.tokenTitles).toEqual({ author_username: 'Author' });
+ expect(wrapper.vm.tokenTitles).toEqual({ author_username: 'Author', label_name: 'Label' });
});
});
@@ -99,6 +118,29 @@ describe('FilteredSearchBarRoot', () => {
expect(wrapper.vm.sortDirectionTooltip).toBe('Sort direction: Descending');
});
});
+
+ describe('filteredRecentSearches', () => {
+ it('returns array of recent searches filtering out any string type (unsupported) items', async () => {
+ wrapper.setData({
+ recentSearches: [{ foo: 'bar' }, 'foo'],
+ });
+
+ await wrapper.vm.$nextTick();
+
+ expect(wrapper.vm.filteredRecentSearches).toHaveLength(1);
+ expect(wrapper.vm.filteredRecentSearches[0]).toEqual({ foo: 'bar' });
+ });
+
+ it('returns undefined when recentSearchesStorageKey prop is not set on component', async () => {
+ wrapper.setProps({
+ recentSearchesStorageKey: '',
+ });
+
+ await wrapper.vm.$nextTick();
+
+ expect(wrapper.vm.filteredRecentSearches).not.toBeDefined();
+ });
+ });
});
describe('watchers', () => {
@@ -139,6 +181,46 @@ describe('FilteredSearchBarRoot', () => {
});
});
+ describe('removeQuotesEnclosure', () => {
+ const mockFilters = [
+ {
+ type: 'author_username',
+ value: {
+ data: 'root',
+ operator: '=',
+ },
+ },
+ {
+ type: 'label_name',
+ value: {
+ data: '"Documentation Update"',
+ operator: '=',
+ },
+ },
+ 'foo',
+ ];
+
+ it('returns filter array with unescaped strings for values which have spaces', () => {
+ expect(wrapper.vm.removeQuotesEnclosure(mockFilters)).toEqual([
+ {
+ type: 'author_username',
+ value: {
+ data: 'root',
+ operator: '=',
+ },
+ },
+ {
+ type: 'label_name',
+ value: {
+ data: 'Documentation Update',
+ operator: '=',
+ },
+ },
+ 'foo',
+ ]);
+ });
+ });
+
describe('handleSortOptionClick', () => {
it('emits component event `onSort` with selected sort by value', () => {
wrapper.vm.handleSortOptionClick(mockSortOptions[1]);
@@ -172,9 +254,12 @@ describe('FilteredSearchBarRoot', () => {
describe('handleHistoryItemSelected', () => {
it('emits `onFilter` event with provided filters param', () => {
+ jest.spyOn(wrapper.vm, 'removeQuotesEnclosure');
+
wrapper.vm.handleHistoryItemSelected(mockHistoryItems[0]);
expect(wrapper.emitted('onFilter')[0]).toEqual([mockHistoryItems[0]]);
+ expect(wrapper.vm.removeQuotesEnclosure).toHaveBeenCalledWith(mockHistoryItems[0]);
});
});
@@ -233,10 +318,21 @@ describe('FilteredSearchBarRoot', () => {
});
});
+ it('calls `blurSearchInput` method to remove focus from filter input field', () => {
+ jest.spyOn(wrapper.vm, 'blurSearchInput');
+
+ wrapper.find(GlFilteredSearch).vm.$emit('submit', mockFilters);
+
+ expect(wrapper.vm.blurSearchInput).toHaveBeenCalled();
+ });
+
it('emits component event `onFilter` with provided filters param', () => {
+ jest.spyOn(wrapper.vm, 'removeQuotesEnclosure');
+
wrapper.vm.handleFilterSubmit(mockFilters);
expect(wrapper.emitted('onFilter')[0]).toEqual([mockFilters]);
+ expect(wrapper.vm.removeQuotesEnclosure).toHaveBeenCalledWith(mockFilters);
});
});
});
@@ -260,13 +356,28 @@ describe('FilteredSearchBarRoot', () => {
expect(glFilteredSearchEl.props('historyItems')).toEqual(mockHistoryItems);
});
+ it('renders search history items dropdown with formatting done using token symbols', async () => {
+ const wrapperFullMount = createComponent({ sortOptions: mockSortOptions, shallow: false });
+ wrapperFullMount.vm.recentSearchesStore.addRecentSearch(mockHistoryItems[0]);
+
+ await wrapperFullMount.vm.$nextTick();
+
+ const searchHistoryItemsEl = wrapperFullMount.findAll(
+ '.gl-search-box-by-click-menu .gl-search-box-by-click-history-item',
+ );
+
+ expect(searchHistoryItemsEl.at(0).text()).toBe('Author := @tobyLabel := ~Bug"duo"');
+
+ wrapperFullMount.destroy();
+ });
+
it('renders sort dropdown component', () => {
expect(wrapper.find(GlButtonGroup).exists()).toBe(true);
expect(wrapper.find(GlDropdown).exists()).toBe(true);
expect(wrapper.find(GlDropdown).props('text')).toBe(mockSortOptions[0].title);
});
- it('renders dropdown items', () => {
+ it('renders sort dropdown items', () => {
const dropdownItemsEl = wrapper.findAll(GlDropdownItem);
expect(dropdownItemsEl).toHaveLength(mockSortOptions.length);
diff --git a/spec/frontend/vue_shared/components/filtered_search_bar/filtered_search_utils_spec.js b/spec/frontend/vue_shared/components/filtered_search_bar/filtered_search_utils_spec.js
new file mode 100644
index 00000000000..a857f84adf1
--- /dev/null
+++ b/spec/frontend/vue_shared/components/filtered_search_bar/filtered_search_utils_spec.js
@@ -0,0 +1,19 @@
+import * as filteredSearchUtils from '~/vue_shared/components/filtered_search_bar/filtered_search_utils';
+
+describe('Filtered Search Utils', () => {
+ describe('stripQuotes', () => {
+ it.each`
+ inputValue | outputValue
+ ${'"Foo Bar"'} | ${'Foo Bar'}
+ ${"'Foo Bar'"} | ${'Foo Bar'}
+ ${'FooBar'} | ${'FooBar'}
+ ${"Foo'Bar"} | ${"Foo'Bar"}
+ ${'Foo"Bar'} | ${'Foo"Bar'}
+ `(
+ 'returns string $outputValue when called with string $inputValue',
+ ({ inputValue, outputValue }) => {
+ expect(filteredSearchUtils.stripQuotes(inputValue)).toBe(outputValue);
+ },
+ );
+ });
+});
diff --git a/spec/frontend/vue_shared/components/filtered_search_bar/mock_data.js b/spec/frontend/vue_shared/components/filtered_search_bar/mock_data.js
index 7e28c4e11e1..dcccb1f49b6 100644
--- a/spec/frontend/vue_shared/components/filtered_search_bar/mock_data.js
+++ b/spec/frontend/vue_shared/components/filtered_search_bar/mock_data.js
@@ -1,5 +1,8 @@
+import { mockLabels } from 'jest/vue_shared/components/sidebar/labels_select_vue/mock_data';
import Api from '~/api';
import AuthorToken from '~/vue_shared/components/filtered_search_bar/tokens/author_token.vue';
+import LabelToken from '~/vue_shared/components/filtered_search_bar/tokens/label_token.vue';
+import MilestoneToken from '~/vue_shared/components/filtered_search_bar/tokens/milestone_token.vue';
export const mockAuthor1 = {
id: 1,
@@ -30,6 +33,28 @@ export const mockAuthor3 = {
export const mockAuthors = [mockAuthor1, mockAuthor2, mockAuthor3];
+export const mockRegularMilestone = {
+ id: 1,
+ name: '4.0',
+ title: '4.0',
+};
+
+export const mockEscapedMilestone = {
+ id: 3,
+ name: '5.0 RC1',
+ title: '5.0 RC1',
+};
+
+export const mockMilestones = [
+ {
+ id: 2,
+ name: '5.0',
+ title: '5.0',
+ },
+ mockRegularMilestone,
+ mockEscapedMilestone,
+];
+
export const mockAuthorToken = {
type: 'author_username',
icon: 'user',
@@ -42,7 +67,29 @@ export const mockAuthorToken = {
fetchAuthors: Api.projectUsers.bind(Api),
};
-export const mockAvailableTokens = [mockAuthorToken];
+export const mockLabelToken = {
+ type: 'label_name',
+ icon: 'labels',
+ title: 'Label',
+ unique: false,
+ symbol: '~',
+ token: LabelToken,
+ operators: [{ value: '=', description: 'is', default: 'true' }],
+ fetchLabels: () => Promise.resolve(mockLabels),
+};
+
+export const mockMilestoneToken = {
+ type: 'milestone_title',
+ icon: 'clock',
+ title: 'Milestone',
+ unique: true,
+ symbol: '%',
+ token: MilestoneToken,
+ operators: [{ value: '=', description: 'is', default: 'true' }],
+ fetchMilestones: () => Promise.resolve({ data: mockMilestones }),
+};
+
+export const mockAvailableTokens = [mockAuthorToken, mockLabelToken];
export const mockHistoryItems = [
[
@@ -53,6 +100,13 @@ export const mockHistoryItems = [
operator: '=',
},
},
+ {
+ type: 'label_name',
+ value: {
+ data: 'Bug',
+ operator: '=',
+ },
+ },
'duo',
],
[
diff --git a/spec/frontend/vue_shared/components/filtered_search_bar/tokens/author_token_spec.js b/spec/frontend/vue_shared/components/filtered_search_bar/tokens/author_token_spec.js
index 45294096eda..160febf9d06 100644
--- a/spec/frontend/vue_shared/components/filtered_search_bar/tokens/author_token_spec.js
+++ b/spec/frontend/vue_shared/components/filtered_search_bar/tokens/author_token_spec.js
@@ -4,7 +4,7 @@ import MockAdapter from 'axios-mock-adapter';
import waitForPromises from 'helpers/wait_for_promises';
import axios from '~/lib/utils/axios_utils';
-import createFlash from '~/flash';
+import { deprecatedCreateFlash as createFlash } from '~/flash';
import AuthorToken from '~/vue_shared/components/filtered_search_bar/tokens/author_token.vue';
import { mockAuthorToken, mockAuthors } from '../mock_data';
diff --git a/spec/frontend/vue_shared/components/filtered_search_bar/tokens/label_token_spec.js b/spec/frontend/vue_shared/components/filtered_search_bar/tokens/label_token_spec.js
new file mode 100644
index 00000000000..0e60ee99327
--- /dev/null
+++ b/spec/frontend/vue_shared/components/filtered_search_bar/tokens/label_token_spec.js
@@ -0,0 +1,170 @@
+import { mount } from '@vue/test-utils';
+import { GlFilteredSearchToken, GlFilteredSearchTokenSegment } from '@gitlab/ui';
+import MockAdapter from 'axios-mock-adapter';
+import waitForPromises from 'helpers/wait_for_promises';
+import {
+ mockRegularLabel,
+ mockLabels,
+} from 'jest/vue_shared/components/sidebar/labels_select_vue/mock_data';
+import axios from '~/lib/utils/axios_utils';
+
+import { deprecatedCreateFlash as createFlash } from '~/flash';
+import LabelToken from '~/vue_shared/components/filtered_search_bar/tokens/label_token.vue';
+
+import { mockLabelToken } from '../mock_data';
+
+jest.mock('~/flash');
+
+const createComponent = ({ config = mockLabelToken, value = { data: '' }, active = false } = {}) =>
+ mount(LabelToken, {
+ propsData: {
+ config,
+ value,
+ active,
+ },
+ provide: {
+ portalName: 'fake target',
+ alignSuggestions: function fakeAlignSuggestions() {},
+ },
+ stubs: {
+ Portal: {
+ template: '<div><slot></slot></div>',
+ },
+ GlFilteredSearchSuggestionList: {
+ template: '<div></div>',
+ methods: {
+ getValue: () => '=',
+ },
+ },
+ },
+ });
+
+describe('LabelToken', () => {
+ let mock;
+ let wrapper;
+
+ beforeEach(() => {
+ mock = new MockAdapter(axios);
+ wrapper = createComponent();
+ });
+
+ afterEach(() => {
+ mock.restore();
+ wrapper.destroy();
+ });
+
+ describe('computed', () => {
+ beforeEach(async () => {
+ // Label title with spaces is always enclosed in quotations by component.
+ wrapper = createComponent({ value: { data: `"${mockRegularLabel.title}"` } });
+
+ wrapper.setData({
+ labels: mockLabels,
+ });
+
+ await wrapper.vm.$nextTick();
+ });
+
+ describe('currentValue', () => {
+ it('returns lowercase string for `value.data`', () => {
+ expect(wrapper.vm.currentValue).toBe('"foo label"');
+ });
+ });
+
+ describe('activeLabel', () => {
+ it('returns object for currently present `value.data`', () => {
+ expect(wrapper.vm.activeLabel).toEqual(mockRegularLabel);
+ });
+ });
+
+ describe('containerStyle', () => {
+ it('returns object containing `backgroundColor` and `color` properties based on `activeLabel` value', () => {
+ expect(wrapper.vm.containerStyle).toEqual({
+ backgroundColor: mockRegularLabel.color,
+ color: mockRegularLabel.textColor,
+ });
+ });
+
+ it('returns empty object when `activeLabel` is not set', async () => {
+ wrapper.setData({
+ labels: [],
+ });
+
+ await wrapper.vm.$nextTick();
+
+ expect(wrapper.vm.containerStyle).toEqual({});
+ });
+ });
+ });
+
+ describe('methods', () => {
+ describe('fetchLabelBySearchTerm', () => {
+ it('calls `config.fetchLabels` with provided searchTerm param', () => {
+ jest.spyOn(wrapper.vm.config, 'fetchLabels');
+
+ wrapper.vm.fetchLabelBySearchTerm('foo');
+
+ expect(wrapper.vm.config.fetchLabels).toHaveBeenCalledWith('foo');
+ });
+
+ it('sets response to `labels` when request is succesful', () => {
+ jest.spyOn(wrapper.vm.config, 'fetchLabels').mockResolvedValue(mockLabels);
+
+ wrapper.vm.fetchLabelBySearchTerm('foo');
+
+ return waitForPromises().then(() => {
+ expect(wrapper.vm.labels).toEqual(mockLabels);
+ });
+ });
+
+ it('calls `createFlash` with flash error message when request fails', () => {
+ jest.spyOn(wrapper.vm.config, 'fetchLabels').mockRejectedValue({});
+
+ wrapper.vm.fetchLabelBySearchTerm('foo');
+
+ return waitForPromises().then(() => {
+ expect(createFlash).toHaveBeenCalledWith('There was a problem fetching labels.');
+ });
+ });
+
+ it('sets `loading` to false when request completes', () => {
+ jest.spyOn(wrapper.vm.config, 'fetchLabels').mockRejectedValue({});
+
+ wrapper.vm.fetchLabelBySearchTerm('foo');
+
+ return waitForPromises().then(() => {
+ expect(wrapper.vm.loading).toBe(false);
+ });
+ });
+ });
+ });
+
+ describe('template', () => {
+ beforeEach(async () => {
+ wrapper = createComponent({ value: { data: `"${mockRegularLabel.title}"` } });
+
+ wrapper.setData({
+ labels: mockLabels,
+ });
+
+ await wrapper.vm.$nextTick();
+ });
+
+ it('renders gl-filtered-search-token component', () => {
+ expect(wrapper.find(GlFilteredSearchToken).exists()).toBe(true);
+ });
+
+ it('renders token item when value is selected', () => {
+ const tokenSegments = wrapper.findAll(GlFilteredSearchTokenSegment);
+
+ expect(tokenSegments).toHaveLength(3); // Label, =, "Foo Label"
+ expect(tokenSegments.at(2).text()).toBe(`~${mockRegularLabel.title}`); // "Foo Label"
+ expect(
+ tokenSegments
+ .at(2)
+ .find('.gl-token')
+ .attributes('style'),
+ ).toBe('background-color: rgb(186, 218, 85); color: rgb(255, 255, 255);');
+ });
+ });
+});
diff --git a/spec/frontend/vue_shared/components/filtered_search_bar/tokens/milestone_token_spec.js b/spec/frontend/vue_shared/components/filtered_search_bar/tokens/milestone_token_spec.js
new file mode 100644
index 00000000000..de893bf44c8
--- /dev/null
+++ b/spec/frontend/vue_shared/components/filtered_search_bar/tokens/milestone_token_spec.js
@@ -0,0 +1,152 @@
+import { mount } from '@vue/test-utils';
+import { GlFilteredSearchToken, GlFilteredSearchTokenSegment } from '@gitlab/ui';
+import MockAdapter from 'axios-mock-adapter';
+import waitForPromises from 'helpers/wait_for_promises';
+import axios from '~/lib/utils/axios_utils';
+
+import createFlash from '~/flash';
+import MilestoneToken from '~/vue_shared/components/filtered_search_bar/tokens/milestone_token.vue';
+
+import {
+ mockMilestoneToken,
+ mockMilestones,
+ mockRegularMilestone,
+ mockEscapedMilestone,
+} from '../mock_data';
+
+jest.mock('~/flash');
+
+const createComponent = ({
+ config = mockMilestoneToken,
+ value = { data: '' },
+ active = false,
+} = {}) =>
+ mount(MilestoneToken, {
+ propsData: {
+ config,
+ value,
+ active,
+ },
+ provide: {
+ portalName: 'fake target',
+ alignSuggestions: function fakeAlignSuggestions() {},
+ },
+ stubs: {
+ Portal: {
+ template: '<div><slot></slot></div>',
+ },
+ GlFilteredSearchSuggestionList: {
+ template: '<div></div>',
+ methods: {
+ getValue: () => '=',
+ },
+ },
+ },
+ });
+
+describe('MilestoneToken', () => {
+ let mock;
+ let wrapper;
+
+ beforeEach(() => {
+ mock = new MockAdapter(axios);
+ wrapper = createComponent();
+ });
+
+ afterEach(() => {
+ mock.restore();
+ wrapper.destroy();
+ });
+
+ describe('computed', () => {
+ beforeEach(async () => {
+ // Milestone title with spaces is always enclosed in quotations by component.
+ wrapper = createComponent({ value: { data: `"${mockEscapedMilestone.title}"` } });
+
+ wrapper.setData({
+ milestones: mockMilestones,
+ });
+
+ await wrapper.vm.$nextTick();
+ });
+
+ describe('currentValue', () => {
+ it('returns lowercase string for `value.data`', () => {
+ expect(wrapper.vm.currentValue).toBe('"5.0 rc1"');
+ });
+ });
+
+ describe('activeMilestone', () => {
+ it('returns object for currently present `value.data`', () => {
+ expect(wrapper.vm.activeMilestone).toEqual(mockEscapedMilestone);
+ });
+ });
+ });
+
+ describe('methods', () => {
+ describe('fetchMilestoneBySearchTerm', () => {
+ it('calls `config.fetchMilestones` with provided searchTerm param', () => {
+ jest.spyOn(wrapper.vm.config, 'fetchMilestones');
+
+ wrapper.vm.fetchMilestoneBySearchTerm('foo');
+
+ expect(wrapper.vm.config.fetchMilestones).toHaveBeenCalledWith('foo');
+ });
+
+ it('sets response to `milestones` when request is successful', () => {
+ jest.spyOn(wrapper.vm.config, 'fetchMilestones').mockResolvedValue({
+ data: mockMilestones,
+ });
+
+ wrapper.vm.fetchMilestoneBySearchTerm();
+
+ return waitForPromises().then(() => {
+ expect(wrapper.vm.milestones).toEqual(mockMilestones);
+ });
+ });
+
+ it('calls `createFlash` with flash error message when request fails', () => {
+ jest.spyOn(wrapper.vm.config, 'fetchMilestones').mockRejectedValue({});
+
+ wrapper.vm.fetchMilestoneBySearchTerm('foo');
+
+ return waitForPromises().then(() => {
+ expect(createFlash).toHaveBeenCalledWith('There was a problem fetching milestones.');
+ });
+ });
+
+ it('sets `loading` to false when request completes', () => {
+ jest.spyOn(wrapper.vm.config, 'fetchMilestones').mockRejectedValue({});
+
+ wrapper.vm.fetchMilestoneBySearchTerm('foo');
+
+ return waitForPromises().then(() => {
+ expect(wrapper.vm.loading).toBe(false);
+ });
+ });
+ });
+ });
+
+ describe('template', () => {
+ beforeEach(async () => {
+ wrapper = createComponent({ value: { data: `"${mockRegularMilestone.title}"` } });
+
+ wrapper.setData({
+ milestones: mockMilestones,
+ });
+
+ await wrapper.vm.$nextTick();
+ });
+
+ it('renders gl-filtered-search-token component', () => {
+ expect(wrapper.find(GlFilteredSearchToken).exists()).toBe(true);
+ });
+
+ it('renders token item when value is selected', () => {
+ const tokenSegments = wrapper.findAll(GlFilteredSearchTokenSegment);
+
+ expect(tokenSegments).toHaveLength(3); // Milestone, =, '%"4.0"'
+ expect(tokenSegments.at(2).text()).toBe(`%"${mockRegularMilestone.title}"`); // "4.0 RC1"
+ });
+ });
+});