require "rails_helper" RSpec.describe ProjectMembershipsController, type: :controller do render_views let(:current_user) { create(:user, :manager) } let!(:project) { create(:project, members: current_user, account: current_user.primary_account) } before do sign_in current_user end describe "#index" do it "responds successfully" do get :index, params: { project_id: project } expect(response).to be_successful end it "renders content" do project = create(:project, name: "My Project", account: current_user.primary_account, members: [ current_user, create(:user, :manager, email: "jane.doe@test.com", accounts: current_user.accounts), create(:user, :associate, email: "john.doe@test.com", accounts: current_user.accounts), ]) get :index, params: { project_id: project } expect(response.body).to have_content "Team" expect(response.body).to have_content "3 Team Members" expect(response.body).to have_content current_user.email expect(response.body).to have_content "jane.doe@test.com" expect(response.body).to have_content "Project Manager" expect(response.body).to have_content "john.doe@test.com" expect(response.body).to have_content "Associate" end it "includes account managers in the team roster" do project = create(:project, name: "My Project", account: current_user.primary_account, members: current_user) account_manager = create(:user, :account_manager, accounts: current_user.accounts) get :index, params: { project_id: project } expect(response.body).to have_content "Team" expect(response.body).to have_content "2 Team Members" expect(response.body).to have_content current_user.email expect(response.body).to have_content account_manager.email end context "for an associate" do let(:current_user) { create(:user, :associate) } it "does not render invite form" do get :index, params: { project_id: project } expect(response.body).not_to have_content "Invite New Member" expect(response.body).not_to have_field "project_membership[user_email]" expect(response.body).not_to have_button "Send Invite" end it "does not render remove links for any users" do project.users << create(:user, :associate, email: "another.user@test.com", primary_account: current_user.primary_account) get :index, params: { project_id: project } expect(response.body).to have_content("another.user@test.com") expect(response.body).not_to have_link("Remove") end end context "for a manager" do let(:current_user) { create(:user, :manager) } it "renders invite form" do get :index, params: { project_id: project } expect(response.body).to have_content "Invite New Member" expect(response.body).to have_field "project_membership[user_email]" expect(response.body).to have_button "Send Invite" end it "renders a remove link for other users" do my_project_membership = project.project_memberships.first their_project_membership = create(:project_membership, project: project, user: create(:user, :associate, email: "another.user@test.com", primary_account: project.account)) get :index, params: { project_id: project } expect(response.body).to have_content("another.user@test.com") expect(response.body).to have_link("Remove", href: project_membership_path(their_project_membership)) expect(response.body).not_to have_link("Remove", href: project_membership_path(my_project_membership)) end end end describe "#create" do let!(:user) { create(:user, email: "user@test.com") } it "responds with redirect" do post :create, params: { project_id: project, project_membership: { user_email: "user@test.com" } } expect(response).to be_redirect expect(response).to redirect_to [project, :project_memberships] expect(flash.notice).to be_present end it "creates a new membership for the invited user" do expect { post :create, params: { project_id: project, project_membership: { user_email: "user@test.com" } } }.to change(ProjectMembership, :count).by(1) membership = ProjectMembership.last expect(membership.project).to eq(project) expect(membership.user).to eq(user) end it "sends an email notification" do post :create, params: { project_id: project, project_membership: { user_email: "user@test.com" } } assert_enqueued_email_with UserMailer, :project_invitation, args: [user, project, user_is_new: false] end context "when invitation cannot be saved" do before do allow_any_instance_of(ProjectMembership).to receive(:save_and_update_account_membership).and_return(false) end it "responds with redirect" do post :create, params: { project_id: project, project_membership: { user_email: "user@test.com" } } expect(response).to be_redirect expect(response).to redirect_to [project, :project_memberships] expect(flash.alert).to be_present end it "creates a new membership for the invited user" do expect { post :create, params: { project_id: project, project_membership: { user_email: "user@test.com" } } }.not_to change(ProjectMembership, :count) end end context "when invited user does not exist yet" do it "creates a new user record" do expect { post :create, params: { project_id: project, project_membership: { user_email: "new.user@test.com" } } }.to change(User, :count) project_membership = ProjectMembership.last user = User.last expect(project_membership.project).to eq(project) expect(project_membership.user).to eq(user) expect(user.email).to eq("new.user@test.com") expect(user.accounts).to include(current_user.primary_account) expect(user.associate?(current_user.primary_account)).to be_truthy end it "sends an email to the new user" do post :create, params: { project_id: project, project_membership: { user_email: "new.user@test.com" } } assert_enqueued_email_with UserMailer, :project_invitation, args: [User.last, project, user_is_new: true] end end context "when invited user does not have access to the account yet" do let!(:existing_user) { create(:user, email: "existing.user@test.com") } it "creates a new account membership record" do expect { post :create, params: { project_id: project, project_membership: { user_email: "existing.user@test.com" } } }.to change(AccountAuth, :count) account_auth = AccountAuth.last project_membership = ProjectMembership.last expect(account_auth.user).to eq existing_user expect(account_auth.account).to eq project.account expect(account_auth.role).to eq "associate" expect(project_membership.user).to eq existing_user expect(project_membership.project).to eq project end it "sends an email to the new user" do post :create, params: { project_id: project, project_membership: { user_email: "new.user@test.com" } } assert_enqueued_email_with UserMailer, :project_invitation, args: [User.last, project, user_is_new: true] end end context "for an associate" do let(:current_user) { create(:user, :associate) } it "raises exception" do expect { post :create, params: { project_id: project, project_membership: { user_email: "new.user@test.com" } } }.to raise_error(Pundit::NotAuthorizedError) end end end describe "#destroy" do let(:project_membership) { current_user.project_memberships.first } it "responds with redirect" do delete :destroy, params: { id: project_membership } expect(response).to be_redirect expect(response).to redirect_to([project, :project_memberships]) expect(flash.alert).to be_present end it "destroys the project membership record" do expect { delete :destroy, params: { id: project_membership } }.to change(ProjectMembership, :count).by(-1) end end end