2026-10-03 10:36:03 +00:00
|
|
|
import assert from "node:assert/strict";
|
|
|
|
|
import { test } from "node:test";
|
|
|
|
|
|
2026-10-04 18:16:39 +00:00
|
|
|
import { catalogKeys, inventoryKeys } from "@metis/assets/client";
|
|
|
|
|
import { companyKeys } from "@metis/companies/client";
|
2026-10-03 10:36:03 +00:00
|
|
|
import { identityKeys } from "@metis/identity/client";
|
|
|
|
|
import type { SessionResponseDTO, UserDTO } from "@metis/identity/common";
|
|
|
|
|
import { QueryClient, QueryClientProvider } from "@tanstack/react-query";
|
|
|
|
|
import { renderToStaticMarkup } from "react-dom/server";
|
|
|
|
|
import { I18nextProvider } from "react-i18next";
|
|
|
|
|
|
feat(dashboard): implement dashboard schemas and API integration
- Added Zod schemas for dashboard queries, entries, summaries, and companies.
- Created dashboard API functions for fetching summary, work, attention, activity, and companies data.
- Developed dashboard components including BlockState, DashboardDetailsDialog, and DashboardPage for rendering dashboard information.
- Implemented quick create functionality for companies, tickets, interventions, assets, and resources.
- Introduced a new ResourceDashboardReader for querying resource-related data.
- Added a StatisticCard component for displaying key performance indicators in the dashboard.
2026-10-08 11:27:27 +00:00
|
|
|
import {
|
2026-10-08 17:25:02 +00:00
|
|
|
DashboardNavigationButton,
|
|
|
|
|
DashboardStateBadge,
|
|
|
|
|
DashboardTable,
|
feat(dashboard): implement dashboard schemas and API integration
- Added Zod schemas for dashboard queries, entries, summaries, and companies.
- Created dashboard API functions for fetching summary, work, attention, activity, and companies data.
- Developed dashboard components including BlockState, DashboardDetailsDialog, and DashboardPage for rendering dashboard information.
- Implemented quick create functionality for companies, tickets, interventions, assets, and resources.
- Introduced a new ResourceDashboardReader for querying resource-related data.
- Added a StatisticCard component for displaying key performance indicators in the dashboard.
2026-10-08 11:27:27 +00:00
|
|
|
dashboardKeys,
|
|
|
|
|
entryDestination,
|
2026-10-08 17:25:02 +00:00
|
|
|
} from "../../../modules/companies/src/web/dashboard";
|
feat(dashboard): implement dashboard schemas and API integration
- Added Zod schemas for dashboard queries, entries, summaries, and companies.
- Created dashboard API functions for fetching summary, work, attention, activity, and companies data.
- Developed dashboard components including BlockState, DashboardDetailsDialog, and DashboardPage for rendering dashboard information.
- Implemented quick create functionality for companies, tickets, interventions, assets, and resources.
- Introduced a new ResourceDashboardReader for querying resource-related data.
- Added a StatisticCard component for displaying key performance indicators in the dashboard.
2026-10-08 11:27:27 +00:00
|
|
|
|
2026-10-03 10:36:03 +00:00
|
|
|
import { App } from "./app";
|
|
|
|
|
import { createAppI18n } from "./i18n";
|
2026-10-04 18:16:39 +00:00
|
|
|
import { workspaceAssets, workspaceCatalog, workspaceCompany } from "./test-fixtures/workspace";
|
2026-10-03 10:36:03 +00:00
|
|
|
|
|
|
|
|
const user: UserDTO = {
|
|
|
|
|
id: "c90b4d83-b06a-4d27-a402-e64899a2dfc2",
|
|
|
|
|
name: "Usuario de prueba",
|
|
|
|
|
email: "user@example.test",
|
|
|
|
|
active: true,
|
|
|
|
|
isInitialAdmin: false,
|
|
|
|
|
};
|
2026-10-08 16:31:46 +00:00
|
|
|
|
|
|
|
|
test("las acciones del dashboard conservan nombre, selección y deshabilitado sin enviar formularios", () => {
|
|
|
|
|
const markup = renderToStaticMarkup(
|
|
|
|
|
<DashboardNavigationButton aria-label="Open tickets: 2" aria-pressed disabled>
|
|
|
|
|
2
|
|
|
|
|
</DashboardNavigationButton>
|
|
|
|
|
);
|
|
|
|
|
assert.ok(markup.includes('aria-label="Open tickets: 2"'));
|
|
|
|
|
assert.ok(markup.includes('aria-pressed="true"'));
|
|
|
|
|
assert.ok(markup.includes('disabled=""'));
|
|
|
|
|
assert.ok(markup.includes('type="button"'));
|
|
|
|
|
assert.ok(markup.includes(">2</button>"));
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
test("los estados del dashboard mantienen texto observable además del color", () => {
|
|
|
|
|
for (const state of ["urgent", "high", "in_progress", "open", null]) {
|
|
|
|
|
const label = state ?? "Unknown";
|
|
|
|
|
const markup = renderToStaticMarkup(
|
|
|
|
|
<DashboardStateBadge state={state}>{label}</DashboardStateBadge>
|
|
|
|
|
);
|
|
|
|
|
assert.ok(markup.includes(`>${label}</span>`));
|
|
|
|
|
if (state === null) {
|
|
|
|
|
assert.equal(markup.includes("data-state="), false);
|
|
|
|
|
} else {
|
|
|
|
|
assert.ok(markup.includes(`data-state="${state}"`));
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
test("las tablas del dashboard conservan caption, cabeceras semánticas y contenido", () => {
|
|
|
|
|
const markup = renderToStaticMarkup(
|
|
|
|
|
<DashboardTable caption="Pending work">
|
|
|
|
|
<thead>
|
|
|
|
|
<tr>
|
|
|
|
|
<th scope="col">Reference</th>
|
|
|
|
|
</tr>
|
|
|
|
|
</thead>
|
|
|
|
|
<tbody>
|
|
|
|
|
<tr>
|
|
|
|
|
<td>T-2026-001</td>
|
|
|
|
|
</tr>
|
|
|
|
|
</tbody>
|
|
|
|
|
</DashboardTable>
|
|
|
|
|
);
|
|
|
|
|
assert.match(markup, /<caption[^>]*>Pending work<\/caption>/);
|
|
|
|
|
assert.ok(markup.includes('<th scope="col">Reference</th>'));
|
|
|
|
|
assert.ok(markup.includes("<td>T-2026-001</td>"));
|
|
|
|
|
});
|
|
|
|
|
|
2026-10-04 18:16:39 +00:00
|
|
|
async function renderPage(
|
|
|
|
|
language: string,
|
|
|
|
|
session?: SessionResponseDTO,
|
|
|
|
|
seed?: (client: QueryClient) => void
|
|
|
|
|
) {
|
2026-10-03 10:36:03 +00:00
|
|
|
const { instance, initialization } = createAppI18n(language);
|
|
|
|
|
await initialization;
|
|
|
|
|
const client = new QueryClient({
|
feat(dashboard): implement dashboard schemas and API integration
- Added Zod schemas for dashboard queries, entries, summaries, and companies.
- Created dashboard API functions for fetching summary, work, attention, activity, and companies data.
- Developed dashboard components including BlockState, DashboardDetailsDialog, and DashboardPage for rendering dashboard information.
- Implemented quick create functionality for companies, tickets, interventions, assets, and resources.
- Introduced a new ResourceDashboardReader for querying resource-related data.
- Added a StatisticCard component for displaying key performance indicators in the dashboard.
2026-10-08 11:27:27 +00:00
|
|
|
defaultOptions: {
|
|
|
|
|
queries: { retry: false, retryOnMount: false, staleTime: Number.POSITIVE_INFINITY },
|
|
|
|
|
},
|
2026-10-03 10:36:03 +00:00
|
|
|
});
|
|
|
|
|
if (session) {
|
|
|
|
|
client.setQueryData(identityKeys.session, session);
|
|
|
|
|
}
|
2026-10-04 18:16:39 +00:00
|
|
|
seed?.(client);
|
2026-10-03 10:36:03 +00:00
|
|
|
try {
|
|
|
|
|
return renderToStaticMarkup(
|
|
|
|
|
<I18nextProvider i18n={instance}>
|
|
|
|
|
<QueryClientProvider client={client}>
|
|
|
|
|
<App />
|
|
|
|
|
</QueryClientProvider>
|
|
|
|
|
</I18nextProvider>
|
|
|
|
|
);
|
|
|
|
|
} finally {
|
|
|
|
|
client.clear();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
test("el acceso protege el contenido y traduce la carga de sesión", async () => {
|
|
|
|
|
for (const [language, pending] of [
|
|
|
|
|
["es", "Comprobando acceso"],
|
|
|
|
|
["en", "Checking access"],
|
|
|
|
|
] as const) {
|
|
|
|
|
const markup = await renderPage(language);
|
|
|
|
|
assert.equal(markup.includes("Tu espacio de soporte"), false);
|
|
|
|
|
assert.equal(markup.includes("Your support workspace"), false);
|
|
|
|
|
assert.match(markup, /role="combobox"/);
|
|
|
|
|
assert.ok(markup.includes(language === "es" ? "Español" : "English"));
|
|
|
|
|
assert.ok(markup.includes(pending));
|
|
|
|
|
assert.ok(markup.includes('role="status"'));
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
test("la configuración única y el login presentan sus formularios traducidos sin acceso al contenido", async () => {
|
|
|
|
|
for (const [language, setupTitle, loginTitle] of [
|
|
|
|
|
["es", "Configura Metis Desk", "Accede a Metis Desk"],
|
|
|
|
|
["en", "Set up Metis Desk", "Sign in to Metis Desk"],
|
|
|
|
|
] as const) {
|
|
|
|
|
const snapshot = { user: null, csrfToken: "a".repeat(64), setupRequired: true };
|
|
|
|
|
const setup = await renderPage(language, snapshot);
|
|
|
|
|
assert.ok(setup.includes(setupTitle));
|
|
|
|
|
assert.match(setup, /autoComplete="new-password"/);
|
|
|
|
|
assert.match(setup, /minLength="8"/);
|
|
|
|
|
assert.equal(setup.includes("Tu espacio de soporte"), false);
|
|
|
|
|
const login = await renderPage(language, { ...snapshot, setupRequired: false });
|
|
|
|
|
assert.ok(login.includes(loginTitle));
|
|
|
|
|
assert.match(login, /autoComplete="current-password"/);
|
|
|
|
|
assert.match(login, /minLength="1"/);
|
|
|
|
|
assert.equal(login.includes('autoComplete="name"'), false);
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
test("solo el administrador inicial ve la navegación de gestión de usuarios", async () => {
|
|
|
|
|
const snapshot = { user, csrfToken: "a".repeat(64), setupRequired: false };
|
|
|
|
|
const normal = await renderPage("es", snapshot);
|
|
|
|
|
assert.ok(normal.includes("Tu espacio de soporte"));
|
|
|
|
|
assert.ok(normal.includes("Mi contraseña"));
|
|
|
|
|
assert.ok(normal.includes("Salir"));
|
2026-10-09 08:18:00 +00:00
|
|
|
assert.equal(normal.includes('href="/settings/users"'), false);
|
2026-10-03 10:36:03 +00:00
|
|
|
const admin = await renderPage("es", { ...snapshot, user: { ...user, isInitialAdmin: true } });
|
2026-10-09 08:18:00 +00:00
|
|
|
assert.ok(admin.includes('href="/settings/users"'));
|
2026-10-03 10:36:03 +00:00
|
|
|
});
|
2026-10-04 18:16:39 +00:00
|
|
|
|
feat(dashboard): implement dashboard schemas and API integration
- Added Zod schemas for dashboard queries, entries, summaries, and companies.
- Created dashboard API functions for fetching summary, work, attention, activity, and companies data.
- Developed dashboard components including BlockState, DashboardDetailsDialog, and DashboardPage for rendering dashboard information.
- Implemented quick create functionality for companies, tickets, interventions, assets, and resources.
- Introduced a new ResourceDashboardReader for querying resource-related data.
- Added a StatisticCard component for displaying key performance indicators in the dashboard.
2026-10-08 11:27:27 +00:00
|
|
|
test("el espacio abre Inicio tras el login y mantiene acceso a las compañías sin formularios visibles", async () => {
|
2026-10-04 18:16:39 +00:00
|
|
|
for (const [language, spaces, panel] of [
|
|
|
|
|
["es", "Espacios de compañías", "Ficha del activo seleccionado"],
|
|
|
|
|
["en", "Company spaces", "Selected asset details"],
|
|
|
|
|
]) {
|
|
|
|
|
const html = await renderPage(
|
|
|
|
|
language ?? "es",
|
|
|
|
|
{ user, csrfToken: "a".repeat(64), setupRequired: false },
|
|
|
|
|
(client) => {
|
|
|
|
|
client.setQueryData(companyKeys.list, { companies: [workspaceCompany] });
|
|
|
|
|
client.setQueryData(companyKeys.detail(workspaceCompany.id), { company: workspaceCompany });
|
|
|
|
|
client.setQueryData(inventoryKeys.company(workspaceCompany.id), {
|
|
|
|
|
assets: workspaceAssets,
|
2026-10-04 19:04:14 +00:00
|
|
|
relationships: [],
|
|
|
|
|
workstations: [],
|
2026-10-04 18:16:39 +00:00
|
|
|
});
|
|
|
|
|
client.setQueryData(catalogKeys.scope(workspaceCompany.id), workspaceCatalog);
|
|
|
|
|
}
|
|
|
|
|
);
|
|
|
|
|
assert.ok(html.includes(`aria-label="${spaces}"`));
|
feat(dashboard): implement dashboard schemas and API integration
- Added Zod schemas for dashboard queries, entries, summaries, and companies.
- Created dashboard API functions for fetching summary, work, attention, activity, and companies data.
- Developed dashboard components including BlockState, DashboardDetailsDialog, and DashboardPage for rendering dashboard information.
- Implemented quick create functionality for companies, tickets, interventions, assets, and resources.
- Introduced a new ResourceDashboardReader for querying resource-related data.
- Added a StatisticCard component for displaying key performance indicators in the dashboard.
2026-10-08 11:27:27 +00:00
|
|
|
assert.ok(html.includes(language === "es" ? "Trabajo pendiente" : "Pending work"));
|
|
|
|
|
assert.ok(html.includes(language === "es" ? "Resumen por compañías" : "Company summary"));
|
|
|
|
|
assert.ok(html.includes(`aria-label="${language === "es" ? "Inicio" : "Home"}"`));
|
|
|
|
|
assert.equal(html.includes(`aria-label="${panel}"`), false);
|
|
|
|
|
assert.ok(
|
|
|
|
|
html.includes(language === "es" ? "Buscar en Metis Desk..." : "Search Metis Desk...")
|
|
|
|
|
);
|
|
|
|
|
assert.ok(html.includes(language === "es" ? "Configuración" : "Settings"));
|
|
|
|
|
assert.equal(html.includes("shell.catalogs"), false);
|
|
|
|
|
assert.ok(html.includes(user.name));
|
2026-10-04 18:16:39 +00:00
|
|
|
assert.equal(html.includes("<form"), false);
|
|
|
|
|
assert.equal(html.includes("2026-10-04"), false);
|
|
|
|
|
}
|
|
|
|
|
});
|
feat(dashboard): implement dashboard schemas and API integration
- Added Zod schemas for dashboard queries, entries, summaries, and companies.
- Created dashboard API functions for fetching summary, work, attention, activity, and companies data.
- Developed dashboard components including BlockState, DashboardDetailsDialog, and DashboardPage for rendering dashboard information.
- Implemented quick create functionality for companies, tickets, interventions, assets, and resources.
- Introduced a new ResourceDashboardReader for querying resource-related data.
- Added a StatisticCard component for displaying key performance indicators in the dashboard.
2026-10-08 11:27:27 +00:00
|
|
|
|
|
|
|
|
test("el dashboard distingue carga, vacío y fallo parcial sin reemplazar métricas desconocidas por cero", async () => {
|
|
|
|
|
const session = { user, csrfToken: "a".repeat(64), setupRequired: false };
|
|
|
|
|
for (const language of ["es", "en"]) {
|
|
|
|
|
const loading = await renderPage(language, session);
|
|
|
|
|
assert.ok(loading.includes("—"));
|
|
|
|
|
assert.ok(loading.includes('role="status"'));
|
|
|
|
|
const empty = await renderPage(language, session, (client) => {
|
|
|
|
|
client.setQueryData(companyKeys.list, { companies: [] });
|
|
|
|
|
client.setQueryData(dashboardKeys.block("summary"), {
|
|
|
|
|
companies: 0,
|
|
|
|
|
tickets: 0,
|
|
|
|
|
interventions: 0,
|
|
|
|
|
assets: 0,
|
|
|
|
|
resources: 0,
|
|
|
|
|
});
|
|
|
|
|
for (const block of ["work", "activity", "companies"]) {
|
|
|
|
|
client.setQueryData(dashboardKeys.block(block), { items: [], total: 0 });
|
|
|
|
|
}
|
|
|
|
|
client.setQueryData(dashboardKeys.block("attention"), { items: [] });
|
|
|
|
|
});
|
|
|
|
|
assert.ok(
|
|
|
|
|
empty.includes(
|
|
|
|
|
language === "es"
|
|
|
|
|
? "No hay tickets ni intervenciones pendientes."
|
|
|
|
|
: "There are no pending tickets or interventions."
|
|
|
|
|
)
|
|
|
|
|
);
|
|
|
|
|
assert.ok(!empty.includes('role="alert"'));
|
|
|
|
|
const partial = await renderPage(language, session, (client) => {
|
|
|
|
|
client.setQueryData(dashboardKeys.block("summary"), {
|
|
|
|
|
companies: 1,
|
|
|
|
|
tickets: 23,
|
|
|
|
|
interventions: 0,
|
|
|
|
|
assets: 5,
|
|
|
|
|
resources: 6,
|
|
|
|
|
});
|
|
|
|
|
const query = client
|
|
|
|
|
.getQueryCache()
|
|
|
|
|
.build(client, { queryKey: dashboardKeys.block("activity") });
|
|
|
|
|
query.setState({ status: "error", error: new Error("failed"), fetchStatus: "idle" });
|
|
|
|
|
});
|
|
|
|
|
assert.ok(partial.includes('role="alert"'));
|
|
|
|
|
assert.ok(partial.includes(">23</strong>"));
|
|
|
|
|
assert.ok(!partial.includes("dashboard.blockError"));
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
test("los enlaces del dashboard mantienen la compañía y la ficha exacta", () => {
|
|
|
|
|
const item = { id: "entry", companyId: "company" };
|
|
|
|
|
assert.deepEqual(entryDestination({ ...item, kind: "ticket" }), {
|
|
|
|
|
area: "tickets",
|
|
|
|
|
companyId: "company",
|
|
|
|
|
id: "entry",
|
|
|
|
|
});
|
|
|
|
|
assert.deepEqual(entryDestination({ ...item, kind: "asset" }), {
|
|
|
|
|
area: "company",
|
|
|
|
|
id: "company",
|
|
|
|
|
section: "inventory",
|
|
|
|
|
entityId: "entry",
|
|
|
|
|
});
|
|
|
|
|
assert.deepEqual(entryDestination({ ...item, kind: "resource" }), {
|
|
|
|
|
area: "company",
|
|
|
|
|
id: "company",
|
|
|
|
|
section: "resources",
|
|
|
|
|
entityId: "entry",
|
|
|
|
|
});
|
|
|
|
|
assert.deepEqual(entryDestination({ ...item, kind: "profile" }), {
|
|
|
|
|
area: "company",
|
|
|
|
|
id: "company",
|
|
|
|
|
section: "profiles",
|
|
|
|
|
entityId: "entry",
|
|
|
|
|
});
|
|
|
|
|
});
|
2026-10-08 14:43:34 +00:00
|
|
|
|
|
|
|
|
test("los bloques del dashboard presentan datos, avisos y actividad reales en ambos idiomas", async () => {
|
|
|
|
|
const session = { user, csrfToken: "a".repeat(64), setupRequired: false };
|
|
|
|
|
for (const language of ["es", "en"]) {
|
|
|
|
|
const html = await renderPage(language, session, (client) => {
|
|
|
|
|
client.setQueryData(companyKeys.list, { companies: [workspaceCompany] });
|
|
|
|
|
client.setQueryData(dashboardKeys.block("summary"), {
|
|
|
|
|
companies: 1,
|
|
|
|
|
tickets: 2,
|
|
|
|
|
interventions: 3,
|
|
|
|
|
assets: 4,
|
|
|
|
|
resources: 5,
|
|
|
|
|
});
|
|
|
|
|
const entry = {
|
|
|
|
|
id: workspaceCompany.id,
|
|
|
|
|
companyId: workspaceCompany.id,
|
|
|
|
|
companyName: workspaceCompany.name,
|
|
|
|
|
name: "Revisar acceso",
|
|
|
|
|
kind: "ticket",
|
|
|
|
|
reference: "T-2026-001",
|
|
|
|
|
state: "open",
|
|
|
|
|
priority: "high",
|
|
|
|
|
recordedAt: "2026-10-01T10:30:00.000Z",
|
|
|
|
|
actorName: "Operador test",
|
|
|
|
|
};
|
|
|
|
|
client.setQueryData(dashboardKeys.block("work"), { items: [entry], total: 1 });
|
|
|
|
|
client.setQueryData(dashboardKeys.block("activity"), { items: [entry], total: 1 });
|
|
|
|
|
client.setQueryData(dashboardKeys.block("attention"), {
|
|
|
|
|
items: [{ kind: "missing_site", count: 2 }],
|
|
|
|
|
});
|
|
|
|
|
client.setQueryData(dashboardKeys.block("companies"), {
|
|
|
|
|
items: [
|
|
|
|
|
{
|
|
|
|
|
id: workspaceCompany.id,
|
|
|
|
|
name: workspaceCompany.name,
|
|
|
|
|
companies: 1,
|
|
|
|
|
tickets: 2,
|
|
|
|
|
interventions: 3,
|
|
|
|
|
assets: 4,
|
|
|
|
|
resources: 5,
|
|
|
|
|
people: 6,
|
|
|
|
|
},
|
|
|
|
|
],
|
|
|
|
|
total: 1,
|
|
|
|
|
});
|
|
|
|
|
});
|
|
|
|
|
assert.ok(html.includes("Revisar acceso"));
|
|
|
|
|
assert.ok(html.includes("T-2026-001"));
|
|
|
|
|
assert.ok(html.includes("Operador test"));
|
|
|
|
|
assert.ok(html.includes(workspaceCompany.name));
|
|
|
|
|
assert.ok(html.includes('dateTime="2026-10-01T10:30:00.000Z"'));
|
|
|
|
|
assert.ok(html.includes('data-state="high"'));
|
|
|
|
|
assert.ok(html.includes('data-state="open"'));
|
|
|
|
|
assert.ok(
|
|
|
|
|
html.includes(
|
|
|
|
|
`aria-label="${language === "es" ? "Tickets abiertos" : "Open tickets"} · ${workspaceCompany.name}: 2"`
|
|
|
|
|
)
|
|
|
|
|
);
|
|
|
|
|
assert.equal(html.includes("dashboard.issues.missing_site"), false);
|
|
|
|
|
assert.equal(html.includes('role="alert"'), false);
|
|
|
|
|
assert.equal(html.includes("<form"), false);
|
|
|
|
|
}
|
|
|
|
|
});
|