Merge branch '39849_controller_sorts' into 'master'
Sort issues and merge requests in ascending and descending order Closes #39849 See merge request gitlab-org/gitlab-ce!21438
This commit is contained in:
commit
1e6b82a575
18 changed files with 424 additions and 236 deletions
|
@ -259,6 +259,16 @@ ul.related-merge-requests > li {
|
|||
display: block;
|
||||
}
|
||||
|
||||
.issue-sort-dropdown {
|
||||
.btn-group {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.reverse-sort-btn {
|
||||
color: $gl-text-color-secondary;
|
||||
}
|
||||
}
|
||||
|
||||
@include media-breakpoint-up(sm) {
|
||||
.emoji-block .row {
|
||||
display: flex;
|
||||
|
|
|
@ -167,12 +167,6 @@ module IssuableCollections
|
|||
case value
|
||||
when 'id_asc' then sort_value_oldest_created
|
||||
when 'id_desc' then sort_value_recently_created
|
||||
when 'created_asc' then sort_value_created_date
|
||||
when 'created_desc' then sort_value_created_date
|
||||
when 'due_date_asc' then sort_value_due_date
|
||||
when 'due_date_desc' then sort_value_due_date
|
||||
when 'milestone_due_asc' then sort_value_milestone
|
||||
when 'milestone_due_desc' then sort_value_milestone
|
||||
when 'downvotes_asc' then sort_value_popularity
|
||||
when 'downvotes_desc' then sort_value_popularity
|
||||
else value
|
||||
|
|
|
@ -136,6 +136,53 @@ module SortingHelper
|
|||
link_to item, path, class: sorted_by == item ? 'is-active' : ''
|
||||
end
|
||||
|
||||
def issuable_sort_option_overrides
|
||||
{
|
||||
sort_value_oldest_created => sort_value_created_date,
|
||||
sort_value_oldest_updated => sort_value_recently_updated,
|
||||
sort_value_milestone_later => sort_value_milestone
|
||||
}
|
||||
end
|
||||
|
||||
def issuable_reverse_sort_order_hash
|
||||
{
|
||||
sort_value_created_date => sort_value_oldest_created,
|
||||
sort_value_recently_created => sort_value_oldest_created,
|
||||
sort_value_recently_updated => sort_value_oldest_updated,
|
||||
sort_value_milestone => sort_value_milestone_later
|
||||
}.merge(issuable_sort_option_overrides)
|
||||
end
|
||||
|
||||
def issuable_sort_option_title(sort_value)
|
||||
sort_value = issuable_sort_option_overrides[sort_value] || sort_value
|
||||
|
||||
sort_options_hash[sort_value]
|
||||
end
|
||||
|
||||
def issuable_sort_direction_button(sort_value)
|
||||
link_class = 'btn btn-default has-tooltip reverse-sort-btn qa-reverse-sort'
|
||||
reverse_sort = issuable_reverse_sort_order_hash[sort_value]
|
||||
|
||||
if reverse_sort
|
||||
reverse_url = page_filter_path(sort: reverse_sort)
|
||||
else
|
||||
reverse_url = '#'
|
||||
link_class += ' disabled'
|
||||
end
|
||||
|
||||
link_to(reverse_url, type: 'button', class: link_class, title: 'Sort direction') do
|
||||
icon_suffix =
|
||||
case sort_value
|
||||
when sort_value_milestone, sort_value_due_date, /_asc\z/
|
||||
'lowest'
|
||||
else
|
||||
'highest'
|
||||
end
|
||||
|
||||
sprite_icon("sort-#{icon_suffix}", size: 16)
|
||||
end
|
||||
end
|
||||
|
||||
# Titles.
|
||||
def sort_title_access_level_asc
|
||||
s_('SortOptions|Access level, ascending')
|
||||
|
|
|
@ -43,14 +43,19 @@ module Awardable
|
|||
end
|
||||
|
||||
def order_upvotes_desc
|
||||
order_votes_desc(AwardEmoji::UPVOTE_NAME)
|
||||
order_votes(AwardEmoji::UPVOTE_NAME, 'DESC')
|
||||
end
|
||||
|
||||
def order_upvotes_asc
|
||||
order_votes(AwardEmoji::UPVOTE_NAME, 'ASC')
|
||||
end
|
||||
|
||||
def order_downvotes_desc
|
||||
order_votes_desc(AwardEmoji::DOWNVOTE_NAME)
|
||||
order_votes(AwardEmoji::DOWNVOTE_NAME, 'DESC')
|
||||
end
|
||||
|
||||
def order_votes_desc(emoji_name)
|
||||
# Order votes by emoji, optional sort order param `descending` defaults to true
|
||||
def order_votes(emoji_name, direction)
|
||||
awardable_table = self.arel_table
|
||||
awards_table = AwardEmoji.arel_table
|
||||
|
||||
|
@ -62,7 +67,7 @@ module Awardable
|
|||
)
|
||||
).join_sources
|
||||
|
||||
joins(join_clause).group(awardable_table[:id]).reorder("COUNT(award_emoji.id) DESC")
|
||||
joins(join_clause).group(awardable_table[:id]).reorder("COUNT(award_emoji.id) #{direction}")
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
@ -145,14 +145,16 @@ module Issuable
|
|||
def sort_by_attribute(method, excluded_labels: [])
|
||||
sorted =
|
||||
case method.to_s
|
||||
when 'downvotes_desc' then order_downvotes_desc
|
||||
when 'label_priority' then order_labels_priority(excluded_labels: excluded_labels)
|
||||
when 'milestone' then order_milestone_due_asc
|
||||
when 'milestone_due_asc' then order_milestone_due_asc
|
||||
when 'milestone_due_desc' then order_milestone_due_desc
|
||||
when 'popularity' then order_upvotes_desc
|
||||
when 'priority' then order_due_date_and_labels_priority(excluded_labels: excluded_labels)
|
||||
when 'upvotes_desc' then order_upvotes_desc
|
||||
when 'downvotes_desc' then order_downvotes_desc
|
||||
when 'label_priority' then order_labels_priority(excluded_labels: excluded_labels)
|
||||
when 'label_priority_desc' then order_labels_priority('DESC', excluded_labels: excluded_labels)
|
||||
when 'milestone', 'milestone_due_asc' then order_milestone_due_asc
|
||||
when 'milestone_due_desc' then order_milestone_due_desc
|
||||
when 'popularity', 'popularity_desc' then order_upvotes_desc
|
||||
when 'popularity_asc' then order_upvotes_asc
|
||||
when 'priority', 'priority_asc' then order_due_date_and_labels_priority(excluded_labels: excluded_labels)
|
||||
when 'priority_desc' then order_due_date_and_labels_priority('DESC', excluded_labels: excluded_labels)
|
||||
when 'upvotes_desc' then order_upvotes_desc
|
||||
else order_by(method)
|
||||
end
|
||||
|
||||
|
@ -160,7 +162,7 @@ module Issuable
|
|||
sorted.with_order_id_desc
|
||||
end
|
||||
|
||||
def order_due_date_and_labels_priority(excluded_labels: [])
|
||||
def order_due_date_and_labels_priority(direction = 'ASC', excluded_labels: [])
|
||||
# The order_ methods also modify the query in other ways:
|
||||
#
|
||||
# - For milestones, we add a JOIN.
|
||||
|
@ -177,11 +179,11 @@ module Issuable
|
|||
|
||||
order_milestone_due_asc
|
||||
.order_labels_priority(excluded_labels: excluded_labels, extra_select_columns: [milestones_due_date])
|
||||
.reorder(Gitlab::Database.nulls_last_order(milestones_due_date, 'ASC'),
|
||||
Gitlab::Database.nulls_last_order('highest_priority', 'ASC'))
|
||||
.reorder(Gitlab::Database.nulls_last_order(milestones_due_date, direction),
|
||||
Gitlab::Database.nulls_last_order('highest_priority', direction))
|
||||
end
|
||||
|
||||
def order_labels_priority(excluded_labels: [], extra_select_columns: [])
|
||||
def order_labels_priority(direction = 'ASC', excluded_labels: [], extra_select_columns: [])
|
||||
params = {
|
||||
target_type: name,
|
||||
target_column: "#{table_name}.id",
|
||||
|
@ -198,7 +200,7 @@ module Issuable
|
|||
|
||||
select(select_columns.join(', '))
|
||||
.group(arel_table[:id])
|
||||
.reorder(Gitlab::Database.nulls_last_order('highest_priority', 'ASC'))
|
||||
.reorder(Gitlab::Database.nulls_last_order('highest_priority', direction))
|
||||
end
|
||||
|
||||
def with_label(title, sort = nil)
|
||||
|
|
|
@ -1,16 +0,0 @@
|
|||
- sorted_by = sort_options_hash[@sort]
|
||||
- viewing_issues = controller.controller_name == 'issues' || controller.action_name == 'issues'
|
||||
|
||||
.dropdown.inline.prepend-left-10
|
||||
%button.dropdown-menu-toggle{ type: 'button', data: { toggle: 'dropdown', display: 'static' } }
|
||||
= sorted_by
|
||||
= icon('chevron-down')
|
||||
%ul.dropdown-menu.dropdown-menu-right.dropdown-menu-selectable.dropdown-menu-sort
|
||||
%li
|
||||
= sortable_item(sort_title_priority, page_filter_path(sort: sort_value_priority, label: true), sorted_by)
|
||||
= sortable_item(sort_title_created_date, page_filter_path(sort: sort_value_created_date, label: true), sorted_by)
|
||||
= sortable_item(sort_title_recently_updated, page_filter_path(sort: sort_value_recently_updated, label: true), sorted_by)
|
||||
= sortable_item(sort_title_milestone, page_filter_path(sort: sort_value_milestone, label: true), sorted_by)
|
||||
= sortable_item(sort_title_due_date, page_filter_path(sort: sort_value_due_date, label: true), sorted_by) if viewing_issues
|
||||
= sortable_item(sort_title_popularity, page_filter_path(sort: sort_value_popularity, label: true), sorted_by)
|
||||
= sortable_item(sort_title_label_priority, page_filter_path(sort: sort_value_label_priority, label: true), sorted_by)
|
32
app/views/shared/issuable/_filter.html.haml
Normal file
32
app/views/shared/issuable/_filter.html.haml
Normal file
|
@ -0,0 +1,32 @@
|
|||
.issues-filters
|
||||
.issues-details-filters.row-content-block.second-block
|
||||
= form_tag page_filter_path(without: [:assignee_id, :author_id, :milestone_title, :label_name, :search]), method: :get, class: 'filter-form js-filter-form' do
|
||||
- if params[:search].present?
|
||||
= hidden_field_tag :search, params[:search]
|
||||
.issues-other-filters
|
||||
.filter-item.inline
|
||||
- if params[:author_id].present?
|
||||
= hidden_field_tag(:author_id, params[:author_id])
|
||||
= dropdown_tag(user_dropdown_label(params[:author_id], "Author"), options: { toggle_class: "js-user-search js-filter-submit js-author-search", title: "Filter by author", filter: true, dropdown_class: "dropdown-menu-user dropdown-menu-selectable dropdown-menu-author js-filter-submit",
|
||||
placeholder: "Search authors", data: { any_user: "Any Author", first_user: current_user&.username, current_user: true, project_id: @project&.id, group_id: @group&.id, selected: params[:author_id], field_name: "author_id", default_label: "Author" } })
|
||||
|
||||
.filter-item.inline
|
||||
- if params[:assignee_id].present?
|
||||
= hidden_field_tag(:assignee_id, params[:assignee_id])
|
||||
= dropdown_tag(user_dropdown_label(params[:assignee_id], "Assignee"), options: { toggle_class: "js-user-search js-filter-submit js-assignee-search", title: "Filter by assignee", filter: true, dropdown_class: "dropdown-menu-user dropdown-menu-selectable dropdown-menu-assignee js-filter-submit",
|
||||
placeholder: "Search assignee", data: { any_user: "Any Assignee", first_user: current_user&.username, null_user: true, current_user: true, project_id: @project&.id, group_id: @group&.id, selected: params[:assignee_id], field_name: "assignee_id", default_label: "Assignee" } })
|
||||
|
||||
.filter-item.inline.milestone-filter
|
||||
= render "shared/issuable/milestone_dropdown", selected: finder.milestones.try(:first), name: :milestone_title, show_any: true, show_upcoming: true, show_started: true
|
||||
|
||||
.filter-item.inline.labels-filter
|
||||
= render "shared/issuable/label_dropdown", selected: selected_labels, use_id: false, selected_toggle: params[:label_name], data_options: { field_name: "label_name[]" }
|
||||
|
||||
- unless @no_filters_set
|
||||
.float-right
|
||||
= render 'shared/issuable/sort_dropdown'
|
||||
|
||||
- has_labels = @labels && @labels.any?
|
||||
.row-content-block.second-block.filtered-labels{ class: ("hidden" unless has_labels) }
|
||||
- if has_labels
|
||||
= render 'shared/labels_row', labels: @labels
|
|
@ -2,7 +2,6 @@
|
|||
- board = local_assigns.fetch(:board, nil)
|
||||
- block_css_class = type != :boards_modal ? 'row-content-block second-block' : ''
|
||||
- user_can_admin_list = board && can?(current_user, :admin_list, board.parent)
|
||||
- show_sorting_dropdown = local_assigns.fetch(:show_sorting_dropdown, true)
|
||||
|
||||
.issues-filters
|
||||
.issues-details-filters.filtered-search-block{ class: block_css_class, "v-pre" => type == :boards_modal }
|
||||
|
@ -142,5 +141,5 @@
|
|||
- if @project
|
||||
#js-add-issues-btn.prepend-left-10{ data: { can_admin_list: can?(current_user, :admin_list, @project) } }
|
||||
#js-toggle-focus-btn
|
||||
- elsif show_sorting_dropdown
|
||||
= render 'shared/sort_dropdown'
|
||||
- elsif type != :boards_modal
|
||||
= render 'shared/issuable/sort_dropdown'
|
||||
|
|
20
app/views/shared/issuable/_sort_dropdown.html.haml
Normal file
20
app/views/shared/issuable/_sort_dropdown.html.haml
Normal file
|
@ -0,0 +1,20 @@
|
|||
- sort_value = @sort
|
||||
- sort_title = issuable_sort_option_title(sort_value)
|
||||
- viewing_issues = controller.controller_name == 'issues' || controller.action_name == 'issues'
|
||||
|
||||
.dropdown.inline.prepend-left-10.issue-sort-dropdown
|
||||
.btn-group{ role: 'group' }
|
||||
.btn-group{ role: 'group' }
|
||||
%button.dropdown-toggle{ type: 'button', data: { toggle: 'dropdown', display: 'static' }, class: 'btn btn-default' }
|
||||
= sort_title
|
||||
= icon('chevron-down')
|
||||
%ul.dropdown-menu.dropdown-menu-right.dropdown-menu-selectable.dropdown-menu-sort
|
||||
%li
|
||||
= sortable_item(sort_title_priority, page_filter_path(sort: sort_value_priority, label: true), sort_title)
|
||||
= sortable_item(sort_title_created_date, page_filter_path(sort: sort_value_created_date, label: true), sort_title)
|
||||
= sortable_item(sort_title_recently_updated, page_filter_path(sort: sort_value_recently_updated, label: true), sort_title)
|
||||
= sortable_item(sort_title_milestone, page_filter_path(sort: sort_value_milestone, label: true), sort_title)
|
||||
= sortable_item(sort_title_due_date, page_filter_path(sort: sort_value_due_date, label: true), sort_title) if viewing_issues
|
||||
= sortable_item(sort_title_popularity, page_filter_path(sort: sort_value_popularity, label: true), sort_title)
|
||||
= sortable_item(sort_title_label_priority, page_filter_path(sort: sort_value_label_priority, label: true), sort_title)
|
||||
= issuable_sort_direction_button(sort_value)
|
5
changelogs/unreleased/39849_controller_sorts.yml
Normal file
5
changelogs/unreleased/39849_controller_sorts.yml
Normal file
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
title: Allow sorting issues and MRs in reverse order
|
||||
merge_request: 21438
|
||||
author:
|
||||
type: changed
|
|
@ -1,179 +0,0 @@
|
|||
require 'spec_helper'
|
||||
|
||||
describe 'Projects > Issuables > Default sort order' do
|
||||
let(:project) { create(:project, :public) }
|
||||
|
||||
let(:first_created_issuable) { issuables.order_created_asc.first }
|
||||
let(:last_created_issuable) { issuables.order_created_desc.first }
|
||||
|
||||
let(:first_updated_issuable) { issuables.order_updated_asc.first }
|
||||
let(:last_updated_issuable) { issuables.order_updated_desc.first }
|
||||
|
||||
context 'for merge requests' do
|
||||
include MergeRequestHelpers
|
||||
|
||||
let!(:issuables) do
|
||||
timestamps = [{ created_at: 3.minutes.ago, updated_at: 20.seconds.ago },
|
||||
{ created_at: 2.minutes.ago, updated_at: 30.seconds.ago },
|
||||
{ created_at: 4.minutes.ago, updated_at: 10.seconds.ago }]
|
||||
|
||||
timestamps.each_with_index do |ts, i|
|
||||
create issuable_type, { title: "#{issuable_type}_#{i}",
|
||||
source_branch: "#{issuable_type}_#{i}",
|
||||
source_project: project }.merge(ts)
|
||||
end
|
||||
|
||||
MergeRequest.all
|
||||
end
|
||||
|
||||
context 'in the "merge requests" tab', :js do
|
||||
let(:issuable_type) { :merge_request }
|
||||
|
||||
it 'is "last created"' do
|
||||
visit_merge_requests project
|
||||
|
||||
expect(first_merge_request).to include(last_created_issuable.title)
|
||||
expect(last_merge_request).to include(first_created_issuable.title)
|
||||
end
|
||||
end
|
||||
|
||||
context 'in the "merge requests / open" tab', :js do
|
||||
let(:issuable_type) { :merge_request }
|
||||
|
||||
it 'is "created date"' do
|
||||
visit_merge_requests_with_state(project, 'open')
|
||||
|
||||
expect(selected_sort_order).to eq('created date')
|
||||
expect(first_merge_request).to include(last_created_issuable.title)
|
||||
expect(last_merge_request).to include(first_created_issuable.title)
|
||||
end
|
||||
end
|
||||
|
||||
context 'in the "merge requests / merged" tab', :js do
|
||||
let(:issuable_type) { :merged_merge_request }
|
||||
|
||||
it 'is "last updated"' do
|
||||
visit_merge_requests_with_state(project, 'merged')
|
||||
|
||||
expect(find('.issues-other-filters')).to have_content('Last updated')
|
||||
expect(first_merge_request).to include(last_updated_issuable.title)
|
||||
expect(last_merge_request).to include(first_updated_issuable.title)
|
||||
end
|
||||
end
|
||||
|
||||
context 'in the "merge requests / closed" tab', :js do
|
||||
let(:issuable_type) { :closed_merge_request }
|
||||
|
||||
it 'is "last updated"' do
|
||||
visit_merge_requests_with_state(project, 'closed')
|
||||
|
||||
expect(find('.issues-other-filters')).to have_content('Last updated')
|
||||
expect(first_merge_request).to include(last_updated_issuable.title)
|
||||
expect(last_merge_request).to include(first_updated_issuable.title)
|
||||
end
|
||||
end
|
||||
|
||||
context 'in the "merge requests / all" tab', :js do
|
||||
let(:issuable_type) { :merge_request }
|
||||
|
||||
it 'is "created date"' do
|
||||
visit_merge_requests_with_state(project, 'all')
|
||||
|
||||
expect(find('.issues-other-filters')).to have_content('Created date')
|
||||
expect(first_merge_request).to include(last_created_issuable.title)
|
||||
expect(last_merge_request).to include(first_created_issuable.title)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context 'for issues' do
|
||||
include IssueHelpers
|
||||
|
||||
let!(:issuables) do
|
||||
timestamps = [{ created_at: 3.minutes.ago, updated_at: 20.seconds.ago },
|
||||
{ created_at: 2.minutes.ago, updated_at: 30.seconds.ago },
|
||||
{ created_at: 4.minutes.ago, updated_at: 10.seconds.ago }]
|
||||
|
||||
timestamps.each_with_index do |ts, i|
|
||||
create issuable_type, { title: "#{issuable_type}_#{i}",
|
||||
project: project }.merge(ts)
|
||||
end
|
||||
|
||||
Issue.all
|
||||
end
|
||||
|
||||
context 'in the "issues" tab', :js do
|
||||
let(:issuable_type) { :issue }
|
||||
|
||||
it 'is "created date"' do
|
||||
visit_issues project
|
||||
|
||||
expect(find('.issues-other-filters')).to have_content('Created date')
|
||||
expect(first_issue).to include(last_created_issuable.title)
|
||||
expect(last_issue).to include(first_created_issuable.title)
|
||||
end
|
||||
end
|
||||
|
||||
context 'in the "issues / open" tab', :js do
|
||||
let(:issuable_type) { :issue }
|
||||
|
||||
it 'is "created date"' do
|
||||
visit_issues_with_state(project, 'open')
|
||||
|
||||
expect(find('.issues-other-filters')).to have_content('Created date')
|
||||
expect(first_issue).to include(last_created_issuable.title)
|
||||
expect(last_issue).to include(first_created_issuable.title)
|
||||
end
|
||||
end
|
||||
|
||||
context 'in the "issues / closed" tab', :js do
|
||||
let(:issuable_type) { :closed_issue }
|
||||
|
||||
it 'is "last updated"' do
|
||||
visit_issues_with_state(project, 'closed')
|
||||
|
||||
expect(find('.issues-other-filters')).to have_content('Last updated')
|
||||
expect(first_issue).to include(last_updated_issuable.title)
|
||||
expect(last_issue).to include(first_updated_issuable.title)
|
||||
end
|
||||
end
|
||||
|
||||
context 'in the "issues / all" tab', :js do
|
||||
let(:issuable_type) { :issue }
|
||||
|
||||
it 'is "created date"' do
|
||||
visit_issues_with_state(project, 'all')
|
||||
|
||||
expect(find('.issues-other-filters')).to have_content('Created date')
|
||||
expect(first_issue).to include(last_created_issuable.title)
|
||||
expect(last_issue).to include(first_created_issuable.title)
|
||||
end
|
||||
end
|
||||
|
||||
context 'when the sort in the URL is id_desc' do
|
||||
let(:issuable_type) { :issue }
|
||||
|
||||
before do
|
||||
visit_issues(project, sort: 'id_desc')
|
||||
end
|
||||
|
||||
it 'shows the sort order as created date' do
|
||||
expect(find('.issues-other-filters')).to have_content('Created date')
|
||||
expect(first_issue).to include(last_created_issuable.title)
|
||||
expect(last_issue).to include(first_created_issuable.title)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def selected_sort_order
|
||||
find('.filter-dropdown-container .dropdown button').text.downcase
|
||||
end
|
||||
|
||||
def visit_merge_requests_with_state(project, state)
|
||||
visit_merge_requests project, state: state
|
||||
end
|
||||
|
||||
def visit_issues_with_state(project, state)
|
||||
visit_issues project, state: state
|
||||
end
|
||||
end
|
226
spec/features/issuables/sorting_list_spec.rb
Normal file
226
spec/features/issuables/sorting_list_spec.rb
Normal file
|
@ -0,0 +1,226 @@
|
|||
# frozen_string_literal: true
|
||||
require 'spec_helper'
|
||||
|
||||
describe 'Sort Issuable List' do
|
||||
let(:project) { create(:project, :public) }
|
||||
|
||||
let(:first_created_issuable) { issuables.order_created_asc.first }
|
||||
let(:last_created_issuable) { issuables.order_created_desc.first }
|
||||
|
||||
let(:first_updated_issuable) { issuables.order_updated_asc.first }
|
||||
let(:last_updated_issuable) { issuables.order_updated_desc.first }
|
||||
|
||||
context 'for merge requests' do
|
||||
include MergeRequestHelpers
|
||||
|
||||
let!(:issuables) do
|
||||
timestamps = [{ created_at: 3.minutes.ago, updated_at: 20.seconds.ago },
|
||||
{ created_at: 2.minutes.ago, updated_at: 30.seconds.ago },
|
||||
{ created_at: 4.minutes.ago, updated_at: 10.seconds.ago }]
|
||||
|
||||
timestamps.each_with_index do |ts, i|
|
||||
create issuable_type, { title: "#{issuable_type}_#{i}",
|
||||
source_branch: "#{issuable_type}_#{i}",
|
||||
source_project: project }.merge(ts)
|
||||
end
|
||||
|
||||
MergeRequest.all
|
||||
end
|
||||
|
||||
context 'default sort order' do
|
||||
context 'in the "merge requests" tab', :js do
|
||||
let(:issuable_type) { :merge_request }
|
||||
|
||||
it 'is "last created"' do
|
||||
visit_merge_requests project
|
||||
|
||||
expect(first_merge_request).to include(last_created_issuable.title)
|
||||
expect(last_merge_request).to include(first_created_issuable.title)
|
||||
end
|
||||
end
|
||||
|
||||
context 'in the "merge requests / open" tab', :js do
|
||||
let(:issuable_type) { :merge_request }
|
||||
|
||||
it 'is "created date"' do
|
||||
visit_merge_requests_with_state(project, 'open')
|
||||
|
||||
expect(selected_sort_order).to eq('created date')
|
||||
expect(first_merge_request).to include(last_created_issuable.title)
|
||||
expect(last_merge_request).to include(first_created_issuable.title)
|
||||
end
|
||||
end
|
||||
|
||||
context 'in the "merge requests / merged" tab', :js do
|
||||
let(:issuable_type) { :merged_merge_request }
|
||||
|
||||
it 'is "last updated"' do
|
||||
visit_merge_requests_with_state(project, 'merged')
|
||||
|
||||
expect(find('.issues-other-filters')).to have_content('Last updated')
|
||||
expect(first_merge_request).to include(last_updated_issuable.title)
|
||||
expect(last_merge_request).to include(first_updated_issuable.title)
|
||||
end
|
||||
end
|
||||
|
||||
context 'in the "merge requests / closed" tab', :js do
|
||||
let(:issuable_type) { :closed_merge_request }
|
||||
|
||||
it 'is "last updated"' do
|
||||
visit_merge_requests_with_state(project, 'closed')
|
||||
|
||||
expect(find('.issues-other-filters')).to have_content('Last updated')
|
||||
expect(first_merge_request).to include(last_updated_issuable.title)
|
||||
expect(last_merge_request).to include(first_updated_issuable.title)
|
||||
end
|
||||
end
|
||||
|
||||
context 'in the "merge requests / all" tab', :js do
|
||||
let(:issuable_type) { :merge_request }
|
||||
|
||||
it 'is "created date"' do
|
||||
visit_merge_requests_with_state(project, 'all')
|
||||
|
||||
expect(find('.issues-other-filters')).to have_content('Created date')
|
||||
expect(first_merge_request).to include(last_created_issuable.title)
|
||||
expect(last_merge_request).to include(first_created_issuable.title)
|
||||
end
|
||||
end
|
||||
|
||||
context 'custom sorting' do
|
||||
let(:issuable_type) { :merge_request }
|
||||
|
||||
it 'supports sorting in asc and desc order' do
|
||||
visit_merge_requests_with_state(project, 'open')
|
||||
|
||||
page.within('.issues-other-filters') do
|
||||
click_button('Created date')
|
||||
click_link('Last updated')
|
||||
end
|
||||
|
||||
expect(first_merge_request).to include(last_updated_issuable.title)
|
||||
expect(last_merge_request).to include(first_updated_issuable.title)
|
||||
|
||||
find('.issues-other-filters .filter-dropdown-container .qa-reverse-sort').click
|
||||
|
||||
expect(first_merge_request).to include(first_updated_issuable.title)
|
||||
expect(last_merge_request).to include(last_updated_issuable.title)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context 'for issues' do
|
||||
include IssueHelpers
|
||||
|
||||
let!(:issuables) do
|
||||
timestamps = [{ created_at: 3.minutes.ago, updated_at: 20.seconds.ago },
|
||||
{ created_at: 2.minutes.ago, updated_at: 30.seconds.ago },
|
||||
{ created_at: 4.minutes.ago, updated_at: 10.seconds.ago }]
|
||||
|
||||
timestamps.each_with_index do |ts, i|
|
||||
create issuable_type, { title: "#{issuable_type}_#{i}",
|
||||
project: project }.merge(ts)
|
||||
end
|
||||
|
||||
Issue.all
|
||||
end
|
||||
|
||||
context 'default sort order' do
|
||||
context 'in the "issues" tab', :js do
|
||||
let(:issuable_type) { :issue }
|
||||
|
||||
it 'is "created date"' do
|
||||
visit_issues project
|
||||
|
||||
expect(find('.issues-other-filters')).to have_content('Created date')
|
||||
expect(first_issue).to include(last_created_issuable.title)
|
||||
expect(last_issue).to include(first_created_issuable.title)
|
||||
end
|
||||
end
|
||||
|
||||
context 'in the "issues / open" tab', :js do
|
||||
let(:issuable_type) { :issue }
|
||||
|
||||
it 'is "created date"' do
|
||||
visit_issues_with_state(project, 'open')
|
||||
|
||||
expect(find('.issues-other-filters')).to have_content('Created date')
|
||||
expect(first_issue).to include(last_created_issuable.title)
|
||||
expect(last_issue).to include(first_created_issuable.title)
|
||||
end
|
||||
end
|
||||
|
||||
context 'in the "issues / closed" tab', :js do
|
||||
let(:issuable_type) { :closed_issue }
|
||||
|
||||
it 'is "last updated"' do
|
||||
visit_issues_with_state(project, 'closed')
|
||||
|
||||
expect(find('.issues-other-filters')).to have_content('Last updated')
|
||||
expect(first_issue).to include(last_updated_issuable.title)
|
||||
expect(last_issue).to include(first_updated_issuable.title)
|
||||
end
|
||||
end
|
||||
|
||||
context 'in the "issues / all" tab', :js do
|
||||
let(:issuable_type) { :issue }
|
||||
|
||||
it 'is "created date"' do
|
||||
visit_issues_with_state(project, 'all')
|
||||
|
||||
expect(find('.issues-other-filters')).to have_content('Created date')
|
||||
expect(first_issue).to include(last_created_issuable.title)
|
||||
expect(last_issue).to include(first_created_issuable.title)
|
||||
end
|
||||
end
|
||||
|
||||
context 'when the sort in the URL is id_desc' do
|
||||
let(:issuable_type) { :issue }
|
||||
|
||||
before do
|
||||
visit_issues(project, sort: 'id_desc')
|
||||
end
|
||||
|
||||
it 'shows the sort order as created date' do
|
||||
expect(find('.issues-other-filters')).to have_content('Created date')
|
||||
expect(first_issue).to include(last_created_issuable.title)
|
||||
expect(last_issue).to include(first_created_issuable.title)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context 'custom sorting' do
|
||||
let(:issuable_type) { :issue }
|
||||
|
||||
it 'supports sorting in asc and desc order' do
|
||||
visit_issues_with_state(project, 'open')
|
||||
|
||||
page.within('.issues-other-filters') do
|
||||
click_button('Created date')
|
||||
click_link('Last updated')
|
||||
end
|
||||
|
||||
expect(first_issue).to include(last_updated_issuable.title)
|
||||
expect(last_issue).to include(first_updated_issuable.title)
|
||||
|
||||
find('.issues-other-filters .filter-dropdown-container .qa-reverse-sort').click
|
||||
|
||||
expect(first_issue).to include(first_updated_issuable.title)
|
||||
expect(last_issue).to include(last_updated_issuable.title)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def selected_sort_order
|
||||
find('.filter-dropdown-container .dropdown button').text.downcase
|
||||
end
|
||||
|
||||
def visit_merge_requests_with_state(project, state)
|
||||
visit_merge_requests project, state: state
|
||||
end
|
||||
|
||||
def visit_issues_with_state(project, state)
|
||||
visit_issues project, state: state
|
||||
end
|
||||
end
|
|
@ -430,7 +430,7 @@ describe 'Filter issues', :js do
|
|||
|
||||
expect_issues_list_count(2)
|
||||
|
||||
sort_toggle = find('.filter-dropdown-container .dropdown-menu-toggle')
|
||||
sort_toggle = find('.filter-dropdown-container .dropdown')
|
||||
sort_toggle.click
|
||||
|
||||
find('.filter-dropdown-container .dropdown-menu li a', text: 'Created date').click
|
||||
|
|
|
@ -20,9 +20,9 @@ describe "User sorts issues" do
|
|||
end
|
||||
|
||||
it 'keeps the sort option' do
|
||||
find('.filter-dropdown-container button.dropdown-menu-toggle').click
|
||||
find('.filter-dropdown-container .dropdown').click
|
||||
|
||||
page.within('.content ul.dropdown-menu.dropdown-menu-right li') do
|
||||
page.within('ul.dropdown-menu.dropdown-menu-right li') do
|
||||
click_link('Milestone')
|
||||
end
|
||||
|
||||
|
@ -40,9 +40,9 @@ describe "User sorts issues" do
|
|||
end
|
||||
|
||||
it "sorts by popularity" do
|
||||
find(".filter-dropdown-container button.dropdown-menu-toggle").click
|
||||
find('.filter-dropdown-container .dropdown').click
|
||||
|
||||
page.within(".content ul.dropdown-menu.dropdown-menu-right li") do
|
||||
page.within('ul.dropdown-menu.dropdown-menu-right li') do
|
||||
click_link("Popularity")
|
||||
end
|
||||
|
||||
|
|
|
@ -19,9 +19,9 @@ describe 'User sorts merge requests' do
|
|||
end
|
||||
|
||||
it 'keeps the sort option' do
|
||||
find('.filter-dropdown-container button.dropdown-menu-toggle').click
|
||||
find('.filter-dropdown-container .dropdown').click
|
||||
|
||||
page.within('.content ul.dropdown-menu.dropdown-menu-right li') do
|
||||
page.within('ul.dropdown-menu.dropdown-menu-right li') do
|
||||
click_link('Milestone')
|
||||
end
|
||||
|
||||
|
@ -49,9 +49,9 @@ describe 'User sorts merge requests' do
|
|||
it 'separates remember sorting with issues' do
|
||||
create(:issue, project: project)
|
||||
|
||||
find('.filter-dropdown-container button.dropdown-menu-toggle').click
|
||||
find('.filter-dropdown-container .dropdown').click
|
||||
|
||||
page.within('.content ul.dropdown-menu.dropdown-menu-right li') do
|
||||
page.within('ul.dropdown-menu.dropdown-menu-right li') do
|
||||
click_link('Milestone')
|
||||
end
|
||||
|
||||
|
@ -70,9 +70,9 @@ describe 'User sorts merge requests' do
|
|||
end
|
||||
|
||||
it 'sorts by popularity' do
|
||||
find('.filter-dropdown-container button.dropdown-menu-toggle').click
|
||||
find('.filter-dropdown-container .dropdown').click
|
||||
|
||||
page.within('.content ul.dropdown-menu.dropdown-menu-right li') do
|
||||
page.within('ul.dropdown-menu.dropdown-menu-right li') do
|
||||
click_link('Popularity')
|
||||
end
|
||||
|
||||
|
|
|
@ -32,7 +32,7 @@ describe 'Issue prioritization' do
|
|||
visit project_issues_path(project, sort: 'label_priority')
|
||||
|
||||
# Ensure we are indicating that issues are sorted by priority
|
||||
expect(page).to have_selector('.dropdown-menu-toggle', text: 'Label priority')
|
||||
expect(page).to have_selector('.dropdown', text: 'Label priority')
|
||||
|
||||
page.within('.issues-holder') do
|
||||
issue_titles = all('.issues-list .issue-title-text').map(&:text)
|
||||
|
@ -70,7 +70,7 @@ describe 'Issue prioritization' do
|
|||
sign_in user
|
||||
visit project_issues_path(project, sort: 'label_priority')
|
||||
|
||||
expect(page).to have_selector('.dropdown-menu-toggle', text: 'Label priority')
|
||||
expect(page).to have_selector('.dropdown', text: 'Label priority')
|
||||
|
||||
page.within('.issues-holder') do
|
||||
issue_titles = all('.issues-list .issue-title-text').map(&:text)
|
||||
|
|
43
spec/helpers/sorting_helper_spec.rb
Normal file
43
spec/helpers/sorting_helper_spec.rb
Normal file
|
@ -0,0 +1,43 @@
|
|||
# frozen_string_literal: true
|
||||
require 'spec_helper'
|
||||
|
||||
describe SortingHelper do
|
||||
include ApplicationHelper
|
||||
include IconsHelper
|
||||
|
||||
describe '#issuable_sort_option_title' do
|
||||
it 'returns correct title for issuable_sort_option_overrides key' do
|
||||
expect(issuable_sort_option_title('created_asc')).to eq('Created date')
|
||||
end
|
||||
|
||||
it 'returns correct title for a valid sort value' do
|
||||
expect(issuable_sort_option_title('priority')).to eq('Priority')
|
||||
end
|
||||
|
||||
it 'returns nil for invalid sort value' do
|
||||
expect(issuable_sort_option_title('invalid_key')).to eq(nil)
|
||||
end
|
||||
end
|
||||
|
||||
describe '#issuable_sort_direction_button' do
|
||||
before do
|
||||
allow(self).to receive(:request).and_return(double(path: 'http://test.com', query_parameters: {}))
|
||||
end
|
||||
|
||||
it 'returns icon with sort-highest when sort is created_date' do
|
||||
expect(issuable_sort_direction_button('created_date')).to include('sort-highest')
|
||||
end
|
||||
|
||||
it 'returns icon with sort-lowest when sort is asc' do
|
||||
expect(issuable_sort_direction_button('created_asc')).to include('sort-lowest')
|
||||
end
|
||||
|
||||
it 'returns icon with sort-lowest when sorting by milestone' do
|
||||
expect(issuable_sort_direction_button('milestone')).to include('sort-lowest')
|
||||
end
|
||||
|
||||
it 'returns icon with sort-lowest when sorting by due_date' do
|
||||
expect(issuable_sort_direction_button('due_date')).to include('sort-lowest')
|
||||
end
|
||||
end
|
||||
end
|
|
@ -13,9 +13,9 @@ module Spec
|
|||
module Features
|
||||
module SortingHelpers
|
||||
def sort_by(value)
|
||||
find('.filter-dropdown-container button.dropdown-menu-toggle').click
|
||||
find('.filter-dropdown-container .dropdown').click
|
||||
|
||||
page.within('.content ul.dropdown-menu.dropdown-menu-right li') do
|
||||
page.within('ul.dropdown-menu.dropdown-menu-right li') do
|
||||
click_link(value)
|
||||
end
|
||||
end
|
||||
|
|
Loading…
Reference in a new issue