Commit 5a84b5fd authored by Alfredo Sumaran's avatar Alfredo Sumaran

Merge branch 'add-filtered-search-to-mr' into 'master'

Add filtered search to MR page

Closes #27131

See merge request !9284
parents 4f6e067e 649c095a
...@@ -74,7 +74,7 @@ const ShortcutsBlob = require('./shortcuts_blob'); ...@@ -74,7 +74,7 @@ const ShortcutsBlob = require('./shortcuts_blob');
case 'projects:merge_requests:index': case 'projects:merge_requests:index':
case 'projects:issues:index': case 'projects:issues:index':
if (gl.FilteredSearchManager) { if (gl.FilteredSearchManager) {
new gl.FilteredSearchManager(); new gl.FilteredSearchManager(page === 'projects:issues:index' ? 'issues' : 'merge_requests');
} }
Issuable.init(); Issuable.init();
new gl.IssuableBulkActions({ new gl.IssuableBulkActions({
......
...@@ -37,23 +37,18 @@ require('./filtered_search_dropdown'); ...@@ -37,23 +37,18 @@ require('./filtered_search_dropdown');
} }
renderContent() { renderContent() {
const dropdownData = [{ const dropdownData = [];
icon: 'fa-pencil',
hint: 'author:', [].forEach.call(this.input.parentElement.querySelectorAll('.dropdown-menu'), (dropdownMenu) => {
tag: '<@author>', const { icon, hint, tag } = dropdownMenu.dataset;
}, { if (icon && hint && tag) {
icon: 'fa-user', dropdownData.push({
hint: 'assignee:', icon: `fa-${icon}`,
tag: '<@assignee>', hint,
}, { tag: `<${tag}>`,
icon: 'fa-clock-o', });
hint: 'milestone:', }
tag: '<%milestone>', });
}, {
icon: 'fa-tag',
hint: 'label:',
tag: '<~label>',
}];
this.droplab.changeHookList(this.hookId, this.dropdown, [droplabFilter], this.config); this.droplab.changeHookList(this.hookId, this.dropdown, [droplabFilter], this.config);
this.droplab.setData(this.hookId, dropdownData); this.droplab.setData(this.hookId, dropdownData);
......
...@@ -52,8 +52,9 @@ ...@@ -52,8 +52,9 @@
} }
renderContent(forceShowList = false) { renderContent(forceShowList = false) {
if (forceShowList && this.getCurrentHook().list.hidden) { const currentHook = this.getCurrentHook();
this.getCurrentHook().list.show(); if (forceShowList && currentHook && currentHook.list.hidden) {
currentHook.list.show();
} }
} }
...@@ -92,11 +93,16 @@ ...@@ -92,11 +93,16 @@
} }
hideDropdown() { hideDropdown() {
this.getCurrentHook().list.hide(); const currentHook = this.getCurrentHook();
if (currentHook) {
currentHook.list.hide();
}
} }
resetFilters() { resetFilters() {
const hook = this.getCurrentHook(); const hook = this.getCurrentHook();
if (hook) {
const data = hook.list.data; const data = hook.list.data;
const results = data.map((o) => { const results = data.map((o) => {
const updated = o; const updated = o;
...@@ -106,6 +112,7 @@ ...@@ -106,6 +112,7 @@
hook.list.render(results); hook.list.render(results);
} }
} }
}
window.gl = window.gl || {}; window.gl = window.gl || {};
gl.FilteredSearchDropdown = FilteredSearchDropdown; gl.FilteredSearchDropdown = FilteredSearchDropdown;
......
...@@ -2,10 +2,12 @@ ...@@ -2,10 +2,12 @@
(() => { (() => {
class FilteredSearchDropdownManager { class FilteredSearchDropdownManager {
constructor(baseEndpoint = '') { constructor(baseEndpoint = '', page) {
this.baseEndpoint = baseEndpoint.replace(/\/$/, ''); this.baseEndpoint = baseEndpoint.replace(/\/$/, '');
this.tokenizer = gl.FilteredSearchTokenizer; this.tokenizer = gl.FilteredSearchTokenizer;
this.filteredSearchTokenKeys = gl.FilteredSearchTokenKeys;
this.filteredSearchInput = document.querySelector('.filtered-search'); this.filteredSearchInput = document.querySelector('.filtered-search');
this.page = page;
this.setupMapping(); this.setupMapping();
...@@ -150,7 +152,7 @@ ...@@ -150,7 +152,7 @@
this.droplab = new DropLab(); this.droplab = new DropLab();
} }
const match = gl.FilteredSearchTokenKeys.searchByKey(dropdownName.toLowerCase()); const match = this.filteredSearchTokenKeys.searchByKey(dropdownName.toLowerCase());
const shouldOpenFilterDropdown = match && this.currentDropdown !== match.key const shouldOpenFilterDropdown = match && this.currentDropdown !== match.key
&& this.mapping[match.key]; && this.mapping[match.key];
const shouldOpenHintDropdown = !match && this.currentDropdown !== 'hint'; const shouldOpenHintDropdown = !match && this.currentDropdown !== 'hint';
......
(() => { (() => {
class FilteredSearchManager { class FilteredSearchManager {
constructor() { constructor(page) {
this.filteredSearchInput = document.querySelector('.filtered-search'); this.filteredSearchInput = document.querySelector('.filtered-search');
this.clearSearchButton = document.querySelector('.clear-search'); this.clearSearchButton = document.querySelector('.clear-search');
this.filteredSearchTokenKeys = gl.FilteredSearchTokenKeys;
if (this.filteredSearchInput) { if (this.filteredSearchInput) {
this.tokenizer = gl.FilteredSearchTokenizer; this.tokenizer = gl.FilteredSearchTokenizer;
this.dropdownManager = new gl.FilteredSearchDropdownManager(this.filteredSearchInput.getAttribute('data-base-endpoint') || ''); this.dropdownManager = new gl.FilteredSearchDropdownManager(this.filteredSearchInput.getAttribute('data-base-endpoint') || '', page);
this.bindEvents(); this.bindEvents();
this.loadSearchParamsFromURL(); this.loadSearchParamsFromURL();
...@@ -117,8 +118,8 @@ ...@@ -117,8 +118,8 @@
const keyParam = decodeURIComponent(split[0]); const keyParam = decodeURIComponent(split[0]);
const value = split[1]; const value = split[1];
// Check if it matches edge conditions listed in gl.FilteredSearchTokenKeys // Check if it matches edge conditions listed in this.filteredSearchTokenKeys
const condition = gl.FilteredSearchTokenKeys.searchByConditionUrl(p); const condition = this.filteredSearchTokenKeys.searchByConditionUrl(p);
if (condition) { if (condition) {
inputValues.push(`${condition.tokenKey}:${condition.value}`); inputValues.push(`${condition.tokenKey}:${condition.value}`);
...@@ -126,7 +127,7 @@ ...@@ -126,7 +127,7 @@
// Sanitize value since URL converts spaces into + // Sanitize value since URL converts spaces into +
// Replace before decode so that we know what was originally + versus the encoded + // Replace before decode so that we know what was originally + versus the encoded +
const sanitizedValue = value ? decodeURIComponent(value.replace(/\+/g, ' ')) : value; const sanitizedValue = value ? decodeURIComponent(value.replace(/\+/g, ' ')) : value;
const match = gl.FilteredSearchTokenKeys.searchByKeyParam(keyParam); const match = this.filteredSearchTokenKeys.searchByKeyParam(keyParam);
if (match) { if (match) {
const indexOf = keyParam.indexOf('_'); const indexOf = keyParam.indexOf('_');
...@@ -171,9 +172,9 @@ ...@@ -171,9 +172,9 @@
paths.push(`state=${currentState}`); paths.push(`state=${currentState}`);
tokens.forEach((token) => { tokens.forEach((token) => {
const condition = gl.FilteredSearchTokenKeys const condition = this.filteredSearchTokenKeys
.searchByConditionKeyValue(token.key, token.value.toLowerCase()); .searchByConditionKeyValue(token.key, token.value.toLowerCase());
const { param } = gl.FilteredSearchTokenKeys.searchByKey(token.key) || {}; const { param } = this.filteredSearchTokenKeys.searchByKey(token.key) || {};
const keyParam = param ? `${token.key}_${param}` : token.key; const keyParam = param ? `${token.key}_${param}` : token.key;
let tokenPath = ''; let tokenPath = '';
......
...@@ -169,10 +169,10 @@ ...@@ -169,10 +169,10 @@
url: issuesPath + "/?author_username=" + userName url: issuesPath + "/?author_username=" + userName
}, 'separator', { }, 'separator', {
text: 'Merge requests assigned to me', text: 'Merge requests assigned to me',
url: mrPath + "/?assignee_id=" + userId url: mrPath + "/?assignee_username=" + userName
}, { }, {
text: "Merge requests I've created", text: "Merge requests I've created",
url: mrPath + "/?author_id=" + userId url: mrPath + "/?author_username=" + userName
} }
]; ];
if (!name) { if (!name) {
......
...@@ -50,6 +50,17 @@ class Projects::MergeRequestsController < Projects::ApplicationController ...@@ -50,6 +50,17 @@ class Projects::MergeRequestsController < Projects::ApplicationController
@labels = LabelsFinder.new(current_user, labels_params).execute @labels = LabelsFinder.new(current_user, labels_params).execute
end end
@users = []
if params[:assignee_id].present?
assignee = User.find_by_id(params[:assignee_id])
@users.push(assignee) if assignee
end
if params[:author_id].present?
author = User.find_by_id(params[:author_id])
@users.push(author) if author
end
respond_to do |format| respond_to do |format|
format.html format.html
format.json do format.json do
......
...@@ -5,18 +5,19 @@ ...@@ -5,18 +5,19 @@
= render "projects/issues/head" = render "projects/issues/head"
= render 'projects/last_push' = render 'projects/last_push'
- content_for :page_specific_javascripts do
= page_specific_javascript_bundle_tag('filtered_search')
%div{ class: container_class } %div{ class: container_class }
.top-area .top-area
= render 'shared/issuable/nav', type: :merge_requests = render 'shared/issuable/nav', type: :merge_requests
.nav-controls .nav-controls
= render 'shared/issuable/search_form', path: namespace_project_merge_requests_path(@project.namespace, @project)
- merge_project = can?(current_user, :create_merge_request, @project) ? @project : (current_user && current_user.fork_of(@project)) - merge_project = can?(current_user, :create_merge_request, @project) ? @project : (current_user && current_user.fork_of(@project))
- if merge_project - if merge_project
= link_to new_namespace_project_merge_request_path(merge_project.namespace, merge_project), class: "btn btn-new", title: "New Merge Request" do = link_to new_namespace_project_merge_request_path(merge_project.namespace, merge_project), class: "btn btn-new", title: "New Merge Request" do
New Merge Request New Merge Request
= render 'shared/issuable/filter', type: :merge_requests = render 'shared/issuable/search_bar', type: :merge_requests
.merge-requests-holder .merge-requests-holder
= render 'merge_requests' = render 'merge_requests'
...@@ -32,7 +32,7 @@ ...@@ -32,7 +32,7 @@
{{hint}} {{hint}}
%span.js-filter-tag.dropdown-light-content %span.js-filter-tag.dropdown-light-content
{{tag}} {{tag}}
#js-dropdown-author.dropdown-menu #js-dropdown-author.dropdown-menu{ data: { icon: 'pencil', hint: 'author', tag: '@author' } }
%ul.filter-dropdown{ 'data-dynamic' => true, 'data-dropdown' => true } %ul.filter-dropdown{ 'data-dynamic' => true, 'data-dropdown' => true }
%li.filter-dropdown-item %li.filter-dropdown-item
%button.btn.btn-link.dropdown-user %button.btn.btn-link.dropdown-user
...@@ -42,7 +42,7 @@ ...@@ -42,7 +42,7 @@
{{name}} {{name}}
%span.dropdown-light-content %span.dropdown-light-content
@{{username}} @{{username}}
#js-dropdown-assignee.dropdown-menu #js-dropdown-assignee.dropdown-menu{ data: { icon: 'user', hint: 'assignee', tag: '@assignee' } }
%ul{ 'data-dropdown' => true } %ul{ 'data-dropdown' => true }
%li.filter-dropdown-item{ 'data-value' => 'none' } %li.filter-dropdown-item{ 'data-value' => 'none' }
%button.btn.btn-link %button.btn.btn-link
...@@ -57,7 +57,7 @@ ...@@ -57,7 +57,7 @@
{{name}} {{name}}
%span.dropdown-light-content %span.dropdown-light-content
@{{username}} @{{username}}
#js-dropdown-milestone.dropdown-menu{ 'data-dropdown' => true } #js-dropdown-milestone.dropdown-menu{ data: { icon: 'clock-o', hint: 'milestone', tag: '%milestone' } }
%ul{ 'data-dropdown' => true } %ul{ 'data-dropdown' => true }
%li.filter-dropdown-item{ 'data-value' => 'none' } %li.filter-dropdown-item{ 'data-value' => 'none' }
%button.btn.btn-link %button.btn.btn-link
...@@ -70,7 +70,7 @@ ...@@ -70,7 +70,7 @@
%li.filter-dropdown-item %li.filter-dropdown-item
%button.btn.btn-link.js-data-value %button.btn.btn-link.js-data-value
{{title}} {{title}}
#js-dropdown-label.dropdown-menu{ 'data-dropdown' => true } #js-dropdown-label.dropdown-menu{ data: { icon: 'tag', hint: 'label', tag: '~label' } }
%ul{ 'data-dropdown' => true } %ul{ 'data-dropdown' => true }
%li.filter-dropdown-item{ 'data-value' => 'none' } %li.filter-dropdown-item{ 'data-value' => 'none' }
%button.btn.btn-link %button.btn.btn-link
......
---
title: Add filtered search to MR page
merge_request:
author:
...@@ -293,13 +293,6 @@ Feature: Project Merge Requests ...@@ -293,13 +293,6 @@ Feature: Project Merge Requests
And I preview a description text like "Bug fixed :smile:" And I preview a description text like "Bug fixed :smile:"
Then I should see the Markdown write tab Then I should see the Markdown write tab
@javascript
Scenario: I search merge request
Given I click link "All"
When I fill in merge request search with "Fe"
Then I should see "Feature NS-03" in merge requests
And I should not see "Bug NS-04" in merge requests
@javascript @javascript
Scenario: I can unsubscribe from merge request Scenario: I can unsubscribe from merge request
Given I visit merge request page "Bug NS-04" Given I visit merge request page "Bug NS-04"
......
require 'spec_helper' require 'spec_helper'
describe 'Dropdown label', js: true, feature: true do describe 'Dropdown label', js: true, feature: true do
include FilteredSearchHelpers
let(:project) { create(:empty_project) } let(:project) { create(:empty_project) }
let(:user) { create(:user) } let(:user) { create(:user) }
let(:filtered_search) { find('.filtered-search') } let(:filtered_search) { find('.filtered-search') }
...@@ -17,12 +19,6 @@ describe 'Dropdown label', js: true, feature: true do ...@@ -17,12 +19,6 @@ describe 'Dropdown label', js: true, feature: true do
let!(:long_label) { create(:label, project: project, title: 'this is a very long title this is a very long title this is a very long title this is a very long title this is a very long title') } let!(:long_label) { create(:label, project: project, title: 'this is a very long title this is a very long title this is a very long title this is a very long title this is a very long title') }
end end
def init_label_search
filtered_search.set('label:')
# This ensures the dropdown is shown
expect(find(js_dropdown_label)).not_to have_css('.filter-dropdown-loading')
end
def search_for_label(label) def search_for_label(label)
init_label_search init_label_search
filtered_search.send_keys(label) filtered_search.send_keys(label)
......
require 'rails_helper' require 'rails_helper'
describe 'Filter issues', js: true, feature: true do describe 'Filter issues', js: true, feature: true do
include FilteredSearchHelpers
include WaitForAjax include WaitForAjax
let!(:group) { create(:group) } let!(:group) { create(:group) }
...@@ -17,19 +18,6 @@ describe 'Filter issues', js: true, feature: true do ...@@ -17,19 +18,6 @@ describe 'Filter issues', js: true, feature: true do
let!(:multiple_words_label) { create(:label, project: project, title: "Two words") } let!(:multiple_words_label) { create(:label, project: project, title: "Two words") }
let!(:closed_issue) { create(:issue, title: 'bug that is closed', project: project, state: :closed) } let!(:closed_issue) { create(:issue, title: 'bug that is closed', project: project, state: :closed) }
let(:filtered_search) { find('.filtered-search') }
def input_filtered_search(search_term, submit: true)
filtered_search.set(search_term)
if submit
filtered_search.send_keys(:enter)
end
end
def expect_filtered_search_input(input)
expect(find('.filtered-search').value).to eq(input)
end
def expect_no_issues_list def expect_no_issues_list
page.within '.issues-list' do page.within '.issues-list' do
......
require 'rails_helper' require 'rails_helper'
feature 'Issue filtering by Labels', feature: true, js: true do feature 'Issue filtering by Labels', feature: true, js: true do
include FilteredSearchHelpers
include MergeRequestHelpers
include WaitForAjax include WaitForAjax
let(:project) { create(:project, :public) } let(:project) { create(:project, :public) }
...@@ -32,123 +34,77 @@ feature 'Issue filtering by Labels', feature: true, js: true do ...@@ -32,123 +34,77 @@ feature 'Issue filtering by Labels', feature: true, js: true do
context 'filter by label bug' do context 'filter by label bug' do
before do before do
select_labels('bug') input_filtered_search('label:~bug')
end end
it 'apply the filter' do it 'apply the filter' do
expect(page).to have_content "Bugfix1" expect(page).to have_content "Bugfix1"
expect(page).to have_content "Bugfix2" expect(page).to have_content "Bugfix2"
expect(page).not_to have_content "Feature1" expect(page).not_to have_content "Feature1"
expect(find('.filtered-labels')).to have_content "bug"
expect(find('.filtered-labels')).not_to have_content "feature"
expect(find('.filtered-labels')).not_to have_content "enhancement"
find('.js-label-filter-remove').click
wait_for_ajax
expect(find('.filtered-labels', visible: false)).to have_no_content "bug"
end end
end end
context 'filter by label feature' do context 'filter by label feature' do
before do before do
select_labels('feature') input_filtered_search('label:~feature')
end end
it 'applies the filter' do it 'applies the filter' do
expect(page).to have_content "Feature1" expect(page).to have_content "Feature1"
expect(page).not_to have_content "Bugfix2" expect(page).not_to have_content "Bugfix2"
expect(page).not_to have_content "Bugfix1" expect(page).not_to have_content "Bugfix1"
expect(find('.filtered-labels')).to have_content "feature"
expect(find('.filtered-labels')).not_to have_content "bug"
expect(find('.filtered-labels')).not_to have_content "enhancement"
end end
end end
context 'filter by label enhancement' do context 'filter by label enhancement' do
before do before do
select_labels('enhancement') input_filtered_search('label:~enhancement')
end end
it 'applies the filter' do it 'applies the filter' do
expect(page).to have_content "Bugfix2" expect(page).to have_content "Bugfix2"
expect(page).not_to have_content "Feature1" expect(page).not_to have_content "Feature1"
expect(page).not_to have_content "Bugfix1" expect(page).not_to have_content "Bugfix1"
expect(find('.filtered-labels')).to have_content "enhancement"
expect(find('.filtered-labels')).not_to have_content "bug"
expect(find('.filtered-labels')).not_to have_content "feature"
end end
end end
context 'filter by label enhancement and bug in issues list' do context 'filter by label enhancement and bug in issues list' do
before do before do
select_labels('bug', 'enhancement') input_filtered_search('label:~bug label:~enhancement')
end end
it 'applies the filters' do it 'applies the filters' do
expect(page).to have_issuable_counts(open: 1, closed: 0, all: 1) expect(page).to have_issuable_counts(open: 1, closed: 0, all: 1)
expect(page).to have_content "Bugfix2" expect(page).to have_content "Bugfix2"
expect(page).not_to have_content "Feature1" expect(page).not_to have_content "Feature1"
expect(find('.filtered-labels')).to have_content "bug"
expect(find('.filtered-labels')).to have_content "enhancement"
expect(find('.filtered-labels')).not_to have_content "feature"
find('.js-label-filter-remove', match: :first).click
wait_for_ajax
expect(page).to have_content "Bugfix2"
expect(page).not_to have_content "Feature1"
expect(page).not_to have_content "Bugfix1"
expect(find('.filtered-labels')).not_to have_content "bug"
expect(find('.filtered-labels')).to have_content "enhancement"
expect(find('.filtered-labels')).not_to have_content "feature"
end end
end end
context 'remove filtered labels' do context 'clear button' do
before do before do
page.within '.labels-filter' do input_filtered_search('label:~bug')
click_button 'Label'
wait_for_ajax
click_link 'bug'
find('.dropdown-menu-close').click
end
page.within '.filtered-labels' do
expect(page).to have_content 'bug'
end
end end
it 'allows user to remove filtered labels' do it 'allows user to remove filtered labels' do
first('.js-label-filter-remove').click first('.clear-search').click
wait_for_ajax filtered_search.send_keys(:enter)
expect(find('.filtered-labels', visible: false)).not_to have_content 'bug' expect(page).to have_issuable_counts(open: 3, closed: 0, all: 3)
expect(find('.labels-filter')).not_to have_content 'bug' expect(page).to have_content "Bugfix2"
expect(page).to have_content "Feature1"
expect(page).to have_content "Bugfix1"
end end
end end
context 'dropdown filtering' do context 'filter dropdown' do
it 'filters by label name' do it 'filters by label name' do
page.within '.labels-filter' do init_label_search
click_button 'Label' filtered_search.send_keys('~bug')
wait_for_ajax
find('.dropdown-input input').set 'bug'
page.within '.dropdown-content' do page.within '.filter-dropdown' do
expect(page).not_to have_content 'enhancement' expect(page).not_to have_content 'enhancement'
expect(page).to have_content 'bug' expect(page).to have_content 'bug'
end end
end end
end end
end
def select_labels(*labels)
page.find('.js-label-select').click
wait_for_ajax
labels.each do |label|
execute_script("$('.dropdown-menu-labels li:contains(\"#{label}\") a').click()")
end
page.first('.labels-filter .dropdown-title .dropdown-menu-close-icon').click
wait_for_ajax
end
end end
require 'rails_helper' require 'rails_helper'
feature 'Merge Request filtering by Milestone', feature: true do feature 'Merge Request filtering by Milestone', feature: true do
include FilteredSearchHelpers
include MergeRequestHelpers
let(:project) { create(:project, :public) } let(:project) { create(:project, :public) }
let!(:user) { create(:user)} let!(:user) { create(:user)}
let(:milestone) { create(:milestone, project: project) } let(:milestone) { create(:milestone, project: project) }
def filter_by_milestone(title)
find(".js-milestone-select").click
find(".milestone-filter a", text: title).click
end
before do before do
project.team << [user, :master] project.team << [user, :master]
login_as(user) login_as(user)
...@@ -15,42 +23,42 @@ feature 'Merge Request filtering by Milestone', feature: true do ...@@ -15,42 +23,42 @@ feature 'Merge Request filtering by Milestone', feature: true do
create(:merge_request, :simple, source_project: project, milestone: milestone) create(:merge_request, :simple, source_project: project, milestone: milestone)
visit_merge_requests(project) visit_merge_requests(project)
filter_by_milestone(Milestone::None.title) input_filtered_search('milestone:none')
expect(page).to have_issuable_counts(open: 1, closed: 0, all: 1) expect(page).to have_issuable_counts(open: 1, closed: 0, all: 1)
expect(page).to have_css('.merge-request', count: 1) expect(page).to have_css('.merge-request', count: 1)
end end
context 'filters by upcoming milestone', js: true do context 'filters by upcoming milestone', js: true do
it 'does not show issues with no expiry' do it 'does not show merge requests with no expiry' do
create(:merge_request, :with_diffs, source_project: project) create(:merge_request, :with_diffs, source_project: project)
create(:merge_request, :simple, source_project: project, milestone: milestone) create(:merge_request, :simple, source_project: project, milestone: milestone)
visit_merge_requests(project) visit_merge_requests(project)
filter_by_milestone(Milestone::Upcoming.title) input_filtered_search('milestone:upcoming')
expect(page).to have_css('.merge-request', count: 0) expect(page).to have_css('.merge-request', count: 0)
end end
it 'shows issues in future' do it 'shows merge requests in future' do
milestone = create(:milestone, project: project, due_date: Date.tomorrow) milestone = create(:milestone, project: project, due_date: Date.tomorrow)
create(:merge_request, :with_diffs, source_project: project) create(:merge_request, :with_diffs, source_project: project)
create(:merge_request, :simple, source_project: project, milestone: milestone) create(:merge_request, :simple, source_project: project, milestone: milestone)
visit_merge_requests(project) visit_merge_requests(project)
filter_by_milestone(Milestone::Upcoming.title) input_filtered_search('milestone:upcoming')
expect(page).to have_issuable_counts(open: 1, closed: 0, all: 1) expect(page).to have_issuable_counts(open: 1, closed: 0, all: 1)
expect(page).to have_css('.merge-request', count: 1) expect(page).to have_css('.merge-request', count: 1)
end end
it 'does not show issues in past' do it 'does not show merge requests in past' do
milestone = create(:milestone, project: project, due_date: Date.yesterday) milestone = create(:milestone, project: project, due_date: Date.yesterday)
create(:merge_request, :with_diffs, source_project: project) create(:merge_request, :with_diffs, source_project: project)
create(:merge_request, :simple, source_project: project, milestone: milestone) create(:merge_request, :simple, source_project: project, milestone: milestone)
visit_merge_requests(project) visit_merge_requests(project)
filter_by_milestone(Milestone::Upcoming.title) input_filtered_search('milestone:upcoming')
expect(page).to have_css('.merge-request', count: 0) expect(page).to have_css('.merge-request', count: 0)
end end
...@@ -61,7 +69,7 @@ feature 'Merge Request filtering by Milestone', feature: true do ...@@ -61,7 +69,7 @@ feature 'Merge Request filtering by Milestone', feature: true do
create(:merge_request, :simple, source_project: project) create(:merge_request, :simple, source_project: project)
visit_merge_requests(project) visit_merge_requests(project)
filter_by_milestone(milestone.title) input_filtered_search("milestone:%'#{milestone.title}'")
expect(page).to have_issuable_counts(open: 1, closed: 0, all: 1) expect(page).to have_issuable_counts(open: 1, closed: 0, all: 1)
expect(page).to have_css('.merge-request', count: 1) expect(page).to have_css('.merge-request', count: 1)
...@@ -77,19 +85,10 @@ feature 'Merge Request filtering by Milestone', feature: true do ...@@ -77,19 +85,10 @@ feature 'Merge Request filtering by Milestone', feature: true do
create(:merge_request, :simple, source_project: project) create(:merge_request, :simple, source_project: project)
visit_merge_requests(project) visit_merge_requests(project)
filter_by_milestone(milestone.title) input_filtered_search("milestone:%\"#{milestone.title}\"")
expect(page).to have_issuable_counts(open: 1, closed: 0, all: 1) expect(page).to have_issuable_counts(open: 1, closed: 0, all: 1)
expect(page).to have_css('.merge-request', count: 1) expect(page).to have_css('.merge-request', count: 1)
end end
end end
def visit_merge_requests(project)
visit namespace_project_merge_requests_path(project.namespace, project)
end
def filter_by_milestone(title)
find(".js-milestone-select").click
find(".milestone-filter a", text: title).click
end
end end
require 'rails_helper' require 'rails_helper'
describe 'Filter merge requests', feature: true do describe 'Filter merge requests', feature: true do
include FilteredSearchHelpers
include MergeRequestHelpers
include WaitForAjax include WaitForAjax
let!(:project) { create(:project) } let!(:project) { create(:project) }
let!(:group) { create(:group) } let!(:group) { create(:group) }
let!(:user) { create(:user)} let!(:user) { create(:user) }
let!(:milestone) { create(:milestone, project: project) } let!(:milestone) { create(:milestone, project: project) }
let!(:label) { create(:label, project: project) } let!(:label) { create(:label, project: project) }
let!(:wontfix) { create(:label, project: project, title: "Won't fix") } let!(:wontfix) { create(:label, project: project, title: "Won't fix") }
...@@ -15,183 +17,134 @@ describe 'Filter merge requests', feature: true do ...@@ -15,183 +17,134 @@ describe 'Filter merge requests', feature: true do
group.add_developer(user) group.add_developer(user)
login_as(user) login_as(user)
create(:merge_request, source_project: project, target_project: project) create(:merge_request, source_project: project, target_project: project)
end
describe 'for assignee from mr#index' do
before do
visit namespace_project_merge_requests_path(project.namespace, project) visit namespace_project_merge_requests_path(project.namespace, project)
end
find('.js-assignee-search').click describe 'for assignee from mr#index' do
let(:search_query) { "assignee:@#{user.username}" }
find('.dropdown-menu-user-link', text: user.username).click before do
input_filtered_search(search_query)
wait_for_ajax expect_mr_list_count(0)
end end
context 'assignee', js: true do context 'assignee', js: true do
it 'updates to current user' do it 'updates to current user' do
expect(find('.js-assignee-search .dropdown-toggle-text')).to have_content(user.name) expect_filtered_search_input(search_query)
end end
it 'does not change when closed link is clicked' do it 'does not change when closed link is clicked' do
find('.issues-state-filters a', text: "Closed").click find('.issues-state-filters a', text: "Closed").click
expect(find('.js-assignee-search .dropdown-toggle-text')).to have_content(user.name) expect_filtered_search_input(search_query)
end end
it 'does not change when all link is clicked' do it 'does not change when all link is clicked' do
find('.issues-state-filters a', text: "All").click find('.issues-state-filters a', text: "All").click
expect(find('.js-assignee-search .dropdown-toggle-text')).to have_content(user.name) expect_filtered_search_input(search_query)
end end
end end
end end
describe 'for milestone from mr#index' do describe 'for milestone from mr#index' do
before do let(:search_query) { "milestone:%#{milestone.title}" }
visit namespace_project_merge_requests_path(project.namespace, project)
find('.js-milestone-select').click before do
input_filtered_search(search_query)
find('.milestone-filter .dropdown-content a', text: milestone.title).click
wait_for_ajax expect_mr_list_count(0)
end end
context 'milestone', js: true do context 'milestone', js: true do
it 'updates to current milestone' do it 'updates to current milestone' do
expect(find('.js-milestone-select .dropdown-toggle-text')).to have_content(milestone.title) expect_filtered_search_input(search_query)
end end
it 'does not change when closed link is clicked' do it 'does not change when closed link is clicked' do
find('.issues-state-filters a', text: "Closed").click find('.issues-state-filters a', text: "Closed").click
expect(find('.js-milestone-select .dropdown-toggle-text')).to have_content(milestone.title) expect_filtered_search_input(search_query)
end end
it 'does not change when all link is clicked' do it 'does not change when all link is clicked' do
find('.issues-state-filters a', text: "All").click find('.issues-state-filters a', text: "All").click
expect(find('.js-milestone-select .dropdown-toggle-text')).to have_content(milestone.title) expect_filtered_search_input(search_query)
end end
end end
end end
describe 'for label from mr#index', js: true do describe 'for label from mr#index', js: true do
before do
visit namespace_project_merge_requests_path(project.namespace, project)
find('.js-label-select').click
wait_for_ajax
end
it 'filters by any label' do
find('.dropdown-menu-labels a', text: 'Any Label').click
page.first('.labels-filter .dropdown-title .dropdown-menu-close-icon').click
wait_for_ajax
expect(find('.labels-filter')).to have_content 'Label'
end
it 'filters by no label' do it 'filters by no label' do
find('.dropdown-menu-labels a', text: 'No Label').click input_filtered_search('label:none')
page.first('.labels-filter .dropdown-title .dropdown-menu-close-icon').click
wait_for_ajax
page.within '.labels-filter' do expect_mr_list_count(1)
expect(page).to have_content 'Labels' expect_filtered_search_input('label:none')
end
expect(find('.js-label-select .dropdown-toggle-text')).to have_content('Labels')
end end
it 'filters by a label' do it 'filters by a label' do
find('.dropdown-menu-labels a', text: label.title).click input_filtered_search("label:~#{label.title}")
page.within '.labels-filter' do
expect(page).to have_content label.title expect_mr_list_count(0)
end expect_filtered_search_input("label:~#{label.title}")
expect(find('.js-label-select .dropdown-toggle-text')).to have_content(label.title)
end end
it "filters by `won't fix` and another label" do it "filters by `won't fix` and another label" do
page.within '.labels-filter' do input_filtered_search("label:~\"#{wontfix.title}\" label:~#{label.title}")
click_link wontfix.title
expect(page).to have_content wontfix.title
click_link label.title
end
expect(find('.js-label-select .dropdown-toggle-text')).to have_content("#{wontfix.title} +1 more") expect_mr_list_count(0)
expect_filtered_search_input("label:~\"#{wontfix.title}\" label:~#{label.title}")
end end
it "filters by `won't fix` label followed by another label after page load" do it "filters by `won't fix` label followed by another label after page load" do
page.within '.labels-filter' do input_filtered_search("label:~\"#{wontfix.title}\"")
click_link wontfix.title
expect(page).to have_content wontfix.title
end
find('body').click
expect(find('.filtered-labels')).to have_content(wontfix.title)
find('.js-label-select').click
wait_for_ajax
find('.dropdown-menu-labels a', text: label.title).click
find('body').click expect_mr_list_count(0)
expect_filtered_search_input("label:~\"#{wontfix.title}\"")
expect(find('.filtered-labels')).to have_content(wontfix.title) input_filtered_search_keys(" label:~#{label.title}")
expect(find('.filtered-labels')).to have_content(label.title)
find('.js-label-select').click expect_filtered_search_input("label:~\"#{wontfix.title}\" label:~#{label.title}")
wait_for_ajax
expect(find('.dropdown-menu-labels li', text: wontfix.title)).to have_css('.is-active')
expect(find('.dropdown-menu-labels li', text: label.title)).to have_css('.is-active')
end
it "selects and unselects `won't fix`" do expect_mr_list_count(0)
find('.dropdown-menu-labels a', text: wontfix.title).click expect_filtered_search_input("label:~\"#{wontfix.title}\" label:~#{label.title}")
find('.dropdown-menu-labels a', text: wontfix.title).click
# Close label dropdown to load
find('body').click
expect(page).not_to have_css('.filtered-labels')
end end
end end
describe 'for assignee and label from issues#index' do describe 'for assignee and label from issues#index' do
before do let(:search_query) { "assignee:@#{user.username} label:~#{label.title}" }
visit namespace_project_merge_requests_path(project.namespace, project)
find('.js-assignee-search').click before do
input_filtered_search("assignee:@#{user.username}")
find('.dropdown-menu-user-link', text: user.username).click
expect(page).not_to have_selector('.mr-list .merge-request') expect_mr_list_count(1)
expect_filtered_search_input("assignee:@#{user.username}")
find('.js-label-select').click input_filtered_search_keys(" label:~#{label.title}")
find('.dropdown-menu-labels .dropdown-content a', text: label.title).click expect_mr_list_count(1)
page.first('.labels-filter .dropdown-title .dropdown-menu-close-icon').click
wait_for_ajax find("#state-opened[href=\"#{URI.parse(current_url).path}?assignee_username=#{user.username}&label_name%5B%5D=#{label.title}&scope=all&state=opened\"]")
end end
context 'assignee and label', js: true do context 'assignee and label', js: true do
it 'updates to current assignee and label' do it 'updates to current assignee and label' do
expect(find('.js-assignee-search .dropdown-toggle-text')).to have_content(user.name) expect_filtered_search_input(search_query)
expect(find('.js-label-select .dropdown-toggle-text')).to have_content(label.title)
end end
it 'does not change when closed link is clicked' do it 'does not change when closed link is clicked' do
find('.issues-state-filters a', text: "Closed").click find('.issues-state-filters a', text: "Closed").click
expect(find('.js-assignee-search .dropdown-toggle-text')).to have_content(user.name) expect_filtered_search_input(search_query)
expect(find('.js-label-select .dropdown-toggle-text')).to have_content(label.title)
end end
it 'does not change when all link is clicked' do it 'does not change when all link is clicked' do
find('.issues-state-filters a', text: "All").click find('.issues-state-filters a', text: "All").click
expect(find('.js-assignee-search .dropdown-toggle-text')).to have_content(user.name) expect_filtered_search_input(search_query)
expect(find('.js-label-select .dropdown-toggle-text')).to have_content(label.title)
end end
end end
end end
...@@ -218,15 +171,13 @@ describe 'Filter merge requests', feature: true do ...@@ -218,15 +171,13 @@ describe 'Filter merge requests', feature: true do
context 'only text', js: true do context 'only text', js: true do
it 'filters merge requests by searched text' do it 'filters merge requests by searched text' do
fill_in 'issuable_search', with: 'Bug' input_filtered_search('bug')
page.within '.mr-list' do expect_mr_list_count(2)
expect(page).to have_selector('.merge-request', count: 2)
end
end end
it 'does not show any merge requests' do it 'does not show any merge requests' do
fill_in 'issuable_search', with: 'testing' input_filtered_search('testing')
page.within '.mr-list' do page.within '.mr-list' do
expect(page).not_to have_selector('.merge-request') expect(page).not_to have_selector('.merge-request')
...@@ -234,82 +185,49 @@ describe 'Filter merge requests', feature: true do ...@@ -234,82 +185,49 @@ describe 'Filter merge requests', feature: true do
end end
end end
context 'text and dropdown options', js: true do context 'filters and searches', js: true do
it 'filters by text and label' do it 'filters by text and label' do
fill_in 'issuable_search', with: 'Bug' input_filtered_search('Bug')
expect(page).to have_issuable_counts(open: 2, closed: 0, all: 2) expect_mr_list_count(2)
page.within '.mr-list' do expect_filtered_search_input('Bug')
expect(page).to have_selector('.merge-request', count: 2)
end
click_button 'Label' input_filtered_search_keys(' label:~bug')
page.within '.labels-filter' do
click_link 'bug'
end
find('.dropdown-menu-close-icon').click
expect(page).to have_issuable_counts(open: 1, closed: 0, all: 1) expect_mr_list_count(1)
page.within '.mr-list' do
expect(page).to have_selector('.merge-request', count: 1)
end
end end
it 'filters by text and milestone' do it 'filters by text and milestone' do
fill_in 'issuable_search', with: 'Bug' input_filtered_search('Bug')
expect(page).to have_issuable_counts(open: 2, closed: 0, all: 2) expect_mr_list_count(2)
page.within '.mr-list' do expect_filtered_search_input('Bug')
expect(page).to have_selector('.merge-request', count: 2)
end
click_button 'Milestone' input_filtered_search_keys(' milestone:%8')
page.within '.milestone-filter' do
click_link '8'
end
expect(page).to have_issuable_counts(open: 1, closed: 0, all: 1) expect_mr_list_count(1)
page.within '.mr-list' do
expect(page).to have_selector('.merge-request', count: 1)
end
end end
it 'filters by text and assignee' do it 'filters by text and assignee' do
fill_in 'issuable_search', with: 'Bug' input_filtered_search('Bug')
expect(page).to have_issuable_counts(open: 2, closed: 0, all: 2) expect_mr_list_count(2)
page.within '.mr-list' do expect_filtered_search_input('Bug')
expect(page).to have_selector('.merge-request', count: 2)
end
click_button 'Assignee' input_filtered_search_keys(" assignee:@#{user.username}")
page.within '.dropdown-menu-assignee' do
click_link user.name
end
expect(page).to have_issuable_counts(open: 1, closed: 0, all: 1) expect_mr_list_count(1)
page.within '.mr-list' do
expect(page).to have_selector('.merge-request', count: 1)
end
end end
it 'filters by text and author' do it 'filters by text and author' do
fill_in 'issuable_search', with: 'Bug' input_filtered_search('Bug')
expect(page).to have_issuable_counts(open: 2, closed: 0, all: 2) expect_mr_list_count(2)
page.within '.mr-list' do expect_filtered_search_input('Bug')
expect(page).to have_selector('.merge-request', count: 2)
end
click_button 'Author' input_filtered_search_keys(" author:@#{user.username}")
page.within '.dropdown-menu-author' do
click_link user.name
end
expect(page).to have_issuable_counts(open: 1, closed: 0, all: 1) expect_mr_list_count(1)
page.within '.mr-list' do
expect(page).to have_selector('.merge-request', count: 1)
end
end end
end end
end end
...@@ -328,18 +246,9 @@ describe 'Filter merge requests', feature: true do ...@@ -328,18 +246,9 @@ describe 'Filter merge requests', feature: true do
end end
it 'is able to filter and sort merge requests' do it 'is able to filter and sort merge requests' do
click_button 'Label' input_filtered_search('label:~bug')
wait_for_ajax
page.within '.labels-filter' do
click_link 'bug'
end
find('.dropdown-menu-close-icon').click
wait_for_ajax
expect(page).to have_issuable_counts(open: 2, closed: 0, all: 2) expect_mr_list_count(2)
page.within '.mr-list' do
expect(page).to have_selector('.merge-request', count: 2)
end
click_button 'Last created' click_button 'Last created'
page.within '.dropdown-menu-sort' do page.within '.dropdown-menu-sort' do
...@@ -352,4 +261,38 @@ describe 'Filter merge requests', feature: true do ...@@ -352,4 +261,38 @@ describe 'Filter merge requests', feature: true do
end end
end end
end end
describe 'filter by assignee id', js: true do
it 'filter by current user' do
visit namespace_project_merge_requests_path(project.namespace, project, assignee_id: user.id)
expect_filtered_search_input("assignee:@#{user.username}")
end
it 'filter by new user' do
new_user = create(:user)
project.add_developer(new_user)
visit namespace_project_merge_requests_path(project.namespace, project, assignee_id: new_user.id)
expect_filtered_search_input("assignee:@#{new_user.username}")
end
end
describe 'filter by author id', js: true do
it 'filter by current user' do
visit namespace_project_merge_requests_path(project.namespace, project, author_id: user.id)
expect_filtered_search_input("author:@#{user.username}")
end
it 'filter by new user' do
new_user = create(:user)
project.add_developer(new_user)
visit namespace_project_merge_requests_path(project.namespace, project, author_id: new_user.id)
expect_filtered_search_input("author:@#{new_user.username}")
end
end
end end
require 'rails_helper' require 'rails_helper'
feature 'Issues filter reset button', feature: true, js: true do feature 'Issues filter reset button', feature: true, js: true do
include FilteredSearchHelpers
include MergeRequestHelpers
include WaitForAjax include WaitForAjax
include IssueHelpers include IssueHelpers
let!(:project) { create(:project, :public) } let!(:project) { create(:project, :public) }
let!(:user) { create(:user)} let!(:user) { create(:user) }
let!(:milestone) { create(:milestone, project: project) } let!(:milestone) { create(:milestone, project: project) }
let!(:bug) { create(:label, project: project, name: 'bug')} let!(:bug) { create(:label, project: project, name: 'bug')}
let!(:mr1) { create(:merge_request, title: "Feature", source_project: project, target_project: project, source_branch: "Feature", milestone: milestone, author: user, assignee: user) } let!(:mr1) { create(:merge_request, title: "Feature", source_project: project, target_project: project, source_branch: "Feature", milestone: milestone, author: user, assignee: user) }
let!(:mr2) { create(:merge_request, title: "Bugfix1", source_project: project, target_project: project, source_branch: "Bugfix1") } let!(:mr2) { create(:merge_request, title: "Bugfix1", source_project: project, target_project: project, source_branch: "Bugfix1") }
let(:merge_request_css) { '.merge-request' } let(:merge_request_css) { '.merge-request' }
let(:clear_search_css) { '.filtered-search-input-container .clear-search' }
before do before do
mr2.labels << bug mr2.labels << bug
...@@ -50,7 +53,7 @@ feature 'Issues filter reset button', feature: true, js: true do ...@@ -50,7 +53,7 @@ feature 'Issues filter reset button', feature: true, js: true do
context 'when author filter has been applied' do context 'when author filter has been applied' do
it 'resets the author filter' do it 'resets the author filter' do
visit_merge_requests(project, author_id: user.id) visit_merge_requests(project, author_username: user.username)
expect(page).to have_css(merge_request_css, count: 1) expect(page).to have_css(merge_request_css, count: 1)
reset_filters reset_filters
...@@ -60,7 +63,7 @@ feature 'Issues filter reset button', feature: true, js: true do ...@@ -60,7 +63,7 @@ feature 'Issues filter reset button', feature: true, js: true do
context 'when assignee filter has been applied' do context 'when assignee filter has been applied' do
it 'resets the assignee filter' do it 'resets the assignee filter' do
visit_merge_requests(project, assignee_id: user.id) visit_merge_requests(project, assignee_username: user.username)
expect(page).to have_css(merge_request_css, count: 1) expect(page).to have_css(merge_request_css, count: 1)
reset_filters reset_filters
...@@ -70,7 +73,7 @@ feature 'Issues filter reset button', feature: true, js: true do ...@@ -70,7 +73,7 @@ feature 'Issues filter reset button', feature: true, js: true do
context 'when all filters have been applied' do context 'when all filters have been applied' do
it 'resets all filters' do it 'resets all filters' do
visit_merge_requests(project, assignee_id: user.id, author_id: user.id, milestone_title: milestone.title, label_name: bug.name, search: 'Bug') visit_merge_requests(project, assignee_username: user.username, author_username: user.username, milestone_title: milestone.title, label_name: bug.name, search: 'Bug')
expect(page).to have_css(merge_request_css, count: 0) expect(page).to have_css(merge_request_css, count: 0)
reset_filters reset_filters
...@@ -82,15 +85,7 @@ feature 'Issues filter reset button', feature: true, js: true do ...@@ -82,15 +85,7 @@ feature 'Issues filter reset button', feature: true, js: true do
it 'the reset link should not be visible' do it 'the reset link should not be visible' do
visit_merge_requests(project) visit_merge_requests(project)
expect(page).to have_css(merge_request_css, count: 2) expect(page).to have_css(merge_request_css, count: 2)
expect(page).not_to have_css '.reset_filters' expect(page).not_to have_css(clear_search_css)
end end
end end
def visit_merge_requests(project, opts = {})
visit namespace_project_merge_requests_path project.namespace, project, opts
end
def reset_filters
find('.reset-filters').click
end
end end
...@@ -186,7 +186,7 @@ describe "Search", feature: true do ...@@ -186,7 +186,7 @@ describe "Search", feature: true do
sleep 2 sleep 2
expect(page).to have_selector('.merge-requests-holder') expect(page).to have_selector('.merge-requests-holder')
expect(find('.js-assignee-search .dropdown-toggle-text')).to have_content(user.name) expect(find('.filtered-search').value).to eq("assignee:@#{user.username}")
end end
it 'takes user to her MR page when MR authored is clicked' do it 'takes user to her MR page when MR authored is clicked' do
...@@ -194,7 +194,7 @@ describe "Search", feature: true do ...@@ -194,7 +194,7 @@ describe "Search", feature: true do
sleep 2 sleep 2
expect(page).to have_selector('.merge-requests-holder') expect(page).to have_selector('.merge-requests-holder')
expect(find('.js-author-search .dropdown-toggle-text')).to have_content(user.name) expect(find('.filtered-search').value).to eq("author:@#{user.username}")
end end
end end
......
...@@ -89,8 +89,8 @@ require('vendor/fuzzaldrin-plus'); ...@@ -89,8 +89,8 @@ require('vendor/fuzzaldrin-plus');
var a1, a2, a3, a4, issuesAssignedToMeLink, issuesIHaveCreatedLink, mrsAssignedToMeLink, mrsIHaveCreatedLink; var a1, a2, a3, a4, issuesAssignedToMeLink, issuesIHaveCreatedLink, mrsAssignedToMeLink, mrsIHaveCreatedLink;
issuesAssignedToMeLink = issuesPath + "/?assignee_username=" + userName; issuesAssignedToMeLink = issuesPath + "/?assignee_username=" + userName;
issuesIHaveCreatedLink = issuesPath + "/?author_username=" + userName; issuesIHaveCreatedLink = issuesPath + "/?author_username=" + userName;
mrsAssignedToMeLink = mrsPath + "/?assignee_id=" + userId; mrsAssignedToMeLink = mrsPath + "/?assignee_username=" + userName;
mrsIHaveCreatedLink = mrsPath + "/?author_id=" + userId; mrsIHaveCreatedLink = mrsPath + "/?author_username=" + userName;
a1 = "a[href='" + issuesAssignedToMeLink + "']"; a1 = "a[href='" + issuesAssignedToMeLink + "']";
a2 = "a[href='" + issuesIHaveCreatedLink + "']"; a2 = "a[href='" + issuesIHaveCreatedLink + "']";
a3 = "a[href='" + mrsAssignedToMeLink + "']"; a3 = "a[href='" + mrsAssignedToMeLink + "']";
......
module FilteredSearchHelpers
def filtered_search
page.find('.filtered-search')
end
def input_filtered_search(search_term, submit: true)
filtered_search.set(search_term)
if submit
filtered_search.send_keys(:enter)
end
end
def input_filtered_search_keys(search_term)
filtered_search.send_keys(search_term)
filtered_search.send_keys(:enter)
end
def expect_filtered_search_input(input)
expect(find('.filtered-search').value).to eq(input)
end
def clear_search_field
find('.filtered-search-input-container .clear-search').click
end
def reset_filters
clear_search_field
filtered_search.send_keys(:enter)
end
def init_label_search
filtered_search.set('label:')
# This ensures the dropdown is shown
expect(find('#js-dropdown-label')).not_to have_css('.filter-dropdown-loading')
end
end
...@@ -10,4 +10,13 @@ module MergeRequestHelpers ...@@ -10,4 +10,13 @@ module MergeRequestHelpers
def last_merge_request def last_merge_request
page.all('ul.mr-list > li').last.text page.all('ul.mr-list > li').last.text
end end
def expect_mr_list_count(open_count, closed_count = 0)
all_count = open_count + closed_count
expect(page).to have_issuable_counts(open: open_count, closed: closed_count, all: all_count)
page.within '.mr-list' do
expect(page).to have_selector('.merge-request', count: open_count)
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