From 86c52197d2849fcc30da06a240727cf32b7fe4f6 Mon Sep 17 00:00:00 2001 From: adm01-debug Date: Fri, 22 May 2026 20:34:00 -0300 Subject: [PATCH] fix(test): move AIRecommendationsPanel import to top-level MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Resolves 2 vitest timeouts (Test timed out in 5000ms) that broke the 'Test Coverage' job and the 'Lint, Typecheck & Test > Run tests' step in CI. Root cause: each of the 16 it() blocks was doing `const { AIRecommendationsPanel } = await import("@/components/ai/AIRecommendationsPanel")` inside the test body. Under vitest pool=threads + maxThreads=2, the dynamic import competes for CPU with parallel workers (notably magic-up-result-panel-keyboard.test.tsx with 276 tests) and blows past the 5000ms testTimeout default — specifically on the first 2 tests that hit the cold import path. Fix: move the import to top-level of the test file. vi.mock is hoisted automatically by vitest, so the mock for useAIRecommendations is still applied before the import resolves. Lab validation (clone fresh of main 1cb3b47): Before: - isolated: 16/16 pass, duration tests=3244ms, test 1 took 2888ms - in test:quality suite: 2/16 timeout (5032ms + 5002ms) After: - isolated: 16/16 pass, duration tests=666ms (~80% faster) - in test:quality suite: AIRecommendationsPanel no longer in failure list Diff: +1 / -16 lines in a single test file. No production code change. Note: this fix unmasks 24 pre-existing failures in 5 other test files that were previously hidden because vitest output truncated after AIRecommendationsPanel consumed 15.5s of the suite budget. Those will be tracked in separate issues: - DevRoute.test.tsx (5 failures, route "/auth" + Login Page) - NotificationDrawer-trigger-fetch-counters.test.tsx (4, useAuth) - AdminConexoesAccess.test.tsx (1) - NotificationDrawer-debounce-config.test.tsx (6, useAuth) - NotificationDrawer-a11y.test.tsx (8, useAuth) --- .../quotes/AIRecommendationsPanel.test.tsx | 17 +---------------- 1 file changed, 1 insertion(+), 16 deletions(-) diff --git a/tests/components/quotes/AIRecommendationsPanel.test.tsx b/tests/components/quotes/AIRecommendationsPanel.test.tsx index 346f895c2..df13b38a2 100644 --- a/tests/components/quotes/AIRecommendationsPanel.test.tsx +++ b/tests/components/quotes/AIRecommendationsPanel.test.tsx @@ -48,6 +48,7 @@ vi.mock("@/hooks/intelligence/useAIRecommendations", () => ({ import { useAIRecommendations } from "@/hooks/intelligence/useAIRecommendations"; import type { ProductForRecommendation } from "@/hooks/intelligence/useAIRecommendations"; +import { AIRecommendationsPanel } from "@/components/ai/AIRecommendationsPanel"; const PRODUCTS: ProductForRecommendation[] = [ { id: "prod-1", name: "Caneta Esferográfica", category: "Escritório", description: "Caneta azul" }, @@ -64,7 +65,6 @@ describe("AIRecommendationsPanel", () => { // ── Form rendering ───────────────────────────────────────────── it("renders client form fields in default state", async () => { - const { AIRecommendationsPanel } = await import("@/components/ai/AIRecommendationsPanel"); renderWithProviders(); expect(screen.getByRole("textbox", { name: /nome do cliente/i })).toBeInTheDocument(); @@ -74,14 +74,12 @@ describe("AIRecommendationsPanel", () => { }); it("shows product count in the action area", async () => { - const { AIRecommendationsPanel } = await import("@/components/ai/AIRecommendationsPanel"); renderWithProviders(); expect(screen.getByText(/3 produtos disponíveis/i)).toBeInTheDocument(); }); it("shows singular 'produto disponível' when exactly 1 product", async () => { - const { AIRecommendationsPanel } = await import("@/components/ai/AIRecommendationsPanel"); renderWithProviders(); expect(screen.getByText(/1 produto disponível/i)).toBeInTheDocument(); @@ -90,21 +88,18 @@ describe("AIRecommendationsPanel", () => { // ── Button disabled/enabled ──────────────────────────────────── it("button is disabled when no client name and no products", async () => { - const { AIRecommendationsPanel } = await import("@/components/ai/AIRecommendationsPanel"); renderWithProviders(); expect(screen.getByRole("button", { name: /gerar recomendações/i })).toBeDisabled(); }); it("button is disabled when products provided but client name is empty", async () => { - const { AIRecommendationsPanel } = await import("@/components/ai/AIRecommendationsPanel"); renderWithProviders(); expect(screen.getByRole("button", { name: /gerar recomendações/i })).toBeDisabled(); }); it("button is disabled when client name set but products array is empty", async () => { - const { AIRecommendationsPanel } = await import("@/components/ai/AIRecommendationsPanel"); renderWithProviders( ); @@ -113,7 +108,6 @@ describe("AIRecommendationsPanel", () => { }); it("button is enabled when both client name and products are provided", async () => { - const { AIRecommendationsPanel } = await import("@/components/ai/AIRecommendationsPanel"); renderWithProviders( ); @@ -129,7 +123,6 @@ describe("AIRecommendationsPanel", () => { isLoading: true, }); - const { AIRecommendationsPanel } = await import("@/components/ai/AIRecommendationsPanel"); renderWithProviders( ); @@ -146,7 +139,6 @@ describe("AIRecommendationsPanel", () => { error: "Falha na conexão com a IA", }); - const { AIRecommendationsPanel } = await import("@/components/ai/AIRecommendationsPanel"); renderWithProviders(); const alert = screen.getByRole("alert"); @@ -167,7 +159,6 @@ describe("AIRecommendationsPanel", () => { ], }); - const { AIRecommendationsPanel } = await import("@/components/ai/AIRecommendationsPanel"); renderWithProviders(); expect(screen.getByText("Caneta Esferográfica")).toBeInTheDocument(); @@ -189,7 +180,6 @@ describe("AIRecommendationsPanel", () => { insights: "Cliente prefere itens premium e sustentáveis.", }); - const { AIRecommendationsPanel } = await import("@/components/ai/AIRecommendationsPanel"); renderWithProviders(); expect(screen.getByText("Insights da IA")).toBeInTheDocument(); @@ -205,7 +195,6 @@ describe("AIRecommendationsPanel", () => { insights: "", }); - const { AIRecommendationsPanel } = await import("@/components/ai/AIRecommendationsPanel"); renderWithProviders(); expect(screen.queryByText("Insights da IA")).not.toBeInTheDocument(); @@ -214,7 +203,6 @@ describe("AIRecommendationsPanel", () => { // ── Empty state ──────────────────────────────────────────────── it("shows empty state prompt when data is null and no recommendations", async () => { - const { AIRecommendationsPanel } = await import("@/components/ai/AIRecommendationsPanel"); renderWithProviders(); expect( @@ -234,7 +222,6 @@ describe("AIRecommendationsPanel", () => { recommendations: [{ productId: "prod-1", score: 0.9, reason: "Ideal" }], }); - const { AIRecommendationsPanel } = await import("@/components/ai/AIRecommendationsPanel"); renderWithProviders( ); @@ -247,7 +234,6 @@ describe("AIRecommendationsPanel", () => { // ── hideClientForm ───────────────────────────────────────────── it("hides the client form when hideClientForm prop is true", async () => { - const { AIRecommendationsPanel } = await import("@/components/ai/AIRecommendationsPanel"); renderWithProviders( ); @@ -265,7 +251,6 @@ describe("AIRecommendationsPanel", () => { recommendations: [{ productId: "prod-1", score: 0.8, reason: "Boa escolha" }], }); - const { AIRecommendationsPanel } = await import("@/components/ai/AIRecommendationsPanel"); renderWithProviders(); const limparBtn = screen.getByRole("button", { name: /limpar resultados/i });