Merge pull request #7583 from opf/fix/30817/dont-apply-filter-when-matching-value
[30817] Don't apply the filter when current value is already validpull/7580/head
commit
9a275f9bea
@ -0,0 +1,179 @@ |
|||||||
|
// -- copyright
|
||||||
|
// OpenProject is a project management system.
|
||||||
|
// Copyright (C) 2012-2015 the OpenProject Foundation (OPF)
|
||||||
|
//
|
||||||
|
// This program is free software; you can redistribute it and/or
|
||||||
|
// modify it under the terms of the GNU General Public License version 3.
|
||||||
|
//
|
||||||
|
// OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows:
|
||||||
|
// Copyright (C) 2006-2013 Jean-Philippe Lang
|
||||||
|
// Copyright (C) 2010-2013 the ChiliProject Team
|
||||||
|
//
|
||||||
|
// This program is free software; you can redistribute it and/or
|
||||||
|
// modify it under the terms of the GNU General Public License
|
||||||
|
// as published by the Free Software Foundation; either version 2
|
||||||
|
// of the License, or (at your option) any later version.
|
||||||
|
//
|
||||||
|
// This program is distributed in the hope that it will be useful,
|
||||||
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
// GNU General Public License for more details.
|
||||||
|
//
|
||||||
|
// You should have received a copy of the GNU General Public License
|
||||||
|
// along with this program; if not, write to the Free Software
|
||||||
|
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||||
|
//
|
||||||
|
// See doc/COPYRIGHT.rdoc for more details.
|
||||||
|
// ++
|
||||||
|
|
||||||
|
import {TestBed} from "@angular/core/testing"; |
||||||
|
import {CurrentUserService} from "core-components/user/current-user.service"; |
||||||
|
import {HalResourceService} from "core-app/modules/hal/services/hal-resource.service"; |
||||||
|
import {Injector} from "@angular/core"; |
||||||
|
import {WorkPackageCacheService} from "core-components/work-packages/work-package-cache.service"; |
||||||
|
import {SchemaCacheService} from "core-components/schemas/schema-cache.service"; |
||||||
|
import {WorkPackageChangeset} from "core-components/wp-edit-form/work-package-changeset"; |
||||||
|
import {WorkPackageFilterValues} from "core-components/wp-edit-form/work-package-filter-values"; |
||||||
|
import {WorkPackageNotificationService} from "core-components/wp-edit/wp-notification.service"; |
||||||
|
import {IWorkPackageCreateServiceToken} from "core-components/wp-new/wp-create.service.interface"; |
||||||
|
import {IWorkPackageEditingServiceToken} from "core-components/wp-edit-form/work-package-editing.service.interface"; |
||||||
|
import {WorkPackagesActivityService} from "core-components/wp-single-view-tabs/activity-panel/wp-activity.service"; |
||||||
|
import {WorkPackageCreateService} from "core-components/wp-new/wp-create.service"; |
||||||
|
import {WorkPackageEditingService} from "core-components/wp-edit-form/work-package-editing-service"; |
||||||
|
import {WorkPackageResource} from "core-app/modules/hal/resources/work-package-resource"; |
||||||
|
import {TypeResource} from "core-app/modules/hal/resources/type-resource"; |
||||||
|
import {HttpClientModule} from "@angular/common/http"; |
||||||
|
import {States} from "core-components/states.service"; |
||||||
|
import {I18nService} from "core-app/modules/common/i18n/i18n.service"; |
||||||
|
import {NotificationsService} from "core-app/modules/common/notifications/notifications.service"; |
||||||
|
import {ConfigurationService} from "core-app/modules/common/config/configuration.service"; |
||||||
|
import {PathHelperService} from "core-app/modules/common/path-helper/path-helper.service"; |
||||||
|
import {UIRouterModule} from "@uirouter/angular"; |
||||||
|
import {WorkPackageDmService} from "core-app/modules/hal/dm-services/work-package-dm.service"; |
||||||
|
import {LoadingIndicatorService} from "core-app/modules/common/loading-indicator/loading-indicator.service"; |
||||||
|
import {OpenProjectFileUploadService} from "core-components/api/op-file-upload/op-file-upload.service"; |
||||||
|
import {HookService} from "core-app/modules/plugins/hook-service"; |
||||||
|
import {IsolatedQuerySpace} from "core-app/modules/work_packages/query-space/isolated-query-space"; |
||||||
|
import {WorkPackageEventsService} from "core-app/modules/work_packages/events/work-package-events.service"; |
||||||
|
import {TimezoneService} from "core-components/datetime/timezone.service"; |
||||||
|
|
||||||
|
describe('WorkPackageFilterValues', () => { |
||||||
|
let resource:WorkPackageResource; |
||||||
|
let injector:Injector; |
||||||
|
let halResourceService:HalResourceService; |
||||||
|
|
||||||
|
let changeset:WorkPackageChangeset; |
||||||
|
let subject:WorkPackageFilterValues; |
||||||
|
let filters:any[]; |
||||||
|
let source:any; |
||||||
|
|
||||||
|
function setupTestBed() { |
||||||
|
// noinspection JSIgnoredPromiseFromCall
|
||||||
|
TestBed.configureTestingModule({ |
||||||
|
imports: [ |
||||||
|
UIRouterModule.forRoot({}), |
||||||
|
HttpClientModule |
||||||
|
], |
||||||
|
providers: [ |
||||||
|
I18nService, |
||||||
|
States, |
||||||
|
IsolatedQuerySpace, |
||||||
|
WorkPackageEventsService, |
||||||
|
TimezoneService, |
||||||
|
PathHelperService, |
||||||
|
ConfigurationService, |
||||||
|
CurrentUserService, |
||||||
|
HookService, |
||||||
|
OpenProjectFileUploadService, |
||||||
|
LoadingIndicatorService, |
||||||
|
WorkPackageDmService, |
||||||
|
HalResourceService, |
||||||
|
NotificationsService, |
||||||
|
WorkPackageNotificationService, |
||||||
|
SchemaCacheService, |
||||||
|
WorkPackageCacheService, |
||||||
|
{ provide: IWorkPackageCreateServiceToken, useClass: WorkPackageCreateService }, |
||||||
|
{ provide: IWorkPackageEditingServiceToken, useClass: WorkPackageEditingService }, |
||||||
|
WorkPackagesActivityService, |
||||||
|
] |
||||||
|
}).compileComponents(); |
||||||
|
|
||||||
|
injector = TestBed.get(Injector); |
||||||
|
halResourceService = injector.get(HalResourceService); |
||||||
|
|
||||||
|
resource = halResourceService.createHalResourceOfClass(WorkPackageResource, source, true); |
||||||
|
changeset = new WorkPackageChangeset(injector, resource); |
||||||
|
|
||||||
|
let type1 = halResourceService.createHalResourceOfClass( |
||||||
|
TypeResource, |
||||||
|
{ _type: 'Type', id: '1', _links: { self: { href: '/api/v3/types/1', name: 'Task' } } } |
||||||
|
); |
||||||
|
let type2 = halResourceService.createHalResourceOfClass( |
||||||
|
TypeResource, |
||||||
|
{ _type: 'Type', id: '2', _links: { self: { href: '/api/v3/types/2', name: 'Bug' } } } |
||||||
|
); |
||||||
|
|
||||||
|
filters = [ |
||||||
|
{ |
||||||
|
id: 'type', |
||||||
|
operator: { id: '=' }, |
||||||
|
values: [type1, type2] |
||||||
|
} |
||||||
|
]; |
||||||
|
|
||||||
|
subject = new WorkPackageFilterValues(injector, changeset, filters); |
||||||
|
} |
||||||
|
|
||||||
|
describe('when a filter value already exists in values', () => { |
||||||
|
describe('with the first type applied', () => { |
||||||
|
beforeEach(() => { |
||||||
|
source = { |
||||||
|
_type: 'WorkPackage', |
||||||
|
id: '1234', |
||||||
|
_links: { |
||||||
|
type: { |
||||||
|
href: '/api/v3/types/1', |
||||||
|
name: 'Task' |
||||||
|
} |
||||||
|
} |
||||||
|
}; |
||||||
|
|
||||||
|
setupTestBed(); |
||||||
|
}); |
||||||
|
|
||||||
|
it('it should not apply the first value (Regression #30817)', (() => { |
||||||
|
subject.applyDefaultsFromFilters(); |
||||||
|
|
||||||
|
expect(changeset.changedAttributes.length).toEqual(0); |
||||||
|
expect(changeset.value('type').href).toEqual('/api/v3/types/1'); |
||||||
|
})); |
||||||
|
}); |
||||||
|
|
||||||
|
describe('with the second type applied', () => { |
||||||
|
beforeEach(() => { |
||||||
|
source = { |
||||||
|
id: '1234', |
||||||
|
_links: { |
||||||
|
type: { |
||||||
|
href: '/api/v3/types/2', |
||||||
|
name: 'Bug' |
||||||
|
} |
||||||
|
} |
||||||
|
}; |
||||||
|
|
||||||
|
setupTestBed(); |
||||||
|
}); |
||||||
|
|
||||||
|
it('it should not apply the first value (Regression #30817)', (() => { |
||||||
|
subject.applyDefaultsFromFilters(); |
||||||
|
|
||||||
|
expect(changeset.changedAttributes.length).toEqual(0); |
||||||
|
expect(changeset.value('type').href).toEqual('/api/v3/types/2'); |
||||||
|
})); |
||||||
|
}) |
||||||
|
|
||||||
|
}); |
||||||
|
}); |
||||||
|
|
||||||
|
|
||||||
|
|
@ -0,0 +1,164 @@ |
|||||||
|
#-- copyright |
||||||
|
# OpenProject is a project management system. |
||||||
|
# Copyright (C) 2012-2018 the OpenProject Foundation (OPF) |
||||||
|
# |
||||||
|
# This program is free software; you can redistribute it and/or |
||||||
|
# modify it under the terms of the GNU General Public License version 3. |
||||||
|
# |
||||||
|
# OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows: |
||||||
|
# Copyright (C) 2006-2017 Jean-Philippe Lang |
||||||
|
# Copyright (C) 2010-2013 the ChiliProject Team |
||||||
|
# |
||||||
|
# This program is free software; you can redistribute it and/or |
||||||
|
# modify it under the terms of the GNU General Public License |
||||||
|
# as published by the Free Software Foundation; either version 2 |
||||||
|
# of the License, or (at your option) any later version. |
||||||
|
# |
||||||
|
# This program is distributed in the hope that it will be useful, |
||||||
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of |
||||||
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
||||||
|
# GNU General Public License for more details. |
||||||
|
# |
||||||
|
# You should have received a copy of the GNU General Public License |
||||||
|
# along with this program; if not, write to the Free Software |
||||||
|
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. |
||||||
|
# |
||||||
|
# See docs/COPYRIGHT.rdoc for more details. |
||||||
|
#++ |
||||||
|
|
||||||
|
require 'spec_helper' |
||||||
|
require_relative './../support//board_index_page' |
||||||
|
require_relative './../support/board_page' |
||||||
|
|
||||||
|
describe 'Status action board', type: :feature, js: true do |
||||||
|
let(:user) do |
||||||
|
FactoryBot.create(:user, |
||||||
|
member_in_project: project, |
||||||
|
member_through_role: role) |
||||||
|
end |
||||||
|
let(:permissions) { |
||||||
|
%i[show_board_views manage_board_views add_work_packages |
||||||
|
edit_work_packages view_work_packages manage_public_queries] |
||||||
|
} |
||||||
|
let(:role) { FactoryBot.create(:role, permissions: permissions) } |
||||||
|
|
||||||
|
|
||||||
|
let(:type_bug) { FactoryBot.create(:type_bug) } |
||||||
|
let(:type_task) { FactoryBot.create(:type_task) } |
||||||
|
|
||||||
|
let(:project) { FactoryBot.create(:project, types: [type_task, type_bug], enabled_module_names: %i[work_package_tracking board_view]) } |
||||||
|
let(:board_index) { Pages::BoardIndex.new(project) } |
||||||
|
|
||||||
|
let!(:priority) { FactoryBot.create :default_priority } |
||||||
|
let!(:open_status) { FactoryBot.create :default_status, name: 'Open' } |
||||||
|
let!(:closed_status) { FactoryBot.create :status, is_closed: true, name: 'Closed' } |
||||||
|
|
||||||
|
let(:task_wp) do |
||||||
|
FactoryBot.create :work_package, |
||||||
|
project: project, |
||||||
|
type: type_task, |
||||||
|
subject: 'Open task item', |
||||||
|
status: open_status |
||||||
|
end |
||||||
|
let(:bug_wp) do |
||||||
|
FactoryBot.create :work_package, |
||||||
|
project: project, |
||||||
|
type: type_bug, |
||||||
|
subject: 'Closed bug item', |
||||||
|
status: closed_status |
||||||
|
end |
||||||
|
|
||||||
|
let!(:workflow_task) { |
||||||
|
FactoryBot.create(:workflow, |
||||||
|
type: type_task, |
||||||
|
role: role, |
||||||
|
old_status_id: open_status.id, |
||||||
|
new_status_id: closed_status.id) |
||||||
|
} |
||||||
|
let!(:workflow_task_back) { |
||||||
|
FactoryBot.create(:workflow, |
||||||
|
type: type_task, |
||||||
|
role: role, |
||||||
|
old_status_id: closed_status.id, |
||||||
|
new_status_id: open_status.id) |
||||||
|
} |
||||||
|
|
||||||
|
let!(:workflow_bug) { |
||||||
|
FactoryBot.create(:workflow, |
||||||
|
type: type_bug, |
||||||
|
role: role, |
||||||
|
old_status_id: open_status.id, |
||||||
|
new_status_id: closed_status.id) |
||||||
|
} |
||||||
|
let!(:workflow_bug_back) { |
||||||
|
FactoryBot.create(:workflow, |
||||||
|
type: type_bug, |
||||||
|
role: role, |
||||||
|
old_status_id: closed_status.id, |
||||||
|
new_status_id: open_status.id) |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
let(:filters) { ::Components::WorkPackages::Filters.new } |
||||||
|
|
||||||
|
before do |
||||||
|
with_enterprise_token :board_view |
||||||
|
task_wp |
||||||
|
bug_wp |
||||||
|
login_as(user) |
||||||
|
end |
||||||
|
|
||||||
|
it 'allows moving of types between lists without changing filters (Regression #30817)' do |
||||||
|
board_index.visit! |
||||||
|
|
||||||
|
# Create new board |
||||||
|
board_page = board_index.create_board action: :Status |
||||||
|
|
||||||
|
# expect lists of default status |
||||||
|
board_page.expect_list 'Open' |
||||||
|
|
||||||
|
board_page.add_list option: 'Closed' |
||||||
|
board_page.expect_list 'Closed' |
||||||
|
|
||||||
|
filters.expect_filter_count 0 |
||||||
|
filters.open |
||||||
|
|
||||||
|
filters.add_filter_by('Type', 'is', [type_task.name, type_bug.name]) |
||||||
|
filters.expect_filter_by('Type', 'is', [type_task.name, type_bug.name]) |
||||||
|
|
||||||
|
# Wait a bit before saving the page to ensure both values are processed |
||||||
|
sleep 2 |
||||||
|
|
||||||
|
board_page.expect_changed |
||||||
|
board_page.save |
||||||
|
|
||||||
|
# Move task to closed |
||||||
|
board_page.move_card(0, from: 'Open', to: 'Closed') |
||||||
|
board_page.expect_card('Open', 'Open task item', present: false) |
||||||
|
board_page.expect_card('Closed', 'Open task item', present: true) |
||||||
|
|
||||||
|
# Expect type unchanged |
||||||
|
board_page.card_for(task_wp).expect_type 'Task' |
||||||
|
board_page.card_for(bug_wp).expect_type 'Bug' |
||||||
|
|
||||||
|
# Wait a bit before moving the items too fast |
||||||
|
sleep 2 |
||||||
|
|
||||||
|
# Move bug to open |
||||||
|
board_page.move_card(0, from: 'Closed', to: 'Open') |
||||||
|
board_page.expect_card('Closed', 'Closed bug item', present: false) |
||||||
|
board_page.expect_card('Open', 'Closed bug item', present: true) |
||||||
|
|
||||||
|
# Expect type unchanged |
||||||
|
board_page.card_for(task_wp).expect_type 'Task' |
||||||
|
board_page.card_for(bug_wp).expect_type 'Bug' |
||||||
|
|
||||||
|
sleep 2 |
||||||
|
|
||||||
|
task_wp.reload |
||||||
|
bug_wp.reload |
||||||
|
|
||||||
|
expect(task_wp.type).to eq(type_task) |
||||||
|
expect(bug_wp.type).to eq(type_bug) |
||||||
|
end |
||||||
|
end |
Loading…
Reference in new issue