diff --git a/package-lock.json b/package-lock.json index 0d2c56a99..53708c878 100644 --- a/package-lock.json +++ b/package-lock.json @@ -96,6 +96,7 @@ "zustand": "^4.5.0" }, "devDependencies": { + "@axe-core/playwright": "^4.11.3", "@playwright/test": "^1.59.1", "@testing-library/dom": "^10.4.1", "@testing-library/jest-dom": "^6.9.1", @@ -207,6 +208,29 @@ "devOptional": true, "license": "MIT" }, + "node_modules/@axe-core/playwright": { + "version": "4.11.3", + "resolved": "https://registry.npmjs.org/@axe-core/playwright/-/playwright-4.11.3.tgz", + "integrity": "sha512-h/kfksv4F0cVIDlKpT4700OehdRgpvuVskuQ2nb7/JmtWUXpe9ftHAPtwyXGvVSsa6SJ64A9ER7Zrzc/sIvC4w==", + "dev": true, + "license": "MPL-2.0", + "dependencies": { + "axe-core": "~4.11.4" + }, + "peerDependencies": { + "playwright-core": ">= 1.0.0" + } + }, + "node_modules/@axe-core/playwright/node_modules/axe-core": { + "version": "4.11.4", + "resolved": "https://registry.npmjs.org/axe-core/-/axe-core-4.11.4.tgz", + "integrity": "sha512-KunSNx+TVpkAw/6ULfhnx+HWRecjqZGTOyquAoWHYLRSdK1tB5Ihce1ZW+UY3fj33bYAFWPu7W/GRSmmrCGuxA==", + "dev": true, + "license": "MPL-2.0", + "engines": { + "node": ">=4" + } + }, "node_modules/@babel/code-frame": { "version": "7.29.0", "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.29.0.tgz", diff --git a/src/components/inventory/risk/RiskTooltip.tsx b/src/components/inventory/risk/RiskTooltip.tsx index 22b6d1b0d..8770872f1 100644 --- a/src/components/inventory/risk/RiskTooltip.tsx +++ b/src/components/inventory/risk/RiskTooltip.tsx @@ -1,5 +1,4 @@ import { forwardRef } from 'react'; -import { cn } from '@/lib/utils'; interface RiskChartDataPoint { fullDate?: string; @@ -25,36 +24,38 @@ export const RiskTooltip = forwardRef<

{data.fullDate}

- +
Estoque Atual - {data.stockClose != null ? data.stockClose.toLocaleString('pt-BR') : '—'} + {typeof data.stockClose === 'number' ? data.stockClose.toLocaleString('pt-BR') : '—'}
{(data.depleted || data.restocked) && (
- {data.depleted !== null && data.depleted > 0 && ( + {typeof data.depleted === 'number' && data.depleted > 0 && (
Saídas -

-{data.depleted}

+

-{data.depleted}

)} - {data.restocked !== null && data.restocked > 0 && ( + {typeof data.restocked === 'number' && data.restocked > 0 && (
Entradas -

+{data.restocked}

+

+{data.restocked}

)}
)} {data.restockDetected && ( -
+
- Reposição Detectada + + Reposição Detectada +
)}
diff --git a/src/components/products/ProductCategoryBadges.test.tsx b/src/components/products/ProductCategoryBadges.test.tsx index 0f2c9dbeb..f9c11644f 100644 --- a/src/components/products/ProductCategoryBadges.test.tsx +++ b/src/components/products/ProductCategoryBadges.test.tsx @@ -1,10 +1,10 @@ +import type { ComponentProps } from 'react'; import { render, screen, fireEvent } from '@testing-library/react'; import { ProductCategoryBadges } from './ProductCategoryBadges'; import { BrowserRouter } from 'react-router-dom'; import { TooltipProvider } from '@/components/ui/tooltip'; import { describe, it, expect, vi, beforeEach } from 'vitest'; - const mockNavigate = vi.fn(); // Mock do hook useCategoryIcons @@ -25,8 +25,7 @@ vi.mock('react-router-dom', async () => { }; }); -const defaultProps = { - +const defaultProps: ComponentProps = { category: { id: 'cat-1', name: 'Squeeze' }, groups: [ { id: 'cat-2', name: 'Garrafas' }, @@ -47,11 +46,10 @@ const renderComponent = (props = defaultProps) => { - + , ); }; - describe('ProductCategoryBadges', () => { beforeEach(() => { vi.clearAllMocks(); @@ -68,7 +66,7 @@ describe('ProductCategoryBadges', () => { renderComponent(); const mainCategory = screen.getByText('Squeeze').parentElement; if (mainCategory) fireEvent.click(mainCategory); - + expect(mockNavigate).toHaveBeenCalledWith('/filtros?categories=uuid-123'); }); @@ -76,7 +74,7 @@ describe('ProductCategoryBadges', () => { renderComponent({ ...defaultProps, categoryUuid: null }); const mainCategory = screen.getByText('Squeeze').parentElement; if (mainCategory) fireEvent.click(mainCategory); - + expect(mockNavigate).toHaveBeenCalledWith('/filtros?categories=cat-1'); }); @@ -130,10 +128,10 @@ describe('ProductCategoryBadges', () => { }); it('não deve renderizar nada se não houver categorias', () => { - const { container } = renderComponent({ - ...defaultProps, - category: null as any, - groups: [] + const { container } = renderComponent({ + ...defaultProps, + category: null as unknown as ComponentProps['category'], + groups: [], }); expect(container.firstChild).toBeNull(); }); diff --git a/src/contexts/ThemeContext.tsx b/src/contexts/ThemeContext.tsx index c4d2f6787..b0a288933 100644 --- a/src/contexts/ThemeContext.tsx +++ b/src/contexts/ThemeContext.tsx @@ -20,6 +20,7 @@ interface ThemeProviderProps { children: ReactNode; defaultTheme?: Theme; storageKey?: string; + tooltipStorageKey?: string; } export function ThemeProvider({ @@ -117,8 +118,11 @@ export function ThemeProvider({ }); }; - if (typeof document !== 'undefined' && 'startViewTransition' in document) { - (document as any).startViewTransition(apply); + const docWithViewTransition = document as Document & { + startViewTransition?: (callback: () => void) => void; + }; + if (typeof document !== 'undefined' && docWithViewTransition.startViewTransition) { + docWithViewTransition.startViewTransition(apply); } else { apply(); } @@ -159,8 +163,10 @@ export function useTheme() { return { theme: 'light', actualTheme: 'light', + tooltipStyle: 'standard', setTheme: () => {}, toggleTheme: () => {}, + setTooltipStyle: () => {}, isFallback: true, } as ThemeContextType; } diff --git a/src/pages/filters/useFiltersPageState.ts b/src/pages/filters/useFiltersPageState.ts index 7a2ef10c7..0778b7d13 100644 --- a/src/pages/filters/useFiltersPageState.ts +++ b/src/pages/filters/useFiltersPageState.ts @@ -487,6 +487,7 @@ export function useFiltersPageState() { hasCategoryFilter, categoryFilteredProductIds, isLoadingCategoryFilter, + categoryFilterError, hasColorFilter, colorFilteredProductIds, isLoadingColorFilter, diff --git a/src/tests/B2BProductDetailFlow.test.tsx b/src/tests/B2BProductDetailFlow.test.tsx index f24dee75d..cc61a10d4 100644 --- a/src/tests/B2BProductDetailFlow.test.tsx +++ b/src/tests/B2BProductDetailFlow.test.tsx @@ -1,4 +1,4 @@ -import { render, screen, fireEvent, waitFor } from '@testing-library/react'; +import { render, screen, fireEvent } from '@testing-library/react'; import { ProductDetailHero } from '@/pages/products/product-detail/ProductDetailHero'; import { BrowserRouter } from 'react-router-dom'; import { TooltipProvider } from '@/components/ui/tooltip'; @@ -38,7 +38,7 @@ vi.mock('@/hooks/products/useCategoryIcons', () => ({ })); vi.mock('@/components/products/SingleVariantPicker', () => ({ - SingleVariantPicker: ({ onSelect }: { onSelect: (v: any) => void }) => ( + SingleVariantPicker: ({ onSelect }: { onSelect: (v: unknown) => void }) => ( @@ -62,20 +62,20 @@ const mockProduct: Product = { category: { id: 1, name: 'Brindes' }, category_id: 'cat-uuid-1', supplier: { id: 'supp-1', name: 'Fornecedor A' }, - tags: { - publicoAlvo: ['Executivos'], - datasComemorativas: [], - endomarketing: [], - ramo: [], - nicho: [] + tags: { + publicoAlvo: ['Executivos'], + datasComemorativas: [], + endomarketing: [], + ramo: [], + nicho: [], }, priceUpdatedAt: new Date().toISOString(), leadTimeDays: 5, -} as any; +} as unknown as Product; const queryClient = new QueryClient(); -const renderPDP = (tags = {}) => { +const renderPDP = () => { return render( @@ -92,11 +92,10 @@ const renderPDP = (tags = {}) => { onOpenPackagingModal={() => {}} onOpenFutureStock={() => {}} onOpenSupplierComparison={() => {}} - tags={tags} /> - + , ); }; @@ -107,47 +106,51 @@ describe('B2B Product Detail Flow Integration', () => { it('Fluxo 1: Adicionar ao Carrinho (Quick Add)', async () => { renderPDP(); - + const cartButton = screen.getByText('Carrinho'); fireEvent.click(cartButton); const variantButton = await screen.findByText('Mock Variant'); fireEvent.click(variantButton); - + const confirmAdd = await screen.findByTestId('product-card-add-to-cart'); fireEvent.click(confirmAdd); - expect(mockAddToActiveCart).toHaveBeenCalledWith(expect.objectContaining({ - product_id: 'prod-123', - quantity: 50, - color_name: 'Azul' - })); + expect(mockAddToActiveCart).toHaveBeenCalledWith( + expect.objectContaining({ + product_id: 'prod-123', + quantity: 50, + color_name: 'Azul', + }), + ); }); it('Fluxo 2: Navegação por Categorias', () => { renderPDP(); const categoryBadge = screen.getByText('Brindes'); fireEvent.click(categoryBadge.parentElement!); - - expect(mockNavigate).toHaveBeenCalledWith(expect.stringContaining('/filtros?categories=cat-uuid-1')); + + expect(mockNavigate).toHaveBeenCalledWith( + expect.stringContaining('/filtros?categories=cat-uuid-1'), + ); }); it('Fluxo 3: Abrir Modais de Ação Rápida (Preços)', async () => { renderPDP(); - + const pricesButton = screen.getByText('Preços'); fireEvent.click(pricesButton); - + const title = await screen.findByText(/Tabela de Preços/i); expect(title).toBeDefined(); }); it('Fluxo 4: Verificação de Tags e Nichos (Indicação)', async () => { - renderPDP({ 'Público-Alvo': ['Executivos'] }); - + renderPDP(); + const indicationButton = screen.getByText('Indicação'); fireEvent.click(indicationButton); - + const modalTitle = await screen.findByText(/Indicado para/i); expect(modalTitle).toBeDefined(); }); diff --git a/src/tests/ThemeInitializer.test.tsx b/src/tests/ThemeInitializer.test.tsx index dec0eb7d6..caf31605e 100644 --- a/src/tests/ThemeInitializer.test.tsx +++ b/src/tests/ThemeInitializer.test.tsx @@ -34,8 +34,10 @@ describe('ThemeInitializer', () => { const mockContext = { theme: 'light' as const, actualTheme: 'light' as const, + tooltipStyle: 'standard' as const, setTheme: vi.fn(), toggleTheme: vi.fn(), + setTooltipStyle: vi.fn(), }; render( @@ -54,8 +56,10 @@ describe('ThemeInitializer', () => { value={{ theme: 'light', actualTheme: 'light', + tooltipStyle: 'standard', setTheme: vi.fn(), toggleTheme: vi.fn(), + setTooltipStyle: vi.fn(), }} > @@ -70,8 +74,10 @@ describe('ThemeInitializer', () => { value={{ theme: 'dark', actualTheme: 'dark', + tooltipStyle: 'standard', setTheme: vi.fn(), toggleTheme: vi.fn(), + setTooltipStyle: vi.fn(), }} >