From 7286157a60e80f4049dfba7a444bb72dd97e77d4 Mon Sep 17 00:00:00 2001 From: Aaron Contreras Date: Mon, 11 Dec 2023 15:14:46 -0500 Subject: [PATCH 01/16] Lint: Use `have_css` in place of `have_selector` --- spec/support/pages/work_packages/work_packages_table.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/spec/support/pages/work_packages/work_packages_table.rb b/spec/support/pages/work_packages/work_packages_table.rb index 8b1eb6d60431..179d1494b894 100644 --- a/spec/support/pages/work_packages/work_packages_table.rb +++ b/spec/support/pages/work_packages/work_packages_table.rb @@ -125,7 +125,7 @@ def expect_work_package_order(*ids) def expect_no_work_package_listed within(table_container) do - expect(page).to have_selector('#empty-row-notification') + expect(page).to have_css('#empty-row-notification') end end From 226024b2eef96fb85583e19e27e31dafbf5c9513 Mon Sep 17 00:00:00 2001 From: Aaron Contreras Date: Mon, 11 Dec 2023 15:15:08 -0500 Subject: [PATCH 02/16] Allow providing Project or String objects to index page expectation --- spec/support/pages/projects/index.rb | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/spec/support/pages/projects/index.rb b/spec/support/pages/projects/index.rb index be1c687f786f..fc1eaabc484b 100644 --- a/spec/support/pages/projects/index.rb +++ b/spec/support/pages/projects/index.rb @@ -57,7 +57,14 @@ def expect_projects_listed(*projects, archived: false) def expect_projects_not_listed(*projects) within '#project-table' do projects.each do |project| - expect(page).not_to have_text(project) + case project + when Project + expect(page).not_to have_text(project.name) + when String + expect(page).not_to have_text(project) + else + raise ArgumentError, "#{project.inspect} is not a Project or a String" + end end end end From 7d253e7a942b78560cdae1ca0673ee32d4778e78 Mon Sep 17 00:00:00 2001 From: Aaron Contreras Date: Mon, 11 Dec 2023 15:15:46 -0500 Subject: [PATCH 03/16] Add expectation for `TopMenu` on blankslates --- spec/support/components/projects/top_menu.rb | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/spec/support/components/projects/top_menu.rb b/spec/support/components/projects/top_menu.rb index 3d28d2f7a771..1e2d5d24115b 100644 --- a/spec/support/components/projects/top_menu.rb +++ b/spec/support/components/projects/top_menu.rb @@ -37,7 +37,13 @@ class TopMenu def toggle page.find_by_id('projects-menu').click - wait_for_network_idle if using_cuprite? + wait_for_network_idle(timeout: 10) if using_cuprite? + end + + # Ensures modal registers as #open? before proceeding + def toggle! + toggle + expect_open end def open? @@ -96,6 +102,10 @@ def expect_no_result(name) end end + def expect_blankslate + expect(page).not_to have_test_selector('op-project-list-modal--no-results', wait: 0) + end + def expect_item_with_hierarchy_level(hierarchy_level:, item_name:) within search_results do hierarchy_selector = hierarchy_level.times.collect { autocompleter_item_selector }.join(' ') From 0461bd9c4e6cf25b97db74c0408507e933ee8167 Mon Sep 17 00:00:00 2001 From: Aaron Contreras Date: Mon, 11 Dec 2023 15:16:05 -0500 Subject: [PATCH 04/16] Add `Projects::Show` page object --- spec/support/pages/projects/show.rb | 55 +++++++++++++++++++++++++++++ 1 file changed, 55 insertions(+) create mode 100644 spec/support/pages/projects/show.rb diff --git a/spec/support/pages/projects/show.rb b/spec/support/pages/projects/show.rb new file mode 100644 index 000000000000..62f0170ede0b --- /dev/null +++ b/spec/support/pages/projects/show.rb @@ -0,0 +1,55 @@ +#-- copyright +# OpenProject is an open source project management software. +# Copyright (C) 2012-2023 the OpenProject GmbH +# +# 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 COPYRIGHT and LICENSE files for more details. +#++ + +require 'support/pages/page' + +module Pages + module Projects + class Show < ::Pages::Page + attr_reader :project + + # rubocop:disable Lint/MissingSuper + def initialize(project) + @project = project + end + # rubocop:enable Lint/MissingSuper + + def path + project_path(project) + end + + def within_sidebar(&) + within('#menu-sidebar', &) + end + + def toast_type + :rails + end + end + end +end From 9b1ea61251cb8a8cc5e26a6c76676e0c7b222d29 Mon Sep 17 00:00:00 2001 From: Aaron Contreras Date: Mon, 11 Dec 2023 15:16:22 -0500 Subject: [PATCH 05/16] Specify behavior for "Viewer" role users --- .../work_packages/share/access_spec.rb | 123 ++++++++++++++++++ 1 file changed, 123 insertions(+) create mode 100644 spec/features/work_packages/share/access_spec.rb diff --git a/spec/features/work_packages/share/access_spec.rb b/spec/features/work_packages/share/access_spec.rb new file mode 100644 index 000000000000..a762991ced36 --- /dev/null +++ b/spec/features/work_packages/share/access_spec.rb @@ -0,0 +1,123 @@ +# -- copyright +# OpenProject is an open source project management software. +# Copyright (C) 2023 the OpenProject GmbH +# +# 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 COPYRIGHT and LICENSE files for more details. +# ++ + +require 'spec_helper' + +RSpec.describe 'Shared User Access', + :js, :with_cuprite, + with_ee: %i[work_package_sharing], + with_flag: { work_package_sharing: true } do + shared_let(:project) { create(:project) } + shared_let(:work_package) { create(:work_package, project:) } + shared_let(:sharer) do + create(:user, + member_with_permissions: { project => %i[share_work_packages + view_shared_work_packages + view_work_packages] }) + end + shared_let(:shared_with_user) { create(:user, firstname: 'Mean', lastname: 'Turkey') } + + shared_let(:viewer_role) { create(:view_work_package_role) } + shared_let(:commenter_role) { create(:comment_work_package_role) } + shared_let(:editor_role) { create(:edit_work_package_role) } + + let(:projects_page) { Pages::Projects::Index.new } + let(:project_page) { Pages::Projects::Show.new(project) } + let(:projects_top_menu) { Components::Projects::TopMenu.new } + let(:global_work_packages_page) { Pages::WorkPackagesTable.new } + let(:work_packages_page) { Pages::WorkPackagesTable.new(project) } + let(:work_package_page) { Pages::FullWorkPackage.new(work_package) } + let(:share_modal) { Components::WorkPackages::ShareModal.new(work_package) } + + specify "Work Package Access" do + using_session "shared-with user" do + login_as(shared_with_user) + + # + # Work Package's project is not visible without the work package having been shared + # 1. Via the Project's Index Page + projects_page.visit! + projects_page.expect_projects_not_listed(project) + + # 2. Via the Projects dropdown in the top menu + projects_top_menu.toggle! + projects_top_menu.expect_blankslate + + # 3. Visiting the Project's URL directly + project_page.visit! + project_page.expect_toast(type: :error, message: I18n.t(:notice_not_authorized)) + + # + # Work Package is not visible without the work package having been shared + # 1. Via the Work Packages Global Index Page + global_work_packages_page.visit! + global_work_packages_page.expect_toast(type: :error, message: I18n.t(:notice_not_authorized)) + + # 2. Via the URL to work package + work_package_page.visit! + work_package_page.expect_toast(type: :error, message: I18n.t(:notice_file_not_found)) + end + + using_session "sharer" do + # Sharing the Work Package with "View" access + login_as(sharer) + + work_package_page.visit! + work_package_page.click_share_button + share_modal.expect_open + + share_modal.invite_user(shared_with_user, 'View') + share_modal.expect_shared_with(shared_with_user) + end + + using_session "shared-with user" do + # Work Package's project is now listed + # 1. Via the Projects Index Page + projects_page.visit! + projects_page.expect_projects_listed(project) + + # 2. Via the Projects dropdown in the top menu + projects_top_menu.toggle! + projects_top_menu.expect_result(project.name) + + # 3. Visiting the Project's URL directly + project_page.visit! + + # + # Work Package is now visible + project_page.within_sidebar do + click_link(I18n.t('label_work_package_plural')) + end + work_packages_page.expect_work_package_listed(work_package) + work_package_page.visit! + + expect(page) + .to have_text(work_package.subject) + end + end +end From bbecc931c0c875e6a219db3160eee009d2b77410 Mon Sep 17 00:00:00 2001 From: Aaron Contreras Date: Mon, 11 Dec 2023 15:19:13 -0500 Subject: [PATCH 06/16] Lint: Use `have_css` in place of `have_selector` --- .../work_packages/abstract_work_package.rb | 34 +++++++++---------- 1 file changed, 17 insertions(+), 17 deletions(-) diff --git a/spec/support/pages/work_packages/abstract_work_package.rb b/spec/support/pages/work_packages/abstract_work_package.rb index 393d299cb3e2..76f2a8734b61 100644 --- a/spec/support/pages/work_packages/abstract_work_package.rb +++ b/spec/support/pages/work_packages/abstract_work_package.rb @@ -46,7 +46,7 @@ def switch_to_tab(tab:) end def expect_tab(tab) - expect(page).to have_selector('.op-tab-row--link_selected', text: tab.to_s.upcase) + expect(page).to have_css('.op-tab-row--link_selected', text: tab.to_s.upcase) end def edit_field(attribute) @@ -93,7 +93,7 @@ def open_in_split_view def ensure_page_loaded expect_angular_frontend_initialized - expect(page).to have_selector('.op-user-activity--user-name', + expect(page).to have_css('.op-user-activity--user-name', text: work_package.journals.last.user.name, minimum: 1, wait: 10) @@ -106,21 +106,21 @@ def disable_ajax_requests end def expect_group(name, &) - expect(page).to have_selector('.attributes-group--header-text', text: name.upcase) + expect(page).to have_css('.attributes-group--header-text', text: name.upcase) if block_given? page.within(".attributes-group[data-group-name='#{name}']", &) end end def expect_no_group(name) - expect(page).not_to have_selector('.attributes-group--header-text', text: name.upcase) + expect(page).not_to have_css('.attributes-group--header-text', text: name.upcase) end def expect_attributes(attribute_expectations) attribute_expectations.each do |label_name, value| label = label_name.to_s if label == 'status' - expect(page).to have_selector("[data-test-selector='op-wp-status-button'] .button", text: value) + expect(page).to have_css("[data-test-selector='op-wp-status-button'] .button", text: value) else expect(page).to have_selector(".inline-edit--container.#{label.camelize(:lower)}", text: value) end @@ -140,41 +140,41 @@ def expect_activity(user, number: nil) end def expect_activity_message(message) - expect(page).to have_selector('.work-package-details-activities-messages .message', + expect(page).to have_css('.work-package-details-activities-messages .message', text: message) end def expect_no_parent visit_tab!('relations') - expect(page).not_to have_selector('[data-test-selector="op-wp-breadcrumb-parent"]') + expect(page).not_to have_css('[data-test-selector="op-wp-breadcrumb-parent"]') end def expect_zen_mode - expect(page).to have_selector('.zen-mode') - expect(page).to have_selector('#main-menu', visible: false) - expect(page).to have_selector('.op-app-header', visible: false) + expect(page).to have_css('.zen-mode') + expect(page).to have_css('#main-menu', visible: false) + expect(page).to have_css('.op-app-header', visible: false) end def expect_no_zen_mode - expect(page).not_to have_selector('.zen-mode') - expect(page).to have_selector('#main-menu', visible: true) - expect(page).to have_selector('.op-app-header', visible: true) + expect(page).not_to have_css('.zen-mode') + expect(page).to have_css('#main-menu', visible: true) + expect(page).to have_css('.op-app-header', visible: true) end def expect_custom_action(name) expect(page) - .to have_selector('.custom-action', text: name) + .to have_css('.custom-action', text: name) end def expect_custom_action_disabled(name) expect(page) - .to have_selector('.custom-action [disabled]', text: name) + .to have_css('.custom-action [disabled]', text: name) end def expect_no_custom_action(name) expect(page) - .not_to have_selector('.custom-action', text: name) + .not_to have_css('.custom-action', text: name) end def expect_custom_action_order(*names) @@ -294,7 +294,7 @@ def click_create_wp_button(type) end def subject_field - expect(page).to have_selector('.inline-edit--container.subject input', wait: 10) + expect(page).to have_css('.inline-edit--container.subject input', wait: 10) find('.inline-edit--container.subject input') end From e47f41b4ba4eb641b39fc19f120caf18c84d5800 Mon Sep 17 00:00:00 2001 From: Aaron Contreras Date: Mon, 11 Dec 2023 15:19:51 -0500 Subject: [PATCH 07/16] Lint: Fix multiline alignment to please Rubocop --- spec/support/pages/work_packages/abstract_work_package.rb | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/spec/support/pages/work_packages/abstract_work_package.rb b/spec/support/pages/work_packages/abstract_work_package.rb index 76f2a8734b61..ada1c72e2fb7 100644 --- a/spec/support/pages/work_packages/abstract_work_package.rb +++ b/spec/support/pages/work_packages/abstract_work_package.rb @@ -94,9 +94,9 @@ def open_in_split_view def ensure_page_loaded expect_angular_frontend_initialized expect(page).to have_css('.op-user-activity--user-name', - text: work_package.journals.last.user.name, - minimum: 1, - wait: 10) + text: work_package.journals.last.user.name, + minimum: 1, + wait: 10) end def disable_ajax_requests @@ -141,7 +141,7 @@ def expect_activity(user, number: nil) def expect_activity_message(message) expect(page).to have_css('.work-package-details-activities-messages .message', - text: message) + text: message) end def expect_no_parent From f7edfeccfd27817ed763d01ae185b26b33cc07bf Mon Sep 17 00:00:00 2001 From: Aaron Contreras Date: Mon, 11 Dec 2023 15:21:22 -0500 Subject: [PATCH 08/16] Lint: Use `visible: :hidden` instead of `visible: false` --- spec/support/pages/work_packages/abstract_work_package.rb | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/spec/support/pages/work_packages/abstract_work_package.rb b/spec/support/pages/work_packages/abstract_work_package.rb index ada1c72e2fb7..eac57fc72fc3 100644 --- a/spec/support/pages/work_packages/abstract_work_package.rb +++ b/spec/support/pages/work_packages/abstract_work_package.rb @@ -152,14 +152,14 @@ def expect_no_parent def expect_zen_mode expect(page).to have_css('.zen-mode') - expect(page).to have_css('#main-menu', visible: false) - expect(page).to have_css('.op-app-header', visible: false) + expect(page).to have_css('#main-menu', visible: :hidden) + expect(page).to have_css('.op-app-header', visible: :hidden) end def expect_no_zen_mode expect(page).not_to have_css('.zen-mode') - expect(page).to have_css('#main-menu', visible: true) - expect(page).to have_css('.op-app-header', visible: true) + expect(page).to have_css('#main-menu') + expect(page).to have_css('.op-app-header') end def expect_custom_action(name) From ff2a661b4d140b01d8abb6cceb0b3236dd816f30 Mon Sep 17 00:00:00 2001 From: Aaron Contreras Date: Mon, 11 Dec 2023 16:39:26 -0500 Subject: [PATCH 09/16] Ensure fields aren't editable with the Viewer Role --- .../work_packages/share/access_spec.rb | 19 ++++++++++++++----- 1 file changed, 14 insertions(+), 5 deletions(-) diff --git a/spec/features/work_packages/share/access_spec.rb b/spec/features/work_packages/share/access_spec.rb index a762991ced36..97e553d46fb0 100644 --- a/spec/features/work_packages/share/access_spec.rb +++ b/spec/features/work_packages/share/access_spec.rb @@ -28,11 +28,11 @@ require 'spec_helper' -RSpec.describe 'Shared User Access', +RSpec.describe 'Shared Work Package Access', :js, :with_cuprite, with_ee: %i[work_package_sharing], with_flag: { work_package_sharing: true } do - shared_let(:project) { create(:project) } + shared_let(:project) { create(:project_with_types) } shared_let(:work_package) { create(:work_package, project:) } shared_let(:sharer) do create(:user, @@ -54,7 +54,7 @@ let(:work_package_page) { Pages::FullWorkPackage.new(work_package) } let(:share_modal) { Components::WorkPackages::ShareModal.new(work_package) } - specify "Work Package Access" do + specify "Shared-with user access" do using_session "shared-with user" do login_as(shared_with_user) @@ -115,9 +115,18 @@ end work_packages_page.expect_work_package_listed(work_package) work_package_page.visit! + work_package_page.ensure_loaded + + # Every field however is read-only + %i[type subject description + assignee responsible + estimatedTime remainingTime + combinedDate percentageDone category version derivedRemainingTime + overallCosts laborCosts].each do |field| + work_package_page.edit_field(field).expect_read_only + end - expect(page) - .to have_text(work_package.subject) + # So is commenting and file upload... end end end From 8ef24c7c1c04f3c4450d609c4edb2438789271cf Mon Sep 17 00:00:00 2001 From: Aaron Contreras Date: Tue, 12 Dec 2023 12:32:57 -0500 Subject: [PATCH 10/16] Remove `work_package_sharing` feature decision from spec --- spec/features/work_packages/share/access_spec.rb | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/spec/features/work_packages/share/access_spec.rb b/spec/features/work_packages/share/access_spec.rb index 97e553d46fb0..548dab281a9a 100644 --- a/spec/features/work_packages/share/access_spec.rb +++ b/spec/features/work_packages/share/access_spec.rb @@ -30,8 +30,7 @@ RSpec.describe 'Shared Work Package Access', :js, :with_cuprite, - with_ee: %i[work_package_sharing], - with_flag: { work_package_sharing: true } do + with_ee: %i[work_package_sharing] do shared_let(:project) { create(:project_with_types) } shared_let(:work_package) { create(:work_package, project:) } shared_let(:sharer) do From 3679382c0943f8df0cc8e8d8c8e333f497d6f8d6 Mon Sep 17 00:00:00 2001 From: Aaron Contreras Date: Tue, 12 Dec 2023 13:09:00 -0500 Subject: [PATCH 11/16] Guard against "Files" and "Activity" access for users with Viewer Role --- .../work_packages/share/access_spec.rb | 18 ++++++++++++++++-- .../work_packages/abstract_work_package.rb | 4 ++++ 2 files changed, 20 insertions(+), 2 deletions(-) diff --git a/spec/features/work_packages/share/access_spec.rb b/spec/features/work_packages/share/access_spec.rb index 548dab281a9a..0ae1db5eac24 100644 --- a/spec/features/work_packages/share/access_spec.rb +++ b/spec/features/work_packages/share/access_spec.rb @@ -32,7 +32,7 @@ :js, :with_cuprite, with_ee: %i[work_package_sharing] do shared_let(:project) { create(:project_with_types) } - shared_let(:work_package) { create(:work_package, project:) } + shared_let(:work_package) { create(:work_package, project:, journal_notes: 'Hello!') } shared_let(:sharer) do create(:user, member_with_permissions: { project => %i[share_work_packages @@ -52,6 +52,8 @@ let(:work_packages_page) { Pages::WorkPackagesTable.new(project) } let(:work_package_page) { Pages::FullWorkPackage.new(work_package) } let(:share_modal) { Components::WorkPackages::ShareModal.new(work_package) } + let(:add_comment_button_selector) { '.work-packages--activity--add-comment' } + let(:attach_files_button_selector) { 'op-attachments--upload-button' } specify "Shared-with user access" do using_session "shared-with user" do @@ -125,7 +127,19 @@ work_package_page.edit_field(field).expect_read_only end - # So is commenting and file upload... + work_package_page.within_active_tab do + # Commenting is disabled + expect(page) + .not_to have_css(add_comment_button_selector) + end + + # And so is viewing and uploading attachments + work_package_page.switch_to_tab(tab: 'Files') + work_package_page.expect_tab('Files') + work_package_page.within_active_tab do + expect(page) + .not_to have_test_selector(attach_files_button_selector) + end end end end diff --git a/spec/support/pages/work_packages/abstract_work_package.rb b/spec/support/pages/work_packages/abstract_work_package.rb index eac57fc72fc3..32bc12a3c253 100644 --- a/spec/support/pages/work_packages/abstract_work_package.rb +++ b/spec/support/pages/work_packages/abstract_work_package.rb @@ -49,6 +49,10 @@ def expect_tab(tab) expect(page).to have_css('.op-tab-row--link_selected', text: tab.to_s.upcase) end + def within_active_tab(&) + within('.work-packages-full-view--split-right .work-packages--panel-inner', &) + end + def edit_field(attribute) work_package_field(attribute) end From d5a89e169f530cdd644da4a1a6e388b61264f9cd Mon Sep 17 00:00:00 2001 From: Aaron Contreras Date: Tue, 12 Dec 2023 13:12:38 -0500 Subject: [PATCH 12/16] Ensure Activity section is loaded before asserting Activity tab access --- spec/features/work_packages/share/access_spec.rb | 1 + 1 file changed, 1 insertion(+) diff --git a/spec/features/work_packages/share/access_spec.rb b/spec/features/work_packages/share/access_spec.rb index 0ae1db5eac24..1fdffdb2ea7a 100644 --- a/spec/features/work_packages/share/access_spec.rb +++ b/spec/features/work_packages/share/access_spec.rb @@ -127,6 +127,7 @@ work_package_page.edit_field(field).expect_read_only end + work_package_page.ensure_page_loaded # waits for activity section to be ready work_package_page.within_active_tab do # Commenting is disabled expect(page) From 33386f3d3eefa66b8d92f70316d98b2a05552402 Mon Sep 17 00:00:00 2001 From: Aaron Contreras Date: Tue, 12 Dec 2023 13:20:27 -0500 Subject: [PATCH 13/16] Add `#invite_user!` as a shortcut to the action + expectation I strongly believe this is less prone to flaky tests and just helps the spec read better and keeps it clean. --- spec/features/work_packages/share/access_spec.rb | 3 +-- spec/support/components/work_packages/share_modal.rb | 10 ++++++++++ 2 files changed, 11 insertions(+), 2 deletions(-) diff --git a/spec/features/work_packages/share/access_spec.rb b/spec/features/work_packages/share/access_spec.rb index 1fdffdb2ea7a..07ec599af97d 100644 --- a/spec/features/work_packages/share/access_spec.rb +++ b/spec/features/work_packages/share/access_spec.rb @@ -92,8 +92,7 @@ work_package_page.click_share_button share_modal.expect_open - share_modal.invite_user(shared_with_user, 'View') - share_modal.expect_shared_with(shared_with_user) + share_modal.invite_user!(shared_with_user, 'View') end using_session "shared-with user" do diff --git a/spec/support/components/work_packages/share_modal.rb b/spec/support/components/work_packages/share_modal.rb index 9ca54a2ce03c..10e72533ac83 100644 --- a/spec/support/components/work_packages/share_modal.rb +++ b/spec/support/components/work_packages/share_modal.rb @@ -220,6 +220,16 @@ def invite_user(users, role_name) alias_method :invite_users, :invite_user alias_method :invite_group, :invite_user + # Augments +invite_user+ by asserting that the modifications to the + # share have reflected in the modal's UI and we're able to continue + # with our spec without any waits or network related assertions. + # + # As a side benefit, it just keeps the spec file cleaner. + def invite_user!(user, role_name) + invite_user(user, role_name) + expect_shared_with(user, role_name) + end + def search_user(search_string) search_autocomplete page.find('[data-test-selector="op-share-wp-invite-autocomplete"]'), query: search_string, From 5d3da17165c6d2458a2cb68b16301aa73b0c0df8 Mon Sep 17 00:00:00 2001 From: Aaron Contreras Date: Wed, 13 Dec 2023 09:48:53 -0500 Subject: [PATCH 14/16] Limit spec to a single-role-assignment at a time --- .../work_packages/share/access_spec.rb | 49 ++++++------------- 1 file changed, 14 insertions(+), 35 deletions(-) diff --git a/spec/features/work_packages/share/access_spec.rb b/spec/features/work_packages/share/access_spec.rb index 07ec599af97d..77aeb01caaad 100644 --- a/spec/features/work_packages/share/access_spec.rb +++ b/spec/features/work_packages/share/access_spec.rb @@ -33,12 +33,7 @@ with_ee: %i[work_package_sharing] do shared_let(:project) { create(:project_with_types) } shared_let(:work_package) { create(:work_package, project:, journal_notes: 'Hello!') } - shared_let(:sharer) do - create(:user, - member_with_permissions: { project => %i[share_work_packages - view_shared_work_packages - view_work_packages] }) - end + shared_let(:sharer) { create(:admin) } shared_let(:shared_with_user) { create(:user, firstname: 'Mean', lastname: 'Turkey') } shared_let(:viewer_role) { create(:view_work_package_role) } @@ -55,35 +50,7 @@ let(:add_comment_button_selector) { '.work-packages--activity--add-comment' } let(:attach_files_button_selector) { 'op-attachments--upload-button' } - specify "Shared-with user access" do - using_session "shared-with user" do - login_as(shared_with_user) - - # - # Work Package's project is not visible without the work package having been shared - # 1. Via the Project's Index Page - projects_page.visit! - projects_page.expect_projects_not_listed(project) - - # 2. Via the Projects dropdown in the top menu - projects_top_menu.toggle! - projects_top_menu.expect_blankslate - - # 3. Visiting the Project's URL directly - project_page.visit! - project_page.expect_toast(type: :error, message: I18n.t(:notice_not_authorized)) - - # - # Work Package is not visible without the work package having been shared - # 1. Via the Work Packages Global Index Page - global_work_packages_page.visit! - global_work_packages_page.expect_toast(type: :error, message: I18n.t(:notice_not_authorized)) - - # 2. Via the URL to work package - work_package_page.visit! - work_package_page.expect_toast(type: :error, message: I18n.t(:notice_file_not_found)) - end - + specify "'View' role share access" do using_session "sharer" do # Sharing the Work Package with "View" access login_as(sharer) @@ -93,9 +60,21 @@ share_modal.expect_open share_modal.invite_user!(shared_with_user, 'View') + + share_modal.close + + # Shared-with users with the "View" role can't become assignees + assignee_field = work_package_page.edit_field(:assignee) + assignee_field.activate! + results = assignee_field.autocomplete('Mean Turkey', select: false) + wait_for_network_idle + expect(results) + .not_to have_css('.ng-option', text: 'Mean Turkey', wait: 0) + assignee_field.cancel_by_escape end using_session "shared-with user" do + login_as(shared_with_user) # Work Package's project is now listed # 1. Via the Projects Index Page projects_page.visit! From 385c2c386db4990351bd5a6d8bda8bdfcf331524 Mon Sep 17 00:00:00 2001 From: Aaron Contreras Date: Wed, 13 Dec 2023 10:24:37 -0500 Subject: [PATCH 15/16] Pair work_package_role factories with what's actually seeded This is just to maintain parity with the roles that are currently default in OpenProject installations. As a note, once we intend to make these work package roles customizable, it's worth revisiting whether we want these specialized factories with default permissions or not. As this is not going to mimick what's actually being used in an instance. --- spec/factories/work_package_role_factory.rb | 17 ++++++++++++++--- 1 file changed, 14 insertions(+), 3 deletions(-) diff --git a/spec/factories/work_package_role_factory.rb b/spec/factories/work_package_role_factory.rb index 79f7a0eed52b..3ae25c7ff90e 100644 --- a/spec/factories/work_package_role_factory.rb +++ b/spec/factories/work_package_role_factory.rb @@ -38,7 +38,8 @@ builtin { Role::BUILTIN_WORK_PACKAGE_VIEWER } permissions do %i(view_work_packages - export_work_packages) + export_work_packages + show_github_content) end end @@ -50,7 +51,12 @@ work_package_assigned add_work_package_notes edit_own_work_package_notes - export_work_packages) + export_work_packages + view_own_time_entries + log_own_time + edit_own_time_entries + show_github_content + view_file_links) end end @@ -65,7 +71,12 @@ edit_own_work_package_notes manage_work_package_relations copy_work_packages - export_work_packages) + export_work_packages + view_own_time_entries + log_own_time + edit_own_time_entries + show_github_content + view_file_links) end end end From 06a0f332b933cfc58c6c07dfc505aa83787c45a2 Mon Sep 17 00:00:00 2001 From: Aaron Contreras Date: Wed, 13 Dec 2023 11:40:59 -0500 Subject: [PATCH 16/16] Add commenter and editor role expectations --- spec/factories/work_package_role_factory.rb | 1 + .../work_packages/share/access_spec.rb | 164 +++++++++++++++++- 2 files changed, 164 insertions(+), 1 deletion(-) diff --git a/spec/factories/work_package_role_factory.rb b/spec/factories/work_package_role_factory.rb index 3ae25c7ff90e..42b7aa76257a 100644 --- a/spec/factories/work_package_role_factory.rb +++ b/spec/factories/work_package_role_factory.rb @@ -49,6 +49,7 @@ permissions do %i(view_work_packages work_package_assigned + add_work_package_attachments add_work_package_notes edit_own_work_package_notes export_work_packages diff --git a/spec/features/work_packages/share/access_spec.rb b/spec/features/work_packages/share/access_spec.rb index 77aeb01caaad..b7f54d3c5b1e 100644 --- a/spec/features/work_packages/share/access_spec.rb +++ b/spec/features/work_packages/share/access_spec.rb @@ -63,7 +63,7 @@ share_modal.close - # Shared-with users with the "View" role can't become assignees + # Shared-with users with the "View" role CAN'T become assignees assignee_field = work_package_page.edit_field(:assignee) assignee_field.activate! results = assignee_field.autocomplete('Mean Turkey', select: false) @@ -121,4 +121,166 @@ end end end + + specify "'Comment' role share access" do + using_session "sharer" do + # Sharing the Work Package with "View" access + login_as(sharer) + + work_package_page.visit! + work_package_page.click_share_button + share_modal.expect_open + + share_modal.invite_user!(shared_with_user, 'Comment') + share_modal.close + + # TODO: This is currently expected failing behavior. + # Will be fixed in #51551 + # Shared-with users with the "Comment" role CAN become assignees + # + # assignee_field = work_package_page.edit_field(:assignee) + # assignee_field.activate! + # results = assignee_field.autocomplete('Mean Turkey', select: false) + # wait_for_network_idle + # expect(results) + # .to have_css('.ng-option', text: 'Mean Turkey', wait: 0) + # assignee_field.cancel_by_escape + end + + using_session "shared-with user" do + login_as(shared_with_user) + # Work Package's project is now listed + # 1. Via the Projects Index Page + projects_page.visit! + projects_page.expect_projects_listed(project) + + # 2. Via the Projects dropdown in the top menu + projects_top_menu.toggle! + projects_top_menu.expect_result(project.name) + + # 3. Visiting the Project's URL directly + project_page.visit! + + # + # Work Package is now visible + project_page.within_sidebar do + click_link(I18n.t('label_work_package_plural')) + end + work_packages_page.expect_work_package_listed(work_package) + work_package_page.visit! + work_package_page.ensure_loaded + + # Every field however is read-only + %i[type subject description + assignee responsible + estimatedTime remainingTime + combinedDate percentageDone category version derivedRemainingTime + overallCosts laborCosts].each do |field| + work_package_page.edit_field(field).expect_read_only + end + + # Spent time is visible and loggable + SpentTimeEditField.new(page, 'spentTime') + .time_log_icon_visible(true) + + work_package_page.ensure_page_loaded # waits for activity section to be ready + work_package_page.within_active_tab do + # Commenting is enabled + expect(page) + .to have_css(add_comment_button_selector) + end + + # Attachments are uploadable + work_package_page.switch_to_tab(tab: 'Files') + work_package_page.expect_tab('Files') + work_package_page.within_active_tab do + expect(page) + .to have_test_selector(attach_files_button_selector) + end + end + end + + specify "'Edit' role share access" do + using_session "sharer" do + # Sharing the Work Package with "View" access + login_as(sharer) + + work_package_page.visit! + work_package_page.click_share_button + share_modal.expect_open + + share_modal.invite_user!(shared_with_user, 'Edit') + + share_modal.close + + # TODO: This is currently expected failing behavior. + # Will be fixed in #51551 + # Shared-with users with the "Edit" role CAN become assignees + # + # assignee_field = work_package_page.edit_field(:assignee) + # assignee_field.activate! + # results = assignee_field.autocomplete('Mean Turkey', select: false) + # wait_for_network_idle + # expect(results) + # .to have_css('.ng-option', text: 'Mean Turkey', wait: 0) + # assignee_field.cancel_by_escape + end + + using_session "shared-with user" do + login_as(shared_with_user) + # Work Package's project is now listed + # 1. Via the Projects Index Page + projects_page.visit! + projects_page.expect_projects_listed(project) + + # 2. Via the Projects dropdown in the top menu + projects_top_menu.toggle! + projects_top_menu.expect_result(project.name) + + # 3. Visiting the Project's URL directly + project_page.visit! + + # + # Work Package is now visible + project_page.within_sidebar do + click_link(I18n.t('label_work_package_plural')) + end + work_packages_page.expect_work_package_listed(work_package) + work_package_page.visit! + work_package_page.ensure_loaded + + # Every field however is editable + %i[type subject description + assignee responsible + estimatedTime remainingTime + combinedDate percentageDone category].each do |field| + expect(work_package_page.edit_field(field)) + .to be_editable + end + # Except for + %i[version derivedRemainingTime + overallCosts laborCosts].each do |field| + work_package_page.edit_field(field).expect_read_only + end + + # Spent time is visible and loggable + SpentTimeEditField.new(page, 'spentTime') + .time_log_icon_visible(true) + + work_package_page.ensure_page_loaded # waits for activity section to be ready + work_package_page.within_active_tab do + # Commenting is enabled + expect(page) + .to have_css(add_comment_button_selector) + end + + # Attachments are uploadable + work_package_page.switch_to_tab(tab: 'Files') + work_package_page.expect_tab('Files') + work_package_page.within_active_tab do + expect(page) + .to have_test_selector(attach_files_button_selector) + end + end + end end