diff --git a/frontend/e2e/deferred-payment-admin.spec.ts b/frontend/e2e/deferred-payment-admin.spec.ts index de5aa64..29bb593 100644 --- a/frontend/e2e/deferred-payment-admin.spec.ts +++ b/frontend/e2e/deferred-payment-admin.spec.ts @@ -59,15 +59,15 @@ test.describe('Deferred payment and admin lookup', () => { const orderCard = page.locator('.orders__card', { hasText: orderId }) await expect(orderCard.getByText(plate)).toBeVisible() await expect(orderCard.locator('.badge')).toHaveText('Väntar på betalning') - await expect(orderCard.getByRole('link', { name: 'Betala nu' })).toBeVisible() + await expect(orderCard.getByRole('link', { name: 'Betala 49 kr' })).toBeVisible() - await orderCard.getByRole('link', { name: 'Betala nu' }).click() + await orderCard.getByRole('link', { name: 'Betala 49 kr' }).click() await expect(page).toHaveURL(new RegExp(`/betalning/${orderId}`)) await completeSwishPayment(page) await expect(page).toHaveURL('/orders') await expect(orderCard.locator('.badge')).toHaveText('Hanteras') - await expect(orderCard.getByRole('link', { name: 'Betala nu' })).not.toBeVisible() + await expect(orderCard.getByRole('link', { name: 'Betala 49 kr' })).not.toBeVisible() }) test('admin finds paid order under Att göra when searching partial order id', async ({ diff --git a/frontend/e2e/order-history.spec.ts b/frontend/e2e/order-history.spec.ts index c692752..89b6750 100644 --- a/frontend/e2e/order-history.spec.ts +++ b/frontend/e2e/order-history.spec.ts @@ -66,8 +66,8 @@ test.describe('Order history', () => { await page.goto('/orders') const unpaidCard = page.locator('.orders__card', { hasText: 'DEF456' }) - await expect(unpaidCard.getByRole('link', { name: 'Betala nu' })).toBeVisible() - await unpaidCard.getByRole('link', { name: 'Betala nu' }).click() + await expect(unpaidCard.getByRole('link', { name: 'Betala 49 kr' })).toBeVisible() + await unpaidCard.getByRole('link', { name: 'Betala 49 kr' }).click() await expect(page).toHaveURL(/\/betalning\/c2eebc99/) await expect(page.getByRole('heading', { name: 'Betalning' })).toBeVisible() diff --git a/frontend/src/__tests__/OrdersPage.spec.ts b/frontend/src/__tests__/OrdersPage.spec.ts index 404f78f..1c87843 100644 --- a/frontend/src/__tests__/OrdersPage.spec.ts +++ b/frontend/src/__tests__/OrdersPage.spec.ts @@ -63,13 +63,39 @@ const mockOrders = [ }, ] +function mockOrdersFetch(orders: unknown) { + vi.mocked(globalThis.fetch).mockImplementation((url, init) => { + const urlStr = String(url) + const method = init?.method ?? 'GET' + + if (urlStr.includes('/payment/swish-info')) { + return mockFetchResponse(200, { number: '1234567890', amount: 49 }) + } + + if (urlStr.includes('/cancel') && method === 'POST') { + return mockFetchResponse(200, { + id: 'c2eebc99-9c0b-4ef8-bb6d-6bb9bd380a12', + plate: 'DEF456', + letterText: 'Vill köpa din bil.', + status: 'cancelled', + trackingId: null, + createdAt: '2026-05-14T13:00:00Z', + }) + } + + if (urlStr.includes('/orders')) { + return mockFetchResponse(200, orders) + } + + return mockFetchResponse(404, { message: 'Not found' }) + }) +} + describe('OrdersPage', () => { beforeEach(() => { localStorage.clear() globalThis.fetch = vi.fn() - vi.mocked(globalThis.fetch).mockResolvedValue( - mockFetchResponse(200, mockOrders), - ) + mockOrdersFetch(mockOrders) }) it('renders heading and subtitle', async () => { @@ -87,6 +113,13 @@ describe('OrdersPage', () => { expect(wrapper.text()).toContain('Laddar beställningar...') }) + it('shows section headings when pending and completed orders exist', async () => { + const { wrapper } = mountPage() + await new Promise((r) => setTimeout(r, 50)) + expect(wrapper.text()).toContain('Obetalda beställningar') + expect(wrapper.text()).toContain('Tidigare beställningar') + }) + it('fetches orders from API on mount', async () => { mountPage() await new Promise((r) => setTimeout(r, 50)) @@ -115,7 +148,9 @@ describe('OrdersPage', () => { await new Promise((r) => setTimeout(r, 50)) const link = wrapper.find('a[href*="postnord"]') expect(link.exists()).toBe(true) + expect(link.classes()).toContain('orders__tracking-btn') expect(link.text()).toContain('PN123456789') + expect(link.text()).toContain('Spåra brev') expect(link.attributes('target')).toBe('_blank') }) @@ -130,9 +165,7 @@ describe('OrdersPage', () => { createdAt: '2026-05-14T13:00:00Z', }, ] - vi.mocked(globalThis.fetch).mockResolvedValue( - mockFetchResponse(200, ordersWithoutTracking), - ) + mockOrdersFetch(ordersWithoutTracking) const { wrapper } = mountPage() await new Promise((r) => setTimeout(r, 50)) const link = wrapper.find('a[href*="postnord"]') @@ -142,9 +175,8 @@ describe('OrdersPage', () => { it('renders order id and message', async () => { const { wrapper } = mountPage() await new Promise((r) => setTimeout(r, 50)) - expect(wrapper.text()).toContain('Beställnings-ID') expect(wrapper.text()).toContain('c1eebc99-9c0b-4ef8-bb6d-6bb9bd380a11') - expect(wrapper.text()).toContain('Meddelande') + expect(wrapper.text()).toContain('Beställnings-ID') expect(wrapper.text()).toContain('Hej fin bil!') expect(wrapper.text()).toContain('Vill köpa din bil.') }) @@ -153,19 +185,24 @@ describe('OrdersPage', () => { const { wrapper } = mountPage() await new Promise((r) => setTimeout(r, 50)) expect(wrapper.text()).toContain('2026') + expect(wrapper.text()).toContain('Skapad') }) it('shows empty state when no orders', async () => { - vi.mocked(globalThis.fetch).mockResolvedValue(mockFetchResponse(200, [])) + mockOrdersFetch([]) const { wrapper } = mountPage() await new Promise((r) => setTimeout(r, 50)) expect(wrapper.text()).toContain('Inga beställningar ännu') }) it('shows error state on API failure', async () => { - vi.mocked(globalThis.fetch).mockResolvedValue( - mockFetchResponse(500, { message: 'Internal server error' }), - ) + vi.mocked(globalThis.fetch).mockImplementation((url) => { + const urlStr = String(url) + if (urlStr.includes('/payment/swish-info')) { + return mockFetchResponse(200, { number: '1234567890', amount: 49 }) + } + return mockFetchResponse(500, { message: 'Internal server error' }) + }) const { wrapper } = mountPage() await new Promise((r) => setTimeout(r, 50)) expect(wrapper.text()).toContain('Kunde inte hämta beställningar') @@ -175,8 +212,21 @@ describe('OrdersPage', () => { const { wrapper } = mountPage() await new Promise((r) => setTimeout(r, 50)) const badges = wrapper.findAll('.badge') - expect(badges[0].classes()).toContain('badge--success') - expect(badges[1].classes()).toContain('badge--muted') + expect(badges[0].classes()).toContain('badge--warning') + expect(badges[1].classes()).toContain('badge--success') + }) + + it('shows order id on pending payment orders', async () => { + const { wrapper } = mountPage() + await new Promise((r) => setTimeout(r, 50)) + + const pendingCard = wrapper + .findAll('.orders__card') + .find((card) => card.text().includes('DEF456')) + expect(pendingCard?.text()).toContain('Beställnings-ID') + expect(pendingCard?.text()).toContain( + 'c2eebc99-9c0b-4ef8-bb6d-6bb9bd380a12', + ) }) it('shows pay button only for pending payment orders', async () => { @@ -186,9 +236,9 @@ describe('OrdersPage', () => { const pendingCard = wrapper .findAll('.orders__card') .find((card) => card.text().includes('DEF456')) - const payLink = pendingCard?.find('a.orders__action-btn') + const payLink = pendingCard?.find('a.orders__pay-btn') expect(payLink?.exists()).toBe(true) - expect(payLink?.text()).toBe('Betala nu') + expect(payLink?.text()).toBe('Betala 49 kr') const href = payLink?.attributes('href') expect(href).toContain('c2eebc99-9c0b-4ef8-bb6d-6bb9bd380a12') @@ -202,7 +252,7 @@ describe('OrdersPage', () => { const sentCard = wrapper .findAll('.orders__card') .find((card) => card.text().includes('ABC123')) - expect(sentCard?.find('a.orders__action-btn').exists()).toBe(false) + expect(sentCard?.find('a.orders__pay-btn').exists()).toBe(false) }) it('shows edit link for pending payment orders', async () => { @@ -212,9 +262,9 @@ describe('OrdersPage', () => { const pendingCard = wrapper .findAll('.orders__card') .find((card) => card.text().includes('DEF456')) - const editLinks = pendingCard?.findAll('a.orders__action-btn') ?? [] - const editLink = editLinks.find((link) => link.text() === 'Redigera') + const editLink = pendingCard?.find('a.orders__edit-btn') expect(editLink?.exists()).toBe(true) + expect(editLink?.text()).toBe('Redigera brev') const href = editLink?.attributes('href') expect(href).toContain('c2eebc99-9c0b-4ef8-bb6d-6bb9bd380a12') @@ -239,19 +289,6 @@ describe('OrdersPage', () => { vi.fn(() => true), ) - vi.mocked(globalThis.fetch) - .mockResolvedValueOnce(mockFetchResponse(200, mockOrders)) - .mockResolvedValueOnce( - mockFetchResponse(200, { - id: 'c2eebc99-9c0b-4ef8-bb6d-6bb9bd380a12', - plate: 'DEF456', - letterText: 'Vill köpa din bil.', - status: 'cancelled', - trackingId: null, - createdAt: '2026-05-14T13:00:00Z', - }), - ) - const { wrapper } = mountPage() await new Promise((r) => setTimeout(r, 50)) @@ -278,7 +315,7 @@ describe('OrdersPage', () => { .findAll('.orders__card') .find((card) => card.text().includes('ABC123')) expect(sentCard?.find('.orders__cancel-btn').exists()).toBe(false) - expect(sentCard?.text()).not.toContain('Redigera') + expect(sentCard?.text()).not.toContain('Redigera brev') expect(sentCard?.text()).not.toContain('Avbryt beställning') }) @@ -293,9 +330,7 @@ describe('OrdersPage', () => { createdAt: '2026-05-15T10:00:00Z', }, ] - vi.mocked(globalThis.fetch).mockResolvedValue( - mockFetchResponse(200, ordersWithProcessing), - ) + mockOrdersFetch(ordersWithProcessing) const { wrapper } = mountPage() await new Promise((r) => setTimeout(r, 50)) expect(wrapper.text()).toContain('Hanteras') diff --git a/frontend/src/pages/OrdersPage.vue b/frontend/src/pages/OrdersPage.vue index 04b0078..b23bec4 100644 --- a/frontend/src/pages/OrdersPage.vue +++ b/frontend/src/pages/OrdersPage.vue @@ -1,14 +1,26 @@