diff --git a/app/components/members/role_form_component.html.erb b/app/components/members/role_form_component.html.erb index 9587e5b69fff..0f2dba3441f9 100644 --- a/app/components/members/role_form_component.html.erb +++ b/app/components/members/role_form_component.html.erb @@ -53,11 +53,11 @@ See COPYRIGHT and LICENSE files for more details.
<%= f.submit t(member.new_record? ? :button_add : :button_change), class: "button -primary -small" %> <%= link_to t(:button_cancel), - '#', + "#", data: { - action: 'members-form#toggleMembershipEdit', - 'members-form-toggling-class-param': row.toggle_item_class_name + action: "members-form#toggleMembershipEdit", + "members-form-toggling-class-param": row.toggle_item_class_name }, - class: 'button -small toggle-membership-button' %> + class: "button -small toggle-membership-button" %>
<% end %> diff --git a/app/components/members/row_component.rb b/app/components/members/row_component.rb index 2ea634c25d49..ec3ed29ab089 100644 --- a/app/components/members/row_component.rb +++ b/app/components/members/row_component.rb @@ -29,7 +29,7 @@ #++ module Members - class RowComponent < ::RowComponent + class RowComponent < ::RowComponent # rubocop:disable OpenProject/AddPreviewForViewComponent property :principal delegate :project, to: :table @@ -115,8 +115,8 @@ def administration_settings_link end def roles_label - project_roles = member.roles.select { |role| role.is_a?(ProjectRole) }.uniq.sort - label = h project_roles.collect(&:name).join(', ') + project_roles = member.roles.grep(ProjectRole).uniq.sort + label = h project_roles.collect(&:name).join(", ") if principal&.admin? label << tag(:br) if project_roles.any? diff --git a/app/components/members/table_component.rb b/app/components/members/table_component.rb index f89b2b283da7..8e567c8ec774 100644 --- a/app/components/members/table_component.rb +++ b/app/components/members/table_component.rb @@ -29,7 +29,7 @@ #++ module Members - class TableComponent < ::TableComponent + class TableComponent < ::TableComponent # rubocop:disable OpenProject/AddPreviewForViewComponent options :authorize_update, :authorize_delete, :authorize_work_package_shares_view, @@ -59,8 +59,8 @@ def subselected_member_ids(model) Member .where( id: model - .reselect('DISTINCT ON (members.user_id) members.id') - .reorder('members.user_id, members.entity_type NULLS FIRST') + .reselect("DISTINCT ON (members.user_id) members.id") + .reorder("members.user_id, members.entity_type NULLS FIRST") ) end @@ -92,7 +92,7 @@ def header_options(name) caption = case name when :shared - I18n.t('members.columns.shared') + I18n.t("members.columns.shared") else User.human_attribute_name(name) end @@ -115,7 +115,7 @@ def empty_row_message if is_filtered I18n.t :notice_no_principals_found else - I18n.t :'members.index.no_results_title_text' + I18n.t :"members.index.no_results_title_text" end end end diff --git a/app/controllers/members_controller.rb b/app/controllers/members_controller.rb index b3d1b689804e..5700bb7b37eb 100644 --- a/app/controllers/members_controller.rb +++ b/app/controllers/members_controller.rb @@ -49,19 +49,19 @@ def create end if overall_result.empty? - flash[:error] = I18n.t('activerecord.errors.models.member.principal_blank') - redirect_to project_members_path(project_id: @project, status: 'all') + flash[:error] = I18n.t("activerecord.errors.models.member.principal_blank") + redirect_to project_members_path(project_id: @project, status: "all") elsif overall_result.all?(&:success?) - display_success(members_added_notice(overall_result.map(&:result))) + flash[:notice] = members_added_notice(overall_result.map(&:result)) - redirect_to project_members_path(project_id: @project, status: 'all') + redirect_to project_members_path(project_id: @project, status: "all") else - display_error(overall_result.first) + display_error(overall_result.first, now: true) set_index_data! respond_to do |format| - format.html { render 'index' } + format.html { render "index" } end end end @@ -72,7 +72,7 @@ def update .call(permitted_params.member) if service_call.success? - display_success(I18n.t(:notice_successful_update)) + flash[:notice] = I18n.t(:notice_successful_update) else display_error(service_call) end @@ -129,7 +129,7 @@ def build_members end if @email - principals << { id: @email, name: I18n.t('members.invite_by_mail', mail: @email) } + principals << { id: @email, name: I18n.t("members.invite_by_mail", mail: @email) } end principals @@ -188,7 +188,7 @@ def set_index_data! def set_roles_and_principles! @roles = ProjectRole.givable # Check if there is at least one principal that can be added to the project - @principals_available = possible_members('', 1) + @principals_available = possible_members("", 1) end def possible_members(criteria, limit) @@ -223,11 +223,13 @@ def sort_by_groups_last(members) members.sort_by { |m| group_ids.include?(m.user_id) ? 1 : -1 } end - def display_error(service_call) - flash[:error] = service_call.errors.full_messages.compact.join(', ') - end + def display_error(service_call, now: false) + message = service_call.errors.full_messages.compact.join(", ") - def display_success(message) - flash[:notice] = message + if now + flash.now[:error] = message + else + flash[:error] = message + end end end diff --git a/app/helpers/work_packages_filter_helper.rb b/app/helpers/work_packages_filter_helper.rb index 3f89e32a8f80..c9a867edcff1 100644 --- a/app/helpers/work_packages_filter_helper.rb +++ b/app/helpers/work_packages_filter_helper.rb @@ -31,8 +31,8 @@ module WorkPackagesFilterHelper def project_work_packages_closed_version_path(version, options = {}) query = { f: [ - filter_object('status_id', 'c'), - filter_object('version_id', '=', version.id) + filter_object("status_id", "c"), + filter_object("version_id", "=", version.id) ] } project_work_packages_with_query_path(version.project, query, options) @@ -41,8 +41,8 @@ def project_work_packages_closed_version_path(version, options = {}) def project_work_packages_open_version_path(version, options = {}) query = { f: [ - filter_object('status_id', 'o'), - filter_object('version_id', '=', version.id) + filter_object("status_id", "o"), + filter_object("version_id", "=", version.id) ] } project_work_packages_with_query_path(version.project, query, options) @@ -51,8 +51,8 @@ def project_work_packages_open_version_path(version, options = {}) def project_work_packages_shared_with_path(principal, project, options = {}) query = { f: [ - filter_object('status_id', '*'), - filter_object('shared_with_user', '=', principal.id) + filter_object("status_id", "*"), + filter_object("shared_with_user", "=", principal.id) ] } project_work_packages_with_query_path(project, query, options) @@ -61,8 +61,8 @@ def project_work_packages_shared_with_path(principal, project, options = {}) def project_work_packages_shared_with_me_path(project, options = {}) query = { f: [ - filter_object('status_id', '*'), - filter_object('shared_with_me', '=', 't') + filter_object("status_id", "*"), + filter_object("shared_with_me", "=", "t") ] } project_work_packages_with_query_path(project, query, options) @@ -71,8 +71,8 @@ def project_work_packages_shared_with_me_path(project, options = {}) def project_work_packages_with_ids_path(ids, project, options = {}) query = { f: [ - filter_object('status_id', '*'), - filter_object('id', '=', ids) + filter_object("status_id", "*"), + filter_object("id", "=", ids) ] } project_work_packages_with_query_path(project, query, options) @@ -83,9 +83,9 @@ def project_work_packages_with_ids_path(ids, project, options = {}) def project_report_property_path(project, property_name, property_id, options = {}) query = { f: [ - filter_object('status_id', '*'), - filter_object('subproject_id', '!*'), - filter_object(property_name, '=', property_id) + filter_object("status_id", "*"), + filter_object("subproject_id", "!*"), + filter_object(property_name, "=", property_id) ], t: default_sort } @@ -95,9 +95,9 @@ def project_report_property_path(project, property_name, property_id, options = def project_report_property_status_path(project, status_id, property, property_id, options = {}) query = { f: [ - filter_object('status_id', '=', status_id), - filter_object('subproject_id', '!*'), - filter_object(property, '=', property_id) + filter_object("status_id", "=", status_id), + filter_object("subproject_id", "!*"), + filter_object(property, "=", property_id) ], t: default_sort } @@ -107,9 +107,9 @@ def project_report_property_status_path(project, status_id, property, property_i def project_report_property_open_path(project, property, property_id, options = {}) query = { f: [ - filter_object('status_id', 'o'), - filter_object('subproject_id', '!*'), - filter_object(property, '=', property_id) + filter_object("status_id", "o"), + filter_object("subproject_id", "!*"), + filter_object(property, "=", property_id) ], t: default_sort } @@ -119,9 +119,9 @@ def project_report_property_open_path(project, property, property_id, options = def project_report_property_closed_path(project, property, property_id, options = {}) query = { f: [ - filter_object('status_id', 'c'), - filter_object('subproject_id', '!*'), - filter_object(property, '=', property_id) + filter_object("status_id", "c"), + filter_object("subproject_id", "!*"), + filter_object(property, "=", property_id) ], t: default_sort } @@ -131,9 +131,9 @@ def project_report_property_closed_path(project, property, property_id, options def project_version_property_path(version, property_name, property_id, options = {}) query = { f: [ - filter_object('status_id', '*'), - filter_object('version_id', '=', version.id), - filter_object(property_name, '=', property_id) + filter_object("status_id", "*"), + filter_object("version_id", "=", version.id), + filter_object(property_name, "=", property_id) ], t: default_sort } @@ -143,7 +143,7 @@ def project_version_property_path(version, property_name, property_id, options = private def default_sort - 'updated_at:desc' + "updated_at:desc" end def project_work_packages_with_query_path(project, query, options = {}) diff --git a/app/models/member.rb b/app/models/member.rb index 2fa3251d31b8..dbed51e89c78 100644 --- a/app/models/member.rb +++ b/app/models/member.rb @@ -34,7 +34,7 @@ class Member < ApplicationRecord ].freeze extend DeprecatedAlias - belongs_to :principal, foreign_key: 'user_id', inverse_of: 'members', optional: false + belongs_to :principal, foreign_key: "user_id", inverse_of: "members", optional: false belongs_to :entity, polymorphic: true, optional: true belongs_to :project, optional: true diff --git a/app/models/members/scopes/with_shared_work_packages_info.rb b/app/models/members/scopes/with_shared_work_packages_info.rb index 289e5c67ae7d..22b9ea98e321 100644 --- a/app/models/members/scopes/with_shared_work_packages_info.rb +++ b/app/models/members/scopes/with_shared_work_packages_info.rb @@ -35,7 +35,7 @@ def with_shared_work_packages_info(only_role_id: nil) Member .from("#{Member.quoted_table_name} members") .joins(shared_work_packages_sql(only_role_id)) - .select('members.*') + .select("members.*") .select("COALESCE(members_sums.shared_work_package_ids, '{}') AS shared_work_package_ids") .select("COALESCE(members_sums.other_shared_work_packages_count, 0) AS other_shared_work_packages_count") .select("COALESCE(members_sums.direct_shared_work_packages_count, 0) AS direct_shared_work_packages_count") diff --git a/spec/contracts/members/delete_contract_spec.rb b/spec/contracts/members/delete_contract_spec.rb index d1e9045010f0..0932630ca570 100644 --- a/spec/contracts/members/delete_contract_spec.rb +++ b/spec/contracts/members/delete_contract_spec.rb @@ -26,11 +26,11 @@ # See COPYRIGHT and LICENSE files for more details. #++ -require 'spec_helper' -require 'contracts/shared/model_contract_shared_context' +require "spec_helper" +require "contracts/shared/model_contract_shared_context" RSpec.describe Members::DeleteContract do - include_context 'ModelContract shared context' + include_context "ModelContract shared context" let(:contract) { described_class.new(member, current_user) } let(:member) { build_stubbed(:member, project:, roles:, principal:) } @@ -38,14 +38,14 @@ let(:roles) { [build_stubbed(:project_role)] } let(:principal) { build_stubbed(:user) } - context 'member is deletable' do - it_behaves_like 'contract is valid for active admins and invalid for regular users' + context "when member is deletable" do + it_behaves_like "contract is valid for active admins and invalid for regular users" - include_examples 'contract reuses the model errors' do + include_examples "contract reuses the model errors" do let(:current_user) { build_stubbed(:user) } end - context 'user has permission' do + context "when user has permission" do let(:current_user) { build_stubbed(:user) } before do @@ -54,22 +54,22 @@ end end - it_behaves_like 'contract is valid' + it_behaves_like "contract is valid" end end - context 'when member is not deletable' do + context "when member is not deletable" do before do allow(member).to receive(:some_roles_deletable?).and_return(false) end - context 'for admin' do + context "for admin" do let(:current_user) { build_stubbed(:admin) } - it_behaves_like 'contract is invalid' + it_behaves_like "contract is invalid" end - context 'when user has permission' do + context "when user has permission" do let(:current_user) { build_stubbed(:user) } before do @@ -78,7 +78,7 @@ end end - it_behaves_like 'contract is invalid' + it_behaves_like "contract is invalid" end end end diff --git a/spec/contracts/work_package_members/delete_contract_spec.rb b/spec/contracts/work_package_members/delete_contract_spec.rb index 90d31f45527e..c16d829481df 100644 --- a/spec/contracts/work_package_members/delete_contract_spec.rb +++ b/spec/contracts/work_package_members/delete_contract_spec.rb @@ -26,11 +26,11 @@ # See COPYRIGHT and LICENSE files for more details. # ++ -require 'spec_helper' -require 'contracts/shared/model_contract_shared_context' +require "spec_helper" +require "contracts/shared/model_contract_shared_context" RSpec.describe WorkPackageMembers::DeleteContract do - include_context 'ModelContract shared context' + include_context "ModelContract shared context" let(:contract) { described_class.new(member, current_user) } let(:member) do @@ -45,14 +45,14 @@ let(:principal) { build_stubbed(:user) } let(:entity) { build_stubbed(:work_package) } - context 'when member is deletable' do - it_behaves_like 'contract is valid for active admins and invalid for regular users' + context "when member is deletable" do + it_behaves_like "contract is valid for active admins and invalid for regular users" - include_examples 'contract reuses the model errors' do + include_examples "contract reuses the model errors" do let(:current_user) { build_stubbed(:user) } end - context 'when user has permission' do + context "when user has permission" do let(:current_user) { build_stubbed(:user) } before do @@ -61,22 +61,22 @@ end end - it_behaves_like 'contract is valid' + it_behaves_like "contract is valid" end end - context 'when member is not deletable' do + context "when member is not deletable" do before do allow(member).to receive(:some_roles_deletable?).and_return(false) end - context 'for admin' do + context "for admin" do let(:current_user) { build_stubbed(:admin) } - it_behaves_like 'contract is invalid' + it_behaves_like "contract is invalid" end - context 'when user has permission' do + context "when user has permission" do let(:current_user) { build_stubbed(:user) } before do @@ -85,7 +85,7 @@ end end - it_behaves_like 'contract is invalid' + it_behaves_like "contract is invalid" end end end diff --git a/spec/controllers/members_controller_spec.rb b/spec/controllers/members_controller_spec.rb index 5808dcc90dcc..bdf02cdaa7bf 100644 --- a/spec/controllers/members_controller_spec.rb +++ b/spec/controllers/members_controller_spec.rb @@ -26,12 +26,12 @@ # See COPYRIGHT and LICENSE files for more details. #++ -require 'spec_helper' +require "spec_helper" RSpec.describe MembersController do shared_let(:admin) { create(:admin) } let(:user) { create(:user) } - let(:project) { create(:project, identifier: 'pet_project') } + let(:project) { create(:project, identifier: "pet_project") } let(:role) { create(:project_role) } let(:member) do create(:member, project:, @@ -41,14 +41,14 @@ before { login_as(admin) } - describe 'create' do + describe "create" do shared_let(:admin) { create(:admin) } - let(:project_2) { create(:project) } + let(:project2) { create(:project) } - it 'works for multiple users' do + it "works for multiple users" do post :create, params: { - project_id: project_2.identifier, + project_id: project2.identifier, member: { user_ids: [admin.id, user.id], role_ids: [role.id] @@ -68,37 +68,37 @@ end end - describe 'update' do + describe "update" do shared_let(:admin) { create(:admin) } - let(:project_2) { create(:project) } - let(:role_1) { create(:project_role) } - let(:role_2) { create(:project_role) } - let(:member_2) do + let(:project2) { create(:project) } + let(:role1) { create(:project_role) } + let(:role2) { create(:project_role) } + let(:member2) do create( :member, - project: project_2, + project: project2, user: admin, - roles: [role_1] + roles: [role1] ) end - it 'however allows roles to be updated through mass assignment' do - put 'update', + it "however allows roles to be updated through mass assignment" do + put "update", params: { project_id: project.identifier, - id: member_2.id, + id: member2.id, member: { - role_ids: [role_1.id, role_2.id] + role_ids: [role1.id, role2.id] } } - expect(Member.find(member_2.id).roles).to include(role_1, role_2) + expect(Member.find(member2.id).roles).to include(role1, role2) expect(response.response_code).to be < 400 end end - describe '#autocomplete_for_member' do - let(:params) { { 'project_id' => project.identifier.to_s } } + describe "#autocomplete_for_member" do + let(:params) { { "project_id" => project.identifier.to_s } } before { login_as(user) } @@ -109,111 +109,109 @@ member end - it 'is success' do + it "is success" do post(:autocomplete_for_member, xhr: true, params:) expect(response).to be_successful end end - describe 'WHEN the user is not authorized' do - it 'is forbidden' do + describe "WHEN the user is not authorized" do + it "is forbidden" do post(:autocomplete_for_member, xhr: true, params:) expect(response.response_code).to eq(403) end end end - describe '#create' do + describe "#create" do render_views let(:user2) { create(:user) } let(:user3) { create(:user) } let(:user4) { create(:user) } - context 'post :create' do - context 'single member' do - let(:action) do - post :create, - params: { - project_id: project.id, - member: { role_ids: [role.id], user_id: user2.id } - } - end + context "for single member" do + let(:action) do + post :create, + params: { + project_id: project.id, + member: { role_ids: [role.id], user_id: user2.id } + } + end - it 'adds a member' do - expect { action }.to change { Member.count }.by(1) - expect(response).to redirect_to '/projects/pet_project/members?status=all' - expect(user2).to be_member_of(project) - end + it "adds a member" do + expect { action }.to change(Member, :count).by(1) + expect(response).to redirect_to "/projects/pet_project/members?status=all" + expect(user2).to be_member_of(project) end + end - context 'multiple members' do - let(:action) do - post :create, - params: { - project_id: project.id, - member: { role_ids: [role.id], user_ids: [user2.id, user3.id, user4.id] } - } - end + context "for multiple members" do + let(:action) do + post :create, + params: { + project_id: project.id, + member: { role_ids: [role.id], user_ids: [user2.id, user3.id, user4.id] } + } + end - it 'adds all members' do - expect { action }.to change { Member.count }.by(3) - expect(response).to redirect_to '/projects/pet_project/members?status=all' - expect(user2).to be_member_of(project) - expect(user3).to be_member_of(project) - expect(user4).to be_member_of(project) - end + it "adds all members" do + expect { action }.to change(Member, :count).by(3) + expect(response).to redirect_to "/projects/pet_project/members?status=all" + expect(user2).to be_member_of(project) + expect(user3).to be_member_of(project) + expect(user4).to be_member_of(project) end + end - context 'with yet-to-be-invited emails' do - let(:emails) { ['h.wurst@openproject.com', 'l.lustig@openproject.com'] } - let(:params) do - { - project_id: project.id, - member: { - role_ids: [role.id], - user_ids: [emails.first] + [user2.id, user3.id] + [emails.last] - } + context "with yet-to-be-invited emails" do + let(:emails) { ["h.wurst@openproject.com", "l.lustig@openproject.com"] } + let(:params) do + { + project_id: project.id, + member: { + role_ids: [role.id], + user_ids: [emails.first] + [user2.id, user3.id] + [emails.last] } - end + } + end - let(:invited_users) { User.where(mail: emails).to_a } - let(:users) { invited_users + [user2, user3] } - let(:original_member_count) { Member.count } + let(:invited_users) { User.where(mail: emails).to_a } + let(:users) { invited_users + [user2, user3] } + let(:original_member_count) { Member.count } - before do - original_member_count + before do + original_member_count - perform_enqueued_jobs do - post :create, params: - end + perform_enqueued_jobs do + post :create, params: end + end - it 'redirects to the members list' do - expect(response).to redirect_to '/projects/pet_project/members?status=all' - end + it "redirects to the members list" do + expect(response).to redirect_to "/projects/pet_project/members?status=all" + end - it 'adds members' do - expect(users.size).to eq 4 # 2 emails, 2 existing users - expect(users).to all be_member_of(project) + it "adds members" do + expect(users.size).to eq 4 # 2 emails, 2 existing users + expect(users).to all be_member_of(project) - expect(Member.count).to eq (original_member_count + users.size) - end + expect(Member.count).to eq (original_member_count + users.size) + end - it 'invites new users' do - mails = ActionMailer::Base.deliveries + it "invites new users" do + mails = ActionMailer::Base.deliveries - expect(invited_users.size).to eq 2 - expect(mails.size).to eq invited_users.size - expect(mails.map(&:to).flatten).to eq invited_users.map(&:mail) + expect(invited_users.size).to eq 2 + expect(mails.size).to eq invited_users.size + expect(mails.map(&:to).flatten).to eq invited_users.map(&:mail) - mails.each do |mail| - expect(mail.subject).to include 'account activation' - end + mails.each do |mail| + expect(mail.subject).to include "account activation" end end end - context 'with a failed save' do + context "with a failed save" do let(:invalid_params) do { project_id: project.id, member: { role_ids: [], @@ -224,12 +222,12 @@ post :create, params: invalid_params end - it 'does not redirect to the members index' do - expect(response).not_to redirect_to '/projects/pet_project/members' + it "does not redirect to the members index" do + expect(response).not_to redirect_to "/projects/pet_project/members" end - it 'shows an error message' do - expect(response.body).to include 'Roles need to be assigned.' + it "shows an error message" do + expect(response.body).to include "Roles need to be assigned." end end end @@ -292,7 +290,7 @@ end end - describe '#update' do + describe "#update" do let(:action) do post :update, params: { @@ -306,9 +304,9 @@ member end - it 'updates the member' do - expect { action }.not_to change { Member.count } - expect(response).to redirect_to '/projects/pet_project/members' + it "updates the member" do + expect { action }.not_to change(Member, :count) + expect(response).to redirect_to "/projects/pet_project/members" end end end diff --git a/spec/helpers/work_packages_filter_helper_spec.rb b/spec/helpers/work_packages_filter_helper_spec.rb index 3ccad9a33f0a..360739f6a30f 100644 --- a/spec/helpers/work_packages_filter_helper_spec.rb +++ b/spec/helpers/work_packages_filter_helper_spec.rb @@ -26,14 +26,14 @@ # See COPYRIGHT and LICENSE files for more details. #++ -require 'spec_helper' +require "spec_helper" RSpec.describe WorkPackagesFilterHelper do let(:project) { create(:project) } let(:version) { create(:version, project:) } - shared_examples_for 'work package path with query_props' do - it 'is the expected path' do + shared_examples_for "work package path with query_props" do + it "is the expected path" do path_regexp = Regexp.new("^#{project_work_packages_path(project.identifier)}\\?query_props=(.*)") expect(path) @@ -46,18 +46,18 @@ end end - describe '#project_work_packages_closed_version_path' do - it_behaves_like 'work package path with query_props' do + describe "#project_work_packages_closed_version_path" do + it_behaves_like "work package path with query_props" do let(:expected_json) do { f: [ { - n: 'status', - o: 'c' + n: "status", + o: "c" }, { - n: 'version', - o: '=', + n: "version", + o: "=", v: version.id.to_s } ] @@ -68,18 +68,18 @@ end end - describe '#project_work_packages_open_version_path' do - it_behaves_like 'work package path with query_props' do + describe "#project_work_packages_open_version_path" do + it_behaves_like "work package path with query_props" do let(:expected_json) do { f: [ { - n: 'status', - o: 'o' + n: "status", + o: "o" }, { - n: 'version', - o: '=', + n: "version", + o: "=", v: version.id.to_s } ] @@ -90,20 +90,20 @@ end end - describe '#project_work_packages_shared_with_path' do - it_behaves_like 'work package path with query_props' do + describe "#project_work_packages_shared_with_path" do + it_behaves_like "work package path with query_props" do let(:principal) { build_stubbed(:user) } let(:expected_json) do { f: [ { - n: 'status', - o: '*' + n: "status", + o: "*" }, { - n: 'sharedWithUser', - o: '=', + n: "sharedWithUser", + o: "=", v: principal.id.to_s } ] @@ -114,20 +114,20 @@ end end - describe '#project_work_packages_with_ids_path' do - it_behaves_like 'work package path with query_props' do + describe "#project_work_packages_with_ids_path" do + it_behaves_like "work package path with query_props" do let(:ids) { [13, 17, 42] } let(:expected_json) do { f: [ { - n: 'status', - o: '*' + n: "status", + o: "*" }, { - n: 'id', - o: '=', + n: "id", + o: "=", v: ids.map(&:to_s) } ] @@ -138,30 +138,30 @@ end end - context 'project reports path helpers' do - let(:property_name) { 'priority' } + describe "project reports path helpers" do + let(:property_name) { "priority" } let(:property_id) { 5 } - describe '#project_report_property_path' do - it_behaves_like 'work package path with query_props' do + describe "#project_report_property_path" do + it_behaves_like "work package path with query_props" do let(:expected_json) do { f: [ { - n: 'status', - o: '*' + n: "status", + o: "*" }, { - n: 'subprojectId', - o: '!*' + n: "subprojectId", + o: "!*" }, { n: property_name, - o: '=', + o: "=", v: property_id.to_s } ], - t: 'updated_at:desc' + t: "updated_at:desc" } end @@ -169,28 +169,28 @@ end end - describe '#project_report_property_status_path' do - it_behaves_like 'work package path with query_props' do + describe "#project_report_property_status_path" do + it_behaves_like "work package path with query_props" do let(:status_id) { 2 } let(:expected_json) do { f: [ { - n: 'status', - o: '=', + n: "status", + o: "=", v: status_id.to_s }, { - n: 'subprojectId', - o: '!*' + n: "subprojectId", + o: "!*" }, { n: property_name, - o: '=', + o: "=", v: property_id.to_s } ], - t: 'updated_at:desc' + t: "updated_at:desc" } end @@ -198,26 +198,26 @@ end end - describe '#project_report_property_open_path' do - it_behaves_like 'work package path with query_props' do + describe "#project_report_property_open_path" do + it_behaves_like "work package path with query_props" do let(:expected_json) do { f: [ { - n: 'status', - o: 'o' + n: "status", + o: "o" }, { - n: 'subprojectId', - o: '!*' + n: "subprojectId", + o: "!*" }, { n: property_name, - o: '=', + o: "=", v: property_id.to_s } ], - t: 'updated_at:desc' + t: "updated_at:desc" } end @@ -225,26 +225,26 @@ end end - describe '#project_report_property_closed_path' do - it_behaves_like 'work package path with query_props' do + describe "#project_report_property_closed_path" do + it_behaves_like "work package path with query_props" do let(:expected_json) do { f: [ { - n: 'status', - o: 'c' + n: "status", + o: "c" }, { - n: 'subprojectId', - o: '!*' + n: "subprojectId", + o: "!*" }, { n: property_name, - o: '=', + o: "=", v: property_id.to_s } ], - t: 'updated_at:desc' + t: "updated_at:desc" } end @@ -252,27 +252,27 @@ end end - describe '#project_version_property_path' do - it_behaves_like 'work package path with query_props' do + describe "#project_version_property_path" do + it_behaves_like "work package path with query_props" do let(:expected_json) do { f: [ { - n: 'status', - o: '*' + n: "status", + o: "*" }, { - n: 'version', - o: '=', + n: "version", + o: "=", v: version.id.to_s }, { n: property_name, - o: '=', + o: "=", v: property_id.to_s } ], - t: 'updated_at:desc' + t: "updated_at:desc" } end diff --git a/spec/models/member_spec.rb b/spec/models/member_spec.rb index 9399930124fa..fd628a78b707 100644 --- a/spec/models/member_spec.rb +++ b/spec/models/member_spec.rb @@ -26,7 +26,7 @@ # See COPYRIGHT and LICENSE files for more details. #++ -require 'spec_helper' +require "spec_helper" RSpec.describe Member do let(:user) { create(:user) } @@ -37,7 +37,7 @@ subject(:member) { build(:global_member, user:, roles: [global_role]) } - describe 'relations' do + describe "relations" do it { expect(member).to have_many(:member_roles).dependent(:destroy) } it { expect(member).to belong_to(:project).optional } it { expect(member).to have_many(:roles).through(:member_roles) } @@ -49,47 +49,47 @@ end end - describe 'validations' do + describe "validations" do it { expect(member).to validate_uniqueness_of(:user_id).scoped_to(%i[project_id entity_type entity_id]) } it { expect(member).to validate_inclusion_of(:entity_type).in_array(["WorkPackage"]).allow_blank } end - describe '#deletable?' do - it 'is true, when no roles are inherited' do + describe "#deletable?" do + it "is true, when no roles are inherited" do member.member_roles.first.inherited_from = nil expect(member).to be_deletable end - it 'is false, when roles are inherited' do + it "is false, when roles are inherited" do member.member_roles.first.inherited_from = 1 expect(member).not_to be_deletable end end - describe '#some_roles_deletable?' do + describe "#some_roles_deletable?" do before do member.roles << project_role end - it 'is true, when no roles are inherited' do + it "is true, when no roles are inherited" do member.member_roles.first.inherited_from = nil expect(member).to be_some_roles_deletable end - it 'is true, when not all roles are inherited' do + it "is true, when not all roles are inherited" do member.member_roles.first.inherited_from = 1 expect(member).to be_some_roles_deletable end - it 'is false, when all roles are inherited' do + it "is false, when all roles are inherited" do member.member_roles.first.inherited_from = 1 member.member_roles.second.inherited_from = 1 expect(member).not_to be_some_roles_deletable end end - describe '#deletable_role?' do - it 'can delete directly assigned roles, but not if role is inherited through a group membership' do + describe "#deletable_role?" do + it "can delete directly assigned roles, but not if role is inherited through a group membership" do # user has the global_role by directly being assigned member.save @@ -115,23 +115,23 @@ end end - describe '.can_be_member_of?' do - it 'returns true when a whitelisted entity is passed in' do + describe ".can_be_member_of?" do + it "returns true when a whitelisted entity is passed in" do result = described_class.can_be_member_of?(build(:work_package)) expect(result).to be_truthy end - it 'returns true when the class name of a whitelisted entity is passed in' do + it "returns true when the class name of a whitelisted entity is passed in" do result = described_class.can_be_member_of?(WorkPackage) expect(result).to be_truthy end - it 'returns false when a non-whitelisted entity is passed in' do + it "returns false when a non-whitelisted entity is passed in" do result = described_class.can_be_member_of?(build(:user)) expect(result).to be_falsey end - it 'returns false when the class name of a whitelisted entity is passed in' do + it "returns false when the class name of a whitelisted entity is passed in" do result = described_class.can_be_member_of?(User) expect(result).to be_falsey end diff --git a/spec/models/members/scopes/with_shared_work_packages_info_spec.rb b/spec/models/members/scopes/with_shared_work_packages_info_spec.rb index ff950ff6f74b..5283e463ab79 100644 --- a/spec/models/members/scopes/with_shared_work_packages_info_spec.rb +++ b/spec/models/members/scopes/with_shared_work_packages_info_spec.rb @@ -26,7 +26,7 @@ # See COPYRIGHT and LICENSE files for more details. # ++ -require 'spec_helper' +require "spec_helper" RSpec.describe Members::Scopes::WithSharedWorkPackagesInfo do let(:project) { create(:project) } @@ -42,16 +42,16 @@ let(:view_work_package_role) { create(:view_work_package_role) } let(:comment_work_package_role) { create(:comment_work_package_role) } - let(:user_a) { create(:user, lastname: 'a', status: Principal.statuses[:active]) } - let(:user_b) { create(:user, lastname: 'b', status: Principal.statuses[:active]) } - let(:user_c) { create(:user, lastname: 'c', status: Principal.statuses[:active]) } - let(:group) { create(:group, lastname: 'g', members: [user_b, user_c]) } + let(:user_a) { create(:user, lastname: "a", status: Principal.statuses[:active]) } + let(:user_b) { create(:user, lastname: "b", status: Principal.statuses[:active]) } + let(:user_c) { create(:user, lastname: "c", status: Principal.statuses[:active]) } + let(:group) { create(:group, lastname: "g", members: [user_b, user_c]) } let!(:active_user_member) do create(:member, project:, roles: [role], - principal: create(:user, lastname: 'x', status: Principal.statuses[:active])) + principal: create(:user, lastname: "x", status: Principal.statuses[:active])) end let!(:user_a_view_member) do create(:member, @@ -94,7 +94,7 @@ project_ids: [project.id]) end - describe '.with_shared_work_packages_info' do + describe ".with_shared_work_packages_info" do subject do Member .with_shared_work_packages_info(only_role_id:) @@ -113,47 +113,47 @@ end end - context 'when only_role_ids is not set' do + context "when only_role_ids is not set" do let(:only_role_id) { nil } - it 'returns info for all roles' do + it "returns info for all roles" do expect(subject).to contain_exactly( - ['x', active_user_member.id, { ids: [], other: 0, direct: 0, inherited: 0, total: 0 }], - ['a', user_a_view_member.id, { ids: both_wps, other: 0, direct: 2, inherited: 0, total: 2 }], - ['a', user_a_comment_member.id, { ids: both_wps, other: 0, direct: 2, inherited: 0, total: 2 }], - ['b', user_b_view_member.id, { ids: only_wp_a, other: 0, direct: 1, inherited: 1, total: 1 }], - ['g', group_comment_member.id, { ids: only_wp_a, other: 0, direct: 1, inherited: 0, total: 1 }], - ['c', user_c_inherited_member.id, { ids: only_wp_a, other: 0, direct: 0, inherited: 1, total: 1 }] + ["x", active_user_member.id, { ids: [], other: 0, direct: 0, inherited: 0, total: 0 }], + ["a", user_a_view_member.id, { ids: both_wps, other: 0, direct: 2, inherited: 0, total: 2 }], + ["a", user_a_comment_member.id, { ids: both_wps, other: 0, direct: 2, inherited: 0, total: 2 }], + ["b", user_b_view_member.id, { ids: only_wp_a, other: 0, direct: 1, inherited: 1, total: 1 }], + ["g", group_comment_member.id, { ids: only_wp_a, other: 0, direct: 1, inherited: 0, total: 1 }], + ["c", user_c_inherited_member.id, { ids: only_wp_a, other: 0, direct: 0, inherited: 1, total: 1 }] ) end end - context 'when only_role_id is set to view' do + context "when only_role_id is set to view" do let(:only_role_id) { view_work_package_role.id } - it 'returns info for view roles' do + it "returns info for view roles" do expect(subject).to contain_exactly( - ['x', active_user_member.id, { ids: [], other: 0, direct: 0, inherited: 0, total: 0 }], - ['a', user_a_view_member.id, { ids: only_wp_a, other: 1, direct: 1, inherited: 0, total: 2 }], - ['a', user_a_comment_member.id, { ids: only_wp_a, other: 1, direct: 1, inherited: 0, total: 2 }], - ['b', user_b_view_member.id, { ids: only_wp_a, other: 1, direct: 1, inherited: 0, total: 1 }], - ['g', group_comment_member.id, { ids: [], other: 1, direct: 0, inherited: 0, total: 1 }], - ['c', user_c_inherited_member.id, { ids: [], other: 1, direct: 0, inherited: 0, total: 1 }] + ["x", active_user_member.id, { ids: [], other: 0, direct: 0, inherited: 0, total: 0 }], + ["a", user_a_view_member.id, { ids: only_wp_a, other: 1, direct: 1, inherited: 0, total: 2 }], + ["a", user_a_comment_member.id, { ids: only_wp_a, other: 1, direct: 1, inherited: 0, total: 2 }], + ["b", user_b_view_member.id, { ids: only_wp_a, other: 1, direct: 1, inherited: 0, total: 1 }], + ["g", group_comment_member.id, { ids: [], other: 1, direct: 0, inherited: 0, total: 1 }], + ["c", user_c_inherited_member.id, { ids: [], other: 1, direct: 0, inherited: 0, total: 1 }] ) end end - context 'when only_role_id is set to comment' do + context "when only_role_id is set to comment" do let(:only_role_id) { comment_work_package_role.id } - it 'returns info for comment roles' do + it "returns info for comment roles" do expect(subject).to contain_exactly( - ['x', active_user_member.id, { ids: [], other: 0, direct: 0, inherited: 0, total: 0 }], - ['a', user_a_view_member.id, { ids: only_wp_b, other: 1, direct: 1, inherited: 0, total: 2 }], - ['a', user_a_comment_member.id, { ids: only_wp_b, other: 1, direct: 1, inherited: 0, total: 2 }], - ['b', user_b_view_member.id, { ids: only_wp_a, other: 1, direct: 0, inherited: 1, total: 1 }], - ['g', group_comment_member.id, { ids: only_wp_a, other: 0, direct: 1, inherited: 0, total: 1 }], - ['c', user_c_inherited_member.id, { ids: only_wp_a, other: 0, direct: 0, inherited: 1, total: 1 }] + ["x", active_user_member.id, { ids: [], other: 0, direct: 0, inherited: 0, total: 0 }], + ["a", user_a_view_member.id, { ids: only_wp_b, other: 1, direct: 1, inherited: 0, total: 2 }], + ["a", user_a_comment_member.id, { ids: only_wp_b, other: 1, direct: 1, inherited: 0, total: 2 }], + ["b", user_b_view_member.id, { ids: only_wp_a, other: 1, direct: 0, inherited: 1, total: 1 }], + ["g", group_comment_member.id, { ids: only_wp_a, other: 0, direct: 1, inherited: 0, total: 1 }], + ["c", user_c_inherited_member.id, { ids: only_wp_a, other: 0, direct: 0, inherited: 1, total: 1 }] ) end end diff --git a/spec/routing/members_spec.rb b/spec/routing/members_spec.rb index 98654e383c33..9b3ca3437c3b 100644 --- a/spec/routing/members_spec.rb +++ b/spec/routing/members_spec.rb @@ -26,28 +26,28 @@ # See COPYRIGHT and LICENSE files for more details. #++ -require 'spec_helper' +require "spec_helper" RSpec.describe MembersController do - context 'project scoped' do + describe "project scoped" do it { - expect(subject).to route(:post, '/projects/5234/members').to(controller: 'members', - action: 'create', - project_id: '5234') + expect(subject).to route(:post, "/projects/5234/members").to(controller: "members", + action: "create", + project_id: "5234") } it { - expect(subject).to route(:get, '/projects/5234/members/autocomplete_for_member') - .to(controller: 'members', - action: 'autocomplete_for_member', - project_id: '5234') + expect(subject).to route(:get, "/projects/5234/members/autocomplete_for_member") + .to(controller: "members", + action: "autocomplete_for_member", + project_id: "5234") } end it { - expect(subject).to route(:put, '/members/5234').to(controller: 'members', - action: 'update', - id: '5234') + expect(subject).to route(:put, "/members/5234").to(controller: "members", + action: "update", + id: "5234") } it { diff --git a/spec/services/members/delete_service_spec.rb b/spec/services/members/delete_service_spec.rb index d1f289a657c3..1832391432a0 100644 --- a/spec/services/members/delete_service_spec.rb +++ b/spec/services/members/delete_service_spec.rb @@ -26,11 +26,11 @@ # See COPYRIGHT and LICENSE files for more details. #++ -require 'spec_helper' -require 'services/base_services/behaves_like_delete_service' +require "spec_helper" +require "services/base_services/behaves_like_delete_service" RSpec.describe Members::DeleteService, type: :model do - it_behaves_like 'BaseServices delete service' do + it_behaves_like "BaseServices delete service" do let(:principal) { user } before do model_instance.principal = principal @@ -50,16 +50,16 @@ instance end - describe '#call' do - context 'when contract validates and the model is destroyed successfully' do - it 'calls the cleanup service' do + describe "#call" do + context "when contract validates and the model is destroyed successfully" do + it "calls the cleanup service" do service_call expect(cleanup_service_instance) .to have_received(:call) end - it 'sends a notification' do + it "sends a notification" do service_call expect(OpenProject::Notifications) @@ -67,7 +67,7 @@ .with(OpenProject::Events::MEMBER_DESTROYED, member: model_instance) end - context 'when the model`s principal is a group' do + context "when the model`s principal is a group" do let(:principal) { build_stubbed(:group) } let!(:cleanup_inherited_roles_service_instance) do instance = instance_double(Groups::CleanupInheritedRolesService, call: nil) @@ -82,7 +82,7 @@ instance end - it 'calls the cleanup inherited roles service' do + it "calls the cleanup inherited roles service" do service_call expect(cleanup_inherited_roles_service_instance) @@ -91,33 +91,33 @@ end end - context 'when member has inherited member_roles' do + context "when member has inherited member_roles" do let(:direct_member_role_a) { build(:member_role) } let(:direct_member_role_b) { build(:member_role) } let(:inherited_member_role) { build(:member_role, inherited_from: 123) } let(:member_roles) { [direct_member_role_a, direct_member_role_b, inherited_member_role] } let!(:model_instance) { create(factory, member_roles:) } - it 'does not destroy the member' do + it "does not destroy the member" do service_call - expect(model_instance).to_not have_received(:destroy) + expect(model_instance).not_to have_received(:destroy) end - it 'does not destroy inherited member roles' do + it "does not destroy inherited member roles" do service_call - expect{ inherited_member_role.reload }.to_not raise_error + expect { inherited_member_role.reload }.not_to raise_error end - it 'destroys direct member roles' do + it "destroys direct member roles" do service_call expect { direct_member_role_a.reload }.to raise_error(ActiveRecord::RecordNotFound) expect { direct_member_role_b.reload }.to raise_error(ActiveRecord::RecordNotFound) end - it 'is successful' do + it "is successful" do expect(subject).to be_success end end diff --git a/spec/services/work_package_members/delete_service_spec.rb b/spec/services/work_package_members/delete_service_spec.rb index d3f227e88570..ff30cc211b0a 100644 --- a/spec/services/work_package_members/delete_service_spec.rb +++ b/spec/services/work_package_members/delete_service_spec.rb @@ -28,11 +28,11 @@ # See COPYRIGHT and LICENSE files for more details. # ++ -require 'spec_helper' -require 'services/base_services/behaves_like_delete_service' +require "spec_helper" +require "services/base_services/behaves_like_delete_service" RSpec.describe WorkPackageMembers::DeleteService, type: :model do - it_behaves_like 'BaseServices delete service' do + it_behaves_like "BaseServices delete service" do let(:model_class) { Member } let(:model_instance) { build_stubbed(:work_package_member, principal:) } let(:principal) { user } @@ -46,9 +46,9 @@ end end - describe '#call' do - context 'when contract validates and the model is destroyed successfully' do - it 'calls the cleanup service' do + describe "#call" do + context "when contract validates and the model is destroyed successfully" do + it "calls the cleanup service" do service_call expect(cleanup_service_instance) @@ -77,33 +77,33 @@ end end - context 'when member has inherited member_roles' do + context "when member has inherited member roles" do let(:direct_member_role_a) { build(:member_role) } let(:direct_member_role_b) { build(:member_role) } let(:inherited_member_role) { build(:member_role, inherited_from: 123) } let(:member_roles) { [direct_member_role_a, direct_member_role_b, inherited_member_role] } let!(:model_instance) { create(factory, principal:, member_roles:) } - it 'does not destroy the member' do + it "does not destroy the member" do service_call - expect(model_instance).to_not have_received(:destroy) + expect(model_instance).not_to have_received(:destroy) end - it 'does not destroy inherited member roles' do + it "does not destroy inherited member role" do service_call - expect{ inherited_member_role.reload }.to_not raise_error + expect { inherited_member_role.reload }.not_to raise_error end - it 'destroys direct member roles' do + it "destroys direct member roles" do service_call expect { direct_member_role_a.reload }.to raise_error(ActiveRecord::RecordNotFound) expect { direct_member_role_b.reload }.to raise_error(ActiveRecord::RecordNotFound) end - it 'is successful' do + it "is successful" do expect(subject).to be_success end end diff --git a/spec/support/pages/members.rb b/spec/support/pages/members.rb index bbd937236f2f..4058df485da9 100644 --- a/spec/support/pages/members.rb +++ b/spec/support/pages/members.rb @@ -26,8 +26,8 @@ # See COPYRIGHT and LICENSE files for more details. #++ -require 'support/components/autocompleter/ng_select_autocomplete_helpers' -require 'support/pages/page' +require "support/components/autocompleter/ng_select_autocomplete_helpers" +require "support/pages/page" module Pages class Members < Page @@ -36,12 +36,13 @@ class Members < Page attr_reader :project_identifier def initialize(project_identifier) + super() @project_identifier = project_identifier end def visit! super - expect(page).to have_css('h2', text: I18n.t(:label_member_plural)) + expect(page).to have_css("h2", text: I18n.t(:label_member_plural)) self end @@ -55,25 +56,25 @@ def open_new_member! end def open_filters! - find_by_id('filter-member-button').click + find_by_id("filter-member-button").click end def search_for_name(name) - fill_in 'name', with: name - find('.simple-filters--controls input[type=submit]').click + fill_in "name", with: name + find(".simple-filters--controls input[type=submit]").click end def expect_menu_item(text, selected: false) if selected - expect(page).to have_css('.op-sidemenu--item-action.selected', text:) + expect(page).to have_css(".op-sidemenu--item-action.selected", text:) else - expect(page).to have_css('.op-sidemenu--item-action', text:) + expect(page).to have_css(".op-sidemenu--item-action", text:) end end def click_menu_item(text) - page.within('#menu-sidebar') do - click_link text + page.within("#menu-sidebar") do + click_on text end end @@ -93,7 +94,7 @@ def add_user!(user_name, as:) select_principal! user_name if user_name select_role! as if as - click_on 'Add' + click_on "Add" end end @@ -158,15 +159,15 @@ def has_group?(name, roles: nil) end def find_user(name) - find('tr', text: name) + find("tr", text: name) end def find_mail(mail) - find('td.email', text: mail) + find("td.email", text: mail) end def find_group(name) - find('tr.group', text: name) + find("tr.group", text: name) end ## @@ -188,7 +189,7 @@ def edit_user!(name, add_roles: [], remove_roles: []) Array(add_roles).each { |role| check role } Array(remove_roles).each { |role| uncheck role } - click_on 'Change' + click_on "Change" end def has_group_membership?(user_name) @@ -208,7 +209,7 @@ def has_roles?(user_name, roles, group: false) def select_principal!(principal_name) select_autocomplete page.find("opce-members-autocompleter"), query: principal_name, - results_selector: '.ng-dropdown-panel-items' + results_selector: ".ng-dropdown-panel-items" end ## @@ -225,31 +226,31 @@ def search_and_select_principal!(query, selection) def search_principal!(query) search_autocomplete page.find("opce-members-autocompleter"), query:, - results_selector: '.ng-dropdown-panel-items' + results_selector: ".ng-dropdown-panel-items" end def select_search_result!(value) - find('.ng-option', text: value).click + find(".ng-option", text: value).click end def has_search_result?(value) - page.has_selector?('.ng-option', text: value) + page.has_selector?(".ng-option", text: value) end def has_no_search_results? - page.has_selector?('.ng-option', text: 'No items found') + page.has_selector?(".ng-option", text: "No items found") end def sort_by(column) - find('.generic-table--sort-header a', text: column.upcase).click + find(".generic-table--sort-header a", text: column.upcase).click end def expect_sorted_by(column, desc: false) - page.within('.generic-table--sort-header', text: column.upcase) do + page.within(".generic-table--sort-header", text: column.upcase) do if desc - expect(page).to have_css('.sort.desc') + expect(page).to have_css(".sort.desc") else - expect(page).to have_css('.sort.asc') + expect(page).to have_css(".sort.asc") end end end @@ -258,20 +259,20 @@ def expect_sorted_by(column, desc: false) # Indicates whether the given principal has been selected as one # of the users to be added to the project in the 'New member' dialogue. def has_selected_new_principal?(name) - has_selector? '.ng-value', text: name + has_selector? ".ng-value", text: name end def select_role!(role_name) - select = find('select#member_role_ids') + select = find("select#member_role_ids") select.select role_name end def expect_role(role_name, present: true) - expect(page).to have_conditional_selector(present, '#member_role_ids option', text: role_name) + expect(page).to have_conditional_selector(present, "#member_role_ids option", text: role_name) end def go_to_page!(number) - find('.op-pagination--pages a', text: number.to_s).click + find(".op-pagination--pages a", text: number.to_s).click end end end