Commit 8ab77787 authored by Nicolò Maria Mezzopera's avatar Nicolò Maria Mezzopera

Merge branch 'filter-pipelines-by-tag-name' into 'master'

Filter Pipelines by Tag Name

See merge request gitlab-org/gitlab!32470
parents 0cb98d3a e1e51f66
...@@ -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
......
...@@ -16195,6 +16195,9 @@ msgstr "" ...@@ -16195,6 +16195,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 ""
...@@ -22414,6 +22417,9 @@ msgstr "" ...@@ -22414,6 +22417,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