test(people): update tests for teamId and add people e2e spec

This commit is contained in:
2026-06-26 01:29:04 +02:00
parent e3434d9c58
commit fadff5251f
22 changed files with 550 additions and 233 deletions
+8 -5
View File
@@ -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,
}),
)
})
+14 -8
View File
@@ -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 -49
View File
@@ -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",
+14 -32
View File
@@ -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")
})
+3 -3
View File
@@ -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)
+28 -12
View File
@@ -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)", () => {