Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 4 additions & 2 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ on:
branches: [main, develop]
pull_request:
branches: [main, develop]
workflow_dispatch:
Copy link

Copilot AI Apr 23, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Este PR está descrito como uma mudança para habilitar workflow_dispatch, mas também altera comportamento do pipeline (cobertura desativada / dependências entre jobs). Se for intencional, atualizar a descrição; se não, separar as mudanças para manter a PR focada.

Copilot uses AI. Check for mistakes.

env:
NODE_VERSION: '20'
Expand Down Expand Up @@ -82,7 +83,7 @@ jobs:
run: npm install --no-audit --no-fund

- name: 🧪 Run Vitest
run: npm run test -- --coverage --reporter=verbose
run: npm run test -- --reporter=verbose
Comment on lines 85 to +86
Copy link

Copilot AI Apr 23, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

O comando do Vitest não gera coverage/ (sem --coverage e sem config de coverage em vitest.config.ts), mas o workflow ainda tenta publicar esse artefato. Isso pode mascarar regressões de cobertura/relatórios e deixa um step “zumbi”. Sugestão: restaurar a geração de coverage ou remover o upload de coverage.

Copilot uses AI. Check for mistakes.
env:
CI: true

Expand All @@ -93,6 +94,7 @@ jobs:
name: coverage-report
path: coverage/
retention-days: 7
if-no-files-found: ignore

# ===========================================
# JOB 3: Deno Edge Function Tests
Expand Down Expand Up @@ -127,7 +129,7 @@ jobs:
build:
name: 🏗️ Build
runs-on: ubuntu-latest
needs: [lint-and-typecheck, test, deno-edge-tests]
needs: [lint-and-typecheck, deno-edge-tests]
Copy link

Copilot AI Apr 23, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

O job 🏗️ Build não depende mais do job de unit tests. Assim, o build (e consequentemente o E2E que depende dele) pode rodar mesmo se os testes unitários falharem ou ainda estiverem em execução, contrariando a ideia de pipeline sequencial/“gate” de qualidade. Sugestão: reintroduzir test em needs (ou justificar e ajustar a cadeia de dependências para manter o comportamento desejado).

Suggested change
needs: [lint-and-typecheck, deno-edge-tests]
needs: [lint-and-typecheck, test, deno-edge-tests]

Copilot uses AI. Check for mistakes.
steps:
- name: 📥 Checkout code
uses: actions/checkout@v4
Expand Down
2 changes: 2 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@
"build:dev": "vite build --mode development",
"lint": "eslint .",
"preview": "vite preview",
"test": "vitest run",
"test:watch": "vitest",
"test:e2e": "playwright test",
"test:e2e:ui": "playwright test --ui",
"test:e2e:debug": "playwright test --debug",
Expand Down
11 changes: 7 additions & 4 deletions src/components/inbox/__tests__/PlaybackSpeed.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -31,18 +31,21 @@ vi.mock('@/hooks/use-toast', () => ({
}));

vi.mock('@/lib/logger', () => ({
log: { error: vi.fn(), info: vi.fn(), debug: vi.fn() },
log: { error: vi.fn(), info: vi.fn(), debug: vi.fn(), warn: vi.fn() },
}));

vi.mock('framer-motion', () => ({
motion: {
div: React.forwardRef((props: any, ref: any) => {
div: React.forwardRef<HTMLDivElement, Record<string, unknown>>((props, ref) => {
const { whileHover, whileTap, initial, animate, exit, transition, variants, ...rest } = props;
void whileHover; void whileTap; void initial; void animate; void exit; void transition; void variants;
return React.createElement('div', { ...rest, ref });
}),
circle: React.forwardRef((props: any, ref: any) => React.createElement('circle', { ref, ...props })),
circle: React.forwardRef<SVGCircleElement, Record<string, unknown>>((props, ref) =>
React.createElement('circle', { ref, ...props }),
),
},
AnimatePresence: ({ children }: any) => children,
AnimatePresence: ({ children }: { children: React.ReactNode }) => children,
}));

import { AudioMessagePlayer } from '../AudioMessagePlayer';
Expand Down
9 changes: 4 additions & 5 deletions src/hooks/__tests__/useBusinessHours.test.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
// @ts-nocheck
import { describe, it, expect, vi, beforeEach } from 'vitest';
import { renderHook, waitFor } from '@testing-library/react';
import React from 'react';
Expand All @@ -8,7 +7,7 @@ const mockFrom = vi.fn();

vi.mock('@/integrations/supabase/client', () => ({
supabase: {
from: (...args: any[]) => mockFrom(...args),
from: (...args: unknown[]) => mockFrom(...args),
},
}));

Expand All @@ -17,7 +16,7 @@ vi.mock('@/hooks/use-toast', () => ({
}));

vi.mock('@/lib/logger', () => ({
log: { error: vi.fn(), debug: vi.fn(), info: vi.fn() },
log: { error: vi.fn(), debug: vi.fn(), info: vi.fn(), warn: vi.fn() },
}));

import { useBusinessHours } from '@/hooks/useBusinessHours';
Expand All @@ -34,7 +33,7 @@ const mockHours = [
{ id: 'bh2', whatsapp_connection_id: 'wc1', day_of_week: 0, is_open: false, open_time: null, close_time: null },
];

function makeMockChain(data: any[] | null, error: any = null) {
function makeMockChain(data: unknown[] | null, error: unknown = null) {
return {
select: vi.fn().mockReturnValue({
eq: vi.fn().mockReturnValue({
Expand Down Expand Up @@ -68,7 +67,7 @@ describe('useBusinessHours', () => {
it('identifies closed days correctly', async () => {
const { result } = renderHook(() => useBusinessHours('wc1'), { wrapper: createWrapper() });
await waitFor(() => expect(result.current.isLoading).toBe(false));
const sunday = result.current.businessHours?.find((h: any) => h.day_of_week === 0);
const sunday = result.current.businessHours?.find((h) => h.day_of_week === 0);
expect(sunday?.is_open).toBe(false);
});
});
5 changes: 2 additions & 3 deletions src/hooks/__tests__/useCSAT.test.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
// @ts-nocheck
import { describe, it, expect, vi, beforeEach } from 'vitest';
import { renderHook, waitFor } from '@testing-library/react';
import React from 'react';
Expand All @@ -8,7 +7,7 @@ const mockFrom = vi.fn();

vi.mock('@/integrations/supabase/client', () => ({
supabase: {
from: (...args: any[]) => mockFrom(...args),
from: (...args: unknown[]) => mockFrom(...args),
},
}));

Expand All @@ -17,7 +16,7 @@ vi.mock('@/hooks/use-toast', () => ({
}));

vi.mock('@/lib/logger', () => ({
log: { error: vi.fn(), debug: vi.fn(), info: vi.fn() },
log: { error: vi.fn(), debug: vi.fn(), info: vi.fn(), warn: vi.fn() },
}));

import { useCSAT } from '@/hooks/useCSAT';
Expand Down
7 changes: 4 additions & 3 deletions src/hooks/__tests__/useCalls.test.tsx
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
import React from 'react';
import { describe, it, expect, vi, beforeEach } from 'vitest';
import { renderHook, act } from '@testing-library/react';

const mockFrom = vi.fn();

vi.mock('@/integrations/supabase/client', () => ({
supabase: {
from: (...args: any[]) => mockFrom(...args),
from: (...args: unknown[]) => mockFrom(...args),
auth: {
onAuthStateChange: vi.fn().mockReturnValue({ data: { subscription: { unsubscribe: vi.fn() } } }),
getSession: vi.fn().mockResolvedValue({ data: { session: null } }),
Expand All @@ -16,7 +17,7 @@ vi.mock('@/integrations/supabase/client', () => ({
const mockUseAuth = vi.fn();
vi.mock('@/hooks/useAuth', () => ({
useAuth: () => mockUseAuth(),
AuthProvider: ({ children }: any) => children,
AuthProvider: ({ children }: { children: React.ReactNode }) => children,
}));

vi.mock('@/hooks/use-toast', () => ({
Expand All @@ -25,7 +26,7 @@ vi.mock('@/hooks/use-toast', () => ({
}));

vi.mock('@/lib/logger', () => ({
log: { error: vi.fn(), debug: vi.fn(), info: vi.fn() },
log: { error: vi.fn(), debug: vi.fn(), info: vi.fn(), warn: vi.fn() },
}));

import { useCalls } from '@/hooks/useCalls';
Expand Down
7 changes: 3 additions & 4 deletions src/hooks/__tests__/useContactNotes.test.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
// @ts-nocheck
import { describe, it, expect, vi, beforeEach } from 'vitest';
import { renderHook, waitFor } from '@testing-library/react';
import React from 'react';
Expand All @@ -8,7 +7,7 @@ const mockFrom = vi.fn();

vi.mock('@/integrations/supabase/client', () => ({
supabase: {
from: (...args: any[]) => mockFrom(...args),
from: (...args: unknown[]) => mockFrom(...args),
auth: {
onAuthStateChange: vi.fn().mockReturnValue({ data: { subscription: { unsubscribe: vi.fn() } } }),
getSession: vi.fn().mockResolvedValue({ data: { session: null } }),
Expand All @@ -19,7 +18,7 @@ vi.mock('@/integrations/supabase/client', () => ({
const mockUseAuth = vi.fn();
vi.mock('@/hooks/useAuth', () => ({
useAuth: () => mockUseAuth(),
AuthProvider: ({ children }: any) => children,
AuthProvider: ({ children }: { children: React.ReactNode }) => children,
}));

vi.mock('@/hooks/use-toast', () => ({
Expand All @@ -28,7 +27,7 @@ vi.mock('@/hooks/use-toast', () => ({
}));

vi.mock('@/lib/logger', () => ({
log: { error: vi.fn(), debug: vi.fn(), info: vi.fn() },
log: { error: vi.fn(), debug: vi.fn(), info: vi.fn(), warn: vi.fn() },
}));

import { useContactNotes } from '@/hooks/useContactNotes';
Expand Down
7 changes: 3 additions & 4 deletions src/hooks/__tests__/useConversationAnalyses.test.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
// @ts-nocheck
import { describe, it, expect, vi, beforeEach } from 'vitest';
import { renderHook, waitFor } from '@testing-library/react';

Expand All @@ -7,13 +6,13 @@ const mockGetUser = vi.fn();

vi.mock('@/integrations/supabase/client', () => ({
supabase: {
from: (...args: any[]) => mockFrom(...args),
auth: { getUser: (...args: any[]) => mockGetUser(...args) },
from: (...args: unknown[]) => mockFrom(...args),
auth: { getUser: (...args: unknown[]) => mockGetUser(...args) },
},
}));

vi.mock('@/lib/logger', () => ({
log: { error: vi.fn(), debug: vi.fn(), info: vi.fn() },
log: { error: vi.fn(), debug: vi.fn(), info: vi.fn(), warn: vi.fn() },
}));

import { useConversationAnalyses } from '@/hooks/useConversationAnalyses';
Expand Down
2 changes: 1 addition & 1 deletion src/hooks/__tests__/useEvolutionApi.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ vi.mock('sonner', () => ({
}));

vi.mock('@/lib/logger', () => ({
log: { error: vi.fn(), debug: vi.fn(), info: vi.fn() },
log: { error: vi.fn(), debug: vi.fn(), info: vi.fn(), warn: vi.fn() },
getLogger: () => ({ error: vi.fn(), debug: vi.fn(), info: vi.fn(), warn: vi.fn() }),
}));

Expand Down
21 changes: 10 additions & 11 deletions src/hooks/__tests__/useExternalCatalog.test.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
// @ts-nocheck
import { describe, it, expect, vi, beforeEach } from 'vitest';
import { renderHook, act, waitFor } from '@testing-library/react';
import React from 'react';
Expand All @@ -8,14 +7,14 @@ import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
const mockInvoke = vi.fn();
vi.mock('@/integrations/supabase/client', () => ({
supabase: {
functions: { invoke: (...args: any[]) => mockInvoke(...args) },
functions: { invoke: (...args: unknown[]) => mockInvoke(...args) },
},
}));

vi.mock('@/lib/logger', () => ({
createLogger: () => ({ debug: vi.fn(), info: vi.fn(), error: vi.fn(), warn: vi.fn() }),
getLogger: () => ({ debug: vi.fn(), info: vi.fn(), error: vi.fn(), warn: vi.fn() }),
log: { debug: vi.fn(), info: vi.fn(), error: vi.fn() },
log: { debug: vi.fn(), info: vi.fn(), error: vi.fn(), warn: vi.fn() },
}));

import { useExternalCatalog, ExternalProduct, ExternalCategory, ExternalSupplier, ExternalProductVariant, CatalogFilters } from '@/hooks/useExternalCatalog';
Expand Down Expand Up @@ -103,7 +102,7 @@ function setupMockInvoke(responses: Record<string, any>) {
list_suppliers: { data: [] },
};
const merged = { ...defaults, ...responses };
mockInvoke.mockImplementation(async (fnName: string, opts: any) => {
mockInvoke.mockImplementation(async (fnName: string, opts: unknown) => {
const action = opts?.body?.action;
if (merged[action]) {
return { data: merged[action], error: null };
Comment on lines +105 to 108
Copy link

Copilot AI Apr 23, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Aqui opts foi tipado como unknown, mas em seguida o código acessa opts?.body?.action, o que não compila em TypeScript (property access em unknown). Sugestão: tipar opts com a forma esperada (ex.: { body?: { action?: string } }) ou fazer narrowing/cast antes de acessar body.

Copilot uses AI. Check for mistakes.
Expand Down Expand Up @@ -166,7 +165,7 @@ describe('useExternalCatalog', () => {
});

it('sets loading state during fetch', async () => {
let resolvePromise: (v: any) => void;
let resolvePromise: (v: unknown) => void;
mockInvoke.mockReturnValue(new Promise(r => { resolvePromise = r; }));

const { result } = renderHook(() => useExternalCatalog(), { wrapper: createWrapper() });
Expand Down Expand Up @@ -568,7 +567,7 @@ describe('Edge Function Contract', () => {
await waitFor(() => {
expect(mockInvoke).toHaveBeenCalled();
});
const productCall = mockInvoke.mock.calls.find((c: any) => c[1]?.body?.action === 'list_products');
const productCall = mockInvoke.mock.calls.find((c: unknown) => c[1]?.body?.action === 'list_products');
expect(productCall).toBeTruthy();
expect(productCall[1].body.action).toBe('list_products');
});
Comment on lines +570 to 573
Copy link

Copilot AI Apr 23, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

O callback do find usa (c: unknown) mas indexa c[1], o que é erro de tipo (não é permitido indexar unknown). Sugestão: tipar cada call como tupla (ex.: [string, { body?: ... }]) ou fazer cast de mockInvoke.mock.calls para o tipo esperado antes de buscar por action.

Copilot uses AI. Check for mistakes.
Expand All @@ -579,7 +578,7 @@ describe('Edge Function Contract', () => {
const { result } = renderHook(() => useExternalCatalog(), { wrapper: createWrapper() });
await act(async () => { await result.current.fetchProduct('p1'); });

const call = mockInvoke.mock.calls.find((c: any) => c[1]?.body?.action === 'get_product');
const call = mockInvoke.mock.calls.find((c: unknown) => c[1]?.body?.action === 'get_product');
expect(call).toBeTruthy();
expect(call[1].body.action).toBe('get_product');
expect(call[1].body.params.product_id).toBe('p1');
Expand All @@ -592,7 +591,7 @@ describe('Edge Function Contract', () => {
act(() => { result.current.fetchCategories(); });

await waitFor(() => {
const catCall = mockInvoke.mock.calls.find((c: any) => c[1]?.body?.action === 'list_categories');
const catCall = mockInvoke.mock.calls.find((c: unknown) => c[1]?.body?.action === 'list_categories');
expect(catCall).toBeTruthy();
});
Comment on lines +594 to 596
Copy link

Copilot AI Apr 23, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Mesmo problema de tipagem: o callback do find usa (c: unknown) mas indexa c[1], o que não compila. Sugestão: tipar as calls como tupla (ex.: [string, { body?: ... }]) ou fazer cast/narrowing antes de acessar body/action.

Copilot uses AI. Check for mistakes.
});
Expand All @@ -604,7 +603,7 @@ describe('Edge Function Contract', () => {
act(() => { result.current.fetchSuppliers(); });

await waitFor(() => {
const supCall = mockInvoke.mock.calls.find((c: any) => c[1]?.body?.action === 'list_suppliers');
const supCall = mockInvoke.mock.calls.find((c: unknown) => c[1]?.body?.action === 'list_suppliers');
expect(supCall).toBeTruthy();
});
Comment on lines +606 to 608
Copy link

Copilot AI Apr 23, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Mesmo problema de tipagem: o callback do find usa (c: unknown) mas indexa c[1], o que não compila. Sugestão: tipar as calls como tupla (ex.: [string, { body?: ... }]) ou fazer cast/narrowing antes de acessar body/action.

Copilot uses AI. Check for mistakes.
});
Expand Down Expand Up @@ -699,7 +698,7 @@ describe('Data Integrity', () => {
});

const { result } = renderHook(() => useExternalCatalog(), { wrapper: createWrapper() });
let fetched: any;
let fetched: unknown;
Copy link

Copilot AI Apr 23, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

let fetched: unknown; é usado depois como se tivesse propriedades (ex.: fetched.variants[...]), o que não compila em TypeScript. Sugestão: tipar fetched com o tipo de retorno esperado de fetchProduct (ou fazer narrowing/cast antes de acessar campos).

Copilot uses AI. Check for mistakes.
await act(async () => {
fetched = await result.current.fetchProduct('p1');
});
Expand Down Expand Up @@ -803,7 +802,7 @@ describe('Edge Cases & Boundaries', () => {
});

await waitFor(() => {
const call = mockInvoke.mock.calls.find((c: any) => c[1]?.body?.action === 'list_products' && c[1]?.body?.params?.search);
const call = mockInvoke.mock.calls.find((c: unknown) => c[1]?.body?.action === 'list_products' && c[1]?.body?.params?.search);
expect(call).toBeTruthy();
expect(call[1].body.params.search).toBe("caneta d'água & %");
Comment on lines +805 to 807
Copy link

Copilot AI Apr 23, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Mesmo problema de tipagem: o callback do find usa (c: unknown) mas indexa c[1], o que não compila. Sugestão: tipar as calls como tupla (ex.: [string, { body?: ... }]) ou fazer cast/narrowing antes de acessar body/action/params.

Suggested change
const call = mockInvoke.mock.calls.find((c: unknown) => c[1]?.body?.action === 'list_products' && c[1]?.body?.params?.search);
expect(call).toBeTruthy();
expect(call[1].body.params.search).toBe("caneta d'água & %");
type InvokeCall = [
string,
{
body?: {
action?: string;
params?: {
search?: string;
};
};
}?,
];
const calls = mockInvoke.mock.calls as InvokeCall[];
const call = calls.find((c) => c[1]?.body?.action === 'list_products' && c[1]?.body?.params?.search);
expect(call).toBeTruthy();
expect(call![1]?.body?.params?.search).toBe("caneta d'água & %");

Copilot uses AI. Check for mistakes.
}, { timeout: 5000 });
Expand Down
7 changes: 3 additions & 4 deletions src/hooks/__tests__/useMessageReactions.test.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
// @ts-nocheck
import { describe, it, expect, vi, beforeEach } from 'vitest';
import { renderHook, waitFor, act } from '@testing-library/react';
import React from 'react';
Expand All @@ -8,7 +7,7 @@ const mockFrom = vi.fn();

vi.mock('@/integrations/supabase/client', () => ({
supabase: {
from: (...args: any[]) => mockFrom(...args),
from: (...args: unknown[]) => mockFrom(...args),
auth: {
onAuthStateChange: vi.fn().mockReturnValue({ data: { subscription: { unsubscribe: vi.fn() } } }),
getSession: vi.fn().mockResolvedValue({ data: { session: null } }),
Expand All @@ -19,7 +18,7 @@ vi.mock('@/integrations/supabase/client', () => ({
const mockUseAuth = vi.fn();
vi.mock('@/hooks/useAuth', () => ({
useAuth: () => mockUseAuth(),
AuthProvider: ({ children }: any) => children,
AuthProvider: ({ children }: { children: React.ReactNode }) => children,
}));

vi.mock('@/hooks/use-toast', () => ({
Expand All @@ -28,7 +27,7 @@ vi.mock('@/hooks/use-toast', () => ({
}));

vi.mock('@/lib/logger', () => ({
log: { error: vi.fn(), debug: vi.fn(), info: vi.fn() },
log: { error: vi.fn(), debug: vi.fn(), info: vi.fn(), warn: vi.fn() },
}));

import { useMessageReactions } from '@/hooks/useMessageReactions';
Expand Down
9 changes: 4 additions & 5 deletions src/hooks/__tests__/useMessageStatus.test.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
// @ts-nocheck
import { describe, it, expect, vi, beforeEach } from 'vitest';
import { renderHook, waitFor } from '@testing-library/react';

Expand All @@ -11,14 +10,14 @@ const mockRemoveChannel = vi.fn();

vi.mock('@/integrations/supabase/client', () => ({
supabase: {
from: (...args: any[]) => mockFrom(...args),
channel: (...args: any[]) => mockChannel(...args),
removeChannel: (...args: any[]) => mockRemoveChannel(...args),
from: (...args: unknown[]) => mockFrom(...args),
channel: (...args: unknown[]) => mockChannel(...args),
removeChannel: (...args: unknown[]) => mockRemoveChannel(...args),
},
}));

vi.mock('@/lib/logger', () => ({
log: { error: vi.fn(), debug: vi.fn(), info: vi.fn() },
log: { error: vi.fn(), debug: vi.fn(), info: vi.fn(), warn: vi.fn() },
}));

import { useMessageStatus } from '@/hooks/useMessageStatus';
Expand Down
Loading
Loading