diff --git a/frontend/playwright/data/dashboard/create-team-invitations.json b/frontend/playwright/data/dashboard/create-team-invitations.json new file mode 100644 index 0000000000..75601e351f --- /dev/null +++ b/frontend/playwright/data/dashboard/create-team-invitations.json @@ -0,0 +1 @@ +{"~:total": 2} diff --git a/frontend/playwright/ui/specs/dashboard-invite-members.spec.js b/frontend/playwright/ui/specs/dashboard-invite-members.spec.js new file mode 100644 index 0000000000..578d2eaa59 --- /dev/null +++ b/frontend/playwright/ui/specs/dashboard-invite-members.spec.js @@ -0,0 +1,86 @@ +import { test, expect } from "@playwright/test"; +import DashboardPage from "../pages/DashboardPage"; + +test.beforeEach(async ({ page }) => { + await DashboardPage.init(page); +}); + +test("Open invite members modal from invitations section", async ({ + page, +}) => { + const dashboardPage = new DashboardPage(page); + await dashboardPage.setupDashboardFull(); + await dashboardPage.setupTeamInvitationsEmpty(); + + await dashboardPage.goToSecondTeamInvitationsSection(); + await expect(page.getByRole("button", { name: "Invite people" })).toBeVisible(); + await page.getByRole("button", { name: "Invite people" }).click(); + await expect(page.getByText("Invite members to the team")).toBeVisible(); +}); + +test("Invite a new member by email", async ({ page }) => { + const dashboardPage = new DashboardPage(page); + await dashboardPage.setupDashboardFull(); + await dashboardPage.setupTeamInvitationsEmpty(); + + await DashboardPage.mockRPC( + page, + "create-team-invitations", + "dashboard/create-team-invitations.json", + { method: "POST" }, + ); + + await dashboardPage.goToSecondTeamInvitationsSection(); + await page.getByRole("button", { name: "Invite people" }).click(); + await expect(page.getByText("Invite members to the team")).toBeVisible(); + + const emailInput = page.getByRole("textbox", { name: "Emails, comma separated" }); + await emailInput.fill("newmember@example.com"); + await emailInput.press("Enter"); + + await page.getByRole("button", { name: "Send invitation" }).click(); + + await expect(page.getByText("Invitation sent successfully")).toBeVisible(); + await expect( + page.getByText("Invite members to the team"), + ).not.toBeVisible(); +}); + +test("Show warning when inviting an existing member", async ({ page }) => { + const dashboardPage = new DashboardPage(page); + await dashboardPage.setupDashboardFull(); + await dashboardPage.setupTeamInvitationsEmpty(); + + await dashboardPage.goToSecondTeamInvitationsSection(); + await page.getByRole("button", { name: "Invite people" }).click(); + await expect(page.getByText("Invite members to the team")).toBeVisible(); + + const emailInput = page.getByRole("textbox", { name: "Emails, comma separated" }); + await emailInput.fill("foo@example.com"); + await emailInput.press("Enter"); + + await expect( + page.getByText( + "Some members are already on the team. We'll invite the rest.", + ), + ).toBeVisible(); +}); + +test("Disable send button when all entered emails are existing members", async ({ + page, +}) => { + const dashboardPage = new DashboardPage(page); + await dashboardPage.setupDashboardFull(); + await dashboardPage.setupTeamInvitationsEmpty(); + + await dashboardPage.goToSecondTeamInvitationsSection(); + await page.getByRole("button", { name: "Invite people" }).click(); + await expect(page.getByText("Invite members to the team")).toBeVisible(); + + const emailInput = page.getByRole("textbox", { name: "Emails, comma separated" }); + await emailInput.fill("foo@example.com"); + await emailInput.press("Enter"); + + const sendButton = page.getByRole("button", { name: "Send invitation" }); + await expect(sendButton).toBeDisabled(); +}); diff --git a/frontend/src/app/main/ui/components/forms.cljs b/frontend/src/app/main/ui/components/forms.cljs index 0d4a5fc55f..89a1bcd42d 100644 --- a/frontend/src/app/main/ui/components/forms.cljs +++ b/frontend/src/app/main/ui/components/forms.cljs @@ -459,7 +459,7 @@ (into [] (distinct) (conj coll item))) (mf/defc multi-input - [{:keys [form label class name trim valid-item-fn caution-item-fn on-submit] :as props}] + [{:keys [form label class trim valid-item-fn caution-item-fn on-submit] :as props}] (let [form (or form (mf/use-ctx form-ctx)) input-name (get props :name) touched? (get-in @form [:touched input-name]) @@ -610,6 +610,7 @@ [:div {:class klass} [:input {:id (name input-name) + :name (name input-name) :class in-klass :type "text" :auto-focus auto-focus? diff --git a/frontend/src/app/main/ui/dashboard/team.cljs b/frontend/src/app/main/ui/dashboard/team.cljs index 8deb4e8b23..613229567c 100644 --- a/frontend/src/app/main/ui/dashboard/team.cljs +++ b/frontend/src/app/main/ui/dashboard/team.cljs @@ -168,10 +168,13 @@ ::mf/register-as :invite-members ::mf/props :obj} [{:keys [team origin invite-email]}] - (let [members (get team :members) + (let [teams (mf/deref refs/teams) + perms (get team :permissions) team-id (get team :id) + members (get-in teams [team-id :members]) + roles (mf/with-memo [perms] (get-available-roles perms)) @@ -824,6 +827,7 @@ [:div {:class (stl/css :empty-invitations-buttons)} [:a {:class (stl/css :btn-empty-invitations) + :role "button" :on-click on-invite-member :data-testid "invite-member"} (tr "dashboard.invite-profile")]]