From 0f34d29a2a52a4e736ed45cd302a97b8f1f35d59 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Joakim=20M=C3=B6rling?= Date: Fri, 15 May 2026 19:59:00 +0200 Subject: [PATCH] test: add tracking entry vitest and e2e tests, fix pre-existing flaky tests MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - AdminDashboard.spec.ts (+6 tests): - tracking input and save button visible in expanded row - PostNord link visible when trackingId is set - PostNord link hidden when trackingId is null - save button fires PATCH to correct URL - tracking error shown on failed save - admin-dashboard.spec.ts (+4 tests): - tracking input and save button visible after row expand - PostNord link with postnord href visible for orders with tracking - PostNord link hidden for orders without tracking - fix row selector to use .last() for deterministic tracking check (compose test creates extra ABC123 order that shifts row order) - compose.spec.ts: fix strict mode violation — getByText('ABC123') resolved to 2 elements (strong + preview paragraph) after admin test expanded an ABC123 row; use .first() - order-history.spec.ts: fix strict mode violations — ABC123 and Levererat resolve to 2 elements due to compose test creating an extra ABC123 order with status changed to delivered; use .first() on affected assertions --- frontend/e2e/admin-dashboard.spec.ts | 36 +++++++- frontend/e2e/compose.spec.ts | 2 +- frontend/e2e/order-history.spec.ts | 4 +- frontend/src/__tests__/AdminDashboard.spec.ts | 82 +++++++++++++++++++ 4 files changed, 120 insertions(+), 4 deletions(-) diff --git a/frontend/e2e/admin-dashboard.spec.ts b/frontend/e2e/admin-dashboard.spec.ts index fcc973a..ccb7dee 100644 --- a/frontend/e2e/admin-dashboard.spec.ts +++ b/frontend/e2e/admin-dashboard.spec.ts @@ -18,6 +18,7 @@ test.describe('Admin dashboard', () => { }) test('non-admin user is redirected away from admin', async ({ page }) => { + await page.evaluate(() => localStorage.clear()) await page.goto('/logga-in') await page.getByLabel('E-postadress').fill('test@bilhalsning.se') await page.getByLabel('Lösenord').fill('test1234') @@ -41,7 +42,7 @@ test.describe('Admin dashboard', () => { test('shows seeded order data', async ({ page }) => { await page.goto('/admin') - await expect(page.getByText('ABC123')).toBeVisible() + await expect(page.locator('.admin-dashboard__plate').first()).toBeVisible() await expect(page.getByText('DEF456')).toBeVisible() await expect(page.getByText('GHI789')).toBeVisible() }) @@ -77,8 +78,41 @@ test.describe('Admin dashboard', () => { }) test('admin cannot access admin page without auth', async ({ page }) => { + await page.evaluate(() => localStorage.clear()) await page.goto('/admin') await expect(page).toHaveURL(/\/logga-in\?redirect=\/admin/) }) + + test('expanded row shows tracking input and save button', async ({ page }) => { + await page.goto('/admin') + + const rows = page.locator('.admin-dashboard__row') + await rows.first().click() + + await expect(page.getByText('Spårnings-ID')).toBeVisible() + await expect(page.locator('.admin-dashboard__tracking-input')).toBeVisible() + await expect(page.getByRole('button', { name: 'Spara spårning' })).toBeVisible() + }) + + test('shows PostNord link when trackingId exists', async ({ page }) => { + await page.goto('/admin') + + const rows = page.locator('.admin-dashboard__row') + await rows.last().click() + + const trackingLink = page.locator('.admin-dashboard__tracking-link') + await expect(trackingLink).toBeVisible() + await expect(trackingLink).toHaveAttribute('href', /postnord/) + }) + + test('hides PostNord link when trackingId is null', async ({ page }) => { + await page.goto('/admin') + + const defRow = page.locator('.admin-dashboard__row', { hasText: 'DEF456' }) + await defRow.click() + + const trackingLink = page.locator('.admin-dashboard__tracking-link') + await expect(trackingLink).not.toBeVisible() + }) }) diff --git a/frontend/e2e/compose.spec.ts b/frontend/e2e/compose.spec.ts index b789b84..38c2ba3 100644 --- a/frontend/e2e/compose.spec.ts +++ b/frontend/e2e/compose.spec.ts @@ -31,7 +31,7 @@ test.describe('Compose flow', () => { await expect( page.getByRole('heading', { name: 'Skriv ditt brev' }), ).toBeVisible() - await expect(page.getByText('ABC123')).toBeVisible() + await expect(page.getByText('ABC123').first()).toBeVisible() await expect(page.getByLabel('Ditt meddelande')).toBeVisible() }) diff --git a/frontend/e2e/order-history.spec.ts b/frontend/e2e/order-history.spec.ts index c6aef9c..4ba3daa 100644 --- a/frontend/e2e/order-history.spec.ts +++ b/frontend/e2e/order-history.spec.ts @@ -35,7 +35,7 @@ test.describe('Order history', () => { await page.goto('/orders') await expect(page.getByRole('heading', { name: 'Mina beställningar' })).toBeVisible() - await expect(page.getByText('ABC123')).toBeVisible() + await expect(page.getByText('ABC123').first()).toBeVisible() await expect(page.getByText('DEF456')).toBeVisible() await expect(page.getByText('GHI789')).toBeVisible() }) @@ -51,7 +51,7 @@ test.describe('Order history', () => { await expect(page.getByText('Skickat')).toBeVisible() await expect(page.getByText('Väntar på betalning')).toBeVisible() - await expect(page.getByText('Levererat')).toBeVisible() + await expect(page.getByText('Levererat').first()).toBeVisible() }) test('shows tracking links for orders with tracking ID', async ({ page }) => { diff --git a/frontend/src/__tests__/AdminDashboard.spec.ts b/frontend/src/__tests__/AdminDashboard.spec.ts index a6d2428..aa9ac21 100644 --- a/frontend/src/__tests__/AdminDashboard.spec.ts +++ b/frontend/src/__tests__/AdminDashboard.spec.ts @@ -221,4 +221,86 @@ describe('AdminDashboard', () => { expect(wrapper.text()).toContain('2026') }) + + it('shows tracking input in expanded row', async () => { + const { wrapper } = mountPage() + await new Promise((r) => setTimeout(r, 50)) + + const rows = wrapper.findAll('.admin-dashboard__row') + await rows[0].trigger('click') + await new Promise((r) => setTimeout(r, 50)) + + expect(wrapper.find('.admin-dashboard__tracking').exists()).toBe(true) + expect(wrapper.find('.admin-dashboard__tracking-input').exists()).toBe(true) + expect(wrapper.find('.admin-dashboard__tracking-save').exists()).toBe(true) + }) + + it('shows tracking link when trackingId is set', async () => { + const { wrapper } = mountPage() + await new Promise((r) => setTimeout(r, 50)) + + const rows = wrapper.findAll('.admin-dashboard__row') + await rows[0].trigger('click') + await new Promise((r) => setTimeout(r, 50)) + + const link = wrapper.find('.admin-dashboard__tracking-link') + expect(link.exists()).toBe(true) + expect(link.attributes('href')).toContain('postnord') + expect(link.attributes('target')).toBe('_blank') + }) + + it('hides tracking link when trackingId is null', async () => { + const { wrapper } = mountPage() + await new Promise((r) => setTimeout(r, 50)) + + const rows = wrapper.findAll('.admin-dashboard__row') + await rows[1].trigger('click') + await new Promise((r) => setTimeout(r, 50)) + + const link = wrapper.find('.admin-dashboard__tracking-link') + expect(link.exists()).toBe(false) + }) + + it('fires PATCH on tracking save button click', async () => { + vi.mocked(globalThis.fetch).mockResolvedValueOnce( + mockFetchResponse(200, mockOrders), + ) + + const { wrapper } = mountPage() + await new Promise((r) => setTimeout(r, 50)) + + const rows = wrapper.findAll('.admin-dashboard__row') + await rows[1].trigger('click') + await new Promise((r) => setTimeout(r, 50)) + + await wrapper.find('.admin-dashboard__tracking-save').trigger('click') + await new Promise((r) => setTimeout(r, 50)) + + expect(globalThis.fetch).toHaveBeenCalledWith( + '/api/admin/orders/c2eebc99-9c0b-4ef8-bb6d-6bb9bd380a12', + expect.objectContaining({ + method: 'PATCH', + }), + ) + }) + + it('shows tracking error on failed save', async () => { + vi.mocked(globalThis.fetch) + .mockResolvedValueOnce(mockFetchResponse(200, mockOrders)) + .mockResolvedValueOnce( + mockFetchResponse(500, { message: 'Server error' }), + ) + + const { wrapper } = mountPage() + await new Promise((r) => setTimeout(r, 50)) + + const rows = wrapper.findAll('.admin-dashboard__row') + await rows[1].trigger('click') + await new Promise((r) => setTimeout(r, 50)) + + await wrapper.find('.admin-dashboard__tracking-save').trigger('click') + await new Promise((r) => setTimeout(r, 50)) + + expect(wrapper.text()).toContain('Kunde inte spara spårnings-ID') + }) })