feat(i18n): localize inventory assignments UI
This commit is contained in:
@@ -0,0 +1,177 @@
|
||||
import { renderToStaticMarkup } from "react-dom/server"
|
||||
import { beforeEach, describe, expect, it, vi } from "vitest"
|
||||
|
||||
import { es } from "@/i18n/dictionaries/es"
|
||||
|
||||
const mocks = vi.hoisted(() => ({
|
||||
getI18n: vi.fn(),
|
||||
findAllRecipients: vi.fn(),
|
||||
findAllItemsWithStock: vi.fn(),
|
||||
findAllAvailableAssets: vi.fn(),
|
||||
findAllItems: vi.fn(),
|
||||
findItemById: vi.fn(),
|
||||
findAllAssets: vi.fn(),
|
||||
findAssignmentById: vi.fn(),
|
||||
createAssignment: vi.fn(),
|
||||
updateAssignment: vi.fn(),
|
||||
push: vi.fn(),
|
||||
toastError: vi.fn(),
|
||||
toastSuccess: vi.fn(),
|
||||
}))
|
||||
|
||||
vi.mock("@/i18n/server", () => ({
|
||||
getI18n: mocks.getI18n,
|
||||
}))
|
||||
|
||||
vi.mock("@/services/recipient.service", () => ({
|
||||
RecipientService: {
|
||||
findAll: mocks.findAllRecipients,
|
||||
},
|
||||
}))
|
||||
|
||||
vi.mock("@/services/item.service", () => ({
|
||||
ItemService: {
|
||||
findAllWithStock: mocks.findAllItemsWithStock,
|
||||
findAll: mocks.findAllItems,
|
||||
findById: mocks.findItemById,
|
||||
},
|
||||
}))
|
||||
|
||||
vi.mock("@/services/asset.service", () => ({
|
||||
AssetService: {
|
||||
findAllAvailable: mocks.findAllAvailableAssets,
|
||||
findAll: mocks.findAllAssets,
|
||||
},
|
||||
}))
|
||||
|
||||
vi.mock("@/services/assignment.service", () => ({
|
||||
AssignmentService: {
|
||||
findById: mocks.findAssignmentById,
|
||||
},
|
||||
}))
|
||||
|
||||
vi.mock("@/actions/assignment.actions", () => ({
|
||||
createAssignment: mocks.createAssignment,
|
||||
updateAssignment: mocks.updateAssignment,
|
||||
}))
|
||||
|
||||
vi.mock("next/navigation", () => ({
|
||||
useRouter: () => ({
|
||||
push: mocks.push,
|
||||
}),
|
||||
}))
|
||||
|
||||
vi.mock("sonner", () => ({
|
||||
toast: {
|
||||
error: mocks.toastError,
|
||||
success: mocks.toastSuccess,
|
||||
},
|
||||
}))
|
||||
|
||||
describe("assignment form pages localization", () => {
|
||||
beforeEach(() => {
|
||||
vi.clearAllMocks()
|
||||
mocks.getI18n.mockResolvedValue({ dictionary: es, locale: "es" })
|
||||
mocks.findAllRecipients.mockResolvedValue([
|
||||
{
|
||||
id: "recipient-1",
|
||||
firstName: "Ada",
|
||||
lastName: "Lovelace",
|
||||
},
|
||||
])
|
||||
mocks.findAllItemsWithStock.mockResolvedValue([
|
||||
{
|
||||
id: "item-1",
|
||||
name: "Laptop",
|
||||
stock: 5,
|
||||
},
|
||||
])
|
||||
mocks.findAllItems.mockResolvedValue([
|
||||
{
|
||||
id: "item-1",
|
||||
name: "Laptop",
|
||||
stock: 5,
|
||||
},
|
||||
])
|
||||
mocks.findAllAvailableAssets.mockResolvedValue([
|
||||
{
|
||||
id: "asset-1",
|
||||
itemId: "item-1",
|
||||
serialNumber: "SN-001",
|
||||
},
|
||||
])
|
||||
mocks.findAllAssets.mockResolvedValue([
|
||||
{
|
||||
id: "asset-1",
|
||||
itemId: "item-1",
|
||||
serialNumber: "SN-001",
|
||||
},
|
||||
])
|
||||
mocks.findItemById.mockResolvedValue({
|
||||
id: "item-1",
|
||||
name: "Laptop",
|
||||
stock: 5,
|
||||
})
|
||||
})
|
||||
|
||||
it("renders the new assignment page with localized heading and form copy", async () => {
|
||||
const { default: NewAssignmentPage } = await import(
|
||||
"@/app/(dashboard)/assignments/new/page"
|
||||
)
|
||||
|
||||
const html = renderToStaticMarkup(await NewAssignmentPage())
|
||||
|
||||
expect(html).toContain("Nueva asignación")
|
||||
expect(html).toContain("Destinatario")
|
||||
expect(html).toContain(
|
||||
'option value="">Selecciona un destinatario</option>',
|
||||
)
|
||||
expect(html).toContain("Artículo")
|
||||
expect(html).toContain('option value="">Selecciona un artículo</option>')
|
||||
expect(html).toContain("Cantidad")
|
||||
expect(html).toContain('placeholder="1"')
|
||||
expect(html).toContain("Crear asignación")
|
||||
expect(html).toContain("Ada Lovelace")
|
||||
expect(html).toContain("Laptop")
|
||||
})
|
||||
|
||||
it("renders the edit assignment page with localized heading, not-found copy, and submit text", async () => {
|
||||
const { default: EditAssignmentPage } = await import(
|
||||
"@/app/(dashboard)/assignments/[assignmentId]/edit/page"
|
||||
)
|
||||
|
||||
mocks.findAssignmentById.mockResolvedValue({
|
||||
id: "assignment-1",
|
||||
recipientId: "recipient-1",
|
||||
itemId: "item-1",
|
||||
assetId: "asset-1",
|
||||
quantity: 1,
|
||||
})
|
||||
|
||||
const html = renderToStaticMarkup(
|
||||
await EditAssignmentPage({
|
||||
params: Promise.resolve({ assignmentId: "assignment-1" }),
|
||||
}),
|
||||
)
|
||||
|
||||
expect(html).toContain("Editar asignación")
|
||||
expect(html).toContain("Destinatario")
|
||||
expect(html).toContain("Artículo")
|
||||
expect(html).toContain("Activo")
|
||||
expect(html).toContain('option value="">Selecciona un activo</option>')
|
||||
expect(html).toContain("Actualizar asignación")
|
||||
expect(html).toContain("Ada Lovelace")
|
||||
expect(html).toContain("Laptop")
|
||||
expect(html).toContain("SN-001")
|
||||
|
||||
mocks.findAssignmentById.mockResolvedValueOnce(null)
|
||||
|
||||
const notFoundHtml = renderToStaticMarkup(
|
||||
await EditAssignmentPage({
|
||||
params: Promise.resolve({ assignmentId: "missing-assignment" }),
|
||||
}),
|
||||
)
|
||||
|
||||
expect(notFoundHtml).toContain("Asignación no encontrada")
|
||||
})
|
||||
})
|
||||
@@ -0,0 +1,124 @@
|
||||
import { createElement } from "react"
|
||||
import { renderToStaticMarkup } from "react-dom/server"
|
||||
import { beforeEach, describe, expect, it, vi } from "vitest"
|
||||
|
||||
import { es } from "@/i18n/dictionaries/es"
|
||||
|
||||
const mocks = vi.hoisted(() => ({
|
||||
findAllWithRecipientPaginated: vi.fn(),
|
||||
getI18n: vi.fn(),
|
||||
refresh: vi.fn(),
|
||||
returnAssignment: vi.fn(),
|
||||
toastError: vi.fn(),
|
||||
toastSuccess: vi.fn(),
|
||||
}))
|
||||
|
||||
vi.mock("@/i18n/server", () => ({
|
||||
getI18n: mocks.getI18n,
|
||||
}))
|
||||
|
||||
vi.mock("@/services/assignment.service", () => ({
|
||||
AssignmentService: {
|
||||
findAllWithRecipientPaginated: mocks.findAllWithRecipientPaginated,
|
||||
},
|
||||
}))
|
||||
|
||||
vi.mock("@/actions/assignment.actions", () => ({
|
||||
returnAssignment: mocks.returnAssignment,
|
||||
}))
|
||||
|
||||
vi.mock("next/navigation", () => ({
|
||||
useRouter: () => ({
|
||||
refresh: mocks.refresh,
|
||||
}),
|
||||
}))
|
||||
|
||||
vi.mock("sonner", () => ({
|
||||
toast: {
|
||||
error: mocks.toastError,
|
||||
success: mocks.toastSuccess,
|
||||
},
|
||||
}))
|
||||
|
||||
vi.mock("@/components/common/pageheader", () => ({
|
||||
default: ({ title, addLabel }: { title: string; addLabel?: string }) =>
|
||||
createElement(
|
||||
"header",
|
||||
null,
|
||||
[title, addLabel].filter(Boolean).join(" | "),
|
||||
),
|
||||
}))
|
||||
|
||||
vi.mock("@/components/common/pagination", () => ({
|
||||
default: ({ totalPages }: { totalPages: number }) =>
|
||||
createElement("nav", { "aria-label": "Pagination" }, totalPages),
|
||||
}))
|
||||
|
||||
describe("assignment pages localization", () => {
|
||||
beforeEach(() => {
|
||||
vi.clearAllMocks()
|
||||
mocks.getI18n.mockResolvedValue({ dictionary: es, locale: "es" })
|
||||
})
|
||||
|
||||
it("renders the assignment list in Spanish with localized action labels and unchanged assignment data", async () => {
|
||||
const { default: AssignmentsPage } = await import(
|
||||
"@/app/(dashboard)/assignments/page"
|
||||
)
|
||||
|
||||
mocks.findAllWithRecipientPaginated.mockResolvedValue({
|
||||
data: [
|
||||
{
|
||||
id: "assignment-1",
|
||||
quantity: 2,
|
||||
recipient: {
|
||||
id: "recipient-1",
|
||||
firstName: "Ada",
|
||||
lastName: "Lovelace",
|
||||
},
|
||||
item: {
|
||||
id: "item-1",
|
||||
name: "Laptop",
|
||||
},
|
||||
asset: {
|
||||
serialNumber: null,
|
||||
},
|
||||
},
|
||||
],
|
||||
totalPages: 1,
|
||||
})
|
||||
|
||||
const html = renderToStaticMarkup(
|
||||
await AssignmentsPage({ searchParams: Promise.resolve({}) }),
|
||||
)
|
||||
|
||||
expect(html).toContain("Asignaciones")
|
||||
expect(html).toContain("Agregar asignación")
|
||||
expect(html).toContain("Destinatario")
|
||||
expect(html).toContain("Artículo")
|
||||
expect(html).toContain("Número de serie")
|
||||
expect(html).toContain("Cantidad")
|
||||
expect(html).toContain("Acciones")
|
||||
expect(html).toContain("Ada Lovelace")
|
||||
expect(html).toContain("Laptop")
|
||||
expect(html).toContain("No disponible")
|
||||
expect(html).toContain('aria-label="Editar asignación"')
|
||||
expect(html).toContain('aria-label="Devolver asignación"')
|
||||
})
|
||||
|
||||
it("renders the localized assignment empty state when no assignments exist", async () => {
|
||||
const { default: AssignmentsPage } = await import(
|
||||
"@/app/(dashboard)/assignments/page"
|
||||
)
|
||||
|
||||
mocks.findAllWithRecipientPaginated.mockResolvedValue({
|
||||
data: [],
|
||||
totalPages: 0,
|
||||
})
|
||||
|
||||
const html = renderToStaticMarkup(
|
||||
await AssignmentsPage({ searchParams: Promise.resolve({}) }),
|
||||
)
|
||||
|
||||
expect(html).toContain("No se encontraron asignaciones.")
|
||||
})
|
||||
})
|
||||
@@ -104,7 +104,7 @@ describe("recipient pages localization", () => {
|
||||
expect(html).toContain("No se encontraron destinatarios.")
|
||||
})
|
||||
|
||||
it("renders localized recipient-owned detail labels and keeps assignments copy unchanged", async () => {
|
||||
it("renders localized recipient-owned detail labels and localizes the embedded assignments title only", async () => {
|
||||
const { default: RecipientInfoPage } = await import(
|
||||
"@/app/(dashboard)/recipients/[recipientId]/page"
|
||||
)
|
||||
@@ -140,10 +140,10 @@ describe("recipient pages localization", () => {
|
||||
expect(html).toContain("Chofer")
|
||||
expect(html).toContain("ada")
|
||||
expect(html).toContain("ada@example.test")
|
||||
expect(html).toContain("Assignments")
|
||||
expect(html).toContain("Asignaciones")
|
||||
expect(html).toContain("Laptop")
|
||||
expect(html).not.toContain(">DRIVER<")
|
||||
expect(html).not.toContain("Asignaciones")
|
||||
expect(html).not.toContain("Assignments")
|
||||
})
|
||||
|
||||
it("renders a localized recipient detail not-found message", async () => {
|
||||
|
||||
@@ -408,6 +408,90 @@ describe("i18n dictionaries", () => {
|
||||
})
|
||||
})
|
||||
|
||||
it("provides localized assignment copy for English and Spanish", () => {
|
||||
expect(getDictionary("en").inventory.assignments).toEqual({
|
||||
list: {
|
||||
title: "Assignments",
|
||||
addLabel: "Add Assignment",
|
||||
empty: "No assignments found.",
|
||||
columns: {
|
||||
recipient: "Recipient",
|
||||
item: "Item",
|
||||
serialNumber: "Serial Number",
|
||||
quantity: "Quantity",
|
||||
actions: "Actions",
|
||||
},
|
||||
actions: {
|
||||
edit: "Edit assignment",
|
||||
return: "Return assignment",
|
||||
},
|
||||
},
|
||||
new: {
|
||||
title: "New Assignment",
|
||||
},
|
||||
edit: {
|
||||
title: "Edit Assignment",
|
||||
notFound: "Assignment not found",
|
||||
},
|
||||
form: {
|
||||
recipientLabel: "Recipient",
|
||||
recipientPlaceholder: "Select a recipient",
|
||||
itemLabel: "Item",
|
||||
itemPlaceholder: "Select an item",
|
||||
assetLabel: "Asset",
|
||||
assetPlaceholder: "Select an asset",
|
||||
quantityLabel: "Quantity",
|
||||
quantityPlaceholder: "1",
|
||||
createSubmit: "Create Assignment",
|
||||
updateSubmit: "Update Assignment",
|
||||
},
|
||||
fallback: {
|
||||
missingValue: "N/A",
|
||||
},
|
||||
})
|
||||
|
||||
expect(getDictionary("es").inventory.assignments).toEqual({
|
||||
list: {
|
||||
title: "Asignaciones",
|
||||
addLabel: "Agregar asignación",
|
||||
empty: "No se encontraron asignaciones.",
|
||||
columns: {
|
||||
recipient: "Destinatario",
|
||||
item: "Artículo",
|
||||
serialNumber: "Número de serie",
|
||||
quantity: "Cantidad",
|
||||
actions: "Acciones",
|
||||
},
|
||||
actions: {
|
||||
edit: "Editar asignación",
|
||||
return: "Devolver asignación",
|
||||
},
|
||||
},
|
||||
new: {
|
||||
title: "Nueva asignación",
|
||||
},
|
||||
edit: {
|
||||
title: "Editar asignación",
|
||||
notFound: "Asignación no encontrada",
|
||||
},
|
||||
form: {
|
||||
recipientLabel: "Destinatario",
|
||||
recipientPlaceholder: "Selecciona un destinatario",
|
||||
itemLabel: "Artículo",
|
||||
itemPlaceholder: "Selecciona un artículo",
|
||||
assetLabel: "Activo",
|
||||
assetPlaceholder: "Selecciona un activo",
|
||||
quantityLabel: "Cantidad",
|
||||
quantityPlaceholder: "1",
|
||||
createSubmit: "Crear asignación",
|
||||
updateSubmit: "Actualizar asignación",
|
||||
},
|
||||
fallback: {
|
||||
missingValue: "No disponible",
|
||||
},
|
||||
})
|
||||
})
|
||||
|
||||
it("provides localized inventory asset UI copy for English and Spanish", () => {
|
||||
expect(getDictionary("en").inventory.assets).toEqual({
|
||||
list: {
|
||||
|
||||
Reference in New Issue
Block a user