test(people): update tests for teamId and add people e2e spec
This commit is contained in:
@@ -27,7 +27,7 @@ async function createPerson(page: Page, name: string, email: string) {
|
||||
await page.goto("/people/new")
|
||||
await page.getByLabel("Nombre").fill(name)
|
||||
await page.getByLabel("Apellido").fill("E2E")
|
||||
await page.getByLabel("Departamento").selectOption("OTHER")
|
||||
await page.getByLabel("Equipo").selectOption({ label: "Other" })
|
||||
await page.getByLabel("Correo electrónico").fill(email)
|
||||
await page.getByLabel("Teléfono").fill("123456789")
|
||||
await page.getByLabel("Rol").selectOption("NO_USER")
|
||||
|
||||
@@ -0,0 +1,143 @@
|
||||
import { expect, type Page, test } from "@playwright/test"
|
||||
|
||||
async function setLocaleCookie(
|
||||
page: Page,
|
||||
locale: "en" | "es",
|
||||
baseURL?: string,
|
||||
) {
|
||||
await page.context().addCookies([
|
||||
{
|
||||
name: "stock-manager-locale",
|
||||
value: locale,
|
||||
url: baseURL ?? "http://127.0.0.1:3100",
|
||||
},
|
||||
])
|
||||
}
|
||||
|
||||
async function signInAsAdmin(page: Page, baseURL?: string) {
|
||||
await setLocaleCookie(page, "en", baseURL)
|
||||
await page.goto("/login")
|
||||
await page.getByLabel("Email").fill("admin@example.test")
|
||||
await page.getByLabel("Password").fill("admin-password")
|
||||
await page.getByRole("button", { name: "Sign In" }).click()
|
||||
await expect(page).toHaveURL("/")
|
||||
}
|
||||
|
||||
async function createTeam(page: Page, name: string) {
|
||||
await page.goto("/people?tab=teams")
|
||||
await page.getByLabel("Team name").fill(name)
|
||||
await page.getByRole("button", { name: "Create Team" }).click()
|
||||
await expect(page.getByText("Team created successfully")).toBeVisible()
|
||||
}
|
||||
|
||||
test.describe("people and teams", () => {
|
||||
test("switches between people and teams tabs via URL", async ({
|
||||
baseURL,
|
||||
page,
|
||||
}) => {
|
||||
await signInAsAdmin(page, baseURL)
|
||||
|
||||
await page.goto("/people?tab=people")
|
||||
await expect(
|
||||
page.getByRole("navigation", { name: "People sections" }),
|
||||
).toBeVisible()
|
||||
await expect(page.getByRole("link", { name: "People" })).toHaveAttribute(
|
||||
"aria-current",
|
||||
"page",
|
||||
)
|
||||
|
||||
await page.goto("/people?tab=teams")
|
||||
await expect(page.getByRole("link", { name: "Teams" })).toHaveAttribute(
|
||||
"aria-current",
|
||||
"page",
|
||||
)
|
||||
await expect(page.getByLabel("Team name")).toBeVisible()
|
||||
|
||||
await page.goto("/people?tab=invalid")
|
||||
await expect(page.getByRole("link", { name: "People" })).toHaveAttribute(
|
||||
"aria-current",
|
||||
"page",
|
||||
)
|
||||
})
|
||||
|
||||
test("creates, renames, and deletes a team", async ({ baseURL, page }) => {
|
||||
const timestamp = Date.now()
|
||||
const originalName = `E2E Team ${timestamp}`
|
||||
const updatedName = `E2E Team Updated ${timestamp}`
|
||||
|
||||
await signInAsAdmin(page, baseURL)
|
||||
await createTeam(page, originalName)
|
||||
|
||||
const row = page.getByRole("row", { name: new RegExp(originalName) })
|
||||
await expect(row).toBeVisible()
|
||||
|
||||
await row.getByRole("button", { name: "Edit team" }).click()
|
||||
const dialog = page.getByRole("dialog")
|
||||
await expect(dialog).toBeVisible()
|
||||
await dialog.getByLabel("Team name").fill(updatedName)
|
||||
await dialog.getByRole("button", { name: "Update Team" }).click()
|
||||
await expect(dialog).not.toBeVisible()
|
||||
await expect(page.getByText("Team updated successfully")).toBeVisible()
|
||||
await expect(page.getByRole("row", { name: updatedName })).toBeVisible()
|
||||
|
||||
await page
|
||||
.getByRole("row", { name: updatedName })
|
||||
.getByRole("button", { name: "Delete team" })
|
||||
.click()
|
||||
await expect(page.getByText("Team deleted successfully")).toBeVisible()
|
||||
await expect(page.getByRole("row", { name: updatedName })).not.toBeVisible()
|
||||
})
|
||||
|
||||
test("creates a person with a team and shows the team in the list", async ({
|
||||
baseURL,
|
||||
page,
|
||||
}) => {
|
||||
const timestamp = Date.now()
|
||||
const teamName = `E2E Person Team ${timestamp}`
|
||||
const personName = `E2E Person ${timestamp}`
|
||||
|
||||
await signInAsAdmin(page, baseURL)
|
||||
await createTeam(page, teamName)
|
||||
|
||||
await page.goto("/people/new")
|
||||
await page.getByLabel("First Name").fill(personName)
|
||||
await page.getByLabel("Last Name").fill("E2E")
|
||||
await page.getByLabel("Team").selectOption({ label: teamName })
|
||||
await page.getByLabel("Email").fill(`e2e-${timestamp}@example.test`)
|
||||
await page.getByLabel("Phone").fill("123456789")
|
||||
await page.getByLabel("Role").selectOption("NO_USER")
|
||||
await page.getByRole("button", { name: "Create Person" }).click()
|
||||
await expect(page.getByText("Person created successfully")).toBeVisible()
|
||||
|
||||
await page.goto("/people?tab=people")
|
||||
const row = page.getByRole("row", { name: new RegExp(personName) })
|
||||
await expect(row).toContainText(teamName)
|
||||
|
||||
await row.getByRole("link", { name: "View person" }).click()
|
||||
await expect(page.getByText(teamName)).toBeVisible()
|
||||
})
|
||||
|
||||
test("shows no team fallback for a person without a team", async ({
|
||||
baseURL,
|
||||
page,
|
||||
}) => {
|
||||
const timestamp = Date.now()
|
||||
const personName = `E2E No Team ${timestamp}`
|
||||
|
||||
await signInAsAdmin(page, baseURL)
|
||||
|
||||
await page.goto("/people/new")
|
||||
await page.getByLabel("First Name").fill(personName)
|
||||
await page.getByLabel("Last Name").fill("E2E")
|
||||
await page.getByLabel("Team").selectOption({ label: "" })
|
||||
await page.getByLabel("Email").fill(`e2e-noteam-${timestamp}@example.test`)
|
||||
await page.getByLabel("Phone").fill("123456789")
|
||||
await page.getByLabel("Role").selectOption("NO_USER")
|
||||
await page.getByRole("button", { name: "Create Person" }).click()
|
||||
await expect(page.getByText("Person created successfully")).toBeVisible()
|
||||
|
||||
await page.goto("/people?tab=people")
|
||||
const row = page.getByRole("row", { name: new RegExp(personName) })
|
||||
await expect(row).toContainText("—")
|
||||
})
|
||||
})
|
||||
@@ -1,8 +1,4 @@
|
||||
import type {
|
||||
PersonDepartment,
|
||||
PrismaClient,
|
||||
UserRole,
|
||||
} from "@/generated/prisma/client"
|
||||
import type { PrismaClient, UserRole } from "@/generated/prisma/client"
|
||||
import { UserStatus } from "@/generated/prisma/client"
|
||||
import { normalizeEmail } from "@/lib/email"
|
||||
|
||||
@@ -54,12 +50,25 @@ export async function createTestCategory(
|
||||
})
|
||||
}
|
||||
|
||||
export async function createTestTeam(
|
||||
prisma: PrismaClient,
|
||||
overrides: Partial<{ name: string }> = {},
|
||||
) {
|
||||
const suffix = nextSuffix()
|
||||
|
||||
return prisma.team.create({
|
||||
data: {
|
||||
name: overrides.name ?? `Test Team ${suffix}`,
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
export async function createTestPerson(
|
||||
prisma: PrismaClient,
|
||||
overrides: Partial<{
|
||||
firstName: string
|
||||
lastName: string
|
||||
department: PersonDepartment
|
||||
teamId: string | null
|
||||
email: string | null
|
||||
phone: string | null
|
||||
}> = {},
|
||||
@@ -70,26 +79,13 @@ export async function createTestPerson(
|
||||
data: {
|
||||
firstName: overrides.firstName ?? "Test",
|
||||
lastName: overrides.lastName ?? `Person-${suffix}`,
|
||||
department: overrides.department ?? "OTHER",
|
||||
teamId: overrides.teamId ?? null,
|
||||
email: overrides.email ?? null,
|
||||
phone: overrides.phone ?? null,
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
export async function createTestTeam(
|
||||
prisma: PrismaClient,
|
||||
overrides: Partial<{ name: string }> = {},
|
||||
) {
|
||||
const suffix = nextSuffix()
|
||||
|
||||
return prisma.team.create({
|
||||
data: {
|
||||
name: overrides.name ?? `Test Team ${suffix}`,
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
export async function createTestItem(
|
||||
prisma: PrismaClient,
|
||||
overrides: Partial<{
|
||||
|
||||
@@ -1,7 +1,11 @@
|
||||
import { afterAll, beforeAll, beforeEach, describe, expect, it } from "vitest"
|
||||
import type { PrismaClient } from "@/generated/prisma/client"
|
||||
import { normalizeEmail } from "@/lib/email"
|
||||
import { createTestPerson, createTestUser } from "../helpers/factories"
|
||||
import {
|
||||
createTestPerson,
|
||||
createTestTeam,
|
||||
createTestUser,
|
||||
} from "../helpers/factories"
|
||||
import {
|
||||
resetIntegrationTestDatabase,
|
||||
startIntegrationTestDatabase,
|
||||
@@ -33,10 +37,12 @@ afterAll(async () => {
|
||||
describe("createPersonUserUseCase", () => {
|
||||
describe("NO_USER role (person-only creation)", () => {
|
||||
it("creates a Person without a User record when role is NO_USER", async () => {
|
||||
const team = await createTestTeam(prisma)
|
||||
|
||||
const result = await createPersonUserUseCase({
|
||||
firstName: "John",
|
||||
lastName: "Doe",
|
||||
department: "IT",
|
||||
teamId: team.id,
|
||||
email: "john@example.test",
|
||||
phone: null,
|
||||
role: "NO_USER",
|
||||
@@ -51,7 +57,7 @@ describe("createPersonUserUseCase", () => {
|
||||
expect(person).toMatchObject({
|
||||
firstName: "John",
|
||||
lastName: "Doe",
|
||||
department: "IT",
|
||||
teamId: team.id,
|
||||
email: "john@example.test",
|
||||
phone: null,
|
||||
userId: null,
|
||||
@@ -66,10 +72,12 @@ describe("createPersonUserUseCase", () => {
|
||||
})
|
||||
|
||||
it("creates a Person with null email when not providing email and role is NO_USER", async () => {
|
||||
const team = await createTestTeam(prisma)
|
||||
|
||||
const result = await createPersonUserUseCase({
|
||||
firstName: "Jane",
|
||||
lastName: "Smith",
|
||||
department: "ENGINEERING",
|
||||
teamId: team.id,
|
||||
email: "jane-noemail@example.test",
|
||||
phone: "555-1234",
|
||||
role: "NO_USER",
|
||||
@@ -87,10 +95,12 @@ describe("createPersonUserUseCase", () => {
|
||||
|
||||
describe("real role (person + user creation)", () => {
|
||||
it("creates Person and User with linked userId when role is ADMIN", async () => {
|
||||
const team = await createTestTeam(prisma)
|
||||
|
||||
const result = await createPersonUserUseCase({
|
||||
firstName: "Admin",
|
||||
lastName: "User",
|
||||
department: "IT",
|
||||
teamId: team.id,
|
||||
email: "admin@example.test",
|
||||
phone: null,
|
||||
role: "ADMIN",
|
||||
@@ -101,12 +111,12 @@ describe("createPersonUserUseCase", () => {
|
||||
expect(result).toEqual({ success: true })
|
||||
|
||||
const person = await prisma.person.findFirstOrThrow({
|
||||
where: { firstName: "Admin", lastName: "User" },
|
||||
where: { firstName: "Admin" },
|
||||
})
|
||||
expect(person).toMatchObject({
|
||||
firstName: "Admin",
|
||||
lastName: "User",
|
||||
department: "IT",
|
||||
teamId: team.id,
|
||||
email: "admin@example.test",
|
||||
})
|
||||
|
||||
@@ -128,6 +138,7 @@ describe("createPersonUserUseCase", () => {
|
||||
})
|
||||
|
||||
it("creates Person and User for all real roles (MANAGER, STAFF, VIEWER)", async () => {
|
||||
const team = await createTestTeam(prisma)
|
||||
const roles = ["MANAGER", "STAFF", "VIEWER"] as const
|
||||
|
||||
for (const role of roles) {
|
||||
@@ -135,7 +146,7 @@ describe("createPersonUserUseCase", () => {
|
||||
const result = await createPersonUserUseCase({
|
||||
firstName: "Person",
|
||||
lastName: suffix,
|
||||
department: "IT",
|
||||
teamId: team.id,
|
||||
email: `${suffix}@example.test`,
|
||||
phone: null,
|
||||
role,
|
||||
@@ -160,10 +171,12 @@ describe("createPersonUserUseCase", () => {
|
||||
})
|
||||
|
||||
it("derives User.name from firstName + lastName", async () => {
|
||||
const team = await createTestTeam(prisma)
|
||||
|
||||
await createPersonUserUseCase({
|
||||
firstName: "Maria",
|
||||
lastName: "Garcia",
|
||||
department: "SALES",
|
||||
teamId: team.id,
|
||||
email: "maria@example.test",
|
||||
phone: null,
|
||||
role: "STAFF",
|
||||
@@ -178,10 +191,12 @@ describe("createPersonUserUseCase", () => {
|
||||
})
|
||||
|
||||
it("hashes the password when creating a User", async () => {
|
||||
const team = await createTestTeam(prisma)
|
||||
|
||||
await createPersonUserUseCase({
|
||||
firstName: "Hash",
|
||||
lastName: "Test",
|
||||
department: "IT",
|
||||
teamId: team.id,
|
||||
email: "hash-test@example.test",
|
||||
phone: null,
|
||||
role: "STAFF",
|
||||
@@ -196,20 +211,21 @@ describe("createPersonUserUseCase", () => {
|
||||
if (!user.passwordHash) throw new Error("Expected password hash")
|
||||
|
||||
const { verifyPassword } = await import("@/lib/security")
|
||||
expect(await verifyPassword("plaintext-password", user.passwordHash)).toBe(
|
||||
true,
|
||||
)
|
||||
expect(
|
||||
await verifyPassword("plaintext-password", user.passwordHash),
|
||||
).toBe(true)
|
||||
})
|
||||
})
|
||||
|
||||
describe("cross-table email uniqueness", () => {
|
||||
it("rejects submission when email already exists in Person table", async () => {
|
||||
const team = await createTestTeam(prisma)
|
||||
await createTestPerson(prisma, { email: "existing-person@example.test" })
|
||||
|
||||
const result = await createPersonUserUseCase({
|
||||
firstName: "Duplicate",
|
||||
lastName: "Person",
|
||||
department: "IT",
|
||||
teamId: team.id,
|
||||
email: "existing-person@example.test",
|
||||
phone: null,
|
||||
role: "NO_USER",
|
||||
@@ -225,12 +241,13 @@ describe("createPersonUserUseCase", () => {
|
||||
})
|
||||
|
||||
it("rejects submission when email already exists in User table", async () => {
|
||||
const team = await createTestTeam(prisma)
|
||||
await createTestUser(prisma, { email: "existing-user@example.test" })
|
||||
|
||||
const result = await createPersonUserUseCase({
|
||||
firstName: "Duplicate",
|
||||
lastName: "User",
|
||||
department: "IT",
|
||||
teamId: team.id,
|
||||
email: "existing-user@example.test",
|
||||
phone: null,
|
||||
role: "STAFF",
|
||||
@@ -249,6 +266,7 @@ describe("createPersonUserUseCase", () => {
|
||||
})
|
||||
|
||||
it("accepts submission when email is unique across both tables", async () => {
|
||||
const team = await createTestTeam(prisma)
|
||||
// Create a Person and a User with different emails
|
||||
await createTestPerson(prisma, { email: "person@example.test" })
|
||||
await createTestUser(prisma, { email: "user@example.test" })
|
||||
@@ -256,7 +274,7 @@ describe("createPersonUserUseCase", () => {
|
||||
const result = await createPersonUserUseCase({
|
||||
firstName: "New",
|
||||
lastName: "Person",
|
||||
department: "IT",
|
||||
teamId: team.id,
|
||||
email: "new@example.test",
|
||||
phone: null,
|
||||
role: "NO_USER",
|
||||
@@ -266,4 +284,24 @@ describe("createPersonUserUseCase", () => {
|
||||
expect(result).toEqual({ success: true })
|
||||
})
|
||||
})
|
||||
|
||||
describe("team validation", () => {
|
||||
it("rejects an unknown team id", async () => {
|
||||
const result = await createPersonUserUseCase({
|
||||
firstName: "No",
|
||||
lastName: "Team",
|
||||
teamId: "00000000-0000-0000-0000-000000000000",
|
||||
email: "no-team@example.test",
|
||||
phone: null,
|
||||
role: "NO_USER",
|
||||
isActive: true,
|
||||
})
|
||||
|
||||
expect(result.success).toBe(false)
|
||||
if (!result.success) {
|
||||
expect(result.errors.teamId).toBeDefined()
|
||||
}
|
||||
expect(await prisma.person.count()).toBe(0)
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
@@ -1,6 +1,10 @@
|
||||
import { afterAll, beforeAll, beforeEach, describe, expect, it } from "vitest"
|
||||
import type { PrismaClient } from "@/generated/prisma/client"
|
||||
import { createTestPerson, createTestUser } from "../helpers/factories"
|
||||
import {
|
||||
createTestPerson,
|
||||
createTestTeam,
|
||||
createTestUser,
|
||||
} from "../helpers/factories"
|
||||
import {
|
||||
resetIntegrationTestDatabase,
|
||||
startIntegrationTestDatabase,
|
||||
@@ -40,7 +44,7 @@ describe("person use-cases", () => {
|
||||
createPersonUseCase({
|
||||
firstName: "Person",
|
||||
lastName: "One",
|
||||
department: "IT",
|
||||
teamId: null,
|
||||
email: "",
|
||||
phone: "",
|
||||
}),
|
||||
@@ -53,7 +57,7 @@ describe("person use-cases", () => {
|
||||
).toMatchObject({
|
||||
firstName: "Person",
|
||||
lastName: "One",
|
||||
department: "IT",
|
||||
teamId: null,
|
||||
email: null,
|
||||
phone: null,
|
||||
userId: null,
|
||||
@@ -62,12 +66,13 @@ describe("person use-cases", () => {
|
||||
|
||||
it("creates a person with linked userId", async () => {
|
||||
const user = await createTestUser(prisma)
|
||||
const team = await createTestTeam(prisma)
|
||||
|
||||
await expect(
|
||||
createPersonUseCase({
|
||||
firstName: "Linked",
|
||||
lastName: "Person",
|
||||
department: "ENGINEERING",
|
||||
teamId: team.id,
|
||||
email: "linked@example.test",
|
||||
phone: null,
|
||||
userId: user.id,
|
||||
@@ -81,7 +86,7 @@ describe("person use-cases", () => {
|
||||
).toMatchObject({
|
||||
firstName: "Linked",
|
||||
lastName: "Person",
|
||||
department: "ENGINEERING",
|
||||
teamId: team.id,
|
||||
email: "linked@example.test",
|
||||
userId: user.id,
|
||||
})
|
||||
@@ -96,7 +101,7 @@ describe("person use-cases", () => {
|
||||
createPersonUseCase({
|
||||
firstName: "Duplicate",
|
||||
lastName: "Email",
|
||||
department: "OTHER",
|
||||
teamId: null,
|
||||
email: "existing@example.test",
|
||||
phone: null,
|
||||
}),
|
||||
@@ -108,7 +113,24 @@ describe("person use-cases", () => {
|
||||
expect(await prisma.person.count()).toBe(1)
|
||||
})
|
||||
|
||||
it("rejects an unknown team id on create", async () => {
|
||||
const result = await createPersonUseCase({
|
||||
firstName: "Unknown",
|
||||
lastName: "Team",
|
||||
teamId: "00000000-0000-0000-0000-000000000000",
|
||||
email: "unknown-team@example.test",
|
||||
phone: null,
|
||||
})
|
||||
|
||||
expect(result.success).toBe(false)
|
||||
if (!result.success) {
|
||||
expect(result.errors.teamId).toBeDefined()
|
||||
}
|
||||
expect(await prisma.person.count()).toBe(0)
|
||||
})
|
||||
|
||||
it("updates a person and rejects duplicate emails", async () => {
|
||||
const team = await createTestTeam(prisma)
|
||||
const person = await createTestPerson(prisma, {
|
||||
email: "person@example.test",
|
||||
phone: "111111111",
|
||||
@@ -122,16 +144,18 @@ describe("person use-cases", () => {
|
||||
id: person.id,
|
||||
firstName: "Edited",
|
||||
lastName: "Person",
|
||||
department: "ENGINEERING",
|
||||
teamId: team.id,
|
||||
email: "edited@example.test",
|
||||
phone: "222222222",
|
||||
}),
|
||||
).resolves.toEqual({ success: true })
|
||||
|
||||
expect(await prisma.person.findUniqueOrThrow({ where: { id: person.id } })).toMatchObject({
|
||||
expect(
|
||||
await prisma.person.findUniqueOrThrow({ where: { id: person.id } }),
|
||||
).toMatchObject({
|
||||
firstName: "Edited",
|
||||
lastName: "Person",
|
||||
department: "ENGINEERING",
|
||||
teamId: team.id,
|
||||
email: "edited@example.test",
|
||||
phone: "222222222",
|
||||
})
|
||||
@@ -141,7 +165,7 @@ describe("person use-cases", () => {
|
||||
id: person.id,
|
||||
firstName: "Edited",
|
||||
lastName: "Person",
|
||||
department: "ENGINEERING",
|
||||
teamId: team.id,
|
||||
email: other.email,
|
||||
phone: "222222222",
|
||||
}),
|
||||
@@ -150,12 +174,37 @@ describe("person use-cases", () => {
|
||||
errors: { email: ["Email already exists"] },
|
||||
})
|
||||
|
||||
expect(await prisma.person.findUniqueOrThrow({ where: { id: person.id } })).toMatchObject({
|
||||
expect(
|
||||
await prisma.person.findUniqueOrThrow({ where: { id: person.id } }),
|
||||
).toMatchObject({
|
||||
email: "edited@example.test",
|
||||
})
|
||||
expect(await prisma.person.count()).toBe(2)
|
||||
})
|
||||
|
||||
it("updates a person team to null", async () => {
|
||||
const team = await createTestTeam(prisma)
|
||||
const person = await createTestPerson(prisma, {
|
||||
teamId: team.id,
|
||||
})
|
||||
|
||||
await expect(
|
||||
updatePersonUseCase({
|
||||
id: person.id,
|
||||
firstName: person.firstName,
|
||||
lastName: person.lastName,
|
||||
teamId: null,
|
||||
email: person.email,
|
||||
phone: person.phone,
|
||||
}),
|
||||
).resolves.toEqual({ success: true })
|
||||
|
||||
const updated = await prisma.person.findUniqueOrThrow({
|
||||
where: { id: person.id },
|
||||
})
|
||||
expect(updated.teamId).toBeNull()
|
||||
})
|
||||
|
||||
it("searches by email and name in paginated results", async () => {
|
||||
await createTestPerson(prisma, {
|
||||
firstName: "Alice",
|
||||
|
||||
@@ -2,7 +2,11 @@ import { afterAll, beforeAll, beforeEach, describe, expect, it } from "vitest"
|
||||
import { type PrismaClient, UserStatus } from "@/generated/prisma/client"
|
||||
import { normalizeEmail } from "@/lib/email"
|
||||
import { getPasswordHash } from "@/lib/security"
|
||||
import { createTestPerson, createTestUser } from "../helpers/factories"
|
||||
import {
|
||||
createTestPerson,
|
||||
createTestTeam,
|
||||
createTestUser,
|
||||
} from "../helpers/factories"
|
||||
import {
|
||||
resetIntegrationTestDatabase,
|
||||
startIntegrationTestDatabase,
|
||||
@@ -34,6 +38,7 @@ afterAll(async () => {
|
||||
describe("updatePersonUserUseCase", () => {
|
||||
describe("person-only update", () => {
|
||||
it("updates only the Person when person has no linked User", async () => {
|
||||
const team = await createTestTeam(prisma)
|
||||
const person = await createTestPerson(prisma, {
|
||||
firstName: "Old",
|
||||
lastName: "Name",
|
||||
@@ -44,7 +49,7 @@ describe("updatePersonUserUseCase", () => {
|
||||
id: person.id,
|
||||
firstName: "New",
|
||||
lastName: "Name",
|
||||
department: "IT",
|
||||
teamId: team.id,
|
||||
email: "new@example.test",
|
||||
phone: "1234",
|
||||
})
|
||||
@@ -57,7 +62,7 @@ describe("updatePersonUserUseCase", () => {
|
||||
expect(updated).toMatchObject({
|
||||
firstName: "New",
|
||||
lastName: "Name",
|
||||
department: "IT",
|
||||
teamId: team.id,
|
||||
email: "new@example.test",
|
||||
phone: "1234",
|
||||
userId: null,
|
||||
@@ -71,7 +76,7 @@ describe("updatePersonUserUseCase", () => {
|
||||
id: person.id,
|
||||
firstName: "Empty",
|
||||
lastName: "Email",
|
||||
department: "OTHER",
|
||||
teamId: null,
|
||||
email: "",
|
||||
phone: null,
|
||||
})
|
||||
@@ -87,6 +92,7 @@ describe("updatePersonUserUseCase", () => {
|
||||
|
||||
describe("person+user update", () => {
|
||||
it("updates Person fields and User role/isActive when person has a User linked", async () => {
|
||||
const team = await createTestTeam(prisma)
|
||||
const user = await createTestUser(prisma, {
|
||||
email: "user-update@example.test",
|
||||
name: "Old Name",
|
||||
@@ -107,7 +113,7 @@ describe("updatePersonUserUseCase", () => {
|
||||
id: person.id,
|
||||
firstName: "Linked",
|
||||
lastName: "Person",
|
||||
department: "ENGINEERING",
|
||||
teamId: team.id,
|
||||
email: "user-update@example.test",
|
||||
phone: null,
|
||||
role: "ADMIN",
|
||||
@@ -120,7 +126,7 @@ describe("updatePersonUserUseCase", () => {
|
||||
where: { id: person.id },
|
||||
include: { user: true },
|
||||
})
|
||||
expect(updatedPerson.department).toBe("ENGINEERING")
|
||||
expect(updatedPerson.teamId).toBe(team.id)
|
||||
expect(updatedPerson.user).toMatchObject({
|
||||
id: user.id,
|
||||
role: "ADMIN",
|
||||
@@ -144,7 +150,7 @@ describe("updatePersonUserUseCase", () => {
|
||||
id: person.id,
|
||||
firstName: person.firstName,
|
||||
lastName: person.lastName,
|
||||
department: "OTHER",
|
||||
teamId: null,
|
||||
email: "pw-reset@example.test",
|
||||
phone: null,
|
||||
role: "STAFF",
|
||||
@@ -190,7 +196,7 @@ describe("updatePersonUserUseCase", () => {
|
||||
id: person.id,
|
||||
firstName: person.firstName,
|
||||
lastName: person.lastName,
|
||||
department: "OTHER",
|
||||
teamId: null,
|
||||
email: "no-pw@example.test",
|
||||
phone: null,
|
||||
role: "STAFF",
|
||||
@@ -216,7 +222,7 @@ describe("updatePersonUserUseCase", () => {
|
||||
id: "00000000-0000-0000-0000-000000000000",
|
||||
firstName: "Ghost",
|
||||
lastName: "Person",
|
||||
department: "OTHER",
|
||||
teamId: null,
|
||||
email: "ghost@example.test",
|
||||
phone: null,
|
||||
})
|
||||
@@ -239,7 +245,7 @@ describe("updatePersonUserUseCase", () => {
|
||||
id: person.id,
|
||||
firstName: "Mine",
|
||||
lastName: "Person",
|
||||
department: "OTHER",
|
||||
teamId: null,
|
||||
email: "theirs@example.test",
|
||||
phone: null,
|
||||
})
|
||||
@@ -249,5 +255,23 @@ describe("updatePersonUserUseCase", () => {
|
||||
errors: { email: ["Email already exists"] },
|
||||
})
|
||||
})
|
||||
|
||||
it("rejects an unknown team id", async () => {
|
||||
const person = await createTestPerson(prisma)
|
||||
|
||||
const result = await updatePersonUserUseCase({
|
||||
id: person.id,
|
||||
firstName: person.firstName,
|
||||
lastName: person.lastName,
|
||||
teamId: "00000000-0000-0000-0000-000000000000",
|
||||
email: person.email,
|
||||
phone: null,
|
||||
})
|
||||
|
||||
expect(result.success).toBe(false)
|
||||
if (!result.success) {
|
||||
expect(result.errors.teamId).toBeDefined()
|
||||
}
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
@@ -147,7 +147,9 @@ describe("user use-cases", () => {
|
||||
}),
|
||||
).resolves.toEqual({ success: true })
|
||||
|
||||
expect(await prisma.user.findUniqueOrThrow({ where: { id: user.id } })).toMatchObject({
|
||||
expect(
|
||||
await prisma.user.findUniqueOrThrow({ where: { id: user.id } }),
|
||||
).toMatchObject({
|
||||
name: "Edited User",
|
||||
email: "edited@example.test",
|
||||
role: "MANAGER",
|
||||
@@ -168,7 +170,9 @@ describe("user use-cases", () => {
|
||||
errors: { email: ["Email already exists"] },
|
||||
})
|
||||
|
||||
expect(await prisma.user.findUniqueOrThrow({ where: { id: user.id } })).toMatchObject({
|
||||
expect(
|
||||
await prisma.user.findUniqueOrThrow({ where: { id: user.id } }),
|
||||
).toMatchObject({
|
||||
email: "edited@example.test",
|
||||
})
|
||||
})
|
||||
@@ -190,7 +194,9 @@ describe("user use-cases", () => {
|
||||
errors: { id: ["You cannot remove your own administrator access"] },
|
||||
})
|
||||
|
||||
expect(await prisma.user.findUniqueOrThrow({ where: { id: admin.id } })).toMatchObject({
|
||||
expect(
|
||||
await prisma.user.findUniqueOrThrow({ where: { id: admin.id } }),
|
||||
).toMatchObject({
|
||||
role: "ADMIN",
|
||||
status: "ACTIVE",
|
||||
})
|
||||
@@ -232,7 +238,9 @@ describe("user use-cases", () => {
|
||||
}),
|
||||
).resolves.toEqual({ success: true })
|
||||
|
||||
expect(await prisma.user.findUniqueOrThrow({ where: { id: firstAdmin.id } })).toMatchObject({
|
||||
expect(
|
||||
await prisma.user.findUniqueOrThrow({ where: { id: firstAdmin.id } }),
|
||||
).toMatchObject({
|
||||
status: "DISABLED",
|
||||
})
|
||||
})
|
||||
@@ -252,7 +260,9 @@ describe("user use-cases", () => {
|
||||
errors: { id: ["You cannot deactivate your own user"] },
|
||||
})
|
||||
|
||||
expect(await prisma.user.findUniqueOrThrow({ where: { id: admin.id } })).toMatchObject({
|
||||
expect(
|
||||
await prisma.user.findUniqueOrThrow({ where: { id: admin.id } }),
|
||||
).toMatchObject({
|
||||
status: "ACTIVE",
|
||||
})
|
||||
})
|
||||
|
||||
@@ -25,6 +25,8 @@ vi.mock("@/use-cases/person.use-cases", () => ({
|
||||
|
||||
import { createNewPerson, updatePerson } from "@/actions/person.actions"
|
||||
|
||||
const validTeamId = "550e8400-e29b-41d4-a716-446655440000"
|
||||
|
||||
describe("person actions localization", () => {
|
||||
beforeEach(() => {
|
||||
vi.clearAllMocks()
|
||||
@@ -35,7 +37,7 @@ describe("person actions localization", () => {
|
||||
const result = await createNewPerson({
|
||||
firstName: "",
|
||||
lastName: "",
|
||||
department: "",
|
||||
teamId: "not-a-uuid",
|
||||
email: "not-an-email",
|
||||
} as unknown as Parameters<typeof createNewPerson>[0])
|
||||
|
||||
@@ -46,7 +48,8 @@ describe("person actions localization", () => {
|
||||
errors: {
|
||||
firstName: [es.inventory.people.schema.firstNameRequired],
|
||||
lastName: [es.inventory.people.schema.lastNameRequired],
|
||||
department: [es.inventory.people.schema.departmentRequired],
|
||||
teamId: [es.inventory.people.schema.teamIdInvalid],
|
||||
email: [es.inventory.people.schema.emailInvalid],
|
||||
},
|
||||
})
|
||||
})
|
||||
@@ -62,7 +65,7 @@ describe("person actions localization", () => {
|
||||
const result = await createNewPerson({
|
||||
firstName: "Ada",
|
||||
lastName: "Lovelace",
|
||||
department: "ENGINEERING",
|
||||
teamId: validTeamId,
|
||||
email: "ada@example.test",
|
||||
})
|
||||
|
||||
@@ -83,7 +86,7 @@ describe("person actions localization", () => {
|
||||
id: "person-1",
|
||||
firstName: "Ada",
|
||||
lastName: "Lovelace",
|
||||
department: "ENGINEERING",
|
||||
teamId: validTeamId,
|
||||
email: "ada@example.test",
|
||||
})
|
||||
|
||||
@@ -98,7 +101,7 @@ describe("person actions localization", () => {
|
||||
const result = await createNewPerson({
|
||||
firstName: "Ada",
|
||||
lastName: "Lovelace",
|
||||
department: "ENGINEERING",
|
||||
teamId: null,
|
||||
userId: "not-a-uuid",
|
||||
} as unknown as Parameters<typeof createNewPerson>[0])
|
||||
|
||||
|
||||
@@ -9,6 +9,7 @@ const actionCopy = {
|
||||
updateFailure: "Error al actualizar la persona",
|
||||
duplicateEmail: "El correo electrónico ya existe",
|
||||
notFound: "Persona no encontrada",
|
||||
teamNotFound: "Equipo no encontrado",
|
||||
}
|
||||
|
||||
describe("person action message localization", () => {
|
||||
@@ -25,6 +26,19 @@ describe("person action message localization", () => {
|
||||
})
|
||||
})
|
||||
|
||||
it("localizes team not found errors", () => {
|
||||
expect(
|
||||
localizePersonFieldErrors(
|
||||
{
|
||||
teamId: ["Team not found"],
|
||||
},
|
||||
actionCopy,
|
||||
),
|
||||
).toEqual({
|
||||
teamId: [actionCopy.teamNotFound],
|
||||
})
|
||||
})
|
||||
|
||||
it("keeps unknown messages unchanged", () => {
|
||||
expect(
|
||||
localizePersonFieldErrors(
|
||||
|
||||
@@ -28,6 +28,8 @@ vi.mock("@/use-cases/person.use-cases", () => ({
|
||||
|
||||
import { updatePersonUserAction } from "@/actions/person.actions"
|
||||
|
||||
const validTeamId = "550e8400-e29b-41d4-a716-446655440000"
|
||||
|
||||
describe("updatePersonUserAction", () => {
|
||||
beforeEach(() => {
|
||||
vi.clearAllMocks()
|
||||
@@ -41,7 +43,7 @@ describe("updatePersonUserAction", () => {
|
||||
id: "",
|
||||
firstName: "Ada",
|
||||
lastName: "Lovelace",
|
||||
department: "ENGINEERING",
|
||||
teamId: validTeamId,
|
||||
email: "ada@example.test",
|
||||
phone: null,
|
||||
})
|
||||
@@ -60,7 +62,7 @@ describe("updatePersonUserAction", () => {
|
||||
id: "person-1",
|
||||
firstName: "Ada",
|
||||
lastName: "Lovelace",
|
||||
department: "ENGINEERING",
|
||||
teamId: validTeamId,
|
||||
email: "not-an-email",
|
||||
phone: null,
|
||||
})
|
||||
@@ -74,12 +76,31 @@ describe("updatePersonUserAction", () => {
|
||||
expect(mocks.updatePersonUserUseCase).not.toHaveBeenCalled()
|
||||
})
|
||||
|
||||
it("rejects invalid teamId with localized teamIdInvalid error", async () => {
|
||||
const result = await updatePersonUserAction({
|
||||
id: "person-1",
|
||||
firstName: "Ada",
|
||||
lastName: "Lovelace",
|
||||
teamId: "not-a-uuid",
|
||||
email: "ada@example.test",
|
||||
phone: null,
|
||||
})
|
||||
|
||||
expect(result).toEqual({
|
||||
success: false,
|
||||
errors: {
|
||||
teamId: [es.inventory.people.schema.teamIdInvalid],
|
||||
},
|
||||
})
|
||||
expect(mocks.updatePersonUserUseCase).not.toHaveBeenCalled()
|
||||
})
|
||||
|
||||
it("rejects short password when role is provided", async () => {
|
||||
const result = await updatePersonUserAction({
|
||||
id: "person-1",
|
||||
firstName: "Ada",
|
||||
lastName: "Lovelace",
|
||||
department: "ENGINEERING",
|
||||
teamId: validTeamId,
|
||||
email: "ada@example.test",
|
||||
phone: null,
|
||||
role: "ADMIN",
|
||||
@@ -101,7 +122,7 @@ describe("updatePersonUserAction", () => {
|
||||
id: "person-1",
|
||||
firstName: "Ada",
|
||||
lastName: "Lovelace",
|
||||
department: "ENGINEERING",
|
||||
teamId: validTeamId,
|
||||
email: "ada@example.test",
|
||||
phone: null,
|
||||
role: "NO_USER" as unknown as "ADMIN",
|
||||
@@ -117,7 +138,7 @@ describe("updatePersonUserAction", () => {
|
||||
id: "person-1",
|
||||
firstName: "Ada",
|
||||
lastName: "Lovelace",
|
||||
department: "ENGINEERING",
|
||||
teamId: validTeamId,
|
||||
email: "ada@example.test",
|
||||
phone: null,
|
||||
role: "SUPER_ADMIN" as unknown as "ADMIN",
|
||||
@@ -140,7 +161,7 @@ describe("updatePersonUserAction", () => {
|
||||
id: "person-1",
|
||||
firstName: "Ada",
|
||||
lastName: "Lovelace",
|
||||
department: "ENGINEERING",
|
||||
teamId: validTeamId,
|
||||
email: "duplicate@example.test",
|
||||
phone: null,
|
||||
})
|
||||
@@ -164,7 +185,7 @@ describe("updatePersonUserAction", () => {
|
||||
id: "missing",
|
||||
firstName: "Ada",
|
||||
lastName: "Lovelace",
|
||||
department: "ENGINEERING",
|
||||
teamId: validTeamId,
|
||||
email: "ada@example.test",
|
||||
phone: null,
|
||||
})
|
||||
@@ -184,7 +205,7 @@ describe("updatePersonUserAction", () => {
|
||||
id: "person-1",
|
||||
firstName: "Ada",
|
||||
lastName: "Lovelace",
|
||||
department: "ENGINEERING",
|
||||
teamId: validTeamId,
|
||||
email: "ada@example.test",
|
||||
phone: null,
|
||||
})
|
||||
@@ -204,7 +225,7 @@ describe("updatePersonUserAction", () => {
|
||||
id: "person-1",
|
||||
firstName: "Ada",
|
||||
lastName: "Lovelace",
|
||||
department: "ENGINEERING",
|
||||
teamId: validTeamId,
|
||||
email: "ada@example.test",
|
||||
phone: null,
|
||||
})
|
||||
|
||||
@@ -10,6 +10,7 @@ const mocks = vi.hoisted(() => ({
|
||||
getI18n: vi.fn(),
|
||||
findByIdWithUser: vi.fn(),
|
||||
findById: vi.fn(),
|
||||
listTeamsUseCase: vi.fn(),
|
||||
personForm: vi.fn(),
|
||||
push: vi.fn(),
|
||||
toastError: vi.fn(),
|
||||
@@ -27,6 +28,10 @@ vi.mock("@/services/person.service", () => ({
|
||||
},
|
||||
}))
|
||||
|
||||
vi.mock("@/use-cases/team.use-cases", () => ({
|
||||
listTeamsUseCase: mocks.listTeamsUseCase,
|
||||
}))
|
||||
|
||||
vi.mock("@/app/(dashboard)/people/_components/edit.person.form", () => ({
|
||||
default: (props: unknown) => {
|
||||
mocks.personForm(props)
|
||||
@@ -54,8 +59,8 @@ const basePerson: PersonWithUser = {
|
||||
id: "person-1",
|
||||
firstName: "Ada",
|
||||
lastName: "Lovelace",
|
||||
department: "ENGINEERING",
|
||||
teamId: null,
|
||||
team: null,
|
||||
email: "ada@example.test",
|
||||
phone: "1234",
|
||||
userId: null,
|
||||
@@ -80,10 +85,16 @@ const personWithUser: PersonWithUser = {
|
||||
},
|
||||
}
|
||||
|
||||
const teams = [
|
||||
{ id: "team-1", name: "Engineering" },
|
||||
{ id: "team-2", name: "Sales" },
|
||||
]
|
||||
|
||||
describe("edit person page wiring", () => {
|
||||
beforeEach(() => {
|
||||
vi.clearAllMocks()
|
||||
mocks.getI18n.mockResolvedValue({ dictionary: en, locale: "en" })
|
||||
mocks.listTeamsUseCase.mockResolvedValue(teams)
|
||||
})
|
||||
|
||||
it("loads the person without user, passes PersonWithoutUser to the edit form", async () => {
|
||||
@@ -112,6 +123,7 @@ describe("edit person page wiring", () => {
|
||||
...en.inventory.people.schema,
|
||||
},
|
||||
roleLabels: en.admin.users.roles,
|
||||
teams,
|
||||
}),
|
||||
)
|
||||
})
|
||||
@@ -142,10 +154,7 @@ describe("edit person page wiring", () => {
|
||||
}),
|
||||
formCopy: es.admin.users.form,
|
||||
roleLabels: es.admin.users.roles,
|
||||
departmentCopy: es.inventory.people.departments,
|
||||
fallbackCopy: expect.objectContaining({
|
||||
unknownDepartment: es.inventory.people.fallback.unknownDepartment,
|
||||
}),
|
||||
teams,
|
||||
}),
|
||||
)
|
||||
})
|
||||
|
||||
@@ -7,6 +7,7 @@ const mocks = vi.hoisted(() => ({
|
||||
getI18n: vi.fn(),
|
||||
findByIdWithUser: vi.fn(),
|
||||
findById: vi.fn(),
|
||||
listTeamsUseCase: vi.fn(),
|
||||
redirect: vi.fn(),
|
||||
personForm: vi.fn(),
|
||||
}))
|
||||
@@ -22,6 +23,10 @@ vi.mock("@/services/person.service", () => ({
|
||||
},
|
||||
}))
|
||||
|
||||
vi.mock("@/use-cases/team.use-cases", () => ({
|
||||
listTeamsUseCase: mocks.listTeamsUseCase,
|
||||
}))
|
||||
|
||||
vi.mock("next/navigation", () => ({
|
||||
redirect: mocks.redirect,
|
||||
useRouter: () => ({
|
||||
@@ -49,10 +54,16 @@ vi.mock("sonner", () => ({
|
||||
},
|
||||
}))
|
||||
|
||||
const teams = [
|
||||
{ id: "team-1", name: "Engineering" },
|
||||
{ id: "team-2", name: "Sales" },
|
||||
]
|
||||
|
||||
describe("person pages", () => {
|
||||
beforeEach(() => {
|
||||
vi.clearAllMocks()
|
||||
mocks.getI18n.mockResolvedValue({ dictionary: es, locale: "es" })
|
||||
mocks.listTeamsUseCase.mockResolvedValue(teams)
|
||||
})
|
||||
|
||||
it("renders the edit person page with Person heading and passes person to unified form", async () => {
|
||||
@@ -66,7 +77,8 @@ describe("person pages", () => {
|
||||
lastName: "Lovelace",
|
||||
email: "ada@example.test",
|
||||
phone: "1234",
|
||||
department: "ENGINEERING",
|
||||
teamId: "team-1",
|
||||
team: { id: "team-1", name: "Engineering" },
|
||||
userId: null,
|
||||
isActive: true,
|
||||
createdAt: new Date("2024-01-01"),
|
||||
@@ -88,6 +100,7 @@ describe("person pages", () => {
|
||||
firstName: "Ada",
|
||||
lastName: "Lovelace",
|
||||
}),
|
||||
teams,
|
||||
}),
|
||||
)
|
||||
})
|
||||
|
||||
@@ -67,7 +67,8 @@ describe("person pages", () => {
|
||||
lastName: "Lovelace",
|
||||
email: "ada@example.test",
|
||||
phone: "1234",
|
||||
department: "ENGINEERING",
|
||||
teamId: "team-1",
|
||||
team: { id: "team-1", name: "Engineering" },
|
||||
userId: null,
|
||||
isActive: true,
|
||||
createdAt: new Date("2024-01-01"),
|
||||
@@ -87,7 +88,7 @@ describe("person pages", () => {
|
||||
expect(html).toContain("Add Person")
|
||||
// No username column — username header must not appear
|
||||
expect(html).not.toContain("Username")
|
||||
// No standalone username cell — only name, email, phone, department columns
|
||||
// No standalone username cell — only name, email, phone, team columns
|
||||
expect(html).not.toContain(">ada<")
|
||||
// Name and other fields rendered
|
||||
expect(html).toContain("Ada Lovelace")
|
||||
@@ -110,7 +111,8 @@ describe("person pages", () => {
|
||||
lastName: "Lovelace",
|
||||
email: "ada@example.test",
|
||||
phone: "1234",
|
||||
department: "ENGINEERING",
|
||||
teamId: "team-1",
|
||||
team: { id: "team-1", name: "Engineering" },
|
||||
userId: "user-1",
|
||||
isActive: true,
|
||||
createdAt: new Date("2024-01-01"),
|
||||
@@ -137,7 +139,8 @@ describe("person pages", () => {
|
||||
lastName: "Jones",
|
||||
email: "bob@example.test",
|
||||
phone: null,
|
||||
department: "IT",
|
||||
teamId: null,
|
||||
team: null,
|
||||
userId: null,
|
||||
isActive: true,
|
||||
createdAt: new Date("2024-01-01"),
|
||||
@@ -193,7 +196,8 @@ describe("person pages", () => {
|
||||
lastName: "Lovelace",
|
||||
email: "ada@example.test",
|
||||
phone: "1234",
|
||||
department: "DRIVER",
|
||||
teamId: "team-2",
|
||||
team: { id: "team-2", name: "Driver" },
|
||||
userId: null,
|
||||
isActive: true,
|
||||
createdAt: new Date("2024-01-01"),
|
||||
@@ -221,7 +225,7 @@ describe("person pages", () => {
|
||||
// Person detail fields
|
||||
expect(html).toContain("Email")
|
||||
expect(html).toContain("Phone")
|
||||
expect(html).toContain("Department")
|
||||
expect(html).toContain("Team")
|
||||
expect(html).toContain("ada@example.test")
|
||||
expect(html).toContain("Driver")
|
||||
// Embedded assignments
|
||||
@@ -239,7 +243,8 @@ describe("person pages", () => {
|
||||
lastName: "Lovelace",
|
||||
email: "ada@example.test",
|
||||
phone: "1234",
|
||||
department: "DRIVER",
|
||||
teamId: null,
|
||||
team: null,
|
||||
userId: "user-1",
|
||||
isActive: true,
|
||||
createdAt: new Date("2024-01-01"),
|
||||
@@ -287,7 +292,8 @@ describe("person pages", () => {
|
||||
lastName: "Lovelace",
|
||||
email: "ada@example.test",
|
||||
phone: "1234",
|
||||
department: "DRIVER",
|
||||
teamId: null,
|
||||
team: null,
|
||||
userId: null,
|
||||
isActive: true,
|
||||
createdAt: new Date("2024-01-01"),
|
||||
|
||||
@@ -7,6 +7,7 @@ import { es } from "@/i18n/dictionaries/es"
|
||||
const mocks = vi.hoisted(() => ({
|
||||
createPersonUser: vi.fn(),
|
||||
getI18n: vi.fn(),
|
||||
listTeamsUseCase: vi.fn(),
|
||||
push: vi.fn(),
|
||||
toastError: vi.fn(),
|
||||
toastSuccess: vi.fn(),
|
||||
@@ -26,6 +27,10 @@ vi.mock("@/services/person.service", () => ({
|
||||
},
|
||||
}))
|
||||
|
||||
vi.mock("@/use-cases/team.use-cases", () => ({
|
||||
listTeamsUseCase: mocks.listTeamsUseCase,
|
||||
}))
|
||||
|
||||
vi.mock("next/navigation", () => ({
|
||||
useRouter: () => ({
|
||||
push: mocks.push,
|
||||
@@ -39,10 +44,16 @@ vi.mock("sonner", () => ({
|
||||
},
|
||||
}))
|
||||
|
||||
const teams = [
|
||||
{ id: "team-1", name: "Engineering" },
|
||||
{ id: "team-2", name: "Sales" },
|
||||
]
|
||||
|
||||
describe("unified creation form page", () => {
|
||||
beforeEach(() => {
|
||||
vi.clearAllMocks()
|
||||
mocks.getI18n.mockResolvedValue({ dictionary: es, locale: "es" })
|
||||
mocks.listTeamsUseCase.mockResolvedValue(teams)
|
||||
})
|
||||
|
||||
it("renders unified form with Person fields, email, password, role, and NO_USER option in Spanish", async () => {
|
||||
@@ -55,7 +66,7 @@ describe("unified creation form page", () => {
|
||||
// Person fields
|
||||
expect(html).toContain("Nombre")
|
||||
expect(html).toContain("Apellido")
|
||||
expect(html).toContain("Departamento")
|
||||
expect(html).toContain("Equipo")
|
||||
expect(html).toContain("Teléfono")
|
||||
|
||||
// User fields
|
||||
@@ -86,7 +97,7 @@ describe("unified creation form page", () => {
|
||||
// Person fields
|
||||
expect(html).toContain("First Name")
|
||||
expect(html).toContain("Last Name")
|
||||
expect(html).toContain("Department")
|
||||
expect(html).toContain("Team")
|
||||
expect(html).toContain("Phone")
|
||||
|
||||
// User fields
|
||||
@@ -108,18 +119,20 @@ describe("unified creation form page", () => {
|
||||
// Person field placeholders
|
||||
expect(html).toContain('placeholder="Nombre"') // firstNamePlaceholder (es)
|
||||
expect(html).toContain('placeholder="Apellido"') // lastNamePlaceholder (es)
|
||||
expect(html).toContain("Selecciona un departamento") // departmentPlaceholder
|
||||
expect(html).toContain("Selecciona un equipo") // teamPlaceholder
|
||||
expect(html).toContain('placeholder="Teléfono"') // phonePlaceholder (es)
|
||||
})
|
||||
|
||||
it("renders department select with all PERSON_DEPARTMENTS values", async () => {
|
||||
it("renders team select with active teams from listTeamsUseCase", async () => {
|
||||
const { default: NewUserPage } = await import(
|
||||
"@/app/(dashboard)/people/new/page"
|
||||
)
|
||||
|
||||
const html = renderToStaticMarkup(await NewUserPage())
|
||||
|
||||
// Department values must use canonical enum values
|
||||
expect(html).toContain('value="ADMINISTRATION"')
|
||||
expect(html).toContain('value="team-1"')
|
||||
expect(html).toContain("Engineering")
|
||||
expect(html).toContain('value="team-2"')
|
||||
expect(html).toContain("Sales")
|
||||
})
|
||||
})
|
||||
|
||||
@@ -1,9 +1,6 @@
|
||||
import { describe, expect, it } from "vitest"
|
||||
|
||||
import {
|
||||
formatPersonDepartment,
|
||||
formatUserRole,
|
||||
} from "@/app/(dashboard)/people/_components/user.copy"
|
||||
import { formatUserRole } from "@/app/(dashboard)/people/_components/user.copy"
|
||||
|
||||
describe("user copy helpers", () => {
|
||||
const roleCopy = {
|
||||
@@ -37,48 +34,3 @@ describe("user copy helpers", () => {
|
||||
).toBe("Rol desconocido")
|
||||
})
|
||||
})
|
||||
|
||||
describe("formatPersonDepartment helper", () => {
|
||||
const departmentCopy = {
|
||||
IT: "IT",
|
||||
ENGINEERING: "Ingeniería",
|
||||
LOGISTICS: "Logística",
|
||||
TRAFFIC: "Tráfico",
|
||||
DRIVER: "Chofer",
|
||||
ADMINISTRATION: "Administración",
|
||||
SALES: "Ventas",
|
||||
OTHER: "Otro",
|
||||
}
|
||||
|
||||
const fallbackCopy = {
|
||||
unknownDepartment: "Departamento desconocido",
|
||||
unknownStatus: "Estado desconocido",
|
||||
}
|
||||
|
||||
it("formats known department values with localized labels", () => {
|
||||
expect(
|
||||
formatPersonDepartment("ENGINEERING", departmentCopy, fallbackCopy),
|
||||
).toBe("Ingeniería")
|
||||
expect(
|
||||
formatPersonDepartment("ADMINISTRATION", departmentCopy, fallbackCopy),
|
||||
).toBe("Administración")
|
||||
})
|
||||
|
||||
it("falls back for unknown department values", () => {
|
||||
expect(
|
||||
formatPersonDepartment("UNKNOWN_DEPT", departmentCopy, fallbackCopy),
|
||||
).toBe("Departamento desconocido")
|
||||
})
|
||||
|
||||
it("falls back for null department values", () => {
|
||||
expect(formatPersonDepartment(null, departmentCopy, fallbackCopy)).toBe(
|
||||
"Departamento desconocido",
|
||||
)
|
||||
})
|
||||
|
||||
it("falls back for undefined department values", () => {
|
||||
expect(
|
||||
formatPersonDepartment(undefined, departmentCopy, fallbackCopy),
|
||||
).toBe("Departamento desconocido")
|
||||
})
|
||||
})
|
||||
|
||||
@@ -32,8 +32,8 @@ describe("admin users dictionary", () => {
|
||||
firstNamePlaceholder: "First name",
|
||||
lastNameLabel: "Last Name",
|
||||
lastNamePlaceholder: "Last name",
|
||||
departmentLabel: "Department",
|
||||
departmentPlaceholder: "Select a department",
|
||||
teamLabel: "Team",
|
||||
teamPlaceholder: "Select a team",
|
||||
emailLabel: "Email",
|
||||
emailPlaceholder: "user@example.com",
|
||||
phoneLabel: "Phone",
|
||||
@@ -127,8 +127,8 @@ describe("admin users dictionary", () => {
|
||||
firstNamePlaceholder: "Nombre",
|
||||
lastNameLabel: "Apellido",
|
||||
lastNamePlaceholder: "Apellido",
|
||||
departmentLabel: "Departamento",
|
||||
departmentPlaceholder: "Selecciona un departamento",
|
||||
teamLabel: "Equipo",
|
||||
teamPlaceholder: "Selecciona un equipo",
|
||||
emailLabel: "Correo electrónico",
|
||||
emailPlaceholder: "usuario@ejemplo.com",
|
||||
phoneLabel: "Teléfono",
|
||||
|
||||
@@ -832,7 +832,7 @@ describe("i18n dictionaries", () => {
|
||||
name: "Name",
|
||||
email: "Email",
|
||||
phone: "Phone",
|
||||
department: "Department",
|
||||
team: "Team",
|
||||
role: "Role",
|
||||
status: "Status",
|
||||
actions: "Actions",
|
||||
@@ -847,7 +847,7 @@ describe("i18n dictionaries", () => {
|
||||
labels: {
|
||||
email: "Email",
|
||||
phone: "Phone",
|
||||
department: "Department",
|
||||
team: "Team",
|
||||
role: "Role",
|
||||
status: "Status",
|
||||
noUser: "No user account",
|
||||
@@ -865,8 +865,8 @@ describe("i18n dictionaries", () => {
|
||||
firstNamePlaceholder: "First name",
|
||||
lastNameLabel: "Last Name",
|
||||
lastNamePlaceholder: "Last name",
|
||||
departmentLabel: "Department",
|
||||
departmentPlaceholder: "Select a department",
|
||||
teamLabel: "Team",
|
||||
teamPlaceholder: "Select a team",
|
||||
emailLabel: "Email",
|
||||
emailPlaceholder: "Email",
|
||||
phoneLabel: "Phone",
|
||||
@@ -880,19 +880,9 @@ describe("i18n dictionaries", () => {
|
||||
updateSubmit: "Update Person",
|
||||
},
|
||||
fallback: {
|
||||
unknownDepartment: "Unknown department",
|
||||
noTeam: "—",
|
||||
unknownStatus: "Unknown status",
|
||||
},
|
||||
departments: {
|
||||
IT: "IT",
|
||||
ENGINEERING: "Engineering",
|
||||
LOGISTICS: "Logistics",
|
||||
TRAFFIC: "Traffic",
|
||||
DRIVER: "Driver",
|
||||
ADMINISTRATION: "Administration",
|
||||
SALES: "Sales",
|
||||
OTHER: "Other",
|
||||
},
|
||||
actions: {
|
||||
createSuccess: "Person created successfully",
|
||||
createFailure: "Failed to create person",
|
||||
@@ -900,14 +890,15 @@ describe("i18n dictionaries", () => {
|
||||
updateFailure: "Failed to update person",
|
||||
duplicateEmail: "Email already exists",
|
||||
notFound: "Person not found",
|
||||
teamNotFound: "Team not found",
|
||||
},
|
||||
schema: {
|
||||
firstNameRequired: "First name is required",
|
||||
lastNameRequired: "Last name is required",
|
||||
departmentRequired: "Department is required",
|
||||
emailInvalid: "Email format is invalid",
|
||||
idRequired: "ID is required",
|
||||
userIdInvalid: "User ID must be a valid UUID",
|
||||
teamIdInvalid: "Team must be a valid id",
|
||||
},
|
||||
})
|
||||
|
||||
@@ -920,7 +911,7 @@ describe("i18n dictionaries", () => {
|
||||
name: "Nombre",
|
||||
email: "Correo electrónico",
|
||||
phone: "Teléfono",
|
||||
department: "Departamento",
|
||||
team: "Equipo",
|
||||
role: "Rol",
|
||||
status: "Estado",
|
||||
actions: "Acciones",
|
||||
@@ -935,7 +926,7 @@ describe("i18n dictionaries", () => {
|
||||
labels: {
|
||||
email: "Correo electrónico",
|
||||
phone: "Teléfono",
|
||||
department: "Departamento",
|
||||
team: "Equipo",
|
||||
role: "Rol",
|
||||
status: "Estado",
|
||||
noUser: "Sin cuenta de usuario",
|
||||
@@ -953,8 +944,8 @@ describe("i18n dictionaries", () => {
|
||||
firstNamePlaceholder: "Nombre",
|
||||
lastNameLabel: "Apellido",
|
||||
lastNamePlaceholder: "Apellido",
|
||||
departmentLabel: "Departamento",
|
||||
departmentPlaceholder: "Selecciona un departamento",
|
||||
teamLabel: "Equipo",
|
||||
teamPlaceholder: "Selecciona un equipo",
|
||||
emailLabel: "Correo electrónico",
|
||||
emailPlaceholder: "Correo electrónico",
|
||||
phoneLabel: "Teléfono",
|
||||
@@ -969,19 +960,9 @@ describe("i18n dictionaries", () => {
|
||||
updateSubmit: "Actualizar persona",
|
||||
},
|
||||
fallback: {
|
||||
unknownDepartment: "Departamento desconocido",
|
||||
noTeam: "—",
|
||||
unknownStatus: "Estado desconocido",
|
||||
},
|
||||
departments: {
|
||||
IT: "IT",
|
||||
ENGINEERING: "Ingeniería",
|
||||
LOGISTICS: "Logística",
|
||||
TRAFFIC: "Tráfico",
|
||||
DRIVER: "Chofer",
|
||||
ADMINISTRATION: "Administración",
|
||||
SALES: "Ventas",
|
||||
OTHER: "Otro",
|
||||
},
|
||||
actions: {
|
||||
createSuccess: "Persona creada correctamente",
|
||||
createFailure: "Error al crear la persona",
|
||||
@@ -989,14 +970,15 @@ describe("i18n dictionaries", () => {
|
||||
updateFailure: "Error al actualizar la persona",
|
||||
duplicateEmail: "El correo electrónico ya existe",
|
||||
notFound: "Persona no encontrada",
|
||||
teamNotFound: "Equipo no encontrado",
|
||||
},
|
||||
schema: {
|
||||
firstNameRequired: "El nombre es obligatorio",
|
||||
lastNameRequired: "El apellido es obligatorio",
|
||||
departmentRequired: "El departamento es obligatorio",
|
||||
emailInvalid: "El correo electrónico no es válido",
|
||||
idRequired: "El ID es obligatorio",
|
||||
userIdInvalid: "El ID de usuario debe ser un UUID válido",
|
||||
teamIdInvalid: "El equipo debe ser un id válido",
|
||||
},
|
||||
})
|
||||
})
|
||||
|
||||
@@ -10,8 +10,8 @@ describe("admin users unified form dictionary", () => {
|
||||
expect(form.firstNamePlaceholder).toBe("First name")
|
||||
expect(form.lastNameLabel).toBe("Last Name")
|
||||
expect(form.lastNamePlaceholder).toBe("Last name")
|
||||
expect(form.departmentLabel).toBe("Department")
|
||||
expect(form.departmentPlaceholder).toBe("Select a department")
|
||||
expect(form.teamLabel).toBe("Team")
|
||||
expect(form.teamPlaceholder).toBe("Select a team")
|
||||
expect(form.phoneLabel).toBe("Phone")
|
||||
expect(form.phonePlaceholder).toBe("Phone")
|
||||
})
|
||||
@@ -23,8 +23,8 @@ describe("admin users unified form dictionary", () => {
|
||||
expect(form.firstNamePlaceholder).toBe("Nombre")
|
||||
expect(form.lastNameLabel).toBe("Apellido")
|
||||
expect(form.lastNamePlaceholder).toBe("Apellido")
|
||||
expect(form.departmentLabel).toBe("Departamento")
|
||||
expect(form.departmentPlaceholder).toBe("Selecciona un departamento")
|
||||
expect(form.teamLabel).toBe("Equipo")
|
||||
expect(form.teamPlaceholder).toBe("Selecciona un equipo")
|
||||
expect(form.phoneLabel).toBe("Teléfono")
|
||||
expect(form.phonePlaceholder).toBe("Teléfono")
|
||||
})
|
||||
|
||||
@@ -125,7 +125,7 @@ describe("core schemas", () => {
|
||||
createPersonSchema.safeParse({
|
||||
firstName: "Per",
|
||||
lastName: "Son",
|
||||
department: "IT",
|
||||
teamId: null,
|
||||
email: "person@example.test",
|
||||
}).success,
|
||||
).toBe(true)
|
||||
@@ -134,7 +134,7 @@ describe("core schemas", () => {
|
||||
createPersonSchema.safeParse({
|
||||
firstName: "Per",
|
||||
lastName: "Son",
|
||||
department: "IT",
|
||||
teamId: null,
|
||||
email: "not-an-email",
|
||||
}).success,
|
||||
).toBe(false)
|
||||
@@ -143,7 +143,7 @@ describe("core schemas", () => {
|
||||
createPersonSchema.safeParse({
|
||||
firstName: "Per",
|
||||
lastName: "Son",
|
||||
department: "IT",
|
||||
teamId: null,
|
||||
email: "",
|
||||
}).success,
|
||||
).toBe(true)
|
||||
|
||||
@@ -8,18 +8,20 @@ import {
|
||||
const schemaCopy = {
|
||||
firstNameRequired: "El nombre es obligatorio",
|
||||
lastNameRequired: "El apellido es obligatorio",
|
||||
departmentRequired: "El departamento es obligatorio",
|
||||
emailInvalid: "El correo electrónico no es válido",
|
||||
idRequired: "El ID es obligatorio",
|
||||
userIdInvalid: "El ID de usuario debe ser un UUID válido",
|
||||
teamIdInvalid: "El equipo debe ser un id válido",
|
||||
}
|
||||
|
||||
const validTeamId = "550e8400-e29b-41d4-a716-446655440000"
|
||||
|
||||
describe("person schema validation", () => {
|
||||
it("uses localized required-field validation messages for create (no username)", () => {
|
||||
const result = buildCreatePersonSchema(schemaCopy).safeParse({
|
||||
firstName: "",
|
||||
lastName: "",
|
||||
department: "",
|
||||
teamId: null,
|
||||
})
|
||||
|
||||
expect(result.success).toBe(false)
|
||||
@@ -28,7 +30,6 @@ describe("person schema validation", () => {
|
||||
|
||||
expect(errors.firstName).toContain(schemaCopy.firstNameRequired)
|
||||
expect(errors.lastName).toContain(schemaCopy.lastNameRequired)
|
||||
expect(errors.department).toContain(schemaCopy.departmentRequired)
|
||||
}
|
||||
})
|
||||
|
||||
@@ -36,7 +37,7 @@ describe("person schema validation", () => {
|
||||
const result = buildCreatePersonSchema(schemaCopy).safeParse({
|
||||
firstName: "Ada",
|
||||
lastName: "Lovelace",
|
||||
department: "ENGINEERING",
|
||||
teamId: null,
|
||||
email: "not-an-email",
|
||||
})
|
||||
|
||||
@@ -52,7 +53,7 @@ describe("person schema validation", () => {
|
||||
const result = buildCreatePersonSchema(schemaCopy).safeParse({
|
||||
firstName: "Ada",
|
||||
lastName: "Lovelace",
|
||||
department: "ENGINEERING",
|
||||
teamId: null,
|
||||
userId: "not-a-uuid",
|
||||
})
|
||||
|
||||
@@ -64,12 +65,27 @@ describe("person schema validation", () => {
|
||||
}
|
||||
})
|
||||
|
||||
it("rejects an invalid teamId", () => {
|
||||
const result = buildCreatePersonSchema(schemaCopy).safeParse({
|
||||
firstName: "Ada",
|
||||
lastName: "Lovelace",
|
||||
teamId: "not-a-uuid",
|
||||
})
|
||||
|
||||
expect(result.success).toBe(false)
|
||||
if (!result.success) {
|
||||
expect(result.error.flatten().fieldErrors.teamId).toContain(
|
||||
schemaCopy.teamIdInvalid,
|
||||
)
|
||||
}
|
||||
})
|
||||
|
||||
it("uses localized update identifier validation messages", () => {
|
||||
const result = buildUpdatePersonSchema(schemaCopy).safeParse({
|
||||
id: "",
|
||||
firstName: "Ada",
|
||||
lastName: "Lovelace",
|
||||
department: "ENGINEERING",
|
||||
teamId: null,
|
||||
email: "ada@example.test",
|
||||
})
|
||||
|
||||
@@ -81,20 +97,20 @@ describe("person schema validation", () => {
|
||||
}
|
||||
})
|
||||
|
||||
it("preserves canonical department values and accepts optional userId UUID", () => {
|
||||
it("accepts a valid teamId UUID and optional userId UUID", () => {
|
||||
const result = buildCreatePersonSchema(schemaCopy).safeParse({
|
||||
firstName: "Ada",
|
||||
lastName: "Lovelace",
|
||||
department: "ENGINEERING",
|
||||
teamId: validTeamId,
|
||||
email: "",
|
||||
userId: "550e8400-e29b-41d4-a716-446655440000",
|
||||
userId: validTeamId,
|
||||
})
|
||||
|
||||
expect(result.success).toBe(true)
|
||||
if (result.success) {
|
||||
expect(result.data.department).toBe("ENGINEERING")
|
||||
expect(result.data.teamId).toBe(validTeamId)
|
||||
expect(result.data.email).toBe("")
|
||||
expect(result.data.userId).toBe("550e8400-e29b-41d4-a716-446655440000")
|
||||
expect(result.data.userId).toBe(validTeamId)
|
||||
}
|
||||
})
|
||||
|
||||
@@ -102,7 +118,7 @@ describe("person schema validation", () => {
|
||||
const result = buildCreatePersonSchema(schemaCopy).safeParse({
|
||||
firstName: "Ada",
|
||||
lastName: "Lovelace",
|
||||
department: "ENGINEERING",
|
||||
teamId: null,
|
||||
})
|
||||
|
||||
expect(result.success).toBe(true)
|
||||
|
||||
@@ -6,34 +6,36 @@ import {
|
||||
unifiedFormRoleSchema,
|
||||
} from "@/schemas/user.schema"
|
||||
|
||||
const validTeamId = "550e8400-e29b-41d4-a716-446655440000"
|
||||
|
||||
const enCopy: UnifiedSchemaCopy = {
|
||||
firstNameRequired: "First name is required",
|
||||
lastNameRequired: "Last name is required",
|
||||
departmentRequired: "Department is required",
|
||||
emailInvalid: "Invalid email",
|
||||
passwordMinLength: "Password must be at least 8 characters",
|
||||
nameRequired: "Name is required",
|
||||
userIdRequired: "User id is required",
|
||||
idRequired: "ID is required",
|
||||
userIdInvalid: "User ID must be a valid UUID",
|
||||
teamIdInvalid: "Team must be a valid id",
|
||||
}
|
||||
|
||||
const esCopy: UnifiedSchemaCopy = {
|
||||
firstNameRequired: "El nombre es obligatorio",
|
||||
lastNameRequired: "El apellido es obligatorio",
|
||||
departmentRequired: "El departamento es obligatorio",
|
||||
emailInvalid: "Correo electrónico no válido",
|
||||
passwordMinLength: "La contraseña debe tener al menos 8 caracteres",
|
||||
nameRequired: "El nombre es obligatorio",
|
||||
userIdRequired: "El ID de usuario es obligatorio",
|
||||
idRequired: "El ID es obligatorio",
|
||||
userIdInvalid: "El ID de usuario debe ser un UUID válido",
|
||||
teamIdInvalid: "El equipo debe ser un id válido",
|
||||
}
|
||||
|
||||
const validPersonOnlyData = {
|
||||
firstName: "John",
|
||||
lastName: "Doe",
|
||||
department: "IT",
|
||||
teamId: null,
|
||||
email: "john@example.test",
|
||||
phone: null,
|
||||
role: "NO_USER" as const,
|
||||
@@ -44,7 +46,7 @@ const validPersonOnlyData = {
|
||||
const validPersonWithUserData = {
|
||||
firstName: "Jane",
|
||||
lastName: "Smith",
|
||||
department: "ENGINEERING",
|
||||
teamId: validTeamId,
|
||||
email: "jane@example.test",
|
||||
phone: "1234567890",
|
||||
role: "ADMIN" as const,
|
||||
@@ -96,7 +98,7 @@ describe("buildUnifiedCreateSchema", () => {
|
||||
const result = schema.safeParse({
|
||||
firstName: "",
|
||||
lastName: "",
|
||||
department: "",
|
||||
teamId: "not-a-uuid",
|
||||
email: "not-an-email",
|
||||
role: "NO_USER",
|
||||
phone: null,
|
||||
@@ -108,7 +110,7 @@ describe("buildUnifiedCreateSchema", () => {
|
||||
const errors = result.error.flatten().fieldErrors
|
||||
expect(errors.firstName).toContain(esCopy.firstNameRequired)
|
||||
expect(errors.lastName).toContain(esCopy.lastNameRequired)
|
||||
expect(errors.department).toContain(esCopy.departmentRequired)
|
||||
expect(errors.teamId).toContain(esCopy.teamIdInvalid)
|
||||
expect(errors.email).toContain(esCopy.emailInvalid)
|
||||
}
|
||||
})
|
||||
@@ -185,7 +187,7 @@ describe("buildUnifiedCreateSchema", () => {
|
||||
const result = schema.safeParse({
|
||||
firstName: "Jane",
|
||||
lastName: "Smith",
|
||||
department: "ENGINEERING",
|
||||
teamId: validTeamId,
|
||||
email: "jane@example.test",
|
||||
role: "ADMIN",
|
||||
password: "corta",
|
||||
@@ -228,36 +230,35 @@ describe("buildUnifiedCreateSchema", () => {
|
||||
})
|
||||
})
|
||||
|
||||
describe("department validation", () => {
|
||||
it("rejects invalid department", () => {
|
||||
describe("teamId validation", () => {
|
||||
it("rejects invalid teamId", () => {
|
||||
const schema = buildUnifiedCreateSchema(enCopy)
|
||||
const result = schema.safeParse({
|
||||
...validPersonOnlyData,
|
||||
department: "INVALID_DEPT",
|
||||
teamId: "INVALID_TEAM",
|
||||
})
|
||||
|
||||
expect(result.success).toBe(false)
|
||||
})
|
||||
|
||||
it("accepts valid departments", () => {
|
||||
it("accepts null teamId", () => {
|
||||
const schema = buildUnifiedCreateSchema(enCopy)
|
||||
const validDepartments = [
|
||||
"IT",
|
||||
"ENGINEERING",
|
||||
"TRAFFIC",
|
||||
"DRIVER",
|
||||
"LOGISTICS",
|
||||
"ADMINISTRATION",
|
||||
"SALES",
|
||||
"OTHER",
|
||||
]
|
||||
for (const dept of validDepartments) {
|
||||
const result = schema.safeParse({
|
||||
...validPersonOnlyData,
|
||||
department: dept,
|
||||
})
|
||||
expect(result.success).toBe(true)
|
||||
}
|
||||
const result = schema.safeParse({
|
||||
...validPersonOnlyData,
|
||||
teamId: null,
|
||||
})
|
||||
|
||||
expect(result.success).toBe(true)
|
||||
})
|
||||
|
||||
it("accepts a valid teamId UUID", () => {
|
||||
const schema = buildUnifiedCreateSchema(enCopy)
|
||||
const result = schema.safeParse({
|
||||
...validPersonOnlyData,
|
||||
teamId: validTeamId,
|
||||
})
|
||||
|
||||
expect(result.success).toBe(true)
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
@@ -5,35 +5,37 @@ import {
|
||||
type UnifiedSchemaCopy,
|
||||
} from "@/schemas/user.schema"
|
||||
|
||||
const validTeamId = "550e8400-e29b-41d4-a716-446655440000"
|
||||
|
||||
const enCopy: UnifiedSchemaCopy = {
|
||||
firstNameRequired: "First name is required",
|
||||
lastNameRequired: "Last name is required",
|
||||
departmentRequired: "Department is required",
|
||||
emailInvalid: "Invalid email",
|
||||
passwordMinLength: "Password must be at least 8 characters",
|
||||
nameRequired: "Name is required",
|
||||
userIdRequired: "User id is required",
|
||||
idRequired: "ID is required",
|
||||
userIdInvalid: "User ID must be a valid UUID",
|
||||
teamIdInvalid: "Team must be a valid id",
|
||||
}
|
||||
|
||||
const esCopy: UnifiedSchemaCopy = {
|
||||
firstNameRequired: "El nombre es obligatorio",
|
||||
lastNameRequired: "El apellido es obligatorio",
|
||||
departmentRequired: "El departamento es obligatorio",
|
||||
emailInvalid: "Correo electrónico no válido",
|
||||
passwordMinLength: "La contraseña debe tener al menos 8 caracteres",
|
||||
nameRequired: "El nombre es obligatorio",
|
||||
userIdRequired: "El ID de usuario es obligatorio",
|
||||
idRequired: "El ID es obligatorio",
|
||||
userIdInvalid: "El ID de usuario debe ser un UUID válido",
|
||||
teamIdInvalid: "El equipo debe ser un id válido",
|
||||
}
|
||||
|
||||
const validPersonOnly = {
|
||||
id: "person-1",
|
||||
firstName: "John",
|
||||
lastName: "Doe",
|
||||
department: "IT",
|
||||
teamId: null,
|
||||
email: "john@example.test",
|
||||
phone: null,
|
||||
}
|
||||
@@ -73,7 +75,7 @@ describe("buildUnifiedUpdateSchema", () => {
|
||||
id: "",
|
||||
firstName: "John",
|
||||
lastName: "Doe",
|
||||
department: "IT",
|
||||
teamId: null,
|
||||
email: "john@example.test",
|
||||
phone: null,
|
||||
})
|
||||
@@ -85,6 +87,31 @@ describe("buildUnifiedUpdateSchema", () => {
|
||||
)
|
||||
}
|
||||
})
|
||||
|
||||
it("rejects invalid teamId", () => {
|
||||
const schema = buildUnifiedUpdateSchema(enCopy)
|
||||
const result = schema.safeParse({
|
||||
...validPersonOnly,
|
||||
teamId: "not-a-uuid",
|
||||
})
|
||||
|
||||
expect(result.success).toBe(false)
|
||||
if (!result.success) {
|
||||
expect(result.error.flatten().fieldErrors.teamId).toContain(
|
||||
enCopy.teamIdInvalid,
|
||||
)
|
||||
}
|
||||
})
|
||||
|
||||
it("accepts a valid teamId UUID", () => {
|
||||
const schema = buildUnifiedUpdateSchema(enCopy)
|
||||
const result = schema.safeParse({
|
||||
...validPersonOnly,
|
||||
teamId: validTeamId,
|
||||
})
|
||||
|
||||
expect(result.success).toBe(true)
|
||||
})
|
||||
})
|
||||
|
||||
describe("person+user update (when person has User linked)", () => {
|
||||
|
||||
Reference in New Issue
Block a user