Commit 9b0d474d authored by Kushal Pandya's avatar Kushal Pandya

Merge branch '353484-fix-not-operator-behaviour-epics-list' into 'master'

Fix Epics list filtering when NOT operator is used

See merge request gitlab-org/gitlab!84569
parents cfefb3fb 5fc73f19
...@@ -12,6 +12,7 @@ import { IssuableListTabs, DEFAULT_PAGE_SIZE } from '~/vue_shared/issuable/list/ ...@@ -12,6 +12,7 @@ import { IssuableListTabs, DEFAULT_PAGE_SIZE } from '~/vue_shared/issuable/list/
import { parsePikadayDate, dateInWords } from '~/lib/utils/datetime_utility'; import { parsePikadayDate, dateInWords } from '~/lib/utils/datetime_utility';
import { s__, sprintf } from '~/locale'; import { s__, sprintf } from '~/locale';
import { transformFetchEpicFilterParams } from '../../roadmap/utils/epic_utils';
import { EpicsSortOptions } from '../constants'; import { EpicsSortOptions } from '../constants';
import groupEpics from '../queries/group_epics.query.graphql'; import groupEpics from '../queries/group_epics.query.graphql';
...@@ -75,8 +76,10 @@ export default { ...@@ -75,8 +76,10 @@ export default {
} }
if (Object.keys(this.filterParams).length) { if (Object.keys(this.filterParams).length) {
const transformedFilterParams = transformFetchEpicFilterParams(this.filterParams);
Object.assign(queryVariables, { Object.assign(queryVariables, {
...this.filterParams, ...transformedFilterParams,
}); });
} }
......
...@@ -10,6 +10,7 @@ query groupEpics( ...@@ -10,6 +10,7 @@ query groupEpics(
$milestoneTitle: String = "" $milestoneTitle: String = ""
$myReactionEmoji: String $myReactionEmoji: String
$confidential: Boolean $confidential: Boolean
$not: NegatedEpicFilterInput
$search: String = "" $search: String = ""
$sortBy: EpicSort $sortBy: EpicSort
$firstPageSize: Int $firstPageSize: Int
...@@ -27,6 +28,7 @@ query groupEpics( ...@@ -27,6 +28,7 @@ query groupEpics(
milestoneTitle: $milestoneTitle milestoneTitle: $milestoneTitle
myReactionEmoji: $myReactionEmoji myReactionEmoji: $myReactionEmoji
confidential: $confidential confidential: $confidential
not: $not
search: $search search: $search
sort: $sortBy sort: $sortBy
first: $firstPageSize first: $firstPageSize
......
...@@ -29,7 +29,7 @@ const fetchGroupEpics = ( ...@@ -29,7 +29,7 @@ const fetchGroupEpics = (
}), }),
}; };
const transformedFilterParams = roadmapItemUtils.transformFetchEpicFilterParams(filterParams); const transformedFilterParams = epicUtils.transformFetchEpicFilterParams(filterParams);
// When epicIid is present, // When epicIid is present,
// Roadmap is being accessed from within an Epic, // Roadmap is being accessed from within an Epic,
......
...@@ -17,3 +17,33 @@ export const scrollToCurrentDay = (parentEl) => { ...@@ -17,3 +17,33 @@ export const scrollToCurrentDay = (parentEl) => {
todayIndicatorEl.scrollIntoView({ block: 'nearest', inline: 'center' }); todayIndicatorEl.scrollIntoView({ block: 'nearest', inline: 'center' });
} }
}; };
/**
* Returns transformed `filterParams` by congregating all `not` params into a
* single object like { not: { labelName: [], ... }, authorUsername: '' }
*
* @param {Object} filterParams
*/
export const transformFetchEpicFilterParams = (filterParams) => {
if (!filterParams) {
return filterParams;
}
const newParams = {};
Object.keys(filterParams).forEach((param) => {
if (param.startsWith('not')) {
// Get the param name like `authorUsername` from `not[authorUsername]`
const key = param.match(/not\[(.+)\]/)[1];
if (key) {
newParams.not = newParams.not || {};
newParams.not[key] = filterParams[param];
}
} else {
newParams[param] = filterParams[param];
}
});
return newParams;
};
...@@ -148,33 +148,3 @@ export const timeframeEndDate = (presetType, timeframe) => { ...@@ -148,33 +148,3 @@ export const timeframeEndDate = (presetType, timeframe) => {
endDate.setDate(endDate.getDate() + DAYS_IN_WEEK); endDate.setDate(endDate.getDate() + DAYS_IN_WEEK);
return endDate; return endDate;
}; };
/**
* Returns transformed `filterParams` by congregating all `not` params into a
* single object like { not: { labelName: [], ... }, authorUsername: '' }
*
* @param {Object} filterParams
*/
export const transformFetchEpicFilterParams = (filterParams) => {
if (!filterParams) {
return filterParams;
}
const newParams = {};
Object.keys(filterParams).forEach((param) => {
if (param.startsWith('not')) {
// Get the param name like `authorUsername` from `not[authorUsername]`
const key = param.match(/not\[(.+)\]/)[1];
if (key) {
newParams.not = newParams.not || {};
newParams.not[key] = filterParams[param];
}
} else {
newParams[param] = filterParams[param];
}
});
return newParams;
};
...@@ -7,8 +7,19 @@ RSpec.describe 'epics list', :js do ...@@ -7,8 +7,19 @@ RSpec.describe 'epics list', :js do
let(:user) { create(:user) } let(:user) { create(:user) }
let(:user_dev) { create(:user) } let(:user_dev) { create(:user) }
let!(:bug_label) { create(:group_label, group: group, title: 'Bug') } let!(:bug_label) { create(:group_label, group: group, title: 'Bug') }
let!(:docs_label) { create(:group_label, group: group, title: 'Documentation') }
let!(:enhancement_label) { create(:group_label, group: group, title: 'Enhancement') }
let!(:critical_label) { create(:group_label, group: group, title: 'Critical') } let!(:critical_label) { create(:group_label, group: group, title: 'Critical') }
def select_token(token, operator, value)
find_field('Search').click
click_link token
first('a', text: operator).click
page.within('.gl-filtered-search-suggestion-list') do
click_link value
end
end
before do before do
stub_licensed_features(epics: true) stub_licensed_features(epics: true)
stub_feature_flags(unfiltered_epic_aggregates: false) stub_feature_flags(unfiltered_epic_aggregates: false)
...@@ -21,9 +32,9 @@ RSpec.describe 'epics list', :js do ...@@ -21,9 +32,9 @@ RSpec.describe 'epics list', :js do
available_sort_options = ['Created date', 'Updated date', 'Start date', 'Due date', 'Title'] available_sort_options = ['Created date', 'Updated date', 'Start date', 'Due date', 'Title']
describe 'within a group' do describe 'within a group' do
let!(:epic1) { create(:epic, group: group, start_date: '2020-12-15', end_date: '2021-1-15') } let!(:epic1) { create(:epic, group: group, start_date: '2020-12-15', end_date: '2021-1-15', labels: [docs_label]) }
let!(:epic2) { create(:epic, group: group, start_date: '2020-12-15') } let!(:epic2) { create(:epic, group: group, start_date: '2020-12-15', labels: [docs_label, enhancement_label]) }
let!(:epic3) { create(:epic, group: group, end_date: '2021-1-15') } let!(:epic3) { create(:epic, group: group, end_date: '2021-1-15', labels: [enhancement_label]) }
let!(:award_emoji_star) { create(:award_emoji, name: 'star', user: user, awardable: epic1) } let!(:award_emoji_star) { create(:award_emoji, name: 'star', user: user, awardable: epic1) }
let!(:award_emoji_upvote) { create(:award_emoji, :upvote, user: user, awardable: epic1) } let!(:award_emoji_upvote) { create(:award_emoji, :upvote, user: user, awardable: epic1) }
let!(:award_emoji_downvote) { create(:award_emoji, :downvote, user: user, awardable: epic2) } let!(:award_emoji_downvote) { create(:award_emoji, :downvote, user: user, awardable: epic2) }
...@@ -78,6 +89,30 @@ RSpec.describe 'epics list', :js do ...@@ -78,6 +89,30 @@ RSpec.describe 'epics list', :js do
it_behaves_like 'filtered search bar', available_tokens, available_sort_options it_behaves_like 'filtered search bar', available_tokens, available_sort_options
it 'filters epics list based on labels with "=" operator' do
select_token('Label', '=', docs_label.title)
find('.gl-search-box-by-click-search-button').click
wait_for_requests
page.within('.issuable-list-container') do
expect(page.find('.issuable-list')).to have_selector('li.issue', count: 2)
end
end
it 'filters epics list based on labels with "!=" operator', :aggregate_failures do
select_token('Label', '=', docs_label.title)
select_token('Label', '!=', enhancement_label.title)
find('.gl-search-box-by-click-search-button').click
wait_for_requests
page.within('.issuable-list-container .issuable-list') do
expect(page).to have_selector('li.issue', count: 1)
expect(page.find('li.issue .issuable-info')).not_to have_selector('.gl-label', text: enhancement_label.title)
end
end
it 'shows bulk editing sidebar with actions and labels select dropdown', :aggregate_failures do it 'shows bulk editing sidebar with actions and labels select dropdown', :aggregate_failures do
click_button 'Edit epics' click_button 'Edit epics'
...@@ -100,8 +135,8 @@ RSpec.describe 'epics list', :js do ...@@ -100,8 +135,8 @@ RSpec.describe 'epics list', :js do
end end
it 'applies label to multiple epics from bulk editing sidebar', :aggregate_failures do it 'applies label to multiple epics from bulk editing sidebar', :aggregate_failures do
# Vertify that no labels are applied already # Vertify that label `Bug` is not applied already
expect(find('.issuable-list li.issue .issuable-info', match: :first)).not_to have_selector('.gl-label') expect(find('.issuable-list li.issue .issuable-info', match: :first)).not_to have_selector('.gl-label', text: bug_label.title)
# Bulk edit all epics to apply label # Bulk edit all epics to apply label
page.within('.issuable-list-container') do page.within('.issuable-list-container') do
...@@ -112,7 +147,7 @@ RSpec.describe 'epics list', :js do ...@@ -112,7 +147,7 @@ RSpec.describe 'epics list', :js do
end end
page.within('aside.right-sidebar') do page.within('aside.right-sidebar') do
click_button 'Label' find('button.js-dropdown-button').click
wait_for_requests wait_for_requests
......
...@@ -61,3 +61,21 @@ describe('scrollToCurrentDay', () => { ...@@ -61,3 +61,21 @@ describe('scrollToCurrentDay', () => {
}); });
}); });
}); });
describe('transformFetchEpicFilterParams', () => {
it('should return congregated `not[]` params in a single key', () => {
const filterParams = {
'not[authorUsername]': 'foo',
'not[myReactionEmoji]': ':emoji:',
authorUsername: 'baz',
};
expect(epicUtils.transformFetchEpicFilterParams(filterParams)).toEqual({
not: {
authorUsername: 'foo',
myReactionEmoji: ':emoji:',
},
authorUsername: 'baz',
});
});
});
...@@ -186,21 +186,3 @@ describe('timeframeEndDate', () => { ...@@ -186,21 +186,3 @@ describe('timeframeEndDate', () => {
}, },
); );
}); });
describe('transformFetchEpicFilterParams', () => {
it('should return congregated `not[]` params in a single key', () => {
const filterParams = {
'not[authorUsername]': 'foo',
'not[myReactionEmoji]': ':emoji:',
authorUsername: 'baz',
};
expect(roadmapItemUtils.transformFetchEpicFilterParams(filterParams)).toEqual({
not: {
authorUsername: 'foo',
myReactionEmoji: ':emoji:',
},
authorUsername: 'baz',
});
});
});
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