Commit 039b1145 authored by David Barr's avatar David Barr

Add default branch to Pipelines page filter if no search term provided

Make the default branch name appear at the top of the dropdown list
when using the branch name filter on the Pipelines page with no
search term.

Changelog: added
parent 3e060e01
...@@ -98,6 +98,11 @@ export default { ...@@ -98,6 +98,11 @@ export default {
type: String, type: String,
required: true, required: true,
}, },
defaultBranchName: {
type: String,
required: false,
default: null,
},
params: { params: {
type: Object, type: Object,
required: true, required: true,
...@@ -347,6 +352,7 @@ export default { ...@@ -347,6 +352,7 @@ export default {
<pipelines-filtered-search <pipelines-filtered-search
class="gl-display-flex gl-flex-grow-1 gl-mr-4" class="gl-display-flex gl-flex-grow-1 gl-mr-4"
:project-id="projectId" :project-id="projectId"
:default-branch-name="defaultBranchName"
:params="validatedParams" :params="validatedParams"
@filterPipelines="filterPipelines" @filterPipelines="filterPipelines"
/> />
......
...@@ -24,6 +24,11 @@ export default { ...@@ -24,6 +24,11 @@ export default {
type: String, type: String,
required: true, required: true,
}, },
defaultBranchName: {
type: String,
required: false,
default: null,
},
params: { params: {
type: Object, type: Object,
required: true, required: true,
...@@ -57,6 +62,7 @@ export default { ...@@ -57,6 +62,7 @@ export default {
token: PipelineBranchNameToken, token: PipelineBranchNameToken,
operators: OPERATOR_IS_ONLY, operators: OPERATOR_IS_ONLY,
projectId: this.projectId, projectId: this.projectId,
defaultBranchName: this.defaultBranchName,
disabled: this.selectedTypes.includes(this.$options.tagType), disabled: this.selectedTypes.includes(this.$options.tagType),
}, },
{ {
......
...@@ -35,6 +35,13 @@ export default { ...@@ -35,6 +35,13 @@ export default {
Api.branches(this.config.projectId, searchterm) Api.branches(this.config.projectId, searchterm)
.then(({ data }) => { .then(({ data }) => {
this.branches = data.map((branch) => branch.name); this.branches = data.map((branch) => branch.name);
if (!searchterm && this.config.defaultBranchName) {
// Shift the default branch to the top of the list
this.branches = this.branches.filter(
(branch) => branch !== this.config.defaultBranchName,
);
this.branches.unshift(this.config.defaultBranchName);
}
this.loading = false; this.loading = false;
}) })
.catch((err) => { .catch((err) => {
......
...@@ -36,6 +36,7 @@ export const initPipelinesIndex = (selector = '#pipelines-list-vue') => { ...@@ -36,6 +36,7 @@ export const initPipelinesIndex = (selector = '#pipelines-list-vue') => {
ciLintPath, ciLintPath,
resetCachePath, resetCachePath,
projectId, projectId,
defaultBranchName,
params, params,
ciRunnerSettingsPath, ciRunnerSettingsPath,
anyRunnersAvailable, anyRunnersAvailable,
...@@ -75,6 +76,7 @@ export const initPipelinesIndex = (selector = '#pipelines-list-vue') => { ...@@ -75,6 +76,7 @@ export const initPipelinesIndex = (selector = '#pipelines-list-vue') => {
ciLintPath, ciLintPath,
resetCachePath, resetCachePath,
projectId, projectId,
defaultBranchName,
params: JSON.parse(params), params: JSON.parse(params),
ciRunnerSettingsPath, ciRunnerSettingsPath,
anyRunnersAvailable: parseBoolean(anyRunnersAvailable), anyRunnersAvailable: parseBoolean(anyRunnersAvailable),
......
...@@ -84,6 +84,7 @@ module Ci ...@@ -84,6 +84,7 @@ module Ci
data = { data = {
endpoint: list_url, endpoint: list_url,
project_id: project.id, project_id: project.id,
default_branch_name: project.default_branch,
params: params.to_json, params: params.to_json,
artifacts_endpoint: downloadable_artifacts_project_pipeline_path(project, artifacts_endpoint_placeholder, format: :json), artifacts_endpoint: downloadable_artifacts_project_pipeline_path(project, artifacts_endpoint_placeholder, format: :json),
artifacts_endpoint_placeholder: artifacts_endpoint_placeholder, artifacts_endpoint_placeholder: artifacts_endpoint_placeholder,
......
...@@ -27,6 +27,7 @@ describe('Pipelines filtered search', () => { ...@@ -27,6 +27,7 @@ describe('Pipelines filtered search', () => {
wrapper = mount(PipelinesFilteredSearch, { wrapper = mount(PipelinesFilteredSearch, {
propsData: { propsData: {
projectId: '21', projectId: '21',
defaultBranchName: 'main',
params, params,
}, },
attachTo: document.body, attachTo: document.body,
...@@ -69,6 +70,7 @@ describe('Pipelines filtered search', () => { ...@@ -69,6 +70,7 @@ describe('Pipelines filtered search', () => {
title: 'Branch name', title: 'Branch name',
unique: true, unique: true,
projectId: '21', projectId: '21',
defaultBranchName: 'main',
operators: OPERATOR_IS_ONLY, operators: OPERATOR_IS_ONLY,
}); });
......
...@@ -27,6 +27,7 @@ jest.mock('~/flash'); ...@@ -27,6 +27,7 @@ jest.mock('~/flash');
const mockProjectPath = 'twitter/flight'; const mockProjectPath = 'twitter/flight';
const mockProjectId = '21'; const mockProjectId = '21';
const mockDefaultBranchName = 'main';
const mockPipelinesEndpoint = `/${mockProjectPath}/pipelines.json`; const mockPipelinesEndpoint = `/${mockProjectPath}/pipelines.json`;
const mockPipelinesIds = mockPipelinesResponse.pipelines.map(({ id }) => id); const mockPipelinesIds = mockPipelinesResponse.pipelines.map(({ id }) => id);
const mockPipelineWithStages = mockPipelinesResponse.pipelines.find( const mockPipelineWithStages = mockPipelinesResponse.pipelines.find(
...@@ -85,6 +86,7 @@ describe('Pipelines', () => { ...@@ -85,6 +86,7 @@ describe('Pipelines', () => {
propsData: { propsData: {
store: new Store(), store: new Store(),
projectId: mockProjectId, projectId: mockProjectId,
defaultBranchName: mockDefaultBranchName,
endpoint: mockPipelinesEndpoint, endpoint: mockPipelinesEndpoint,
params: {}, params: {},
...props, ...props,
......
import { GlFilteredSearchToken, GlFilteredSearchSuggestion, GlLoadingIcon } from '@gitlab/ui'; import { GlFilteredSearchToken, GlFilteredSearchSuggestion, GlLoadingIcon } from '@gitlab/ui';
import { nextTick } from 'vue';
import { shallowMount } from '@vue/test-utils'; import { shallowMount } from '@vue/test-utils';
import waitForPromises from 'helpers/wait_for_promises';
import Api from '~/api'; import Api from '~/api';
import PipelineBranchNameToken from '~/pipelines/components/pipelines_list/tokens/pipeline_branch_name_token.vue'; import PipelineBranchNameToken from '~/pipelines/components/pipelines_list/tokens/pipeline_branch_name_token.vue';
import { branches, mockBranchesAfterMap } from '../mock_data'; import { branches, mockBranchesAfterMap } from '../mock_data';
...@@ -10,6 +12,8 @@ describe('Pipeline Branch Name Token', () => { ...@@ -10,6 +12,8 @@ describe('Pipeline Branch Name 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 findLoadingIcon = () => wrapper.find(GlLoadingIcon);
const getBranchSuggestions = () =>
findAllFilteredSearchSuggestions().wrappers.map((w) => w.text());
const stubs = { const stubs = {
GlFilteredSearchToken: { GlFilteredSearchToken: {
...@@ -24,6 +28,7 @@ describe('Pipeline Branch Name Token', () => { ...@@ -24,6 +28,7 @@ describe('Pipeline Branch Name Token', () => {
title: 'Branch name', title: 'Branch name',
unique: true, unique: true,
projectId: '21', projectId: '21',
defaultBranchName: null,
disabled: false, disabled: false,
}, },
value: { value: {
...@@ -31,6 +36,19 @@ describe('Pipeline Branch Name Token', () => { ...@@ -31,6 +36,19 @@ describe('Pipeline Branch Name Token', () => {
}, },
}; };
const optionsWithDefaultBranchName = (options) => {
return {
propsData: {
...defaultProps,
config: {
...defaultProps.config,
defaultBranchName: 'main',
},
},
...options,
};
};
const createComponent = (options, data) => { const createComponent = (options, data) => {
wrapper = shallowMount(PipelineBranchNameToken, { wrapper = shallowMount(PipelineBranchNameToken, {
propsData: { propsData: {
...@@ -94,5 +112,34 @@ describe('Pipeline Branch Name Token', () => { ...@@ -94,5 +112,34 @@ describe('Pipeline Branch Name Token', () => {
expect(findAllFilteredSearchSuggestions()).toHaveLength(mockBranches.length); expect(findAllFilteredSearchSuggestions()).toHaveLength(mockBranches.length);
}); });
it('shows the default branch first if no branch was searched for', async () => {
const mockBranches = [{ name: 'branch-1' }];
jest.spyOn(Api, 'branches').mockResolvedValue({ data: mockBranches });
createComponent(optionsWithDefaultBranchName({ stubs }), { loading: false });
await nextTick();
expect(getBranchSuggestions()).toEqual(['main', 'branch-1']);
});
it('does not show the default branch if a search term was provided', async () => {
const mockBranches = [{ name: 'branch-1' }];
jest.spyOn(Api, 'branches').mockResolvedValue({ data: mockBranches });
createComponent(optionsWithDefaultBranchName(), { loading: false });
findFilteredSearchToken().vm.$emit('input', { data: 'branch-1' });
await waitForPromises();
expect(getBranchSuggestions()).toEqual(['branch-1']);
});
it('shows the default branch only once if it appears in the results', async () => {
const mockBranches = [{ name: 'main' }];
jest.spyOn(Api, 'branches').mockResolvedValue({ data: mockBranches });
createComponent(optionsWithDefaultBranchName({ stubs }), { loading: false });
await nextTick();
expect(getBranchSuggestions()).toEqual(['main']);
});
}); });
}); });
...@@ -106,6 +106,7 @@ RSpec.describe Ci::PipelinesHelper do ...@@ -106,6 +106,7 @@ RSpec.describe Ci::PipelinesHelper do
it 'has the expected keys' do it 'has the expected keys' do
expect(subject.keys).to match_array([:endpoint, expect(subject.keys).to match_array([:endpoint,
:project_id, :project_id,
:default_branch_name,
:params, :params,
:artifacts_endpoint, :artifacts_endpoint,
:artifacts_endpoint_placeholder, :artifacts_endpoint_placeholder,
......
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