Commit d9145389 authored by Martin Wortschack's avatar Martin Wortschack

Merge branch 'filter-pipeline-by-branch' into 'master'

Filter pipelines by branch name

See merge request gitlab-org/gitlab!31386
parents ec533848 e7be625f
...@@ -249,15 +249,22 @@ export default { ...@@ -249,15 +249,22 @@ export default {
createFlash(s__('Pipelines|Something went wrong while cleaning runners cache.')); createFlash(s__('Pipelines|Something went wrong while cleaning runners cache.'));
}); });
}, },
resetRequestData() {
this.requestData = { page: this.page, scope: this.scope };
},
filterPipelines(filters) { filterPipelines(filters) {
this.resetRequestData();
filters.forEach(filter => { filters.forEach(filter => {
// do not add Any for username query param, so we
// can fetch all trigger authors
if (filter.value.data !== ANY_TRIGGER_AUTHOR) {
this.requestData[filter.type] = filter.value.data; this.requestData[filter.type] = filter.value.data;
}
}); });
// set query params back to default if filtering by Any author if (filters.length === 0) {
// or input is cleared on submit this.resetRequestData();
if (this.requestData.username === ANY_TRIGGER_AUTHOR || filters.length === 0) {
this.requestData = { page: this.page, scope: this.scope };
} }
this.updateContent(this.requestData); this.updateContent(this.requestData);
......
...@@ -2,8 +2,10 @@ ...@@ -2,8 +2,10 @@
import { GlFilteredSearch } from '@gitlab/ui'; import { GlFilteredSearch } from '@gitlab/ui';
import { __, s__ } from '~/locale'; 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 Api from '~/api'; import Api from '~/api';
import createFlash from '~/flash'; import createFlash from '~/flash';
import { FETCH_AUTHOR_ERROR_MESSAGE, FETCH_BRANCH_ERROR_MESSAGE } from '../constants';
export default { export default {
components: { components: {
...@@ -22,6 +24,7 @@ export default { ...@@ -22,6 +24,7 @@ export default {
data() { data() {
return { return {
projectUsers: null, projectUsers: null,
projectBranches: null,
}; };
}, },
computed: { computed: {
...@@ -31,11 +34,21 @@ export default { ...@@ -31,11 +34,21 @@ export default {
type: 'username', type: 'username',
icon: 'user', icon: 'user',
title: s__('Pipeline|Trigger author'), title: s__('Pipeline|Trigger author'),
dataType: 'username',
unique: true, unique: true,
token: PipelineTriggerAuthorToken, token: PipelineTriggerAuthorToken,
operators: [{ value: '=', description: __('is'), default: 'true' }], operators: [{ value: '=', description: __('is'), default: 'true' }],
triggerAuthors: this.projectUsers, triggerAuthors: this.projectUsers,
projectId: this.projectId,
},
{
type: 'ref',
icon: 'branch',
title: s__('Pipeline|Branch name'),
unique: true,
token: PipelineBranchNameToken,
operators: [{ value: '=', description: __('is'), default: 'true' }],
branches: this.projectBranches,
projectId: this.projectId,
}, },
]; ];
}, },
...@@ -46,7 +59,16 @@ export default { ...@@ -46,7 +59,16 @@ export default {
this.projectUsers = users; this.projectUsers = users;
}) })
.catch(err => { .catch(err => {
createFlash(__('There was a problem fetching project users.')); createFlash(FETCH_AUTHOR_ERROR_MESSAGE);
throw err;
});
Api.branches(this.projectId)
.then(({ data }) => {
this.projectBranches = data.map(branch => branch.name);
})
.catch(err => {
createFlash(FETCH_BRANCH_ERROR_MESSAGE);
throw err; throw err;
}); });
}, },
......
<script>
import { GlFilteredSearchToken, GlFilteredSearchSuggestion, GlLoadingIcon } from '@gitlab/ui';
import Api from '~/api';
import { FETCH_BRANCH_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 {
branches: this.config.branches,
loading: true,
};
},
methods: {
fetchBranchBySearchTerm(searchTerm) {
Api.branches(this.config.projectId, searchTerm)
.then(res => {
this.branches = res.data.map(branch => branch.name);
this.loading = false;
})
.catch(err => {
createFlash(FETCH_BRANCH_ERROR_MESSAGE);
this.loading = false;
throw err;
});
},
searchBranches: debounce(function debounceSearch({ data }) {
this.fetchBranchBySearchTerm(data);
}, FILTER_PIPELINES_SEARCH_DELAY),
},
};
</script>
<template>
<gl-filtered-search-token
:config="config"
v-bind="{ ...$props, ...$attrs }"
v-on="$listeners"
@input="searchBranches"
>
<template #suggestions>
<gl-loading-icon v-if="loading" />
<template v-else>
<gl-filtered-search-suggestion
v-for="(branch, index) in branches"
:key="index"
:value="branch"
>
{{ branch }}
</gl-filtered-search-suggestion>
</template>
</template>
</gl-filtered-search-token>
</template>
...@@ -4,8 +4,16 @@ import { ...@@ -4,8 +4,16 @@ import {
GlAvatar, GlAvatar,
GlFilteredSearchSuggestion, GlFilteredSearchSuggestion,
GlDropdownDivider, GlDropdownDivider,
GlLoadingIcon,
} from '@gitlab/ui'; } from '@gitlab/ui';
import { ANY_TRIGGER_AUTHOR } from '../../constants'; import Api from '~/api';
import createFlash from '~/flash';
import { debounce } from 'lodash';
import {
ANY_TRIGGER_AUTHOR,
FETCH_AUTHOR_ERROR_MESSAGE,
FILTER_PIPELINES_SEARCH_DELAY,
} from '../../constants';
export default { export default {
anyTriggerAuthor: ANY_TRIGGER_AUTHOR, anyTriggerAuthor: ANY_TRIGGER_AUTHOR,
...@@ -14,6 +22,7 @@ export default { ...@@ -14,6 +22,7 @@ export default {
GlAvatar, GlAvatar,
GlFilteredSearchSuggestion, GlFilteredSearchSuggestion,
GlDropdownDivider, GlDropdownDivider,
GlLoadingIcon,
}, },
props: { props: {
config: { config: {
...@@ -25,26 +34,49 @@ export default { ...@@ -25,26 +34,49 @@ export default {
required: true, required: true,
}, },
}, },
data() {
return {
users: this.config.triggerAuthors,
loading: true,
};
},
computed: { computed: {
currentValue() { currentValue() {
return this.value.data.toLowerCase(); return this.value.data.toLowerCase();
}, },
filteredTriggerAuthors() {
return this.config.triggerAuthors.filter(user => {
return user.username.toLowerCase().includes(this.currentValue);
});
},
activeUser() { activeUser() {
return this.config.triggerAuthors.find(user => { return this.users.find(user => {
return user.username.toLowerCase() === this.currentValue; return user.username.toLowerCase() === this.currentValue;
}); });
}, },
}, },
methods: {
fetchAuthorBySearchTerm(searchTerm) {
Api.projectUsers(this.config.projectId, searchTerm)
.then(res => {
this.users = res;
this.loading = false;
})
.catch(err => {
createFlash(FETCH_AUTHOR_ERROR_MESSAGE);
this.loading = false;
throw err;
});
},
searchAuthors: debounce(function debounceSearch({ data }) {
this.fetchAuthorBySearchTerm(data);
}, FILTER_PIPELINES_SEARCH_DELAY),
},
}; };
</script> </script>
<template> <template>
<gl-filtered-search-token :config="config" v-bind="{ ...$props, ...$attrs }" v-on="$listeners"> <gl-filtered-search-token
:config="config"
v-bind="{ ...$props, ...$attrs }"
v-on="$listeners"
@input="searchAuthors"
>
<template #view="{inputValue}"> <template #view="{inputValue}">
<gl-avatar <gl-avatar
v-if="activeUser" v-if="activeUser"
...@@ -60,8 +92,11 @@ export default { ...@@ -60,8 +92,11 @@ export default {
$options.anyTriggerAuthor $options.anyTriggerAuthor
}}</gl-filtered-search-suggestion> }}</gl-filtered-search-suggestion>
<gl-dropdown-divider /> <gl-dropdown-divider />
<gl-loading-icon v-if="loading" />
<template v-else>
<gl-filtered-search-suggestion <gl-filtered-search-suggestion
v-for="user in filteredTriggerAuthors" v-for="user in users"
:key="user.username" :key="user.username"
:value="user.username" :value="user.username"
> >
...@@ -74,5 +109,6 @@ export default { ...@@ -74,5 +109,6 @@ export default {
</div> </div>
</gl-filtered-search-suggestion> </gl-filtered-search-suggestion>
</template> </template>
</template>
</gl-filtered-search-token> </gl-filtered-search-token>
</template> </template>
import { __ } from '~/locale';
export const CANCEL_REQUEST = 'CANCEL_REQUEST'; export const CANCEL_REQUEST = 'CANCEL_REQUEST';
export const PIPELINES_TABLE = 'PIPELINES_TABLE'; export const PIPELINES_TABLE = 'PIPELINES_TABLE';
export const LAYOUT_CHANGE_DELAY = 300; export const LAYOUT_CHANGE_DELAY = 300;
export const FILTER_PIPELINES_SEARCH_DELAY = 200;
export const ANY_TRIGGER_AUTHOR = 'Any'; export const ANY_TRIGGER_AUTHOR = 'Any';
export const TestStatus = { export const TestStatus = {
...@@ -8,3 +11,6 @@ export const TestStatus = { ...@@ -8,3 +11,6 @@ export const TestStatus = {
SKIPPED: 'skipped', SKIPPED: 'skipped',
SUCCESS: 'success', SUCCESS: 'success',
}; };
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.');
...@@ -19,7 +19,7 @@ export default class PipelinesService { ...@@ -19,7 +19,7 @@ export default class PipelinesService {
} }
getPipelines(data = {}) { getPipelines(data = {}) {
const { scope, page, username } = data; const { scope, page, username, ref } = data;
const { CancelToken } = axios; const { CancelToken } = axios;
const queryParams = { scope, page }; const queryParams = { scope, page };
...@@ -28,6 +28,10 @@ export default class PipelinesService { ...@@ -28,6 +28,10 @@ export default class PipelinesService {
queryParams.username = username; queryParams.username = username;
} }
if (ref) {
queryParams.ref = ref;
}
this.cancelationSource = CancelToken.source(); this.cancelationSource = CancelToken.source();
return axios.get(this.endpoint, { return axios.get(this.endpoint, {
......
...@@ -35,13 +35,18 @@ export default { ...@@ -35,13 +35,18 @@ export default {
}, },
onChangeWithFilter(params) { onChangeWithFilter(params) {
const { username } = this.requestData; const { username, ref } = this.requestData;
const paramsData = params;
if (username) { if (username) {
return { ...params, username }; paramsData.username = username;
} }
return params; if (ref) {
paramsData.ref = ref;
}
return paramsData;
}, },
updateInternalState(parameters) { updateInternalState(parameters) {
......
...@@ -12,7 +12,7 @@ class Projects::PipelinesController < Projects::ApplicationController ...@@ -12,7 +12,7 @@ class Projects::PipelinesController < Projects::ApplicationController
before_action :authorize_update_pipeline!, only: [:retry, :cancel] before_action :authorize_update_pipeline!, only: [:retry, :cancel]
before_action do before_action do
push_frontend_feature_flag(:junit_pipeline_view) push_frontend_feature_flag(:junit_pipeline_view)
push_frontend_feature_flag(:filter_pipelines_search) push_frontend_feature_flag(:filter_pipelines_search, default_enabled: true)
push_frontend_feature_flag(:dag_pipeline_tab) push_frontend_feature_flag(:dag_pipeline_tab)
end end
before_action :ensure_pipeline, only: [:show] before_action :ensure_pipeline, only: [:show]
...@@ -269,7 +269,7 @@ class Projects::PipelinesController < Projects::ApplicationController ...@@ -269,7 +269,7 @@ class Projects::PipelinesController < Projects::ApplicationController
end end
def index_params def index_params
params.permit(:scope, :username) params.permit(:scope, :username, :ref)
end end
end end
......
---
title: Filter pipelines by trigger author and branch name
merge_request: 31386
author:
type: added
...@@ -15237,6 +15237,9 @@ msgstr "" ...@@ -15237,6 +15237,9 @@ msgstr ""
msgid "Pipelines|parent" msgid "Pipelines|parent"
msgstr "" msgstr ""
msgid "Pipeline|Branch name"
msgstr ""
msgid "Pipeline|Commit" msgid "Pipeline|Commit"
msgstr "" msgstr ""
...@@ -21292,6 +21295,9 @@ msgstr "" ...@@ -21292,6 +21295,9 @@ msgstr ""
msgid "There was a problem communicating with your device." msgid "There was a problem communicating with your device."
msgstr "" msgstr ""
msgid "There was a problem fetching project branches."
msgstr ""
msgid "There was a problem fetching project users." msgid "There was a problem fetching project users."
msgstr "" msgstr ""
......
...@@ -195,6 +195,26 @@ describe Projects::PipelinesController do ...@@ -195,6 +195,26 @@ describe Projects::PipelinesController do
end end
end end
context 'filter by ref' do
let!(:pipeline) { create(:ci_pipeline, :running, project: project, ref: 'branch-1') }
context 'when pipelines with the ref exists' do
it 'returns matched pipelines' do
get_pipelines_index_json(ref: 'branch-1')
check_pipeline_response(returned: 1, all: 1, running: 1, pending: 0, finished: 0)
end
end
context 'when no pipeline with the ref exists' do
it 'returns empty list' do
get_pipelines_index_json(ref: 'invalid-ref')
check_pipeline_response(returned: 0, all: 0, running: 0, pending: 0, finished: 0)
end
end
end
def get_pipelines_index_json(params = {}) def get_pipelines_index_json(params = {})
get :index, params: { get :index, params: {
namespace_id: project.namespace, namespace_id: project.namespace,
......
...@@ -3,7 +3,13 @@ import { mount } from '@vue/test-utils'; ...@@ -3,7 +3,13 @@ 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, pipelineWithStages } from '../mock_data'; import {
users,
mockSearch,
pipelineWithStages,
branches,
mockBranchesAfterMap,
} from '../mock_data';
import { GlFilteredSearch } from '@gitlab/ui'; import { GlFilteredSearch } from '@gitlab/ui';
describe('Pipelines filtered search', () => { describe('Pipelines filtered search', () => {
...@@ -30,6 +36,7 @@ describe('Pipelines filtered search', () => { ...@@ -30,6 +36,7 @@ describe('Pipelines filtered search', () => {
mock = new MockAdapter(axios); mock = new MockAdapter(axios);
jest.spyOn(Api, 'projectUsers').mockResolvedValue(users); jest.spyOn(Api, 'projectUsers').mockResolvedValue(users);
jest.spyOn(Api, 'branches').mockResolvedValue({ data: branches });
createComponent(); createComponent();
}); });
...@@ -52,9 +59,19 @@ describe('Pipelines filtered search', () => { ...@@ -52,9 +59,19 @@ describe('Pipelines filtered search', () => {
type: 'username', type: 'username',
icon: 'user', icon: 'user',
title: 'Trigger author', title: 'Trigger author',
dataType: 'username',
unique: true, unique: true,
triggerAuthors: users, triggerAuthors: users,
projectId: '21',
operators: [expect.objectContaining({ value: '=' })],
});
expect(getSearchToken('ref')).toMatchObject({
type: 'ref',
icon: 'branch',
title: 'Branch name',
unique: true,
branches: mockBranchesAfterMap,
projectId: '21',
operators: [expect.objectContaining({ value: '=' })], operators: [expect.objectContaining({ value: '=' })],
}); });
}); });
...@@ -65,6 +82,12 @@ describe('Pipelines filtered search', () => { ...@@ -65,6 +82,12 @@ describe('Pipelines filtered search', () => {
expect(wrapper.vm.projectUsers).toEqual(users); expect(wrapper.vm.projectUsers).toEqual(users);
}); });
it('fetches and sets branches', () => {
expect(Api.branches).toHaveBeenCalled();
expect(wrapper.vm.projectBranches).toEqual(mockBranchesAfterMap);
});
it('emits filterPipelines on submit with correct filter', () => { it('emits filterPipelines on submit with correct filter', () => {
findFilteredSearch().vm.$emit('submit', mockSearch); findFilteredSearch().vm.$emit('submit', mockSearch);
......
...@@ -479,4 +479,90 @@ export const users = [ ...@@ -479,4 +479,90 @@ export const users = [
}, },
]; ];
export const mockSearch = { type: 'username', value: { data: 'root', operator: '=' } }; export const branches = [
{
name: 'branch-1',
commit: {
id: '21fb056cc47dcf706670e6de635b1b326490ebdc',
short_id: '21fb056c',
created_at: '2020-05-07T10:58:28.000-04:00',
parent_ids: null,
title: 'Add new file',
message: 'Add new file',
author_name: 'Administrator',
author_email: 'admin@example.com',
authored_date: '2020-05-07T10:58:28.000-04:00',
committer_name: 'Administrator',
committer_email: 'admin@example.com',
committed_date: '2020-05-07T10:58:28.000-04:00',
web_url:
'http://192.168.1.22:3000/root/dag-pipeline/-/commit/21fb056cc47dcf706670e6de635b1b326490ebdc',
},
merged: false,
protected: false,
developers_can_push: false,
developers_can_merge: false,
can_push: true,
default: false,
web_url: 'http://192.168.1.22:3000/root/dag-pipeline/-/tree/branch-1',
},
{
name: 'branch-10',
commit: {
id: '66673b07efef254dab7d537f0433a40e61cf84fe',
short_id: '66673b07',
created_at: '2020-03-16T11:04:46.000-04:00',
parent_ids: null,
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',
},
merged: false,
protected: false,
developers_can_push: false,
developers_can_merge: false,
can_push: true,
default: false,
web_url: 'http://192.168.1.22:3000/root/dag-pipeline/-/tree/branch-10',
},
{
name: 'branch-11',
commit: {
id: '66673b07efef254dab7d537f0433a40e61cf84fe',
short_id: '66673b07',
created_at: '2020-03-16T11:04:46.000-04:00',
parent_ids: null,
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',
},
merged: false,
protected: false,
developers_can_push: false,
developers_can_merge: false,
can_push: true,
default: false,
web_url: 'http://192.168.1.22:3000/root/dag-pipeline/-/tree/branch-11',
},
];
export const mockSearch = [
{ type: 'username', value: { data: 'root', operator: '=' } },
{ type: 'ref', value: { data: 'master', operator: '=' } },
];
export const mockBranchesAfterMap = ['branch-1', 'branch-10', 'branch-11'];
...@@ -5,7 +5,7 @@ import axios from '~/lib/utils/axios_utils'; ...@@ -5,7 +5,7 @@ import axios from '~/lib/utils/axios_utils';
import waitForPromises from 'helpers/wait_for_promises'; import waitForPromises from 'helpers/wait_for_promises';
import PipelinesComponent from '~/pipelines/components/pipelines.vue'; import PipelinesComponent from '~/pipelines/components/pipelines.vue';
import Store from '~/pipelines/stores/pipelines_store'; import Store from '~/pipelines/stores/pipelines_store';
import { pipelineWithStages, stageReply, users, mockSearch } from './mock_data'; import { pipelineWithStages, stageReply, users, mockSearch, branches } from './mock_data';
import { GlFilteredSearch } from '@gitlab/ui'; import { GlFilteredSearch } from '@gitlab/ui';
describe('Pipelines', () => { describe('Pipelines', () => {
...@@ -63,7 +63,9 @@ describe('Pipelines', () => { ...@@ -63,7 +63,9 @@ describe('Pipelines', () => {
beforeEach(() => { beforeEach(() => {
mock = new MockAdapter(axios); mock = new MockAdapter(axios);
pipelines = getJSONFixture(jsonFixtureName); pipelines = getJSONFixture(jsonFixtureName);
jest.spyOn(Api, 'projectUsers').mockResolvedValue(users); jest.spyOn(Api, 'projectUsers').mockResolvedValue(users);
jest.spyOn(Api, 'branches').mockResolvedValue({ data: branches });
}); });
afterEach(() => { afterEach(() => {
...@@ -674,9 +676,9 @@ describe('Pipelines', () => { ...@@ -674,9 +676,9 @@ describe('Pipelines', () => {
it('updates request data and query params on filter submit', () => { it('updates request data and query params on filter submit', () => {
const updateContentMock = jest.spyOn(wrapper.vm, 'updateContent'); const updateContentMock = jest.spyOn(wrapper.vm, 'updateContent');
const expectedQueryParams = { page: '1', scope: 'all', username: 'root' }; const expectedQueryParams = { page: '1', scope: 'all', username: 'root', ref: 'master' };
findFilteredSearch().vm.$emit('submit', [mockSearch]); findFilteredSearch().vm.$emit('submit', mockSearch);
expect(wrapper.vm.requestData).toEqual(expectedQueryParams); expect(wrapper.vm.requestData).toEqual(expectedQueryParams);
expect(updateContentMock).toHaveBeenCalledWith(expectedQueryParams); expect(updateContentMock).toHaveBeenCalledWith(expectedQueryParams);
......
import { GlFilteredSearchToken, GlFilteredSearchSuggestion, GlLoadingIcon } from '@gitlab/ui';
import { shallowMount } from '@vue/test-utils';
import PipelineBranchNameToken from '~/pipelines/components/tokens/pipeline_branch_name_token.vue';
import { branches } 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: 'ref',
icon: 'branch',
title: 'Branch name',
dataType: 'ref',
unique: true,
branches,
projectId: '21',
},
value: {
data: '',
},
};
const createComponent = (options, data) => {
wrapper = shallowMount(PipelineBranchNameToken, {
propsData: {
...defaultProps,
},
data() {
return {
...data,
};
},
...options,
});
};
beforeEach(() => {
createComponent();
});
afterEach(() => {
wrapper.destroy();
wrapper = null;
});
it('passes config correctly', () => {
expect(findFilteredSearchToken().props('config')).toEqual(defaultProps.config);
});
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 branches correctly', () => {
it('renders all trigger authors', () => {
createComponent({ stubs }, { branches, loading: false });
expect(findAllFilteredSearchSuggestions()).toHaveLength(branches.length);
});
it('renders only the branch searched for', () => {
const mockBranches = ['master'];
createComponent({ stubs }, { branches: mockBranches, loading: false });
expect(findAllFilteredSearchSuggestions()).toHaveLength(mockBranches.length);
});
});
});
import { GlFilteredSearchToken, GlFilteredSearchSuggestion } from '@gitlab/ui'; import { GlFilteredSearchToken, GlFilteredSearchSuggestion, GlLoadingIcon } from '@gitlab/ui';
import { shallowMount } from '@vue/test-utils'; import { shallowMount } from '@vue/test-utils';
import PipelineTriggerAuthorToken from '~/pipelines/components/tokens/pipeline_trigger_author_token.vue'; import PipelineTriggerAuthorToken from '~/pipelines/components/tokens/pipeline_trigger_author_token.vue';
import { users } from '../mock_data'; import { users } from '../mock_data';
...@@ -8,6 +8,7 @@ describe('Pipeline Trigger Author Token', () => { ...@@ -8,6 +8,7 @@ describe('Pipeline Trigger Author Token', () => {
const findFilteredSearchToken = () => wrapper.find(GlFilteredSearchToken); const findFilteredSearchToken = () => wrapper.find(GlFilteredSearchToken);
const findAllFilteredSearchSuggestions = () => wrapper.findAll(GlFilteredSearchSuggestion); const findAllFilteredSearchSuggestions = () => wrapper.findAll(GlFilteredSearchSuggestion);
const findLoadingIcon = () => wrapper.find(GlLoadingIcon);
const stubs = { const stubs = {
GlFilteredSearchToken: { GlFilteredSearchToken: {
...@@ -24,20 +25,27 @@ describe('Pipeline Trigger Author Token', () => { ...@@ -24,20 +25,27 @@ describe('Pipeline Trigger Author Token', () => {
unique: true, unique: true,
triggerAuthors: users, triggerAuthors: users,
}, },
value: {
data: '',
},
}; };
const createComponent = (props = {}, options) => { const createComponent = (options, data) => {
wrapper = shallowMount(PipelineTriggerAuthorToken, { wrapper = shallowMount(PipelineTriggerAuthorToken, {
propsData: { propsData: {
...props,
...defaultProps, ...defaultProps,
}, },
data() {
return {
...data,
};
},
...options, ...options,
}); });
}; };
beforeEach(() => { beforeEach(() => {
createComponent({ value: { data: '' } }); createComponent();
}); });
afterEach(() => { afterEach(() => {
...@@ -49,14 +57,41 @@ describe('Pipeline Trigger Author Token', () => { ...@@ -49,14 +57,41 @@ describe('Pipeline Trigger Author Token', () => {
expect(findFilteredSearchToken().props('config')).toEqual(defaultProps.config); expect(findFilteredSearchToken().props('config')).toEqual(defaultProps.config);
}); });
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 trigger authors correctly', () => { describe('shows trigger authors correctly', () => {
beforeEach(() => {});
it('renders all trigger authors', () => { it('renders all trigger authors', () => {
createComponent({ value: { data: '' } }, { stubs }); createComponent({ stubs }, { users, loading: false });
expect(findAllFilteredSearchSuggestions()).toHaveLength(7);
// should have length of all users plus the static 'Any' option
expect(findAllFilteredSearchSuggestions()).toHaveLength(users.length + 1);
}); });
it('renders only the trigger author searched for', () => { it('renders only the trigger author searched for', () => {
createComponent({ value: { data: 'root' } }, { stubs }); createComponent(
{ stubs },
{
users: [
{ name: 'Arnold', username: 'admin', state: 'active', avatar_url: 'avatar-link' },
],
loading: false,
},
);
expect(findAllFilteredSearchSuggestions()).toHaveLength(2); expect(findAllFilteredSearchSuggestions()).toHaveLength(2);
}); });
}); });
......
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