From 3cd2874ed7f93a25225192b78c51e42d8bbfc5f4 Mon Sep 17 00:00:00 2001 From: consultoria-as Date: Wed, 29 Apr 2026 07:11:40 +0000 Subject: [PATCH] test(e2e): improve catalog test with mocked APIs and auth - catalog.spec.js: added fake JWT auth setup, mocked brand/search APIs with Playwright route.fulfill, asserts actual rendered cards and search dropdown visibility --- .gitignore | 4 +++ tests/e2e/catalog.spec.js | 76 ++++++++++++++++++++++++++++++++------- 2 files changed, 67 insertions(+), 13 deletions(-) diff --git a/.gitignore b/.gitignore index 45a4bee..d1fff11 100644 --- a/.gitignore +++ b/.gitignore @@ -87,3 +87,7 @@ package-lock.json # Backups backups/ + +# Local tools (AWS CLI) +tools/ + diff --git a/tests/e2e/catalog.spec.js b/tests/e2e/catalog.spec.js index dd40836..8b4520d 100644 --- a/tests/e2e/catalog.spec.js +++ b/tests/e2e/catalog.spec.js @@ -1,24 +1,74 @@ const { test, expect } = require('@playwright/test'); +const FAKE_TOKEN = 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHAiOjk5OTk5OTk5OTksIm5hbWUiOiJUZXN0IFVzZXIifQ.signature'; + +async function setupAuth(page) { + await page.goto('/pos/login'); + await page.evaluate((token) => { + localStorage.setItem('pos_token', token); + localStorage.setItem('pos_tenant_id', '11'); + }, FAKE_TOKEN); +} + +async function mockCatalogAPIs(page) { + await page.route('/pos/api/catalog/brands?mode=local', async (route) => { + await route.fulfill({ + status: 200, + contentType: 'application/json', + body: JSON.stringify({ + data: [ + { id_brand: 1, name_brand: 'Toyota' }, + { id_brand: 2, name_brand: 'Nissan' }, + ], + }), + }); + }); + + await page.route(/\/pos\/api\/catalog\/search\?q=.*&limit=20/, async (route) => { + await route.fulfill({ + status: 200, + contentType: 'application/json', + body: JSON.stringify({ + data: [ + { + id_part: 1, + oem_part_number: 'ABC-123', + name: 'Filtro de aceite', + vehicle_info: 'Toyota Corolla 2020', + local_stock: 5, + }, + ], + }), + }); + }); +} + test.describe('Nexus POS — Catalog', () => { test('catalog page loads with brand grid', async ({ page }) => { + await setupAuth(page); + await mockCatalogAPIs(page); await page.goto('/pos/catalog'); - // Wait for app shell or main content - await expect(page.locator('#navGrid, .grid, .content-grid')).toBeVisible({ timeout: 10000 }); - // Brand grid or loading state should exist - const content = await page.locator('body').textContent(); - expect(content).toMatch(/catálogo|catalogo|marca|brand/i); + + await expect(page).toHaveTitle(/Catalogo|Catálogo/i); + await expect(page.locator('#navGrid')).toBeVisible({ timeout: 5000 }); + // Wait for brands to render + await page.waitForSelector('.nav-card', { timeout: 5000 }); + const cards = page.locator('.nav-card'); + await expect(cards).toHaveCount(2); }); - test('search functionality shows results or loading', async ({ page }) => { + test('search functionality works', async ({ page }) => { + await setupAuth(page); + await mockCatalogAPIs(page); await page.goto('/pos/catalog'); - const searchInput = page.locator('#searchInput, input[placeholder*="buscar" i], input[type="search"]').first(); - await expect(searchInput).toBeVisible({ timeout: 10000 }); - await searchInput.fill('balata'); + + const searchInput = page.locator('#searchInput'); + await expect(searchInput).toBeVisible(); + await searchInput.fill('filtro'); await searchInput.press('Enter'); - // Wait for dropdown or results to appear - await page.waitForTimeout(800); - const hasResults = await page.locator('#searchDropdown, .search-dropdown, .parts-grid, .grid').first().isVisible().catch(() => false); - expect(hasResults || true).toBe(true); // Smoke: search interaction completes + + // Assert search dropdown becomes visible with results + await expect(page.locator('#searchDropdown')).toHaveClass(/is-visible/, { timeout: 5000 }); + await expect(page.locator('.search-result-item')).toBeVisible(); }); });