Files
stock-manager/tests/unit/i18n/dictionaries.test.ts
T

327 lines
9.1 KiB
TypeScript

import { describe, expect, it } from "vitest"
import { dictionaries, getDictionary } from "@/i18n/dictionaries"
import { SUPPORTED_LOCALES } from "@/i18n/locales"
describe("i18n dictionaries", () => {
it("provides dictionaries for every supported locale and no extra locales", () => {
expect(Object.keys(dictionaries).sort()).toEqual(
[...SUPPORTED_LOCALES].sort(),
)
})
it("returns localized login copy for English and Spanish", () => {
expect(getDictionary("en").login).toEqual({
title: "Sign In",
usernameLabel: "Username",
passwordLabel: "Password",
submitLabel: "Sign In",
})
expect(getDictionary("es").login).toEqual({
title: "Iniciar sesión",
usernameLabel: "Usuario",
passwordLabel: "Contraseña",
submitLabel: "Iniciar sesión",
})
})
it("provides localized language switcher copy for English and Spanish", () => {
expect(getDictionary("en").common.languageSwitcher).toEqual({
label: "Language",
options: {
en: "English",
es: "Spanish",
},
})
expect(getDictionary("es").common.languageSwitcher).toEqual({
label: "Idioma",
options: {
en: "Inglés",
es: "Español",
},
})
})
it("provides localized shell and common copy for English and Spanish", () => {
expect(getDictionary("en").layout).toEqual({
sidebar: {
home: "Home",
inventory: "Inventory",
items: "Items",
categories: "Categories",
assets: "Assets",
recipients: "Recipients",
movements: "Movements",
assignments: "Assignments",
users: "Users",
},
navbar: {
accountLabel: "My Account",
},
addMenu: {
add: "Add",
import: "Import",
category: "Category",
item: "Item",
asset: "Asset",
recipient: "Recipient",
assignment: "Assignment",
},
resetDatabase: {
idle: "Reset Database",
loading: "Resetting...",
successToast: "Database reset successfully",
errorToast: "Error resetting database",
},
logout: {
label: "Sign Out",
},
})
expect(getDictionary("es").layout).toEqual({
sidebar: {
home: "Inicio",
inventory: "Inventario",
items: "Artículos",
categories: "Categorías",
assets: "Activos",
recipients: "Destinatarios",
movements: "Movimientos",
assignments: "Asignaciones",
users: "Usuarios",
},
navbar: {
accountLabel: "Mi cuenta",
},
addMenu: {
add: "Añadir",
import: "Importar",
category: "Categoría",
item: "Artículo",
asset: "Activo",
recipient: "Destinatario",
assignment: "Asignación",
},
resetDatabase: {
idle: "Reiniciar base de datos",
loading: "Reiniciando...",
successToast: "Base de datos reiniciada correctamente",
errorToast: "Error al reiniciar la base de datos",
},
logout: {
label: "Cerrar sesión",
},
})
expect(getDictionary("en").common.search).toEqual({
placeholder: "Search...",
label: "Search",
clearLabel: "Clear search",
})
expect(getDictionary("es").common.search).toEqual({
placeholder: "Buscar...",
label: "Buscar",
clearLabel: "Limpiar búsqueda",
})
expect(getDictionary("en").common.pagination).toEqual({
summaryPrefix: "Showing page",
summarySeparator: "of",
previous: "Previous",
next: "Next",
})
expect(getDictionary("es").common.pagination).toEqual({
summaryPrefix: "Mostrando página",
summarySeparator: "de",
previous: "Anterior",
next: "Siguiente",
})
expect(getDictionary("en").common.submitButton).toEqual({
defaultLabel: "Submit",
processing: "Processing",
success: "Success",
})
expect(getDictionary("en").common.forbidden).toEqual({
title: "Access denied",
description: "You do not have permission to access this section.",
homeLink: "Back to home",
})
expect(getDictionary("es").common.submitButton).toEqual({
defaultLabel: "Enviar",
processing: "Procesando",
success: "Completado",
})
expect(getDictionary("es").common.forbidden).toEqual({
title: "Acceso denegado",
description: "No tienes permisos para acceder a esta sección.",
homeLink: "Volver al inicio",
})
})
it("provides localized inventory category copy for English and Spanish", () => {
expect(getDictionary("en").inventory.categories).toEqual({
list: {
title: "Categories",
addLabel: "Add Category",
empty: "No categories found.",
columns: {
name: "Name",
items: "Items",
actions: "Actions",
},
actions: {
edit: "Edit category",
delete: "Delete category",
},
},
new: {
title: "New Category",
},
edit: {
title: "Edit Category",
},
form: {
nameLabel: "Name",
namePlaceholder: "Category name",
createSubmit: "Create Category",
updateSubmit: "Update Category",
},
delete: {
label: "Delete category",
pending: "Deleting...",
unknownError: "Unknown error",
},
actions: {
createSuccess: "Category created successfully",
createFailure: "Failed to create category",
updateSuccess: "Category updated successfully",
updateFailure: "Failed to update category",
deleteSuccess: "Category deleted successfully",
deleteFailure: "Failed to delete category",
duplicateName: "Category already exists",
unchangedName: "Category name unchanged",
notFound: "Category not found",
hasItems: "Cannot delete category with items",
},
schema: {
nameRequired: "Name is required and must be at least 3 characters long",
idRequired: "ID is required",
},
})
expect(getDictionary("es").inventory.categories).toEqual({
list: {
title: "Categorías",
addLabel: "Agregar categoría",
empty: "No se encontraron categorías.",
columns: {
name: "Nombre",
items: "Artículos",
actions: "Acciones",
},
actions: {
edit: "Editar categoría",
delete: "Eliminar categoría",
},
},
new: {
title: "Nueva categoría",
},
edit: {
title: "Editar categoría",
},
form: {
nameLabel: "Nombre",
namePlaceholder: "Nombre de la categoría",
createSubmit: "Crear categoría",
updateSubmit: "Actualizar categoría",
},
delete: {
label: "Eliminar categoría",
pending: "Eliminando...",
unknownError: "Error desconocido",
},
actions: {
createSuccess: "Categoría creada correctamente",
createFailure: "Error al crear la categoría",
updateSuccess: "Categoría actualizada correctamente",
updateFailure: "Error al actualizar la categoría",
deleteSuccess: "Categoría eliminada correctamente",
deleteFailure: "Error al eliminar la categoría",
duplicateName: "La categoría ya existe",
unchangedName: "El nombre de la categoría no cambió",
notFound: "Categoría no encontrada",
hasItems: "No se puede eliminar una categoría con artículos",
},
schema: {
nameRequired:
"El nombre es obligatorio y debe tener al menos 3 caracteres",
idRequired: "El ID es obligatorio",
},
})
})
it("keeps dashboard home dictionary keys aligned across locales", () => {
expect(getDictionary("en").dashboardHome).toEqual({
heading: "Dashboard",
cards: {
items: {
title: "Total Items",
countLabel: "Total",
},
assets: {
title: "Total Assets",
countLabel: "Total",
},
recipients: {
title: "Total Recipients",
countLabel: "Total",
},
},
})
expect(getDictionary("es").dashboardHome).toEqual({
heading: "Panel de control",
cards: {
items: {
title: "Total de artículos",
countLabel: "Total",
},
assets: {
title: "Total de activos",
countLabel: "Total",
},
recipients: {
title: "Total de destinatarios",
countLabel: "Total",
},
},
})
})
it("has exact structural parity between English and Spanish dictionaries", () => {
expect(extractKeyPaths(getDictionary("es"))).toEqual(
extractKeyPaths(getDictionary("en")),
)
})
})
function extractKeyPaths(value: unknown, prefix = ""): string[] {
if (!isPlainObject(value)) return [prefix]
return Object.keys(value)
.sort()
.flatMap((key) =>
extractKeyPaths(value[key], prefix ? `${prefix}.${key}` : key),
)
}
function isPlainObject(value: unknown): value is Record<string, unknown> {
return typeof value === "object" && value !== null && !Array.isArray(value)
}