integratedhub/tests/e2e/invite-tenant.spec.ts
open-swe-dev-andrinoid[bot] 39f8152f5e Apply patch [skip ci]
2026-01-20 19:55:55 +00:00

106 lines
4.0 KiB
TypeScript

import { expect, test, Page } from "@playwright/test";
import { applyDeterministicPage, e2eHeaders, useE2EUser } from "./helpers.ts";
test("landlord can generate a tenant invite email", async ({ page }: { page: 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 }: { page: 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();
});