1import type { Page } from "@playwright/test";
2import { expectDenied, type Api } from "../../helpers/api";
3import {
4 contactUrl,
5 createContact,
6 createContactWithUser,
7 createCustomer,
8 pageMenu,
9} from "../../helpers/directory";
10import { expect, test, uid, usePersona } from "../../helpers/fixtures";
11import { pickOption } from "../../helpers/portal";
12
13usePersona("manager");
14
15test("create a contact with an invite, then edit its name and phone", async ({ page, api }) => {
16 const email = `e2e-contact-${uid()}@example.com`;
17 const dialog = await openNewContact(page);
18 await dialog.getByRole("textbox", { name: "First Name" }).fill("Priya");
19 await dialog.getByRole("textbox", { name: "Email" }).fill(email);
20 await expect(dialog.getByRole("checkbox", { name: "Invite as User" })).toBeChecked();
21 await dialog.getByRole("button", { name: "Create" }).click();
22
23 await expect(page).toHaveURL(/\/opendesk\/contacts\/.+/);
24 const contact = await api.exists("Contact", { email_id: email });
25 expect(contact).toBeTruthy();
26 expect(await api.exists("User Invitation", { email })).toBeTruthy();
27
28 await page.getByRole("button", { name: "Edit" }).click();
29 const edit = page.getByRole("dialog");
30 await edit.getByRole("textbox", { name: "Last Name" }).fill("Raman");
31 await edit.getByRole("button", { name: "Add Phone" }).click();
32 await edit.getByRole("textbox").last().fill("9876543210");
33 await edit.getByRole("button", { name: "Save" }).click();
34
35 await expect
36 .poll(async () => {
37 const doc = await api.get("Contact", contact!);
38 return [doc.last_name, doc.phone_nos.map((p) => p.phone)];
39 })
40 .toEqual(["Raman", [expect.stringContaining("9876543210")]]);
41});
42
43test("editing a contact makes a newly added email primary", async ({ page, api }) => {
44 const contact = await createContact(api, "Emails");
45 const second = `e2e-second-${uid()}@example.com`;
46 await page.goto(contactUrl(contact.name));
47
48 const dialog = await openEdit(page);
49 await dialog.getByRole("button", { name: "Add Email" }).click();
50 await dialog.getByPlaceholder("name@example.com").last().fill(second);
51 await dialog.getByRole("button", { name: "Set as primary" }).click();
52 await dialog.getByRole("button", { name: "Save" }).click();
53 await expect(dialog).toHaveCount(0);
54
55 const doc = await api.get("Contact", contact.name);
56 expect(doc.email_ids.map((row) => [row.email_id, row.is_primary])).toEqual([
57 [contact.email, 0],
58 [second, 1],
59 ]);
60});
61
62test("a contact with a user is linked to a customer from its edit dialog", async ({ page, api }) => {
63 const contact = await createContactWithUser(api, "Linked");
64 const customer = await createCustomer(api);
65 await page.goto(contactUrl(contact.name));
66
67 const dialog = await openEdit(page);
68 await pickOption(page, "Select Customer", customer);
69 await dialog.getByRole("button", { name: "Save" }).click();
70 await expect(dialog).toHaveCount(0);
71 const members = (await api.get("OD Customer", customer)).contacts;
72 expect(members.map((row) => row.contact_name)).toEqual([contact.name]);
73
74 await page.getByText(customer, { exact: true }).click();
75 await expect(page).toHaveURL(new RegExp(`/opendesk/customers/${encodeURIComponent(customer)}`));
76});
77
78test("a manager edits a contact that has a user", async ({ page, api }) => {
79 const contact = await createContactWithUser(api, "Signed");
80 await page.goto(contactUrl(contact.name));
81 const dialog = await openEdit(page);
82 await dialog.getByRole("textbox", { name: "Last Name" }).fill("Up");
83 await dialog.getByRole("button", { name: "Save" }).click();
84 await expect(dialog).toHaveCount(0);
85 expect((await api.get("Contact", contact.name)).last_name).toBe("Up");
86});
87
88test("the feedback tab lists a contact's reviews and filters them by rating", async ({ page, api }) => {
89 const contact = await createContact(api, "Rater");
90 await rate(api, contact, 1, "Loved the quick fix");
91 await rate(api, contact, 0.2, "Waited far too long");
92 await page.goto(`${contactUrl(contact.name)}#feedback`);
93
94 const loved = page.getByText("Loved the quick fix");
95 const waited = page.getByText("Waited far too long");
96 await expect(page.getByText("User Reviews")).toBeVisible();
97 await expect(loved).toBeVisible();
98 await expect(waited).toBeVisible();
99
100 await page.getByRole("radio", { name: "Negative" }).click();
101 await expect(loved).toHaveCount(0);
102 await expect(waited).toBeVisible();
103
104 await page.getByRole("radio", { name: "Positive" }).click();
105 await expect(waited).toHaveCount(0);
106 await expect(loved).toBeVisible();
107});
108
109test("a contact is invited as a user and the invite resent", async ({ page, api }) => {
110 const contact = await createContact(api, "Invitee");
111 await page.goto(contactUrl(contact.name));
112
113 await pageMenu(page).click();
114 await page.getByRole("menuitem", { name: "Invite as User" }).click();
115 await expect(page.getByText("Invitation sent")).toBeVisible();
116 await expect(page.getByText("Invited", { exact: true })).toBeVisible();
117 expect(await api.exists("User Invitation", { email: contact.email, contact: contact.name })).toBeTruthy();
118
119 await pageMenu(page).click();
120 await expect(page.getByRole("menuitem", { name: "Invite as User" })).toHaveCount(0);
121 await page.getByRole("menuitem", { name: "Resend Invite" }).click();
122 await expect(page.getByText("Invitation email resent successfully")).toBeVisible();
123});
124
125test("deleting a contact keeps its tickets and clears their contact", async ({ page, api }) => {
126 const contact = await createContact(api, "Leaver");
127 const ticket = await api.insert("OD Ticket", { subject: `E2E left ${uid()}`, raised_by: contact.email });
128 expect(ticket.contact).toBe(contact.name);
129 await page.goto(contactUrl(contact.name));
130
131 await pageMenu(page).click();
132 await page.getByRole("menuitem", { name: "Delete" }).click();
133 const dialog = page.getByRole("dialog", { name: "Delete" });
134 await expect(dialog.getByRole("checkbox", { name: "Delete 1 ticket(s)" })).not.toBeChecked();
135 await dialog.getByRole("button", { name: "Delete" }).click();
136
137 await expect(page).toHaveURL(/\/opendesk\/contacts$/);
138 expect(await api.exists("Contact", { name: contact.name })).toBeUndefined();
139 expect((await api.get("OD Ticket", ticket.name)).contact).toBeFalsy();
140});
141
142test("a contact invited with a customer carries the customer on the invite", async ({ page, api }) => {
143 const customer = await createCustomer(api);
144 const email = `e2e-invited-${uid()}@example.com`;
145 const dialog = await openNewContact(page);
146 await dialog.getByRole("textbox", { name: "First Name" }).fill("Noor");
147 await dialog.getByRole("textbox", { name: "Email" }).fill(email);
148 await dialog.getByRole("button", { name: "Customer", exact: true }).click();
149 await pickOption(page, "", customer, { opensItself: true });
150 await dialog.getByRole("button", { name: "Create" }).click();
151
152 await expect(page).toHaveURL(/\/opendesk\/contacts\/.+/);
153 const contact = await api.exists("Contact", { email_id: email });
154 expect(await api.exists("User Invitation", { email, customer, contact })).toBeTruthy();
155});
156
157test.describe("agent", () => {
158 usePersona("agent");
159
160 test("an agent reads a contact without the manager actions", async ({ page, api, apiAs }) => {
161 const existing = await createContact(api, "Readonly");
162 await page.goto(contactUrl(existing.name));
163 await expect(page.getByText(existing.email)).toBeVisible();
164 await expect(page.getByRole("button", { name: "Edit" })).toHaveCount(0);
165 const agent = await apiAs("agent");
166 await expectDenied(
167 agent.raw("opendesk.api.contact.edit_contact", { name: existing.name, doc: { first_name: "Changed" } })
168 );
169 await expectDenied(agent.raw("opendesk.api.contact.delete_contact", { name: existing.name }));
170 });
171});
172
173async function openEdit(page: Page) {
174 await page.getByRole("button", { name: "Edit" }).click();
175 return page.getByRole("dialog");
176}
177
178async function openNewContact(page: Page) {
179 await page.goto("/opendesk/contacts");
180 await page.getByRole("button", { name: "Create" }).click();
181 return page.getByRole("dialog", { name: "Create Contact" });
182}
183
184/** A ticket from the contact, rated out of 1. */
185async function rate(api: Api, contact: { name: string; email: string }, rating: number, text: string) {
186 await api.insert("OD Ticket", {
187 subject: `E2E rated ${uid()}`,
188 raised_by: contact.email,
189 feedback_rating: rating,
190 feedback_extra: text,
191 });
192}
193