Skip to content

Commit

Permalink
Merge pull request #14387 from opf/code-maintenance/51273-add-sharing…
Browse files Browse the repository at this point in the history
…-feature-specs

Guard access behavior for shared work packages with a feature spec
  • Loading branch information
klaustopher authored Dec 14, 2023
2 parents 42d17ae + 06a0f33 commit 9706ae9
Show file tree
Hide file tree
Showing 8 changed files with 411 additions and 27 deletions.
18 changes: 15 additions & 3 deletions spec/factories/work_package_role_factory.rb
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand All @@ -48,9 +49,15 @@
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)
export_work_packages
view_own_time_entries
log_own_time
edit_own_time_entries
show_github_content
view_file_links)
end
end

Expand All @@ -65,7 +72,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
286 changes: 286 additions & 0 deletions spec/features/work_packages/share/access_spec.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,286 @@
# -- 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 Work Package Access',
: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:, journal_notes: 'Hello!') }
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) }
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) }
let(:add_comment_button_selector) { '.work-packages--activity--add-comment' }
let(:attach_files_button_selector) { 'op-attachments--upload-button' }

specify "'View' 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, '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!
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

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)
.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

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
12 changes: 11 additions & 1 deletion spec/support/components/projects/top_menu.rb
Original file line number Diff line number Diff line change
Expand Up @@ -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?
Expand Down Expand Up @@ -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(' ')
Expand Down
10 changes: 10 additions & 0 deletions spec/support/components/work_packages/share_modal.rb
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down
9 changes: 8 additions & 1 deletion spec/support/pages/projects/index.rb
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
Loading

0 comments on commit 9706ae9

Please sign in to comment.