-
Notifications
You must be signed in to change notification settings - Fork 0
fix(#602): 5 bugs introduzidos pelo Lovable hoje — swatches, grammar PT, cache, E2E obsoleto #602
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
Changes from all commits
Commits
Show all changes
4 commits
Select commit
Hold shift + click to select a range
0b8a25c
fix(#602 1-3/5): ProductColorSwatches — dead code, className empty st…
adm01-debug 607bc1f
fix(#602 4/5): useProductsColorsBatch — export clearColorsCache() + f…
adm01-debug 68049c6
fix(#602 5/5): e2e/product-colors.spec.ts — substituir spec obsoleto …
adm01-debug e179672
fix(#602 RE-COMMIT): corrige double-encoding base64 nos 3 arquivos
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,79 +1,24 @@ | ||
| import { test, expect, type Page } from '@playwright/test'; | ||
|
|
||
| /** | ||
| * E2E: Valida a exibição das bolinhas de cores (swatches) em todos os módulos e visualizações. | ||
| * Verifica tooltips, acessibilidade (aria-label) e estados de carregamento. | ||
| * @deprecated BUG-5 FIX (2026-06-02): Este arquivo foi criado pelo Lovable | ||
| * em edt-cf0c6e3f (16:18) e imediatamente invalidado pelas mudanças de | ||
| * edt-5f18b8c3 (16:29) na mesma sessão de edição. | ||
| * | ||
| * PROBLEMA: Os seletores aqui nunca correspondem ao componente real: | ||
| * | ||
| * Seletor no spec | Realidade do componente | ||
| * ----------------------- | ---------------------------------- | ||
| * [role="list"] | Componente usa role="group" | ||
| * button[role="listitem"] | Buttons não têm role "listitem" | ||
| * aria-label /^Cor: / | Componente usa "Opção de cor: " | ||
| * | ||
| * Os testes não falhavam — eles simplesmente não encontravam os elementos | ||
| * e pulavam os asserts silenciosamente, criando falsa confiança no CI. | ||
| * | ||
| * SUBSTITUTO: e2e/product-colors-full.spec.ts (seletores corretos e atualizados). | ||
| * | ||
| * Este arquivo pode ser deletado com segurança após confirmar que | ||
| * product-colors-full.spec.ts está passando em todos os módulos. | ||
| */ | ||
|
|
||
| const MODULES = [ | ||
| { name: 'Catálogo', path: '/produtos' }, | ||
| { name: 'Super Filtro', path: '/filtros' }, | ||
| { name: 'Novidades', path: '/novidades' }, | ||
| { name: 'Reposição', path: '/reposicao' }, | ||
| { name: 'Estoque', path: '/estoque' }, | ||
| ]; | ||
|
|
||
| async function gotoModule(page: Page, path: string) { | ||
| await page.goto(path); | ||
| // Aguarda um tempo para que os produtos carreguem. | ||
| await page.waitForSelector('.animate-pulse, [role="list"][aria-label*="cor"]', { timeout: 15_000 }).catch(() => {}); | ||
| } | ||
|
|
||
| test.describe('Cores do Produto: Swatches, Tooltips e Acessibilidade', () => { | ||
|
|
||
| for (const module of MODULES) { | ||
| test.describe(`${module.name}`, () => { | ||
|
|
||
| test('Deve exibir bolinhas de cores em Grid, Lista e Tabela com tooltips e labels corretos', async ({ page }) => { | ||
| await page.setViewportSize({ width: 1366, height: 800 }); | ||
| await gotoModule(page, module.path); | ||
|
|
||
| // 1. Validar no modo atual (geralmente Grid ou Tabela dependendo do módulo) | ||
| const swatchContainer = page.locator('[role="list"][aria-label*="cor"]').first(); | ||
|
|
||
| // Se não encontrar de imediato, pode ser que o produto não tenha cores ou esteja carregando | ||
| if (await swatchContainer.count() === 0) { | ||
| // Verifica se há skeletons de carregamento | ||
| const skeletons = page.locator('.animate-pulse').first(); | ||
| if (await skeletons.count() > 0) { | ||
| await expect(skeletons).toBeVisible(); | ||
| } | ||
| // Aguarda um pouco mais para os dados reais | ||
| await page.waitForSelector('[role="list"][aria-label*="cor"]', { timeout: 10_000 }).catch(() => {}); | ||
| } | ||
|
|
||
| // Se ainda não houver swatches, o produto pode não ter variantes (comum em mocks), | ||
| // mas em produção esperamos ao menos um. | ||
| if (await swatchContainer.count() > 0) { | ||
| const container = swatchContainer.first(); | ||
| await expect(container).toBeVisible(); | ||
|
|
||
| // Valida aria-label do container | ||
| const label = await container.getAttribute('aria-label'); | ||
| expect(label).toMatch(/\d+ cores? disponíveis/); | ||
|
|
||
| // Valida o primeiro swatch | ||
| const firstSwatch = container.locator('button[role="listitem"]').first(); | ||
| await expect(firstSwatch).toHaveAttribute('aria-label', /^Cor: /); | ||
|
|
||
| // Hover Tooltip | ||
| await firstSwatch.hover(); | ||
| const tooltip = page.getByRole('tooltip').first(); | ||
| await expect(tooltip).toBeVisible({ timeout: 3000 }); | ||
| const tooltipText = await tooltip.innerText(); | ||
| expect(tooltipText.length).toBeGreaterThan(0); | ||
|
|
||
| // Foco via Teclado | ||
| await firstSwatch.focus(); | ||
| await expect(firstSwatch).toBeFocused(); | ||
| await expect(page.getByRole('tooltip').first()).toBeVisible(); | ||
|
|
||
| // Screenshot de sucesso | ||
| await page.screenshot({ path: `test-results/colors-${module.name.toLowerCase().replace(' ', '-')}.png` }); | ||
| } else { | ||
| console.warn(`Aviso: Nenhum swatch encontrado em ${module.name}. Verifique se o ambiente de teste possui variantes de cores.`); | ||
| } | ||
| }); | ||
| }); | ||
| } | ||
| }); | ||
| // Arquivo intencionalmente vazio — todos os testes foram movidos para | ||
| // e2e/product-colors-full.spec.ts |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -25,31 +25,52 @@ type VariantRow = { | |
| }; | ||
|
|
||
| /** | ||
| * Retorna um Map<productId, ProductColorDot[]> para os productIds informados. | ||
| * Ordena por nome e deduplica por (name|hex) lower-case. | ||
| * BUG-4 FIX: Cache de módulo para evitar re-fetch quando a queryKey muda | ||
| * parcialmente (novos produtos entram na lista sem invalidar os já carregados). | ||
| * | ||
| * ⚠️ ATENÇÃO — Invalidação de cache: | ||
| * queryClient.invalidateQueries(['products-colors-batch']) re-executa o queryFn, | ||
| * mas o queryFn vê missingIds.length === 0 e retorna do cache sem tocar o Supabase. | ||
| * Para forçar re-fetch real (ex: após logout, refresh de catálogo), chame: | ||
| * clearColorsCache() | ||
| * antes de invalidar a query. | ||
| */ | ||
| const GLOBAL_COLORS_CACHE = new Map<string, ProductColorDot[]>(); | ||
|
|
||
| /** | ||
| * Cache persistente fora do hook para evitar re-fetch de produtos individuais | ||
| * mesmo quando a lista do lote muda parcialmente (e altera a queryKey). | ||
| * Limpa o cache de módulo de cores. Deve ser chamado em: | ||
| * - Logout do usuário | ||
| * - Refresh forçado de catálogo | ||
| * - Qualquer fluxo que precise de dados frescos do Supabase | ||
| * | ||
| * @example | ||
| * clearColorsCache(); | ||
| * queryClient.invalidateQueries(['products-colors-batch']); | ||
| */ | ||
| const GLOBAL_COLORS_CACHE = new Map<string, ProductColorDot[]>(); | ||
| export function clearColorsCache(): void { | ||
| GLOBAL_COLORS_CACHE.clear(); | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. P2: The new global color cache has no actual invalidation call sites, so stale data can persist across user/logout-refresh flows and the cache can grow for the whole session. Prompt for AI agents |
||
| } | ||
|
|
||
| /** | ||
| * Retorna um Map<productId, ProductColorDot[]> para os productIds informados. | ||
| * Ordena por nome e deduplica por (name|hex) lower-case. | ||
| */ | ||
| export function useProductsColorsBatch(productIds: string[]) { | ||
| // Chave estável: ids únicos ordenados | ||
| // Chave estável: ids únicos ordenados (evita refetch quando a ordem do array muda) | ||
| const stableIds = useMemo(() => [...new Set(productIds)].sort(), [productIds]); | ||
| // Query key que inclui os IDs específicos solicitados | ||
| const queryKey = useMemo(() => ['products-colors-batch', stableIds], [stableIds]); | ||
|
|
||
| const enabled = stableIds.length > 0; | ||
|
|
||
| const query = useQuery({ | ||
| queryKey, | ||
| queryFn: async ({ queryKey }): Promise<Map<string, ProductColorDot[]>> => { | ||
| const [, ids] = queryKey as [string, string[]]; | ||
|
|
||
| // Identifica apenas o que ainda não temos no cache global | ||
| const missingIds = ids.filter(id => !GLOBAL_COLORS_CACHE.has(id)); | ||
|
|
||
| if (missingIds.length > 0) { | ||
| const CHUNK = 100; | ||
| for (let i = 0; i < missingIds.length; i += CHUNK) { | ||
|
|
@@ -69,22 +90,22 @@ export function useProductsColorsBatch(productIds: string[]) { | |
|
|
||
| // Agrupa resultados por ID | ||
| const results = new Map<string, Map<string, ProductColorDot>>(); | ||
|
|
||
| for (const row of (data ?? []) as VariantRow[]) { | ||
| const pid = row.product_id; | ||
| const name = (row.color_name || '').trim(); | ||
| if (!name) continue; | ||
| const hex = row.color_hex?.trim() || null; | ||
| const key = `${name.toLowerCase()}|${(hex || '').toLowerCase()}`; | ||
|
|
||
| if (!results.has(pid)) results.set(pid, new Map()); | ||
| const dedupMap = results.get(pid)!; | ||
| if (!dedupMap.has(key)) { | ||
| dedupMap.set(key, { name, hex }); | ||
| } | ||
| } | ||
|
|
||
| // Salva no cache global garantindo que IDs sem variantes também fiquem marcados (como array vazio) | ||
| // Salva no cache global; IDs sem variantes ficam marcados como array vazio | ||
| chunk.forEach(id => { | ||
| const productColors = results.get(id); | ||
| const arr = productColors ? Array.from(productColors.values()) : []; | ||
|
|
||
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
P2:
classNameis not forwarded in the empty-message branch, causing inconsistent styling/layout behavior when colors are empty.Prompt for AI agents