222 lines
7.3 KiB
TypeScript
222 lines
7.3 KiB
TypeScript
import assert from "node:assert/strict";
|
|
import { test } from "node:test";
|
|
|
|
import { UniqueID } from "@repo/rdx-ddd";
|
|
import { Maybe } from "@repo/rdx-utils";
|
|
import { QueryClient, QueryClientProvider } from "@tanstack/react-query";
|
|
import { createInstance } from "i18next";
|
|
import type { ReactNode } from "react";
|
|
import { renderToStaticMarkup } from "react-dom/server";
|
|
import { I18nextProvider } from "react-i18next";
|
|
|
|
import { buildCatalogResponse } from "../api/application/asset-catalog-response.builder";
|
|
import { AssetCatalog } from "../api/domain/asset-catalog.aggregate";
|
|
import { seedAssetCatalog } from "../api/domain/asset-catalog-seed";
|
|
import type { RelationshipDTO } from "../common";
|
|
|
|
import { AssetForm } from "./asset-form";
|
|
import { AssetRelationships } from "./asset-relationships";
|
|
import { catalogKeys } from "./catalog-api";
|
|
import { CatalogRelationForm } from "./catalog-relation-form";
|
|
import { CatalogTemplateForm } from "./catalog-template-form";
|
|
import { assetsEnglish, assetsSpanish } from "./i18n";
|
|
import { inventoryKeys } from "./inventory-api";
|
|
import { AssetInventoryPage } from "./inventory-page";
|
|
import { WorkstationForm } from "./workstation-form";
|
|
import { WorkstationsPage } from "./workstations-page";
|
|
|
|
const companyId = UniqueID.generateNewID().toPrimitive();
|
|
const lookups = {
|
|
sites: [],
|
|
people: [],
|
|
onCreateSite: () => undefined,
|
|
onCreatePerson: () => undefined,
|
|
};
|
|
async function render(language: string, node: ReactNode, relationships: RelationshipDTO[] = []) {
|
|
const i18n = createInstance();
|
|
await i18n.init({
|
|
lng: language,
|
|
resources: { es: { assets: assetsSpanish }, en: { assets: assetsEnglish } },
|
|
});
|
|
const client = new QueryClient();
|
|
client.setQueryData(inventoryKeys.company(companyId), {
|
|
assets: [],
|
|
relationships,
|
|
workstations: [],
|
|
});
|
|
client.setQueryData(
|
|
catalogKeys.scope(companyId),
|
|
buildCatalogResponse(seedAssetCatalog(AssetCatalog.create()), Maybe.none())
|
|
);
|
|
try {
|
|
return renderToStaticMarkup(
|
|
<I18nextProvider i18n={i18n}>
|
|
<QueryClientProvider client={client}>{node}</QueryClientProvider>
|
|
</I18nextProvider>
|
|
);
|
|
} finally {
|
|
client.clear();
|
|
}
|
|
}
|
|
test("Inventario traducido con alta de cabecera y sin formulario junto al listado", async () => {
|
|
for (const language of ["es", "en"]) {
|
|
const html = await render(
|
|
language,
|
|
<AssetInventoryPage companyId={companyId} csrfToken="test" lookups={lookups} />
|
|
);
|
|
assert.equal(html.includes("<form"), false);
|
|
assert.ok(html.includes(language === "es" ? "Nuevo activo" : "New asset"));
|
|
assert.ok(
|
|
html.includes(
|
|
language === "es"
|
|
? "Esta compañía todavía no tiene activos."
|
|
: "This company has no assets yet."
|
|
)
|
|
);
|
|
}
|
|
});
|
|
test("El activo ofrece altas relacionadas, ayuda inferior y no encadena una tercera alta", async () => {
|
|
const html = await render(
|
|
"es",
|
|
<AssetForm
|
|
companyId={companyId}
|
|
csrfToken="test"
|
|
items={[]}
|
|
lookups={lookups}
|
|
onSaved={() => undefined}
|
|
/>
|
|
);
|
|
for (const label of ["Nueva plantilla", "Nueva sede", "Nueva persona"]) {
|
|
assert.ok(html.includes(`aria-label="${label}"`));
|
|
}
|
|
assert.ok(html.indexOf('name="notes"') < html.indexOf("<aside"));
|
|
assert.ok(html.indexOf("<aside") < html.indexOf('type="submit"'));
|
|
const child = await render(
|
|
"es",
|
|
<CatalogTemplateForm
|
|
allowQuickCreate={false}
|
|
csrfToken="test"
|
|
items={[]}
|
|
onSaved={() => undefined}
|
|
scope={companyId}
|
|
/>
|
|
);
|
|
assert.equal(child.includes('aria-label="Nuevo campo"'), false);
|
|
});
|
|
|
|
test("Las fichas leen ambos sentidos y conservan la auditoría de relaciones retiradas", async () => {
|
|
const sourceId = UniqueID.generateNewID().toPrimitive();
|
|
const targetId = UniqueID.generateNewID().toPrimitive();
|
|
const link: RelationshipDTO = {
|
|
id: UniqueID.generateNewID().toPrimitive(),
|
|
companyId,
|
|
values: {
|
|
sourceId,
|
|
targetId,
|
|
typeId: UniqueID.generateNewID().toPrimitive(),
|
|
name: "Alojado en",
|
|
inverseName: "Aloja",
|
|
symmetric: false,
|
|
notes: "Rack 1",
|
|
},
|
|
createdBy: UniqueID.generateNewID().toPrimitive(),
|
|
createdByName: "Técnico",
|
|
createdAt: "2026-10-04T10:00:00.000Z",
|
|
endedBy: null,
|
|
endedByName: null,
|
|
endedAt: null,
|
|
};
|
|
const source = await render(
|
|
"es",
|
|
<AssetRelationships assetId={sourceId} companyId={companyId} csrfToken="test" />,
|
|
[link]
|
|
);
|
|
const target = await render(
|
|
"es",
|
|
<AssetRelationships assetId={targetId} companyId={companyId} csrfToken="test" />,
|
|
[link]
|
|
);
|
|
assert.ok(source.includes("Alojado en"));
|
|
assert.ok(target.includes("Aloja"));
|
|
assert.ok(source.includes("Creada por Técnico"));
|
|
assert.ok(source.includes("Retirar relación"));
|
|
const ended = await render(
|
|
"es",
|
|
<AssetRelationships assetId={sourceId} companyId={companyId} csrfToken="test" />,
|
|
[
|
|
{
|
|
...link,
|
|
endedBy: link.createdBy,
|
|
endedByName: "Técnico",
|
|
endedAt: "2026-10-04T11:00:00.000Z",
|
|
},
|
|
]
|
|
);
|
|
assert.ok(ended.includes("Relaciones retiradas"));
|
|
assert.ok(ended.includes("Retirada por Técnico"));
|
|
assert.ok(!ended.includes(">Retirar relación<"));
|
|
});
|
|
test("El formulario de tipo de relación presenta ayuda justo antes de sus operaciones", async () => {
|
|
for (const language of ["es", "en"]) {
|
|
const html = await render(
|
|
language,
|
|
<CatalogRelationForm csrfToken="test" onSaved={() => undefined} scope={companyId} />
|
|
);
|
|
assert.ok(html.indexOf('name="description"') < html.indexOf("<aside"));
|
|
assert.ok(html.indexOf("<aside") < html.indexOf('type="submit"'));
|
|
assert.ok(
|
|
html.includes(
|
|
language === "es" ? "Nombre visto desde el otro activo" : "Name shown from the other asset"
|
|
)
|
|
);
|
|
}
|
|
});
|
|
|
|
test("Puestos de trabajo: listado traducido, alta en cabecera y formulario modal", async () => {
|
|
for (const language of ["es", "en"]) {
|
|
const html = await render(
|
|
language,
|
|
<WorkstationsPage companyId={companyId} csrfToken="test" lookups={lookups} />
|
|
);
|
|
assert.ok(html.includes(language === "es" ? "Nuevo puesto" : "New workstation"));
|
|
assert.ok(
|
|
html.includes(
|
|
language === "es"
|
|
? "Esta compañía todavía no tiene puestos de trabajo."
|
|
: "This company has no workstations yet."
|
|
)
|
|
);
|
|
assert.equal(html.includes("<form"), false);
|
|
}
|
|
});
|
|
test("Puestos de trabajo: altas rápidas y ayuda inferior; un activo anidado no ofrece terceras altas", async () => {
|
|
const html = await render(
|
|
"es",
|
|
<WorkstationForm
|
|
companyId={companyId}
|
|
csrfToken="test"
|
|
lookups={lookups}
|
|
onSaved={() => undefined}
|
|
/>
|
|
);
|
|
for (const label of ["Nueva sede", "Nueva persona", "Nuevo activo"]) {
|
|
assert.ok(html.includes(`aria-label="${label}"`));
|
|
}
|
|
assert.ok(html.indexOf('name="active"') < html.indexOf("<aside"));
|
|
assert.ok(html.indexOf("<aside") < html.indexOf('type="submit"'));
|
|
const nested = await render(
|
|
"es",
|
|
<AssetForm
|
|
allowQuickCreate={false}
|
|
companyId={companyId}
|
|
csrfToken="test"
|
|
items={[]}
|
|
lookups={lookups}
|
|
onSaved={() => undefined}
|
|
/>
|
|
);
|
|
for (const label of ["Nueva sede", "Nueva persona", "Nueva plantilla"]) {
|
|
assert.ok(!nested.includes(`aria-label="${label}"`));
|
|
}
|
|
});
|