-
Notifications
You must be signed in to change notification settings - Fork 0
Lovable sync 1780253995 #552
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
Closed
Closed
Changes from all commits
Commits
Show all changes
15 commits
Select commit
Hold shift + click to select a range
fa7c094
Changes
lovable-dev[bot] d17c197
Fast Visual Edit
lovable-dev[bot] b9376f9
Changes
lovable-dev[bot] 90b1060
Changes
lovable-dev[bot] b32d60b
Changes
lovable-dev[bot] 3fed361
Changes
lovable-dev[bot] a77ca09
Fast Visual Edit
lovable-dev[bot] f5f0f89
Changes
lovable-dev[bot] dcbf435
Changes
lovable-dev[bot] 4c8608a
Changes
lovable-dev[bot] a8038e1
Changes
lovable-dev[bot] 2cf806b
Changes
lovable-dev[bot] 5fd957f
Fast Visual Edit
lovable-dev[bot] 38eb7db
Changes
lovable-dev[bot] 1ed69b4
Removed Supabase connection card
lovable-dev[bot] 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
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 |
|---|---|---|
| @@ -0,0 +1,90 @@ | ||
| import { test, expect } from '@playwright/test'; | ||
|
|
||
| test.describe('Novelty Card Variations @mobile', () => { | ||
| test.beforeEach(async ({ context }) => { | ||
| // Mock the novelties API to provide specific edge cases | ||
| await context.route('**/functions/v1/novelties**', async route => { | ||
|
|
||
| await route.fulfill({ | ||
| status: 200, | ||
| contentType: 'application/json', | ||
| body: JSON.stringify([ | ||
| { | ||
| novelty_id: 'var-1', | ||
| product_id: 'p-1', | ||
| product_name: 'Produto com título extremamente longo que deve ocupar pelo menos três ou quatro linhas no grid para testar o alinhamento e o truncamento de texto', | ||
| product_sku: 'SKU-LONG-TITLE', | ||
| product_image: 'https://placehold.co/400x400?text=Normal', | ||
| base_price: 150.50, | ||
| stock_quantity: 100, | ||
| stock_status: 'in-stock', | ||
| detected_at: new Date().toISOString(), | ||
| days_remaining: 30, | ||
| supplier_name: 'Fornecedor A', | ||
| category_name: 'Categoria Alpha' | ||
| }, | ||
|
Comment on lines
+11
to
+24
|
||
| { | ||
| novelty_id: 'var-2', | ||
| product_id: 'p-2', | ||
| product_name: 'Preço sob consulta', | ||
| product_sku: 'SKU-QUERY-PRICE', | ||
| product_image: 'https://placehold.co/400x400?text=Price+Query', | ||
| base_price: 0, | ||
| stock_quantity: 50, | ||
| stock_status: 'low-stock', | ||
| detected_at: new Date().toISOString(), | ||
| days_remaining: 15, | ||
| supplier_name: 'Fornecedor B', | ||
| category_name: 'Categoria Beta' | ||
| }, | ||
|
Comment on lines
+25
to
+38
|
||
| { | ||
| novelty_id: 'var-3', | ||
| product_id: 'p-3', | ||
| product_name: 'Imagem Ausente', | ||
| product_sku: 'SKU-NO-IMAGE', | ||
| product_image: null, | ||
| base_price: 89.90, | ||
| stock_quantity: 0, | ||
| stock_status: 'out-of-stock', | ||
| detected_at: new Date().toISOString(), | ||
| days_remaining: 5, | ||
| supplier_name: 'Fornecedor C', | ||
| category_name: 'Categoria Gamma' | ||
| } | ||
|
Comment on lines
+39
to
+52
|
||
| ]) | ||
| }); | ||
| }); | ||
| }); | ||
|
|
||
| test('Card Edge Cases - Visual Consistency', async ({ page }) => { | ||
| await page.goto('/novidades'); | ||
| const grid = page.locator('div[role="list"]'); | ||
| await grid.waitFor({ state: 'visible' }); | ||
|
Comment on lines
+60
to
+61
|
||
|
|
||
| // Take screenshot of the variations | ||
| await expect(grid).toHaveScreenshot('novelty-card-variations.png', { | ||
| maxDiffPixelRatio: 0.05 | ||
| }); | ||
|
|
||
| // Check specific heights to ensure alignment | ||
| const cards = page.locator('div[role="listitem"]'); | ||
| const count = await cards.count(); | ||
|
Comment on lines
+69
to
+70
|
||
| expect(count).toBe(3); | ||
|
|
||
| for (let i = 0; i < count; i++) { | ||
| const card = cards.nth(i); | ||
| const h3 = card.locator('h3'); | ||
| const priceContainer = card.locator('.min-h-\\[3\\.25rem\\]'); | ||
|
|
||
| const h3Box = await h3.boundingBox(); | ||
| const priceBox = await priceContainer.boundingBox(); | ||
|
|
||
| if (h3Box) { | ||
| // Should have a consistent minimum height even with different content | ||
| expect(h3Box.height).toBeGreaterThanOrEqual(40); | ||
| } | ||
| if (priceBox) { | ||
| expect(priceBox.height).toBeGreaterThanOrEqual(52); | ||
| } | ||
| } | ||
| }); | ||
| }); | ||
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 |
|---|---|---|
| @@ -0,0 +1,247 @@ | ||
| import { test, expect, type Page } from '@playwright/test'; | ||
|
|
||
| import AxeBuilder from '@axe-core/playwright'; | ||
|
|
||
| const viewports = [ | ||
| { width: 360, height: 800, name: 'mobile-360' }, | ||
| { width: 768, height: 1024, name: 'tablet-768' }, | ||
| { width: 1024, height: 768, name: 'tablet-1024' }, | ||
| { width: 1440, height: 900, name: 'desktop-1440' }, | ||
| ]; | ||
|
|
||
| test.describe('Novelty Grid Advanced Visual & A11y @mobile', () => { | ||
| test.beforeEach(async ({ context }) => { | ||
| await context.addInitScript(() => { | ||
| const defaultFlags = { | ||
| 'mfa': 'false', | ||
| 'ai_recommendations': 'true', | ||
| 'presentation_mode': 'true', | ||
| 'voice_commands': 'true', | ||
| 'magic_up': 'true', | ||
| 'e2e_tests': 'true', | ||
| 'advanced_analytics': 'true', | ||
| 'custom_kits_v2': 'false' | ||
| }; | ||
|
|
||
| Object.entries(defaultFlags).forEach(([flag, value]) => { | ||
| localStorage.setItem(`ff_${flag}`, value); | ||
| }); | ||
|
|
||
| if ('serviceWorker' in navigator) { | ||
| navigator.serviceWorker.getRegistrations().then(registrations => { | ||
| for (const registration of registrations) { | ||
| registration.unregister(); | ||
| } | ||
| }); | ||
| } | ||
| }); | ||
| }); | ||
|
|
||
| for (const viewport of viewports) { | ||
| test.describe(`Viewport: ${viewport.name}`, () => { | ||
| test.use({ viewport: { width: viewport.width, height: viewport.height } }); | ||
|
|
||
| test('Header Immediate Rendering & Visual', async ({ page }) => { | ||
| await page.goto('/novidades', { waitUntil: 'domcontentloaded' }); | ||
|
|
||
| const header = page.locator('div.flex.flex-col.gap-4').first(); | ||
| const title = header.locator('[data-testid="page-title-novidades"]'); | ||
| const desc = header.locator('[data-testid="novelty-description"]'); | ||
|
Comment on lines
+46
to
+48
|
||
|
|
||
| await expect(title).toBeVisible(); | ||
| await expect(title).toHaveText('Novidades'); | ||
| await expect(desc).toHaveText('Produtos recém-chegados ao catálogo nos últimos 30 dias'); | ||
|
|
||
| await expect(header).toHaveScreenshot(`novelty-header-only-${viewport.name}.png`); | ||
| }); | ||
|
|
||
| test('Grid Visual Regression & Scroll', async ({ page }) => { | ||
| await page.goto('/novidades'); | ||
| const grid = page.locator('div[role="list"]'); | ||
| await grid.waitFor({ state: 'visible' }); | ||
| await page.waitForTimeout(1000); | ||
|
Comment on lines
+59
to
+61
|
||
|
|
||
| await expect(grid).toHaveScreenshot(`novelty-grid-initial-${viewport.name}.png`, { | ||
| maxDiffPixelRatio: 0.02, | ||
| }); | ||
|
|
||
| await grid.evaluate(el => el.scrollTop = 1000); | ||
| await page.waitForTimeout(800); | ||
|
|
||
| await expect(grid).toHaveScreenshot(`novelty-grid-scrolled-${viewport.name}.png`, { | ||
| maxDiffPixelRatio: 0.02, | ||
| }); | ||
| }); | ||
|
|
||
| test('Accessibility Scan', async ({ page }) => { | ||
| await page.goto('/novidades'); | ||
| const grid = page.locator('div[role="list"]'); | ||
| await grid.waitFor({ state: 'visible' }); | ||
|
|
||
| const results = await new AxeBuilder({ page }) | ||
| .include('div[role="list"]') | ||
| .analyze(); | ||
|
|
||
|
Comment on lines
+80
to
+83
|
||
| if (results.violations.length > 0) { | ||
| console.error(`A11y Violations for viewport ${viewport.name}:`); | ||
| results.violations.forEach(v => { | ||
| console.error(`- [${v.id}] ${v.help} (${v.impact})`); | ||
| console.error(` URL: ${v.helpUrl}`); | ||
| console.error(` Nodes: ${v.nodes.length}`); | ||
| }); | ||
| } | ||
|
|
||
| expect(results.violations, `A11y violations found in ${viewport.name}: ${results.violations.map(v => v.id).join(', ')}`).toEqual([]); | ||
| }); | ||
|
|
||
| test('Browser Preferences - Accessibility Consistency', async ({ page }) => { | ||
| // Simulate high contrast / large font via CSS injection | ||
| await page.addStyleTag({ | ||
| content: ` | ||
| html { font-size: 20px !important; } | ||
| * { transition: none !important; animation: none !important; } | ||
| ` | ||
| }); | ||
|
|
||
| await page.goto('/novidades'); | ||
| const grid = page.locator('div[role="list"]'); | ||
| await grid.waitFor({ state: 'visible' }); | ||
|
|
||
| await expect(grid).toHaveScreenshot(`novelty-grid-a11y-prefs-${viewport.name}.png`); | ||
| }); | ||
|
|
||
| test('Keyboard Navigation', async ({ page }) => { | ||
| await page.goto('/novidades'); | ||
| await page.keyboard.press('Tab'); | ||
|
|
||
| const activeElement = await page.evaluate(() => document.activeElement?.tagName); | ||
| expect(activeElement).toBeDefined(); | ||
|
|
||
| await page.keyboard.press('Tab'); | ||
| await expect(page).toHaveScreenshot(`novelty-keyboard-focus-${viewport.name}.png`); | ||
| }); | ||
| }); | ||
| } | ||
|
|
||
| test('Skeleton State & Layout Stability', async ({ page }) => { | ||
| // Intercept with delay to see skeleton | ||
| await page.route('**/api/external-db', async route => { | ||
| if (route.request().postDataJSON()?.operation === 'select') { | ||
| await new Promise(resolve => setTimeout(resolve, 2000)); | ||
| } | ||
| await route.continue(); | ||
| }); | ||
|
Comment on lines
+127
to
+132
|
||
|
|
||
| await page.goto('/novidades'); | ||
| const skeleton = page.locator('.animate-spin').first(); | ||
| await expect(skeleton).toBeVisible(); | ||
|
|
||
| // Capture skeleton grid | ||
| const grid = page.locator('div.grid').filter({ has: page.locator('.animate-pulse') }).first(); | ||
| await expect(grid).toHaveScreenshot('novelty-skeleton-state.png'); | ||
|
|
||
| // Wait for data and check stability | ||
| await page.waitForSelector('div[role="list"]'); | ||
| const realGrid = page.locator('div[role="list"]'); | ||
| await expect(realGrid).toBeVisible(); | ||
| await expect(realGrid).toHaveScreenshot('novelty-data-loaded-stability.png'); | ||
| }); | ||
|
|
||
| test('Pagination & Alignment Check', async ({ page }) => { | ||
| // Mock enough products for multiple pages | ||
| await page.route('**/api/external-db', async route => { | ||
| const body = route.request().postDataJSON(); | ||
| if (body?.operation === 'select' && body?.table === 'products') { | ||
| const mockProducts = Array.from({ length: 45 }, (_, i) => ({ | ||
| id: `page-mock-${i}`, | ||
| name: `Product ${i} ${i % 3 === 0 ? 'with a very very very long name to test wrapping and alignment consistency across the grid' : ''}`, | ||
| sku: `SKU-${i}`, | ||
| primary_image_url: null, | ||
| sale_price: i % 5 === 0 ? null : 100 + i, | ||
| category_id: 'cat-1', | ||
| supplier_id: 'sup-1', | ||
| created_at: new Date().toISOString(), | ||
| stock_quantity: 100, | ||
| min_quantity: 10 | ||
| })); | ||
| await route.fulfill({ | ||
| status: 200, | ||
| contentType: 'application/json', | ||
| body: JSON.stringify({ records: mockProducts, count: 45 }) | ||
| }); | ||
| } else { | ||
| await route.continue(); | ||
| } | ||
| }); | ||
|
Comment on lines
+151
to
+174
|
||
|
|
||
| await page.goto('/novidades'); | ||
| const paginator = page.locator('nav[aria-label="pagination"]'); | ||
| await paginator.waitFor(); | ||
|
|
||
| // Check first page alignment | ||
| const firstPageItems = page.locator('div[role="listitem"]'); | ||
| await expect(firstPageItems).toHaveCount(20); | ||
| await expect(page).toHaveScreenshot('novelty-pagination-page-1.png'); | ||
|
|
||
| // Click next | ||
| await page.click('a[aria-label="Go to next page"]'); | ||
| await page.waitForTimeout(500); | ||
| await expect(page.locator('div[role="listitem"]')).toHaveCount(20); | ||
| await expect(page).toHaveScreenshot('novelty-pagination-page-2.png'); | ||
|
|
||
| // Click last page (3) | ||
| await page.click('a:text("3")'); | ||
| await page.waitForTimeout(500); | ||
|
Comment on lines
+186
to
+193
|
||
| await expect(page.locator('div[role="listitem"]')).toHaveCount(5); | ||
| await expect(page).toHaveScreenshot('novelty-pagination-last-page.png'); | ||
| }); | ||
|
|
||
| test('Card Variations: Long Title & Consultation Price', async ({ page }) => { | ||
| await page.route('**/api/external-db', async route => { | ||
| const body = route.request().postDataJSON(); | ||
| if (body?.operation === 'select' && body?.table === 'products') { | ||
|
Comment on lines
+199
to
+201
|
||
| const mockProducts = [ | ||
| { | ||
| id: 'var-1', | ||
| name: 'Short Title', | ||
| sku: 'SKU-1', | ||
| sale_price: 100, | ||
| created_at: new Date().toISOString(), | ||
| stock_quantity: 100, | ||
| min_quantity: 10 | ||
| }, | ||
| { | ||
| id: 'var-2', | ||
| name: 'This is a very long product name that should definitely wrap to multiple lines and potentially push the layout down if not handled correctly by min-height constraints', | ||
| sku: 'SKU-2', | ||
| sale_price: 200, | ||
| created_at: new Date().toISOString(), | ||
| stock_quantity: 100, | ||
| min_quantity: 10 | ||
| }, | ||
| { | ||
| id: 'var-3', | ||
| name: 'Consultation Price Item', | ||
| sku: 'SKU-3', | ||
| sale_price: null, | ||
| created_at: new Date().toISOString(), | ||
| stock_quantity: 100, | ||
| min_quantity: 10 | ||
| } | ||
| ]; | ||
| await route.fulfill({ | ||
| status: 200, | ||
| contentType: 'application/json', | ||
| body: JSON.stringify({ records: mockProducts }) | ||
| }); | ||
|
Comment on lines
+231
to
+235
|
||
| } else { | ||
| await route.continue(); | ||
| } | ||
| }); | ||
|
|
||
| await page.goto('/novidades'); | ||
| const cards = page.locator('div[role="listitem"]'); | ||
| await expect(cards).toHaveCount(3); | ||
|
|
||
| await expect(page.locator('div[role="list"]')).toHaveScreenshot('novelty-card-variations.png'); | ||
| }); | ||
| }); | ||
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
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.
The new novelty tests install fixtures on routes such as this
functions/v1/noveltiesURL (and**/api/external-dbin the companion visual spec), but/novidadesgets its data throughuseNoveltiesWithDetails→invokeExternalDb, which queries Supabase REST forproductsand only falls back toexternal-db-bridge; no request is made to these mocked endpoints. In CI the assertions for exactly 3 or 45 mocked cards therefore run against real/fallback data instead of the fixture and will fail or time out depending on the environment.Useful? React with 👍 / 👎.