Commit c2c3949a authored by Heinrich Lee Yu's avatar Heinrich Lee Yu

Merge branch...

Merge branch '331649-issue_boards_filtered_search-enable-gl-filtered-search-on-issue-boards' into 'master'

Clean up issue_boards_filtered_search feature flag

See merge request gitlab-org/gitlab!80771
parents 8f37a584 5936ac3f
......@@ -151,10 +151,10 @@ export default {
});
}
if (this.filterParams['not[iteration_id]']) {
if (this.filterParams['not[iterationId]']) {
filteredSearchValue.push({
type: 'iteration_id',
value: { data: this.filterParams['not[iteration_id]'], operator: '!=' },
type: 'iteration',
value: { data: this.filterParams['not[iterationId]'], operator: '!=' },
});
}
......
import { transformBoardConfig } from 'ee_else_ce/boards/boards_util';
import FilteredSearchManager from 'ee_else_ce/filtered_search/filtered_search_manager';
import IssuableFilteredSearchTokenKeys from 'ee_else_ce/filtered_search/issuable_filtered_search_token_keys';
import { updateHistory } from '~/lib/utils/url_utility';
import FilteredSearchContainer from '../filtered_search/container';
import vuexstore from './stores';
export default class FilteredSearchBoards extends FilteredSearchManager {
constructor(store, updateUrl = false, cantEdit = []) {
super({
page: 'boards',
isGroupDecendent: true,
stateFiltersSelector: '.issues-state-filters',
isGroup: IS_EE,
useDefaultState: false,
filteredSearchTokenKeys: IssuableFilteredSearchTokenKeys,
});
this.store = store;
this.updateUrl = updateUrl;
// Issue boards is slightly different, we handle all the requests async
// instead or reloading the page, we just re-fire the list ajax requests
this.isHandledAsync = true;
this.cantEdit = cantEdit.filter((i) => typeof i === 'string');
this.cantEditWithValue = cantEdit.filter((i) => typeof i === 'object');
if (vuexstore.state.boardConfig) {
const boardConfigPath = transformBoardConfig(vuexstore.state.boardConfig);
// TODO Refactor: https://gitlab.com/gitlab-org/gitlab/-/issues/329274
// here we are using "window.location.search" as a temporary store
// only to unpack the params and do another validation inside
// 'performSearch' and 'setFilter' vuex actions.
if (boardConfigPath !== '') {
const filterPath = window.location.search ? `${window.location.search}&` : '?';
updateHistory({
url: `${filterPath}${transformBoardConfig(vuexstore.state.boardConfig)}`,
});
}
}
}
updateObject(path) {
const groupByParam = new URLSearchParams(window.location.search).get('group_by');
this.store.path = `${path.substr(1)}${groupByParam ? `&group_by=${groupByParam}` : ''}`;
updateHistory({
url: `?${path.substr(1)}${groupByParam ? `&group_by=${groupByParam}` : ''}`,
});
vuexstore.dispatch('performSearch');
}
removeTokens() {
const tokens = FilteredSearchContainer.container.querySelectorAll('.js-visual-token');
// Remove all the tokens as they will be replaced by the search manager
[].forEach.call(tokens, (el) => {
el.parentNode.removeChild(el);
});
this.filteredSearchInput.value = '';
}
updateTokens() {
this.removeTokens();
this.loadSearchParamsFromURL();
// Get the placeholder back if search is empty
this.filteredSearchInput.dispatchEvent(new Event('input'));
}
canEdit(tokenName, tokenValue) {
if (this.cantEdit.includes(tokenName)) return false;
return (
this.cantEditWithValue.findIndex(
(token) => token.name === tokenName && token.value === tokenValue,
) === -1
);
}
}
......@@ -10,5 +10,6 @@ export const gqlClient = createDefaultClient(
return object.__typename === 'BoardList' ? object.iid : defaultDataIdFromObject(object);
},
},
batchMax: 2,
},
);
......@@ -8,8 +8,6 @@ import BoardAddNewColumnTrigger from '~/boards/components/board_add_new_column_t
import BoardApp from '~/boards/components/board_app.vue';
import '~/boards/filters/due_date_filters';
import { issuableTypes } from '~/boards/constants';
import eventHub from '~/boards/eventhub';
import FilteredSearchBoards from '~/boards/filtered_search_boards';
import initBoardsFilteredSearch from '~/boards/mount_filtered_search_issue_boards';
import store from '~/boards/stores';
import toggleFocusMode from '~/boards/toggle_focus';
......@@ -50,17 +48,6 @@ function mountBoardApp(el) {
},
});
if (!gon?.features?.issueBoardsFilteredSearch) {
// Warning: FilteredSearchBoards has an implicit dependency on the Vuex state 'boardConfig'
// Improve this situation in the future.
const filterManager = new FilteredSearchBoards({ path: '' }, true, []);
filterManager.setup();
eventHub.$on('updateTokens', () => {
filterManager.updateTokens();
});
}
// eslint-disable-next-line no-new
new Vue({
el,
......@@ -110,10 +97,14 @@ export default () => {
}
});
if (gon?.features?.issueBoardsFilteredSearch) {
const { releasesFetchPath } = $boardApp.dataset;
initBoardsFilteredSearch(apolloProvider, isLoggedIn(), releasesFetchPath);
}
const { releasesFetchPath, epicFeatureAvailable, iterationFeatureAvailable } = $boardApp.dataset;
initBoardsFilteredSearch(
apolloProvider,
isLoggedIn(),
releasesFetchPath,
parseBoolean(epicFeatureAvailable),
parseBoolean(iterationFeatureAvailable),
);
mountBoardApp($boardApp);
......
......@@ -4,7 +4,13 @@ import store from '~/boards/stores';
import { convertObjectPropsToCamelCase } from '~/lib/utils/common_utils';
import { queryToObject } from '~/lib/utils/url_utility';
export default (apolloProvider, isSignedIn, releasesFetchPath) => {
export default (
apolloProvider,
isSignedIn,
releasesFetchPath,
epicFeatureAvailable,
iterationFeatureAvailable,
) => {
const el = document.getElementById('js-issue-board-filtered-search');
const rawFilterParams = queryToObject(window.location.search, { gatherArrays: true });
......@@ -23,6 +29,8 @@ export default (apolloProvider, isSignedIn, releasesFetchPath) => {
initialFilterParams,
isSignedIn,
releasesFetchPath,
epicFeatureAvailable,
iterationFeatureAvailable,
},
store, // TODO: https://gitlab.com/gitlab-org/gitlab/-/issues/324094
apolloProvider,
......
......@@ -7,7 +7,6 @@ class Groups::BoardsController < Groups::ApplicationController
before_action :assign_endpoint_vars
before_action do
push_frontend_feature_flag(:issue_boards_filtered_search, group, default_enabled: :yaml)
push_frontend_feature_flag(:board_multi_select, group, default_enabled: :yaml)
push_frontend_feature_flag(:iteration_cadences, group, default_enabled: :yaml)
experiment(:prominent_create_board_btn, subject: current_user) do |e|
......
......@@ -7,7 +7,6 @@ class Projects::BoardsController < Projects::ApplicationController
before_action :check_issues_available!
before_action :assign_endpoint_vars
before_action do
push_frontend_feature_flag(:issue_boards_filtered_search, project&.group, default_enabled: :yaml)
push_frontend_feature_flag(:board_multi_select, project, default_enabled: :yaml)
push_frontend_feature_flag(:iteration_cadences, project&.group, default_enabled: :yaml)
experiment(:prominent_create_board_btn, subject: current_user) do |e|
......
......@@ -5,10 +5,6 @@
- placeholder = local_assigns[:placeholder] || _('Search or filter results...')
- block_css_class = type != :productivity_analytics ? 'row-content-block second-block' : ''
- is_epic_board = board&.to_type == "EpicBoard"
- if @group.present?
- ff_resource = @group
- else
- ff_resource = board&.resource_parent&.group
- if is_epic_board
- user_can_admin_list = can?(current_user, :admin_epic_board_list, board.resource_parent)
......@@ -31,7 +27,7 @@
= check_box_tag checkbox_id, nil, false, class: "check-all-issues left"
- if is_epic_board
#js-board-filtered-search{ data: { full_path: @group&.full_path } }
- elsif Feature.enabled?(:issue_boards_filtered_search, ff_resource, default_enabled: :yaml) && board
- elsif board
#js-issue-board-filtered-search
- else
.issues-other-filters.filtered-search-wrapper.d-flex.flex-column.flex-md-row
......
---
name: issue_boards_filtered_search
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/61752
rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/331649
milestone: '14.1'
type: development
group: group::product planning
default_enabled: true
......@@ -2,7 +2,7 @@ import {
FiltersInfo as FiltersInfoCE,
formatIssueInput as formatIssueInputCe,
} from '~/boards/boards_util';
import { getIdFromGraphQLId } from '~/graphql_shared/utils';
import { getIdFromGraphQLId, isGid } from '~/graphql_shared/utils';
import { objectToQuery, queryToObject } from '~/lib/utils/url_utility';
import {
EPIC_LANE_BASE_HEIGHT,
......@@ -43,6 +43,10 @@ function fullIterationId(id) {
return null;
}
if (isGid(id)) {
return id;
}
if (id === IterationIDs.CURRENT) {
return 'CURRENT';
}
......@@ -232,6 +236,7 @@ export const FiltersInfo = {
},
iterationId: {
negatedSupport: true,
transform: (iterationId) => fullIterationId(iterationId),
remap: (k, v) => {
return v.endsWith(IterationFilterType.any) ||
v.endsWith(IterationFilterType.none) ||
......
......@@ -22,6 +22,7 @@ export default {
weight: __('Weight'),
},
mixins: [glFeatureFlagMixin()],
inject: ['epicFeatureAvailable', 'iterationFeatureAvailable'],
computed: {
isGroupBoard() {
return this.boardType === BoardType.group;
......@@ -36,26 +37,34 @@ export default {
const tokens = [
...this.tokensCE,
{
type: 'epic',
title: epic,
icon: 'epic',
token: EpicToken,
unique: true,
symbol: '&',
idProperty: 'id',
useIdValue: true,
fullPath: this.epicsGroupPath,
},
{
icon: 'iteration',
title: iteration,
type: 'iteration',
operators: OPERATOR_IS_AND_IS_NOT,
token: IterationToken,
unique: true,
fetchIterations: this.fetchIterations,
},
...(this.epicFeatureAvailable
? [
{
type: 'epic',
title: epic,
icon: 'epic',
token: EpicToken,
unique: true,
symbol: '&',
idProperty: 'id',
useIdValue: true,
fullPath: this.epicsGroupPath,
},
]
: []),
...(this.iterationFeatureAvailable
? [
{
icon: 'iteration',
title: iteration,
type: 'iteration',
operators: OPERATOR_IS_AND_IS_NOT,
token: IterationToken,
unique: true,
fetchIterations: this.fetchIterations,
},
]
: []),
{
type: 'weight',
title: weight,
......
......@@ -20,7 +20,6 @@ RSpec.describe 'Issue board filters', :js do
let(:filter_submit) { find('.gl-search-box-by-click-search-button') }
before do
stub_feature_flags(issue_boards_filtered_search: true)
stub_licensed_features(epics: true, iterations: true)
project.add_maintainer(user)
......
......@@ -3,7 +3,6 @@
require 'spec_helper'
RSpec.describe 'Scoped issue boards', :js do
include FilteredSearchHelpers
include MobileHelpers
let_it_be(:user) { create(:user) }
......@@ -23,6 +22,10 @@ RSpec.describe 'Scoped issue boards', :js do
let(:edit_board) { find('.btn', text: 'Edit board') }
let(:view_scope) { find('.btn', text: 'View scope') }
let(:board_title) { find('.boards-selector-wrapper .dropdown-menu-toggle') }
let(:filtered_search) { find('[data-testid="issue-board-filtered-search"]') }
let(:filter_input) { find('.gl-filtered-search-last-item')}
let(:filter_first_suggestion) { find('.gl-filtered-search-suggestion-list').first('.gl-filtered-search-suggestion') }
let(:filter_submit) { find('.gl-search-box-by-click-search-button') }
before do
allow_next_instance_of(ApplicationHelper) do |helper|
......@@ -53,22 +56,22 @@ RSpec.describe 'Scoped issue boards', :js do
it 'creates board filtering by milestone' do
create_board_milestone(milestone.title)
expect(page).to have_css('.js-visual-token')
expect(find('.tokens-container')).to have_content(:all, milestone.title)
expect(page).to have_css('.gl-filtered-search-token')
expect(find('.gl-filtered-search-scrollable')).to have_content(:all, milestone.title)
expect(page).to have_selector('.board-card', count: 1)
end
it 'creates board filtering by No milestone' do
create_board_milestone('No milestone')
expect(find('.tokens-container')).to have_content("")
expect(find('.gl-filtered-search-scrollable')).to have_content("")
expect(page).to have_selector('.board-card', count: 2)
end
it 'creates board filtering by Any Milestone' do
create_board_milestone('Any Milestone')
expect(find('.tokens-container')).to have_content("")
expect(find('.gl-filtered-search-scrollable')).to have_content("")
expect(page).to have_selector('.board-card', count: 3)
end
......@@ -89,17 +92,17 @@ RSpec.describe 'Scoped issue boards', :js do
it 'creates board filtering by one label' do
create_board_label(label_1.title)
expect(page).to have_css('.js-visual-token')
expect(find('.tokens-container')).to have_content(:all, label_1.title)
expect(page).to have_css('.gl-filtered-search-token')
expect(find('.gl-filtered-search-scrollable')).to have_content(:all, label_1.title)
expect(page).to have_selector('.board-card', count: 2)
end
it 'creates board filtering by multiple labels' do
create_board_label([label_1.title, label_2.title])
expect(page).to have_css('.js-visual-token')
expect(find('.tokens-container')).to have_content(:all, label_1.title)
expect(find('.tokens-container')).to have_content(:all, label_2.title)
expect(page).to have_css('.gl-filtered-search-token')
expect(find('.gl-filtered-search-scrollable')).to have_content(:all, label_1.title)
expect(find('.gl-filtered-search-scrollable')).to have_content(:all, label_2.title)
expect(page).to have_selector('.board-card', count: 1)
end
......@@ -134,14 +137,14 @@ RSpec.describe 'Scoped issue boards', :js do
it 'creates board filtering by assignee' do
create_board_assignee(user.name)
expect(page).to have_css('.js-visual-token')
expect(find('.tokens-container')).to have_content(:all, user.name)
expect(page).to have_css('.gl-filtered-search-token')
expect(find('.gl-filtered-search-scrollable')).to have_content(:all, user.name)
expect(page).to have_selector('.board-card', count: 1)
# Does not display assignee in search hint
filtered_search.click
page.within('#js-dropdown-hint') do
page.within('.gl-filtered-search-suggestion-list') do
expect(page).to have_content('Label')
expect(page).not_to have_content('Assignee')
end
......@@ -150,7 +153,7 @@ RSpec.describe 'Scoped issue boards', :js do
it 'creates board filtering by "Any assignee"' do
create_board_assignee('Any assignee')
expect(page).not_to have_css('.js-visual-token')
expect(page).not_to have_css('.gl-filtered-search-token')
expect(page).to have_selector('.board-card', count: 3)
end
......@@ -175,7 +178,7 @@ RSpec.describe 'Scoped issue boards', :js do
# Does not display assignee in search hint
filtered_search.click
page.within('#js-dropdown-hint') do
page.within('.gl-filtered-search-suggestion-list') do
expect(page).to have_content('Label')
expect(page).not_to have_content('Weight')
end
......@@ -238,14 +241,14 @@ RSpec.describe 'Scoped issue boards', :js do
it 'sets board milestone' do
update_board_milestone(milestone.title)
expect(find('.tokens-container')).to have_content(:all, milestone.title)
expect(find('.gl-filtered-search-scrollable')).to have_content(:all, milestone.title)
expect(page).to have_selector('.board-card', count: 1)
end
it 'sets board to any milestone' do
update_board_milestone('Any Milestone')
expect(find('.tokens-container')).not_to have_content(milestone.title)
expect(find('.gl-filtered-search-scrollable')).not_to have_content(milestone.title)
find('.board-card', match: :first)
......@@ -257,7 +260,7 @@ RSpec.describe 'Scoped issue boards', :js do
it 'sets board to upcoming milestone' do
update_board_milestone('Upcoming')
expect(find('.tokens-container')).not_to have_content(milestone.title)
expect(find('.gl-filtered-search-scrollable')).not_to have_content(milestone.title)
find('.board', match: :first)
......@@ -268,7 +271,7 @@ RSpec.describe 'Scoped issue boards', :js do
update_board_milestone(milestone.title)
filtered_search.click
page.within('#js-dropdown-hint') do
page.within('.gl-filtered-search-suggestion-list') do
expect(page).to have_content('Label')
expect(page).not_to have_content('Milestone')
end
......@@ -360,8 +363,8 @@ RSpec.describe 'Scoped issue boards', :js do
update_board_label(label_title)
expect(page).to have_css('.js-visual-token')
expect(find('.tokens-container')).to have_content(:all, label_title)
expect(page).to have_css('.gl-filtered-search-token')
expect(find('.gl-filtered-search-scrollable')).to have_content(:all, label_title)
expect(page).to have_selector('.board-card', count: 2)
end
......@@ -379,9 +382,9 @@ RSpec.describe 'Scoped issue boards', :js do
update_board_label(label_2_title)
expect(page).to have_css('.js-visual-token')
expect(find('.tokens-container')).to have_content(:all, label_title)
expect(find('.tokens-container')).to have_content(:all, label_2_title)
expect(page).to have_css('.gl-filtered-search-token')
expect(find('.gl-filtered-search-scrollable')).to have_content(:all, label_title)
expect(find('.gl-filtered-search-scrollable')).to have_content(:all, label_2_title)
expect(page).to have_selector('.board-card', count: 1)
end
......@@ -395,7 +398,7 @@ RSpec.describe 'Scoped issue boards', :js do
update_board_label(label_title)
input_filtered_search("label=~#{label_2_title}")
set_filter('label', label_2_title)
expect(page).to have_selector('.board-card', count: 0)
end
......@@ -423,8 +426,8 @@ RSpec.describe 'Scoped issue boards', :js do
it 'sets board assignee' do
update_board_assignee(user.name)
expect(page).to have_css('.js-visual-token')
expect(find('.tokens-container')).to have_content(:all, user.name)
expect(page).to have_css('.gl-filtered-search-token')
expect(find('.gl-filtered-search-scrollable')).to have_content(:all, user.name)
expect(page).to have_selector('.board-card', count: 1)
end
......@@ -432,7 +435,7 @@ RSpec.describe 'Scoped issue boards', :js do
it 'sets board to Any assignee' do
update_board_assignee('Any assignee')
expect(page).not_to have_css('.js-visual-token')
expect(page).not_to have_css('.gl-filtered-search-token')
expect(page).to have_selector('.board-card', count: 3)
end
......@@ -440,7 +443,7 @@ RSpec.describe 'Scoped issue boards', :js do
update_board_assignee(user.name)
filtered_search.click
page.within('#js-dropdown-hint') do
page.within('.gl-filtered-search-suggestion-list') do
expect(page).to have_content('Label')
expect(page).not_to have_content('Assignee')
end
......@@ -467,7 +470,7 @@ RSpec.describe 'Scoped issue boards', :js do
update_board_weight(1)
filtered_search.click
page.within('#js-dropdown-hint') do
page.within('.gl-filtered-search-suggestion-list') do
expect(page).to have_content('Label')
expect(page).not_to have_content('Weight')
end
......@@ -650,4 +653,12 @@ RSpec.describe 'Scoped issue boards', :js do
def click_on_board_modal
find('.board-config-modal .modal-content').click
end
def set_filter(type, filter_value)
filter_input.click
filter_input.set("#{type}:")
filter_first_suggestion.click # Select `=` operator
click_on filter_value
filter_submit.click
end
end
......@@ -33,6 +33,10 @@ RSpec.describe 'epics swimlanes filtering', :js do
let(:all_issues) { [confidential_issue, issue1, issue2, issue3, issue4, issue5, issue6, issue7, issue8] }
let(:filter_input) { find('.gl-filtered-search-term-input')}
let(:filter_first_suggestion) { find('.gl-filtered-search-suggestion-list').first('.gl-filtered-search-suggestion') }
let(:filter_submit) { find('.gl-search-box-by-click-search-button') }
before_all do
project.add_maintainer(user)
project.add_maintainer(user2)
......@@ -120,18 +124,20 @@ RSpec.describe 'epics swimlanes filtering', :js do
end
def set_filter(type, text)
find('.filtered-search').native.send_keys("#{type}:=#{text}")
filter_input.click
filter_input.set("#{type}:")
filter_first_suggestion.click # Select `=` operator
end
def submit_filter
find('.filtered-search').native.send_keys(:enter)
filter_submit.click
end
def click_filter_link(link_text)
page.within('.filtered-search-box') do
expect(page).to have_button(link_text)
page.within('.gl-filtered-search-suggestion-list') do
expect(page).to have_link(link_text)
click_button(link_text)
click_on link_text
end
end
end
......@@ -19,24 +19,32 @@ RSpec.describe 'Filter issues by iteration', :js do
let_it_be(:iteration_2_issue) { create(:issue, project: project, iteration: iteration_2) }
let_it_be(:no_iteration_issue) { create(:issue, project: project) }
let(:filter_input) { find('.gl-filtered-search-term-input')}
let(:filter_first_suggestion) { find('.gl-filtered-search-suggestion-list').first('.gl-filtered-search-suggestion') }
let(:filter_submit) { find('.gl-search-box-by-click-search-button') }
shared_examples 'filters by iteration' do
context 'when iterations are not available' do
before do
stub_licensed_features(iterations: false)
stub_feature_flags(vue_issues_list: true)
visit page_path
end
it 'does not show the iteration filter option' do
find('.filtered-search').set('iter')
filter_input.click
expect(find('#js-dropdown-hint')).not_to have_selector('.filter-dropdown .filter-dropdown-item', text: 'Iteration')
page.within('.gl-filtered-search-suggestion-list') do
expect(page).not_to have_content('Iteration')
end
end
end
context 'when iterations are available' do
before do
stub_licensed_features(iterations: true)
stub_feature_flags(vue_issues_list: true)
visit page_path
......@@ -67,7 +75,7 @@ RSpec.describe 'Filter issues by iteration', :js do
context 'when passing specific iteration by title' do
before do
input_filtered_search("iteration:=\"#{iteration_1.title}\"")
set_filter('iteration', iteration_1.title)
end
it_behaves_like 'filters issues by iteration'
......@@ -75,7 +83,7 @@ RSpec.describe 'Filter issues by iteration', :js do
context 'when passing Current iteration' do
before do
input_filtered_search("iteration:=Current", extra_space: false)
set_filter('iteration', 'Current')
end
it_behaves_like 'filters issues by iteration'
......@@ -85,15 +93,7 @@ RSpec.describe 'Filter issues by iteration', :js do
before do
visit page_path
page.within('.filtered-search-wrapper') do
find('.filtered-search').set('iter')
click_button('Iteration')
find('.btn-helptext', text: 'is not').click
click_button(iteration_title)
find('.filtered-search').send_keys(:enter)
end
set_negated_filter('iteration', iteration_title)
end
context 'with specific iteration' do
......@@ -186,4 +186,20 @@ RSpec.describe 'Filter issues by iteration', :js do
it_behaves_like 'filters by iteration'
end
def set_filter(type, filter_value)
filter_input.click
filter_input.set("#{type}:")
filter_first_suggestion.click # Select `=` operator
click_on filter_value
filter_submit.click
end
def set_negated_filter(type, filter_value)
filter_input.click
filter_input.set("#{type}:")
click_on '!='
click_on filter_value
filter_submit.click
end
end
......@@ -16,6 +16,8 @@ describe('IssueBoardFilter', () => {
provide: {
isSignedIn: true,
releasesFetchPath: '/releases',
epicFeatureAvailable: true,
iterationFeatureAvailable: true,
},
});
};
......
......@@ -70,7 +70,7 @@ describe('setFilters', () => {
filterVariables: {
weight: 3,
not: {
iterationId: '1',
iterationId: 'gid://gitlab/Iteration/1',
},
},
},
......
......@@ -22,8 +22,6 @@ RSpec.describe 'Issue board filters', :js do
let(:filter_submit) { find('.gl-search-box-by-click-search-button') }
before do
stub_feature_flags(issue_boards_filtered_search: true)
project.add_maintainer(user)
sign_in(user)
......
......@@ -13,6 +13,10 @@ RSpec.describe 'Project issue boards', :js do
let_it_be(:user) { create(:user) }
let_it_be(:user2) { create(:user) }
let(:filtered_search) { find('[data-testid="issue-board-filtered-search"]') }
let(:filter_input) { find('.gl-filtered-search-term-input') }
let(:filter_submit) { find('.gl-search-box-by-click-search-button') }
context 'signed in user' do
before do
project.add_maintainer(user)
......@@ -90,8 +94,7 @@ RSpec.describe 'Project issue boards', :js do
end
it 'search closed list' do
find('.filtered-search').set(issue8.title)
find('.filtered-search').native.send_keys(:enter)
set_filter_and_search_by_token_value(issue8.title)
wait_for_requests
......@@ -101,8 +104,7 @@ RSpec.describe 'Project issue boards', :js do
end
it 'search list' do
find('.filtered-search').set(issue5.title)
find('.filtered-search').native.send_keys(:enter)
set_filter_and_search_by_token_value(issue5.title)
wait_for_requests
......@@ -111,26 +113,6 @@ RSpec.describe 'Project issue boards', :js do
expect(find('.board:nth-child(4)')).to have_selector('.board-card', count: 0)
end
context 'search list negation queries' do
before do
visit_project_board_path_without_query_limit(project, board)
end
it 'does not have the != option' do
find('.filtered-search').set('label:')
wait_for_requests
within('#js-dropdown-operator') do
tokens = all(:css, 'li.filter-dropdown-item')
expect(tokens.count).to eq(2)
button = tokens[0].find('button')
expect(button).to have_content('=')
button = tokens[1].find('button')
expect(button).to have_content('!=')
end
end
end
it 'allows user to delete board' do
remove_list
......@@ -309,8 +291,8 @@ RSpec.describe 'Project issue boards', :js do
context 'filtering' do
it 'filters by author' do
set_filter("author", user2.username)
click_filter_link(user2.username)
submit_filter
click_on user2.username
filter_submit.click
wait_for_requests
wait_for_board_cards(2, 1)
......@@ -319,8 +301,8 @@ RSpec.describe 'Project issue boards', :js do
it 'filters by assignee' do
set_filter("assignee", user.username)
click_filter_link(user.username)
submit_filter
click_on user.username
filter_submit.click
wait_for_requests
......@@ -330,8 +312,8 @@ RSpec.describe 'Project issue boards', :js do
it 'filters by milestone' do
set_filter("milestone", "\"#{milestone.title}")
click_filter_link(milestone.title)
submit_filter
click_on milestone.title
filter_submit.click
wait_for_requests
wait_for_board_cards(2, 1)
......@@ -341,8 +323,8 @@ RSpec.describe 'Project issue boards', :js do
it 'filters by label' do
set_filter("label", testing.title)
click_filter_link(testing.title)
submit_filter
click_on testing.title
filter_submit.click
wait_for_requests
wait_for_board_cards(2, 1)
......@@ -351,8 +333,10 @@ RSpec.describe 'Project issue boards', :js do
it 'filters by label with encoded character' do
set_filter("label", a_plus.title)
click_filter_link(a_plus.title)
submit_filter
# This one is a char encoding issue like the & issue
click_on a_plus.title
filter_submit.click
wait_for_requests
wait_for_board_cards(1, 1)
wait_for_empty_boards((2..4))
......@@ -360,8 +344,8 @@ RSpec.describe 'Project issue boards', :js do
it 'filters by label with space after reload', :quarantine do
set_filter("label", "\"#{accepting.title}")
click_filter_link(accepting.title)
submit_filter
click_on accepting.title
filter_submit.click
# Test after reload
page.evaluate_script 'window.location.reload()'
......@@ -384,13 +368,13 @@ RSpec.describe 'Project issue boards', :js do
it 'removes filtered labels' do
inspect_requests(inject_headers: { 'X-GITLAB-DISABLE-SQL-QUERY-LIMIT' => 'https://gitlab.com/gitlab-org/gitlab/-/issues/323426' }) do
set_filter("label", testing.title)
click_filter_link(testing.title)
submit_filter
click_on testing.title
filter_submit.click
wait_for_board_cards(2, 1)
find('.clear-search').click
submit_filter
find('[data-testid="filtered-search-clear-button"]').click
filter_submit.click
end
wait_for_board_cards(2, 8)
......@@ -400,9 +384,9 @@ RSpec.describe 'Project issue boards', :js do
create_list(:labeled_issue, 30, project: project, labels: [planning, testing])
set_filter("label", testing.title)
click_filter_link(testing.title)
click_on testing.title
inspect_requests(inject_headers: { 'X-GITLAB-DISABLE-SQL-QUERY-LIMIT' => 'https://gitlab.com/gitlab-org/gitlab/-/issues/323426' }) do
submit_filter
filter_submit.click
end
wait_for_requests
......@@ -442,10 +426,10 @@ RSpec.describe 'Project issue boards', :js do
it 'filters by multiple labels', :quarantine do
set_filter("label", testing.title)
click_filter_link(testing.title)
click_on testing.title
set_filter("label", bug.title)
click_filter_link(bug.title)
click_on bug.title
submit_filter
......@@ -463,7 +447,7 @@ RSpec.describe 'Project issue boards', :js do
wait_for_requests
end
page.within('.tokens-container') do
page.within('.gl-filtered-search-token') do
expect(page).to have_content(bug.title)
end
......@@ -561,19 +545,26 @@ RSpec.describe 'Project issue boards', :js do
end
end
def set_filter_and_search_by_token_value(value)
filter_input.click
filter_input.set(value)
filter_submit.click
end
def set_filter(type, text)
find('.filtered-search').native.send_keys("#{type}:=#{text}")
filter_input.click
filter_input.native.send_keys("#{type}:=#{text}")
end
def submit_filter
find('.filtered-search').native.send_keys(:enter)
filter_input.native.send_keys(:enter)
end
def click_filter_link(link_text)
page.within('.filtered-search-box') do
page.within(filtered_search) do
expect(page).to have_button(link_text)
click_button(link_text)
click_on link_text
end
end
......
......@@ -179,38 +179,6 @@ RSpec.describe 'Labels Hierarchy', :js do
it_behaves_like 'assigning labels from sidebar'
end
context 'on project board issue sidebar' do
let(:board) { create(:board, project: project_1) }
before do
project_1.add_developer(user)
visit project_board_path(project_1, board)
wait_for_requests
find('.board-card').click
end
it_behaves_like 'assigning labels from sidebar'
end
context 'on group board issue sidebar' do
let(:board) { create(:board, group: parent) }
before do
parent.add_developer(user)
visit group_board_path(parent, board)
wait_for_requests
find('.board-card').click
end
it_behaves_like 'assigning labels from sidebar'
end
end
context 'issuable filtering' do
......@@ -242,29 +210,5 @@ RSpec.describe 'Labels Hierarchy', :js do
it_behaves_like 'filtering by ancestor labels for groups'
end
context 'on project boards filter' do
let(:board) { create(:board, project: project_1) }
before do
project_1.add_developer(user)
visit project_board_path(project_1, board)
end
it_behaves_like 'filtering by ancestor labels for projects', true
end
context 'on group boards filter' do
let(:board) { create(:board, group: parent) }
before do
parent.add_developer(user)
visit group_board_path(parent, board)
end
it_behaves_like 'filtering by ancestor labels for groups', true
end
end
end
......@@ -304,8 +304,6 @@ RSpec.configure do |config|
# As we're ready to change `master` usages to `main`, let's enable it
stub_feature_flags(main_branch_over_master: false)
stub_feature_flags(issue_boards_filtered_search: false)
# Disable issue respositioning to avoid heavy load on database when importing big projects.
# This is only turned on when app is handling heavy project imports.
# Can be removed when we find a better way to deal with the problem.
......
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