105 lines
4.0 KiB
TypeScript
105 lines
4.0 KiB
TypeScript
import { expect, test } from "@playwright/test";
|
|
import { applyDeterministicPage, e2eHeaders, useE2EUser } from "./helpers.ts";
|
|
|
|
test("landlord can generate a tenant invite email", async ({ page }) => {
|
|
await applyDeterministicPage(page);
|
|
await useE2EUser(page, "landlord");
|
|
|
|
await page.goto("/landlord/tenants/invite");
|
|
|
|
await expect(
|
|
page.getByRole("heading", { name: /invite a tenant/i }),
|
|
).toBeVisible();
|
|
|
|
const generateButton = page.getByRole("button", {
|
|
name: /generate email template/i,
|
|
});
|
|
await page.getByLabel(/tenant name/i).fill("Sam Tenant");
|
|
await page.getByLabel(/tenant email address/i).fill(
|
|
"sam.tenant@example.com",
|
|
);
|
|
await page.getByLabel(/select a property/i).selectOption("e2e-prop-1");
|
|
|
|
await generateButton.click();
|
|
|
|
// Wait for the template to be generated (API call to complete)
|
|
await page.waitForFunction(() => {
|
|
const textarea = document.querySelector("textarea");
|
|
return textarea && textarea.value && !textarea.value.includes("INVITE_ID");
|
|
}, { timeout: 10000 });
|
|
|
|
const templateArea = page.locator("textarea");
|
|
await expect(templateArea).toHaveValue(/sam\.tenant@example\.com/i);
|
|
await expect(templateArea).toHaveValue(
|
|
/tenant\/invite\/.+/i,
|
|
);
|
|
const firstTemplate = await templateArea.inputValue();
|
|
const firstInviteMatch = firstTemplate.match(/tenant\/invite\/([^\s#]+)/i);
|
|
const firstInviteId = firstInviteMatch?.[1]?.replace(/[^a-z0-9-]/gi, "") ||
|
|
"";
|
|
expect(firstInviteId).toBeTruthy();
|
|
expect(firstInviteId).not.toBe("INVITE_ID");
|
|
|
|
await expect(
|
|
page.getByRole("link", { name: /open email app/i }),
|
|
).toHaveAttribute("href", /mailto:/i);
|
|
await expect(
|
|
page.getByRole("button", { name: /^copy$/i }),
|
|
).toBeEnabled();
|
|
|
|
await page.goto("/landlord/tenants/invite");
|
|
await page.getByLabel(/tenant name/i).fill("Sam Tenant");
|
|
await page.getByLabel(/tenant email address/i).fill(
|
|
"jamie.roommate@example.com",
|
|
);
|
|
await page.getByLabel(/select a property/i).selectOption("e2e-prop-1");
|
|
await generateButton.click();
|
|
|
|
// Wait for the template to be generated (API call to complete)
|
|
await page.waitForFunction(() => {
|
|
const textarea = document.querySelector("textarea");
|
|
return textarea && textarea.value && !textarea.value.includes("INVITE_ID");
|
|
}, { timeout: 10000 });
|
|
|
|
const secondTemplate = await templateArea.inputValue();
|
|
const secondInviteMatch = secondTemplate.match(/tenant\/invite\/([^\s#]+)/i);
|
|
const secondInviteId = secondInviteMatch?.[1]?.replace(/[^a-z0-9-]/gi, "") ||
|
|
"";
|
|
expect(secondInviteId).toBe(firstInviteId);
|
|
expect(secondInviteId).not.toBe("INVITE_ID");
|
|
|
|
await page.goto(`/landlord/tenants/invite?invite_id=${firstInviteId}`);
|
|
const emailInput = page.getByLabel(/tenant email address/i);
|
|
await expect(emailInput).toHaveValue(/sam\.tenant@example\.com/i);
|
|
await expect(emailInput).toHaveValue(/jamie\.roommate@example\.com/i);
|
|
});
|
|
|
|
test("tenant invite preview shows property details and actions", async ({ page }) => {
|
|
await applyDeterministicPage(page);
|
|
|
|
const inviteResponse = await page.request.post("/api/update/tenant_invite", {
|
|
headers: { "Content-Type": "application/json", ...e2eHeaders("landlord") },
|
|
data: {
|
|
property_id: "e2e-prop-1",
|
|
invited_emails: ["taylor.tenant@example.com"],
|
|
},
|
|
});
|
|
const invitePayload = await inviteResponse.json();
|
|
const inviteId = invitePayload?.invite?.id || "";
|
|
expect(inviteId).toBeTruthy();
|
|
|
|
await useE2EUser(page, "tenant");
|
|
await page.goto(`/tenant/invite/${inviteId}`);
|
|
|
|
await expect(page.getByRole("heading", { name: /skyline lofts/i }))
|
|
.toBeVisible();
|
|
await expect(page.getByText(/invite sent to taylor\.tenant@example\.com/i))
|
|
.toBeVisible();
|
|
await expect(page.getByRole("img", { name: /skyline lofts photo/i }))
|
|
.toBeVisible();
|
|
await expect(page.getByRole("button", { name: /start lease/i }))
|
|
.toBeVisible();
|
|
await expect(page.getByRole("button", { name: /decline/i })).toBeVisible();
|
|
await expect(page.getByRole("link", { name: /cancel/i })).toBeVisible();
|
|
});
|