Commit e1e51f66 authored by Payton Burdette's avatar Payton Burdette Committed by Nicolò Maria Mezzopera

Add filter by tag name

Add filter by tag name,
also bring in public api
for fetching project tags.
parent 0dcc35a9
...@@ -53,6 +53,7 @@ const Api = { ...@@ -53,6 +53,7 @@ const Api = {
environmentsPath: '/api/:version/projects/:id/environments', environmentsPath: '/api/:version/projects/:id/environments',
rawFilePath: '/api/:version/projects/:id/repository/files/:path/raw', rawFilePath: '/api/:version/projects/:id/repository/files/:path/raw',
issuePath: '/api/:version/projects/:id/issues/:issue_iid', issuePath: '/api/:version/projects/:id/issues/:issue_iid',
tagsPath: '/api/:version/projects/:id/repository/tags',
group(groupId, callback = () => {}) { group(groupId, callback = () => {}) {
const url = Api.buildUrl(Api.groupPath).replace(':id', groupId); const url = Api.buildUrl(Api.groupPath).replace(':id', groupId);
...@@ -564,6 +565,18 @@ const Api = { ...@@ -564,6 +565,18 @@ const Api = {
return axios.put(url, data); return axios.put(url, data);
}, },
tags(id, query = '', options = {}) {
const url = Api.buildUrl(this.tagsPath).replace(':id', encodeURIComponent(id));
return axios.get(url, {
params: {
search: query,
per_page: DEFAULT_PER_PAGE,
...options,
},
});
},
buildUrl(url) { buildUrl(url) {
return joinPaths(gon.relative_url_root || '', url.replace(':version', gon.api_version)); return joinPaths(gon.relative_url_root || '', url.replace(':version', gon.api_version));
}, },
......
...@@ -10,8 +10,8 @@ import NavigationControls from './nav_controls.vue'; ...@@ -10,8 +10,8 @@ import NavigationControls from './nav_controls.vue';
import { getParameterByName } from '../../lib/utils/common_utils'; import { getParameterByName } from '../../lib/utils/common_utils';
import CIPaginationMixin from '../../vue_shared/mixins/ci_pagination_api_mixin'; import CIPaginationMixin from '../../vue_shared/mixins/ci_pagination_api_mixin';
import PipelinesFilteredSearch from './pipelines_filtered_search.vue'; import PipelinesFilteredSearch from './pipelines_filtered_search.vue';
import { ANY_TRIGGER_AUTHOR, RAW_TEXT_WARNING } from '../constants';
import { validateParams } from '../utils'; import { validateParams } from '../utils';
import { ANY_TRIGGER_AUTHOR, RAW_TEXT_WARNING, FILTER_TAG_IDENTIFIER } from '../constants';
import glFeatureFlagsMixin from '~/vue_shared/mixins/gl_feature_flags_mixin'; import glFeatureFlagsMixin from '~/vue_shared/mixins/gl_feature_flags_mixin';
export default { export default {
...@@ -266,10 +266,18 @@ export default { ...@@ -266,10 +266,18 @@ export default {
filters.forEach(filter => { filters.forEach(filter => {
// do not add Any for username query param, so we // do not add Any for username query param, so we
// can fetch all trigger authors // can fetch all trigger authors
if (filter.type && filter.value.data !== ANY_TRIGGER_AUTHOR) { if (
filter.type &&
filter.value.data !== ANY_TRIGGER_AUTHOR &&
filter.type !== FILTER_TAG_IDENTIFIER
) {
this.requestData[filter.type] = filter.value.data; this.requestData[filter.type] = filter.value.data;
} }
if (filter.type === FILTER_TAG_IDENTIFIER) {
this.requestData.ref = filter.value.data;
}
if (!filter.type) { if (!filter.type) {
createFlash(RAW_TEXT_WARNING, 'warning'); createFlash(RAW_TEXT_WARNING, 'warning');
} }
......
...@@ -4,9 +4,15 @@ import { __, s__ } from '~/locale'; ...@@ -4,9 +4,15 @@ import { __, s__ } from '~/locale';
import PipelineTriggerAuthorToken from './tokens/pipeline_trigger_author_token.vue'; import PipelineTriggerAuthorToken from './tokens/pipeline_trigger_author_token.vue';
import PipelineBranchNameToken from './tokens/pipeline_branch_name_token.vue'; import PipelineBranchNameToken from './tokens/pipeline_branch_name_token.vue';
import PipelineStatusToken from './tokens/pipeline_status_token.vue'; import PipelineStatusToken from './tokens/pipeline_status_token.vue';
import PipelineTagNameToken from './tokens/pipeline_tag_name_token.vue';
import { map } from 'lodash'; import { map } from 'lodash';
export default { export default {
userType: 'username',
branchType: 'ref',
tagType: 'tag',
statusType: 'status',
defaultTokensLength: 1,
components: { components: {
GlFilteredSearch, GlFilteredSearch,
}, },
...@@ -20,11 +26,19 @@ export default { ...@@ -20,11 +26,19 @@ export default {
required: true, required: true,
}, },
}, },
data() {
return {
internalValue: [],
};
},
computed: { computed: {
selectedTypes() {
return this.value.map(i => i.type);
},
tokens() { tokens() {
return [ return [
{ {
type: 'username', type: this.$options.userType,
icon: 'user', icon: 'user',
title: s__('Pipeline|Trigger author'), title: s__('Pipeline|Trigger author'),
unique: true, unique: true,
...@@ -33,16 +47,27 @@ export default { ...@@ -33,16 +47,27 @@ export default {
projectId: this.projectId, projectId: this.projectId,
}, },
{ {
type: 'ref', type: this.$options.branchType,
icon: 'branch', icon: 'branch',
title: s__('Pipeline|Branch name'), title: s__('Pipeline|Branch name'),
unique: true, unique: true,
token: PipelineBranchNameToken, token: PipelineBranchNameToken,
operators: [{ value: '=', description: __('is'), default: 'true' }], operators: [{ value: '=', description: __('is'), default: 'true' }],
projectId: this.projectId, projectId: this.projectId,
disabled: this.selectedTypes.includes(this.$options.tagType),
},
{
type: this.$options.tagType,
icon: 'tag',
title: s__('Pipeline|Tag name'),
unique: true,
token: PipelineTagNameToken,
operators: [{ value: '=', description: __('is'), default: 'true' }],
projectId: this.projectId,
disabled: this.selectedTypes.includes(this.$options.branchType),
}, },
{ {
type: 'status', type: this.$options.statusType,
icon: 'status', icon: 'status',
title: s__('Pipeline|Status'), title: s__('Pipeline|Status'),
unique: true, unique: true,
...@@ -51,12 +76,20 @@ export default { ...@@ -51,12 +76,20 @@ export default {
}, },
]; ];
}, },
paramsValue() { parsedParams() {
return map(this.params, (val, key) => ({ return map(this.params, (val, key) => ({
type: key, type: key,
value: { data: val, operator: '=' }, value: { data: val, operator: '=' },
})); }));
}, },
value: {
get() {
return this.internalValue.length > 0 ? this.internalValue : this.parsedParams;
},
set(value) {
this.internalValue = value;
},
},
}, },
methods: { methods: {
onSubmit(filters) { onSubmit(filters) {
...@@ -69,9 +102,9 @@ export default { ...@@ -69,9 +102,9 @@ export default {
<template> <template>
<div class="row-content-block"> <div class="row-content-block">
<gl-filtered-search <gl-filtered-search
v-model="value"
:placeholder="__('Filter pipelines')" :placeholder="__('Filter pipelines')"
:available-tokens="tokens" :available-tokens="tokens"
:value="paramsValue"
@submit="onSubmit" @submit="onSubmit"
/> />
</div> </div>
......
<script>
import { GlFilteredSearchToken, GlFilteredSearchSuggestion, GlLoadingIcon } from '@gitlab/ui';
import Api from '~/api';
import { FETCH_TAG_ERROR_MESSAGE, FILTER_PIPELINES_SEARCH_DELAY } from '../../constants';
import createFlash from '~/flash';
import { debounce } from 'lodash';
export default {
components: {
GlFilteredSearchToken,
GlFilteredSearchSuggestion,
GlLoadingIcon,
},
props: {
config: {
type: Object,
required: true,
},
value: {
type: Object,
required: true,
},
},
data() {
return {
tags: null,
loading: true,
};
},
created() {
this.fetchTags();
},
methods: {
fetchTags(searchTerm) {
Api.tags(this.config.projectId, searchTerm)
.then(({ data }) => {
this.tags = data.map(tag => tag.name);
this.loading = false;
})
.catch(err => {
createFlash(FETCH_TAG_ERROR_MESSAGE);
this.loading = false;
throw err;
});
},
searchTags: debounce(function debounceSearch({ data }) {
this.fetchTags(data);
}, FILTER_PIPELINES_SEARCH_DELAY),
},
};
</script>
<template>
<gl-filtered-search-token v-bind="{ ...$props, ...$attrs }" v-on="$listeners" @input="searchTags">
<template #suggestions>
<gl-loading-icon v-if="loading" />
<template v-else>
<gl-filtered-search-suggestion v-for="(tag, index) in tags" :key="index" :value="tag">
{{ tag }}
</gl-filtered-search-suggestion>
</template>
</template>
</gl-filtered-search-token>
</template>
...@@ -6,6 +6,7 @@ export const LAYOUT_CHANGE_DELAY = 300; ...@@ -6,6 +6,7 @@ export const LAYOUT_CHANGE_DELAY = 300;
export const FILTER_PIPELINES_SEARCH_DELAY = 200; export const FILTER_PIPELINES_SEARCH_DELAY = 200;
export const ANY_TRIGGER_AUTHOR = 'Any'; export const ANY_TRIGGER_AUTHOR = 'Any';
export const SUPPORTED_FILTER_PARAMETERS = ['username', 'ref', 'status']; export const SUPPORTED_FILTER_PARAMETERS = ['username', 'ref', 'status'];
export const FILTER_TAG_IDENTIFIER = 'tag';
export const TestStatus = { export const TestStatus = {
FAILED: 'failed', FAILED: 'failed',
...@@ -15,6 +16,7 @@ export const TestStatus = { ...@@ -15,6 +16,7 @@ export const TestStatus = {
export const FETCH_AUTHOR_ERROR_MESSAGE = __('There was a problem fetching project users.'); export const FETCH_AUTHOR_ERROR_MESSAGE = __('There was a problem fetching project users.');
export const FETCH_BRANCH_ERROR_MESSAGE = __('There was a problem fetching project branches.'); export const FETCH_BRANCH_ERROR_MESSAGE = __('There was a problem fetching project branches.');
export const FETCH_TAG_ERROR_MESSAGE = __('There was a problem fetching project tags.');
export const RAW_TEXT_WARNING = s__( export const RAW_TEXT_WARNING = s__(
'Pipeline|Raw text search is not currently supported. Please use the available search tokens.', 'Pipeline|Raw text search is not currently supported. Please use the available search tokens.',
); );
---
title: Filter Pipelines by Tag Name
merge_request: 32470
author:
type: added
...@@ -102,6 +102,7 @@ you can filter the pipeline list by: ...@@ -102,6 +102,7 @@ you can filter the pipeline list by:
- Trigger author - Trigger author
- Branch name - Branch name
- Status ([since GitLab 13.1](https://gitlab.com/gitlab-org/gitlab/-/issues/217617)) - Status ([since GitLab 13.1](https://gitlab.com/gitlab-org/gitlab/-/issues/217617))
- Tag ([since GitLab 13.1](https://gitlab.com/gitlab-org/gitlab/-/issues/217617))
### Run a pipeline manually ### Run a pipeline manually
......
...@@ -16171,6 +16171,9 @@ msgstr "" ...@@ -16171,6 +16171,9 @@ msgstr ""
msgid "Pipeline|Stop pipeline #%{pipelineId}?" msgid "Pipeline|Stop pipeline #%{pipelineId}?"
msgstr "" msgstr ""
msgid "Pipeline|Tag name"
msgstr ""
msgid "Pipeline|Trigger author" msgid "Pipeline|Trigger author"
msgstr "" msgstr ""
...@@ -22378,6 +22381,9 @@ msgstr "" ...@@ -22378,6 +22381,9 @@ msgstr ""
msgid "There was a problem fetching project branches." msgid "There was a problem fetching project branches."
msgstr "" msgstr ""
msgid "There was a problem fetching project tags."
msgstr ""
msgid "There was a problem fetching project users." msgid "There was a problem fetching project users."
msgstr "" msgstr ""
......
...@@ -725,4 +725,26 @@ describe('Api', () => { ...@@ -725,4 +725,26 @@ describe('Api', () => {
.catch(done.fail); .catch(done.fail);
}); });
}); });
describe('tags', () => {
it('fetches all tags of a particular project', done => {
const query = 'dummy query';
const options = { unused: 'option' };
const projectId = 8;
const expectedUrl = `${dummyUrlRoot}/api/${dummyApiVersion}/projects/${projectId}/repository/tags`;
mock.onGet(expectedUrl).reply(200, [
{
name: 'test',
},
]);
Api.tags(projectId, query, options)
.then(({ data }) => {
expect(data.length).toBe(1);
expect(data[0].name).toBe('test');
})
.then(done)
.catch(done.fail);
});
});
}); });
...@@ -3,7 +3,7 @@ import { mount } from '@vue/test-utils'; ...@@ -3,7 +3,7 @@ import { mount } from '@vue/test-utils';
import MockAdapter from 'axios-mock-adapter'; import MockAdapter from 'axios-mock-adapter';
import axios from '~/lib/utils/axios_utils'; import axios from '~/lib/utils/axios_utils';
import PipelinesFilteredSearch from '~/pipelines/components/pipelines_filtered_search.vue'; import PipelinesFilteredSearch from '~/pipelines/components/pipelines_filtered_search.vue';
import { users, mockSearch, branches } from '../mock_data'; import { users, mockSearch, branches, tags } from '../mock_data';
import { GlFilteredSearch } from '@gitlab/ui'; import { GlFilteredSearch } from '@gitlab/ui';
describe('Pipelines filtered search', () => { describe('Pipelines filtered search', () => {
...@@ -15,6 +15,10 @@ describe('Pipelines filtered search', () => { ...@@ -15,6 +15,10 @@ describe('Pipelines filtered search', () => {
findFilteredSearch() findFilteredSearch()
.props('availableTokens') .props('availableTokens')
.find(token => token.type === type); .find(token => token.type === type);
const findBranchToken = () => getSearchToken('ref');
const findTagToken = () => getSearchToken('tag');
const findUserToken = () => getSearchToken('username');
const findStatusToken = () => getSearchToken('status');
const createComponent = (params = {}) => { const createComponent = (params = {}) => {
wrapper = mount(PipelinesFilteredSearch, { wrapper = mount(PipelinesFilteredSearch, {
...@@ -31,6 +35,7 @@ describe('Pipelines filtered search', () => { ...@@ -31,6 +35,7 @@ describe('Pipelines filtered search', () => {
jest.spyOn(Api, 'projectUsers').mockResolvedValue(users); jest.spyOn(Api, 'projectUsers').mockResolvedValue(users);
jest.spyOn(Api, 'branches').mockResolvedValue({ data: branches }); jest.spyOn(Api, 'branches').mockResolvedValue({ data: branches });
jest.spyOn(Api, 'tags').mockResolvedValue({ data: tags });
createComponent(); createComponent();
}); });
...@@ -49,7 +54,7 @@ describe('Pipelines filtered search', () => { ...@@ -49,7 +54,7 @@ describe('Pipelines filtered search', () => {
}); });
it('displays search tokens', () => { it('displays search tokens', () => {
expect(getSearchToken('username')).toMatchObject({ expect(findUserToken()).toMatchObject({
type: 'username', type: 'username',
icon: 'user', icon: 'user',
title: 'Trigger author', title: 'Trigger author',
...@@ -58,7 +63,7 @@ describe('Pipelines filtered search', () => { ...@@ -58,7 +63,7 @@ describe('Pipelines filtered search', () => {
operators: [expect.objectContaining({ value: '=' })], operators: [expect.objectContaining({ value: '=' })],
}); });
expect(getSearchToken('ref')).toMatchObject({ expect(findBranchToken()).toMatchObject({
type: 'ref', type: 'ref',
icon: 'branch', icon: 'branch',
title: 'Branch name', title: 'Branch name',
...@@ -67,13 +72,21 @@ describe('Pipelines filtered search', () => { ...@@ -67,13 +72,21 @@ describe('Pipelines filtered search', () => {
operators: [expect.objectContaining({ value: '=' })], operators: [expect.objectContaining({ value: '=' })],
}); });
expect(getSearchToken('status')).toMatchObject({ expect(findStatusToken()).toMatchObject({
type: 'status', type: 'status',
icon: 'status', icon: 'status',
title: 'Status', title: 'Status',
unique: true, unique: true,
operators: [expect.objectContaining({ value: '=' })], operators: [expect.objectContaining({ value: '=' })],
}); });
expect(findTagToken()).toMatchObject({
type: 'tag',
icon: 'tag',
title: 'Tag name',
unique: true,
operators: [expect.objectContaining({ value: '=' })],
});
}); });
it('emits filterPipelines on submit with correct filter', () => { it('emits filterPipelines on submit with correct filter', () => {
...@@ -83,6 +96,48 @@ describe('Pipelines filtered search', () => { ...@@ -83,6 +96,48 @@ describe('Pipelines filtered search', () => {
expect(wrapper.emitted('filterPipelines')[0]).toEqual([mockSearch]); expect(wrapper.emitted('filterPipelines')[0]).toEqual([mockSearch]);
}); });
it('disables tag name token when branch name token is active', () => {
findFilteredSearch().vm.$emit('input', [
{ type: 'ref', value: { data: 'branch-1', operator: '=' } },
{ type: 'filtered-search-term', value: { data: '' } },
]);
return wrapper.vm.$nextTick().then(() => {
expect(findBranchToken().disabled).toBe(false);
expect(findTagToken().disabled).toBe(true);
});
});
it('disables branch name token when tag name token is active', () => {
findFilteredSearch().vm.$emit('input', [
{ type: 'tag', value: { data: 'tag-1', operator: '=' } },
{ type: 'filtered-search-term', value: { data: '' } },
]);
return wrapper.vm.$nextTick().then(() => {
expect(findBranchToken().disabled).toBe(true);
expect(findTagToken().disabled).toBe(false);
});
});
it('resets tokens disabled state on clear', () => {
findFilteredSearch().vm.$emit('clearInput');
return wrapper.vm.$nextTick().then(() => {
expect(findBranchToken().disabled).toBe(false);
expect(findTagToken().disabled).toBe(false);
});
});
it('resets tokens disabled state when clearing tokens by backspace', () => {
findFilteredSearch().vm.$emit('input', [{ type: 'filtered-search-term', value: { data: '' } }]);
return wrapper.vm.$nextTick().then(() => {
expect(findBranchToken().disabled).toBe(false);
expect(findTagToken().disabled).toBe(false);
});
});
describe('Url query params', () => { describe('Url query params', () => {
const params = { const params = {
username: 'deja.green', username: 'deja.green',
......
...@@ -560,6 +560,101 @@ export const branches = [ ...@@ -560,6 +560,101 @@ export const branches = [
}, },
]; ];
export const tags = [
{
name: 'tag-3',
message: '',
target: '66673b07efef254dab7d537f0433a40e61cf84fe',
commit: {
id: '66673b07efef254dab7d537f0433a40e61cf84fe',
short_id: '66673b07',
created_at: '2020-03-16T11:04:46.000-04:00',
parent_ids: ['def28bf679235071140180495f25b657e2203587'],
title: 'Update .gitlab-ci.yml',
message: 'Update .gitlab-ci.yml',
author_name: 'Administrator',
author_email: 'admin@example.com',
authored_date: '2020-03-16T11:04:46.000-04:00',
committer_name: 'Administrator',
committer_email: 'admin@example.com',
committed_date: '2020-03-16T11:04:46.000-04:00',
web_url:
'http://192.168.1.22:3000/root/dag-pipeline/-/commit/66673b07efef254dab7d537f0433a40e61cf84fe',
},
release: null,
protected: false,
},
{
name: 'tag-2',
message: '',
target: '66673b07efef254dab7d537f0433a40e61cf84fe',
commit: {
id: '66673b07efef254dab7d537f0433a40e61cf84fe',
short_id: '66673b07',
created_at: '2020-03-16T11:04:46.000-04:00',
parent_ids: ['def28bf679235071140180495f25b657e2203587'],
title: 'Update .gitlab-ci.yml',
message: 'Update .gitlab-ci.yml',
author_name: 'Administrator',
author_email: 'admin@example.com',
authored_date: '2020-03-16T11:04:46.000-04:00',
committer_name: 'Administrator',
committer_email: 'admin@example.com',
committed_date: '2020-03-16T11:04:46.000-04:00',
web_url:
'http://192.168.1.22:3000/root/dag-pipeline/-/commit/66673b07efef254dab7d537f0433a40e61cf84fe',
},
release: null,
protected: false,
},
{
name: 'tag-1',
message: '',
target: '66673b07efef254dab7d537f0433a40e61cf84fe',
commit: {
id: '66673b07efef254dab7d537f0433a40e61cf84fe',
short_id: '66673b07',
created_at: '2020-03-16T11:04:46.000-04:00',
parent_ids: ['def28bf679235071140180495f25b657e2203587'],
title: 'Update .gitlab-ci.yml',
message: 'Update .gitlab-ci.yml',
author_name: 'Administrator',
author_email: 'admin@example.com',
authored_date: '2020-03-16T11:04:46.000-04:00',
committer_name: 'Administrator',
committer_email: 'admin@example.com',
committed_date: '2020-03-16T11:04:46.000-04:00',
web_url:
'http://192.168.1.22:3000/root/dag-pipeline/-/commit/66673b07efef254dab7d537f0433a40e61cf84fe',
},
release: null,
protected: false,
},
{
name: 'master-tag',
message: '',
target: '66673b07efef254dab7d537f0433a40e61cf84fe',
commit: {
id: '66673b07efef254dab7d537f0433a40e61cf84fe',
short_id: '66673b07',
created_at: '2020-03-16T11:04:46.000-04:00',
parent_ids: ['def28bf679235071140180495f25b657e2203587'],
title: 'Update .gitlab-ci.yml',
message: 'Update .gitlab-ci.yml',
author_name: 'Administrator',
author_email: 'admin@example.com',
authored_date: '2020-03-16T11:04:46.000-04:00',
committer_name: 'Administrator',
committer_email: 'admin@example.com',
committed_date: '2020-03-16T11:04:46.000-04:00',
web_url:
'http://192.168.1.22:3000/root/dag-pipeline/-/commit/66673b07efef254dab7d537f0433a40e61cf84fe',
},
release: null,
protected: false,
},
];
export const mockSearch = [ export const mockSearch = [
{ type: 'username', value: { data: 'root', operator: '=' } }, { type: 'username', value: { data: 'root', operator: '=' } },
{ type: 'ref', value: { data: 'master', operator: '=' } }, { type: 'ref', value: { data: 'master', operator: '=' } },
...@@ -567,3 +662,5 @@ export const mockSearch = [ ...@@ -567,3 +662,5 @@ export const mockSearch = [
]; ];
export const mockBranchesAfterMap = ['branch-1', 'branch-10', 'branch-11']; export const mockBranchesAfterMap = ['branch-1', 'branch-10', 'branch-11'];
export const mockTagsAfterMap = ['tag-3', 'tag-2', 'tag-1', 'master-tag'];
...@@ -22,10 +22,9 @@ describe('Pipeline Branch Name Token', () => { ...@@ -22,10 +22,9 @@ describe('Pipeline Branch Name Token', () => {
type: 'ref', type: 'ref',
icon: 'branch', icon: 'branch',
title: 'Branch name', title: 'Branch name',
dataType: 'ref',
unique: true, unique: true,
branches,
projectId: '21', projectId: '21',
disabled: false,
}, },
value: { value: {
data: '', data: '',
...@@ -83,7 +82,7 @@ describe('Pipeline Branch Name Token', () => { ...@@ -83,7 +82,7 @@ describe('Pipeline Branch Name Token', () => {
}); });
describe('shows branches correctly', () => { describe('shows branches correctly', () => {
it('renders all trigger authors', () => { it('renders all branches', () => {
createComponent({ stubs }, { branches, loading: false }); createComponent({ stubs }, { branches, loading: false });
expect(findAllFilteredSearchSuggestions()).toHaveLength(branches.length); expect(findAllFilteredSearchSuggestions()).toHaveLength(branches.length);
......
import Api from '~/api';
import { GlFilteredSearchToken, GlFilteredSearchSuggestion, GlLoadingIcon } from '@gitlab/ui';
import { shallowMount } from '@vue/test-utils';
import PipelineTagNameToken from '~/pipelines/components/tokens/pipeline_tag_name_token.vue';
import { tags, mockTagsAfterMap } from '../mock_data';
describe('Pipeline Branch Name Token', () => {
let wrapper;
const findFilteredSearchToken = () => wrapper.find(GlFilteredSearchToken);
const findAllFilteredSearchSuggestions = () => wrapper.findAll(GlFilteredSearchSuggestion);
const findLoadingIcon = () => wrapper.find(GlLoadingIcon);
const stubs = {
GlFilteredSearchToken: {
template: `<div><slot name="suggestions"></slot></div>`,
},
};
const defaultProps = {
config: {
type: 'tag',
icon: 'tag',
title: 'Tag name',
unique: true,
projectId: '21',
disabled: false,
},
value: {
data: '',
},
};
const createComponent = (options, data) => {
wrapper = shallowMount(PipelineTagNameToken, {
propsData: {
...defaultProps,
},
data() {
return {
...data,
};
},
...options,
});
};
beforeEach(() => {
jest.spyOn(Api, 'tags').mockResolvedValue({ data: tags });
createComponent();
});
afterEach(() => {
wrapper.destroy();
wrapper = null;
});
it('passes config correctly', () => {
expect(findFilteredSearchToken().props('config')).toEqual(defaultProps.config);
});
it('fetches and sets project tags', () => {
expect(Api.tags).toHaveBeenCalled();
expect(wrapper.vm.tags).toEqual(mockTagsAfterMap);
expect(findLoadingIcon().exists()).toBe(false);
});
describe('displays loading icon correctly', () => {
it('shows loading icon', () => {
createComponent({ stubs }, { loading: true });
expect(findLoadingIcon().exists()).toBe(true);
});
it('does not show loading icon', () => {
createComponent({ stubs }, { loading: false });
expect(findLoadingIcon().exists()).toBe(false);
});
});
describe('shows tags correctly', () => {
it('renders all tags', () => {
createComponent({ stubs }, { tags, loading: false });
expect(findAllFilteredSearchSuggestions()).toHaveLength(tags.length);
});
it('renders only the tag searched for', () => {
const mockTags = ['master-tag'];
createComponent({ stubs }, { tags: mockTags, loading: false });
expect(findAllFilteredSearchSuggestions()).toHaveLength(mockTags.length);
});
});
});
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