128 lines
6.0 KiB
TypeScript
128 lines
6.0 KiB
TypeScript
import { fireEvent, render, screen, waitFor, within } from "@testing-library/react";
|
|
import userEvent from "@testing-library/user-event";
|
|
import { StrictMode } from "react";
|
|
import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
|
|
|
|
import App from "./App";
|
|
|
|
const bootstrap = { theme: "white", s2sUrl: "ws://localhost:8420/v1/realtime", apipilot: true };
|
|
beforeEach(() => {
|
|
window.history.replaceState({}, "", "/?debug=true&phoneNumber=1234");
|
|
vi.spyOn(HTMLDialogElement.prototype, "showModal").mockImplementation(function (this: HTMLDialogElement) {
|
|
this.open = true;
|
|
});
|
|
vi.spyOn(HTMLDialogElement.prototype, "close").mockImplementation(function (this: HTMLDialogElement) {
|
|
this.open = false;
|
|
});
|
|
vi.stubGlobal(
|
|
"fetch",
|
|
vi.fn((input: string) => {
|
|
const path = new URL(input, window.location.origin).pathname;
|
|
const data: Record<string, unknown> = {
|
|
"/api/config": bootstrap,
|
|
"/api/user/phone": { result: { name: "Ana Vargas", contact: { emailAddress: "ana@example.test" } } },
|
|
"/api/user/reports": {
|
|
count: 1,
|
|
entries: [{ sourceScheduleLabel: null, date: null }],
|
|
hints: [],
|
|
truncated: true,
|
|
note: "Lista abreviada",
|
|
},
|
|
"/api/apipilot/tools": {
|
|
success: true,
|
|
data: { tools: [{ name: "get_reports", description: "Consulta tus reportes", parameters: {} }] },
|
|
},
|
|
};
|
|
return Promise.resolve(new Response(JSON.stringify(data[path] ?? {})));
|
|
}),
|
|
);
|
|
});
|
|
afterEach(() => {
|
|
vi.unstubAllGlobals();
|
|
window.history.replaceState({}, "", "/");
|
|
});
|
|
|
|
describe("React application", () => {
|
|
it("offers configuration retry without exposing a broken start control", async () => {
|
|
vi.mocked(fetch).mockRejectedValueOnce(new Error("offline"));
|
|
const user = userEvent.setup();
|
|
render(<App />);
|
|
await screen.findByRole("alert");
|
|
expect(screen.queryByRole("button", { name: "Iniciar conversación de voz" })).not.toBeInTheDocument();
|
|
await user.click(screen.getByRole("button", { name: "Reintentar" }));
|
|
expect(await screen.findByRole("button", { name: "Iniciar conversación de voz" })).toBeEnabled();
|
|
});
|
|
it("loads the configured theme and persists voice settings across remounts under StrictMode", async () => {
|
|
const user = userEvent.setup();
|
|
const view = render(
|
|
<StrictMode>
|
|
<App />
|
|
</StrictMode>,
|
|
);
|
|
await user.click(await screen.findByRole("button", { name: "Configuración" }));
|
|
expect(document.documentElement.dataset.theme).toBe("white");
|
|
await user.selectOptions(screen.getByRole("combobox"), "Serena");
|
|
expect(localStorage.getItem("s2s.ws.voice")).toBeNull();
|
|
await user.click(screen.getByRole("button", { name: "Guardar" }));
|
|
expect(localStorage.getItem("s2s.ws.voice")).toBe("Serena");
|
|
expect(screen.queryByRole("dialog")).not.toBeInTheDocument();
|
|
view.unmount();
|
|
render(
|
|
<StrictMode>
|
|
<App />
|
|
</StrictMode>,
|
|
);
|
|
await user.click(await screen.findByRole("button", { name: "Configuración" }));
|
|
expect(screen.getByRole("combobox")).toHaveValue("Serena");
|
|
});
|
|
it("discards unsaved settings on cancel while keeping live noise-gate calibration", async () => {
|
|
const user = userEvent.setup();
|
|
render(<App />);
|
|
await user.click(await screen.findByRole("button", { name: "Configuración" }));
|
|
await user.selectOptions(screen.getByRole("combobox"), "Serena");
|
|
fireEvent.change(screen.getByRole("slider"), { target: { value: "-35" } });
|
|
await user.click(screen.getByRole("button", { name: "Cancelar" }));
|
|
await user.click(screen.getByRole("button", { name: "Configuración" }));
|
|
expect(screen.getByRole("combobox")).toHaveValue("Aiden");
|
|
expect(screen.getByRole("slider")).toHaveValue("-35");
|
|
expect(localStorage.getItem("s2s.ws.voice")).toBeNull();
|
|
expect(localStorage.getItem("s2s.ws.noiseGate")).toBe("-35");
|
|
});
|
|
it("saves and clears session-only RAG context and supports keyboard dialog dismissal", async () => {
|
|
const user = userEvent.setup();
|
|
render(<App />);
|
|
await user.click(await screen.findByRole("button", { name: "Contexto RAG personalizado" }));
|
|
await user.type(screen.getByRole("textbox"), "Datos de prueba para esta conversación");
|
|
await user.click(screen.getByRole("button", { name: "Enviar a la sesión" }));
|
|
expect(within(screen.getByRole("dialog")).getByRole("status")).toHaveTextContent("Contexto actualizado");
|
|
fireEvent(screen.getByRole("dialog"), new Event("cancel", { bubbles: true }));
|
|
expect(screen.queryByRole("dialog")).not.toBeInTheDocument();
|
|
await user.click(screen.getByRole("button", { name: "Contexto RAG personalizado" }));
|
|
expect(screen.getByRole("textbox")).toHaveValue("Datos de prueba para esta conversación");
|
|
await user.click(screen.getByRole("button", { name: "Vaciar" }));
|
|
expect(screen.getByRole("textbox")).toHaveValue("");
|
|
});
|
|
it("shows nested profile fields and discovers remote tools after bootstrap", async () => {
|
|
const user = userEvent.setup();
|
|
render(<App />);
|
|
await user.click(await screen.findByRole("button", { name: "Ver datos de Ana Vargas" }));
|
|
expect(within(screen.getByRole("dialog")).getByRole("link", { name: "ana@example.test" })).toHaveAttribute(
|
|
"href",
|
|
"mailto:ana@example.test",
|
|
);
|
|
await user.click(screen.getByRole("button", { name: "Cerrar" }));
|
|
await user.click(screen.getByRole("button", { name: "Herramientas" }));
|
|
await waitFor(() => {
|
|
expect(screen.getByText("get_reports")).toBeInTheDocument();
|
|
});
|
|
expect(screen.getByText("Consulta tus reportes")).toBeInTheDocument();
|
|
});
|
|
it("keeps debug controls out of the normal user screen", async () => {
|
|
window.history.replaceState({}, "", "/");
|
|
render(<App />);
|
|
expect(await screen.findByRole("button", { name: "Iniciar conversación de voz" })).toBeEnabled();
|
|
expect(screen.queryByRole("button", { name: "Configuración" })).not.toBeInTheDocument();
|
|
expect(screen.getByText("Aún no hay mensajes")).toBeInTheDocument();
|
|
});
|
|
});
|