Commit d8bb8fd4 authored by Mayra Cabrera's avatar Mayra Cabrera

Merge branch 'jivanvl-migrate-dropdown-branches-page' into 'master'

Move branches dropdown to gl-dropdown [RUN ALL RSPEC] [RUN AS-IF-FOSS]

See merge request gitlab-org/gitlab!57760
parents 0f5d90d2 f9802861
import Vue from 'vue';
import SortDropdown from './components/sort_dropdown.vue';
const mountDropdownApp = (el) => {
const { mode, projectBranchesFilteredPath, sortOptions } = el.dataset;
return new Vue({
el,
name: 'SortBranchesDropdownApp',
components: {
SortDropdown,
},
provide: {
mode,
projectBranchesFilteredPath,
sortOptions: JSON.parse(sortOptions),
},
render: (createElement) => createElement(SortDropdown),
});
};
export default () => {
const el = document.getElementById('js-branches-sort-dropdown');
return el ? mountDropdownApp(el) : null;
};
<script>
import { GlDropdown, GlDropdownItem, GlSearchBoxByClick } from '@gitlab/ui';
import { mergeUrlParams, visitUrl, getParameterValues } from '~/lib/utils/url_utility';
import { s__ } from '~/locale';
const OVERVIEW_MODE = 'overview';
export default {
i18n: {
searchPlaceholder: s__('Branches|Filter by branch name'),
},
components: {
GlDropdown,
GlDropdownItem,
GlSearchBoxByClick,
},
inject: ['projectBranchesFilteredPath', 'sortOptions', 'mode'],
data() {
return {
selectedKey: 'updated_desc',
searchTerm: '',
};
},
computed: {
shouldShowDropdown() {
return this.mode !== OVERVIEW_MODE;
},
selectedSortMethodName() {
return this.sortOptions[this.selectedKey];
},
},
created() {
const sortValue = getParameterValues('sort');
const searchValue = getParameterValues('search');
if (sortValue.length > 0) {
[this.selectedKey] = sortValue;
}
if (searchValue.length > 0) {
[this.searchTerm] = searchValue;
}
},
methods: {
isSortMethodSelected(sortKey) {
return sortKey === this.selectedKey;
},
visitUrlFromOption(sortKey) {
this.selectedKey = sortKey;
const urlParams = {};
if (this.mode !== OVERVIEW_MODE) {
urlParams.sort = sortKey;
}
urlParams.search = this.searchTerm.length > 0 ? this.searchTerm : null;
const newUrl = mergeUrlParams(urlParams, this.projectBranchesFilteredPath);
visitUrl(newUrl);
},
},
};
</script>
<template>
<div class="gl-display-flex gl-pr-4">
<gl-search-box-by-click
v-model="searchTerm"
:placeholder="$options.i18n.searchPlaceholder"
class="gl-pr-4"
data-testid="branch-search"
@submit="visitUrlFromOption(selectedKey)"
/>
<gl-dropdown
v-if="shouldShowDropdown"
:text="selectedSortMethodName"
data-testid="branches-dropdown"
>
<gl-dropdown-item
v-for="(value, key) in sortOptions"
:key="key"
:is-checked="isSortMethodSelected(key)"
is-check-item
@click="visitUrlFromOption(key)"
>{{ value }}</gl-dropdown-item
>
</gl-dropdown>
</div>
</template>
import AjaxLoadingSpinner from '~/branches/ajax_loading_spinner';
import BranchSortDropdown from '~/branches/branch_sort_dropdown';
import DeleteModal from '~/branches/branches_delete_modal';
import initDiverganceGraph from '~/branches/divergence_graph';
AjaxLoadingSpinner.init();
new DeleteModal(); // eslint-disable-line no-new
initDiverganceGraph(document.querySelector('.js-branch-list').dataset.divergingCountsEndpoint);
BranchSortDropdown();
......@@ -12,6 +12,9 @@ class Projects::BranchesController < Projects::ApplicationController
# Support legacy URLs
before_action :redirect_for_legacy_index_sort_or_search, only: [:index]
before_action :limit_diverging_commit_counts!, only: [:diverging_commit_counts]
before_action do
push_frontend_feature_flag(:gldropdown_branches)
end
feature_category :source_code_management
......
......@@ -20,6 +20,10 @@ module BranchesHelper
end
end
end
def gldropdrown_branches_enabled?
Feature.enabled?(:gldropdown_branches)
end
end
BranchesHelper.prepend_if_ee('EE::BranchesHelper')
......@@ -16,6 +16,7 @@
= link_to s_('Branches|All'), project_branches_filtered_path(@project, state: 'all'), title: s_('Branches|Show all branches')
.nav-controls
- if !gldropdrown_branches_enabled?
= form_tag(project_branches_filtered_path(@project, state: 'all'), method: :get) do
= search_field_tag :search, params[:search], { placeholder: s_('Branches|Filter by branch name'), id: 'branch-search', class: 'form-control search-text-input input-short', spellcheck: false }
......@@ -32,6 +33,9 @@
%li
= link_to title, project_branches_filtered_path(@project, state: 'all', search: params[:search], sort: value), class: ("is-active" if @sort == value)
- else
#js-branches-sort-dropdown{ data: { project_branches_filtered_path: project_branches_path(@project, state: 'all'), sort_options: branches_sort_options_hash.to_json, mode: @mode } }
- if can? current_user, :push_code, @project
= link_to project_merged_branches_path(@project),
class: 'gl-button btn btn-danger btn-danger-secondary has-tooltip qa-delete-merged-branches',
......
---
name: gldropdown_branches
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/57760
rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/326549
milestone: '13.11'
type: development
group: group::continuous integration
default_enabled: false
......@@ -9,12 +9,31 @@ RSpec.describe "User deletes branch", :js do
before do
project.add_developer(user)
sign_in(user)
end
it "deletes branch" do
stub_feature_flags(gldropdown_branches: false)
visit(project_branches_path(project))
fill_in("branch-search", with: "improve/awesome").native.send_keys(:enter)
page.within(".js-branch-improve\\/awesome") do
accept_alert { find(".btn-danger").click }
end
wait_for_requests
expect(page).to have_css(".js-branch-improve\\/awesome", visible: :hidden)
end
context 'with gldropdown_branches enabled' do
it "deletes branch" do
fill_in("branch-search", with: "improve/awesome").native.send_keys(:enter)
visit(project_branches_path(project))
branch_search = find('input[data-testid="branch-search"]')
branch_search.set('improve/awesome')
branch_search.native.send_keys(:enter)
page.within(".js-branch-improve\\/awesome") do
accept_alert { find(".btn-danger").click }
......@@ -24,4 +43,5 @@ RSpec.describe "User deletes branch", :js do
expect(page).to have_css(".js-branch-improve\\/awesome", visible: :hidden)
end
end
end
......@@ -86,6 +86,7 @@ RSpec.describe 'Branches' do
describe 'Find branches' do
it 'shows filtered branches', :js do
stub_feature_flags(gldropdown_branches: false)
visit project_branches_path(project)
fill_in 'branch-search', with: 'fix'
......@@ -94,6 +95,20 @@ RSpec.describe 'Branches' do
expect(page).to have_content('fix')
expect(find('.all-branches')).to have_selector('li', count: 1)
end
context 'with gldropdown_branches enabled' do
it 'shows filtered branches', :js do
visit project_branches_path(project)
branch_search = find('input[data-testid="branch-search"]')
branch_search.set('fix')
branch_search.native.send_keys(:enter)
expect(page).to have_content('fix')
expect(find('.all-branches')).to have_selector('li', count: 1)
end
end
end
describe 'Delete unprotected branch on Overview' do
......@@ -115,6 +130,7 @@ RSpec.describe 'Branches' do
end
it 'sorts the branches by name' do
stub_feature_flags(gldropdown_branches: false)
visit project_branches_filtered_path(project, state: 'all')
click_button "Last updated" # Open sorting dropdown
......@@ -123,7 +139,21 @@ RSpec.describe 'Branches' do
expect(page).to have_content(sorted_branches(repository, count: 20, sort_by: :name))
end
context 'with gldropdown_branches enabled' do
it 'sorts the branches by name', :js do
visit project_branches_filtered_path(project, state: 'all')
click_button "Last updated" # Open sorting dropdown
within '[data-testid="branches-dropdown"]' do
find('p', text: 'Name').click
end
expect(page).to have_content(sorted_branches(repository, count: 20, sort_by: :name))
end
end
it 'sorts the branches by oldest updated' do
stub_feature_flags(gldropdown_branches: false)
visit project_branches_filtered_path(project, state: 'all')
click_button "Last updated" # Open sorting dropdown
......@@ -132,6 +162,19 @@ RSpec.describe 'Branches' do
expect(page).to have_content(sorted_branches(repository, count: 20, sort_by: :updated_asc))
end
context 'with gldropdown_branches enabled' do
it 'sorts the branches by oldest updated', :js do
visit project_branches_filtered_path(project, state: 'all')
click_button "Last updated" # Open sorting dropdown
within '[data-testid="branches-dropdown"]' do
find('p', text: 'Oldest updated').click
end
expect(page).to have_content(sorted_branches(repository, count: 20, sort_by: :updated_asc))
end
end
it 'avoids a N+1 query in branches index' do
control_count = ActiveRecord::QueryRecorder.new { visit project_branches_path(project) }.count
......@@ -143,6 +186,7 @@ RSpec.describe 'Branches' do
describe 'Find branches on All branches' do
it 'shows filtered branches', :js do
stub_feature_flags(gldropdown_branches: false)
visit project_branches_filtered_path(project, state: 'all')
fill_in 'branch-search', with: 'fix'
......@@ -151,10 +195,25 @@ RSpec.describe 'Branches' do
expect(page).to have_content('fix')
expect(find('.all-branches')).to have_selector('li', count: 1)
end
context 'with gldropdown_branches enabled' do
it 'shows filtered branches', :js do
visit project_branches_filtered_path(project, state: 'all')
branch_search = find('input[data-testid="branch-search"]')
branch_search.set('fix')
branch_search.native.send_keys(:enter)
expect(page).to have_content('fix')
expect(find('.all-branches')).to have_selector('li', count: 1)
end
end
end
describe 'Delete unprotected branch on All branches' do
it 'removes branch after confirmation', :js do
stub_feature_flags(gldropdown_branches: false)
visit project_branches_filtered_path(project, state: 'all')
fill_in 'branch-search', with: 'fix'
......@@ -168,6 +227,24 @@ RSpec.describe 'Branches' do
expect(page).not_to have_content('fix')
expect(find('.all-branches')).to have_selector('li', count: 0)
end
context 'with gldropdown_branches enabled' do
it 'removes branch after confirmation', :js do
visit project_branches_filtered_path(project, state: 'all')
branch_search = find('input[data-testid="branch-search"]')
branch_search.set('fix')
branch_search.native.send_keys(:enter)
expect(page).to have_content('fix')
expect(find('.all-branches')).to have_selector('li', count: 1)
accept_confirm { find('.js-branch-fix .btn-danger').click }
expect(page).not_to have_content('fix')
expect(find('.all-branches')).to have_selector('li', count: 0)
end
end
end
context 'on project with 0 branch' do
......
......@@ -22,6 +22,7 @@ RSpec.describe 'Protected Branches', :js do
end
it 'does not allow developer to removes protected branch' do
stub_feature_flags(gldropdown_branches: false)
visit project_branches_path(project)
fill_in 'branch-search', with: 'fix'
......@@ -29,6 +30,17 @@ RSpec.describe 'Protected Branches', :js do
expect(page).to have_css('.btn-danger.disabled')
end
context 'with gldropdown_branches enabled' do
it 'does not allow developer to removes protected branch' do
visit project_branches_path(project)
find('input[data-testid="branch-search"]').set('fix')
find('input[data-testid="branch-search"]').native.send_keys(:enter)
expect(page).to have_css('.btn-danger.disabled')
end
end
end
end
......@@ -45,6 +57,7 @@ RSpec.describe 'Protected Branches', :js do
end
it 'removes branch after modal confirmation' do
stub_feature_flags(gldropdown_branches: false)
visit project_branches_path(project)
fill_in 'branch-search', with: 'fix'
......@@ -63,6 +76,28 @@ RSpec.describe 'Protected Branches', :js do
expect(page).to have_content('No branches to show')
end
context 'with gldropdown_branches enabled' do
it 'removes branch after modal confirmation' do
visit project_branches_path(project)
find('input[data-testid="branch-search"]').set('fix')
find('input[data-testid="branch-search"]').native.send_keys(:enter)
expect(page).to have_content('fix')
expect(find('.all-branches')).to have_selector('li', count: 1)
page.find('[data-target="#modal-delete-branch"]').click
expect(page).to have_css('.js-delete-branch[disabled]')
fill_in 'delete_branch_input', with: 'fix'
click_link 'Delete protected branch'
find('input[data-testid="branch-search"]').set('fix')
find('input[data-testid="branch-search"]').native.send_keys(:enter)
expect(page).to have_content('No branches to show')
end
end
end
end
......
import { GlSearchBoxByClick } from '@gitlab/ui';
import { mount } from '@vue/test-utils';
import { extendedWrapper } from 'helpers/vue_test_utils_helper';
import SortDropdown from '~/branches/components/sort_dropdown.vue';
import * as urlUtils from '~/lib/utils/url_utility';
describe('Branches Sort Dropdown', () => {
let wrapper;
const createWrapper = (props = {}) => {
return extendedWrapper(
mount(SortDropdown, {
provide: {
mode: 'overview',
projectBranchesFilteredPath: '/root/ci-cd-project-demo/-/branches?state=all',
sortOptions: {
name_asc: 'Name',
updated_asc: 'Oldest updated',
updated_desc: 'Last updated',
},
...props,
},
}),
);
};
const findSearchBox = () => wrapper.findComponent(GlSearchBoxByClick);
const findBranchesDropdown = () => wrapper.findByTestId('branches-dropdown');
afterEach(() => {
if (wrapper) {
wrapper.destroy();
}
});
describe('When in overview mode', () => {
beforeEach(() => {
wrapper = createWrapper();
});
it('should have a search box with a placeholder', () => {
const searchBox = findSearchBox();
expect(searchBox.exists()).toBe(true);
expect(searchBox.find('input').attributes('placeholder')).toBe('Filter by branch name');
});
it('should not have a branches dropdown when in overview mode', () => {
const branchesDropdown = findBranchesDropdown();
expect(branchesDropdown.exists()).toBe(false);
});
});
describe('when in All branches mode', () => {
beforeEach(() => {
wrapper = createWrapper({ mode: 'all' });
});
it('should have a search box with a placeholder', () => {
const searchBox = findSearchBox();
expect(searchBox.exists()).toBe(true);
expect(searchBox.find('input').attributes('placeholder')).toBe('Filter by branch name');
});
it('should have a branches dropdown when in all branches mode', () => {
const branchesDropdown = findBranchesDropdown();
expect(branchesDropdown.exists()).toBe(true);
});
});
describe('when submitting a search term', () => {
beforeEach(() => {
urlUtils.visitUrl = jest.fn();
wrapper = createWrapper();
});
it('should call visitUrl', () => {
const searchBox = findSearchBox();
searchBox.vm.$emit('submit');
expect(urlUtils.visitUrl).toHaveBeenCalledWith(
'/root/ci-cd-project-demo/-/branches?state=all',
);
});
});
});
......@@ -47,4 +47,19 @@ RSpec.describe BranchesHelper do
end
end
end
describe '#gl_dropdown_branches_enabled?' do
context 'when the feature is enabled' do
it 'returns true' do
expect(helper.gldropdrown_branches_enabled?).to be_truthy
end
end
context 'when the feature is disabled' do
it 'returns false' do
stub_feature_flags(gldropdown_branches: false)
expect(helper.gldropdrown_branches_enabled?).to be_falsy
end
end
end
end
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