Register PostNord shipments, admin notes, and guarded status transitions with customer emails. Expandable admin UI, V11 migration, serial E2E suite, and AGENTS.md Docker-only E2E guidance. Co-authored-by: Cursor <cursoragent@cursor.com>
99 lines
3.5 KiB
TypeScript
99 lines
3.5 KiB
TypeScript
import { test, expect } from '@playwright/test'
|
|
|
|
/**
|
|
* Admin order status and shipment flows (serial — mutates seeded orders).
|
|
* Requires docker e2e stack with dev seeds (DEF456, JKL012, ABC123).
|
|
*/
|
|
test.describe.configure({ mode: 'serial' })
|
|
|
|
const PENDING_ORDER_SHORT_ID = 'c2eebc99'
|
|
const PROCESSING_PLATE = 'JKL012'
|
|
const SENT_ORDER_SHORT_ID = 'c1eebc99'
|
|
|
|
async function loginAsAdmin(page: import('@playwright/test').Page) {
|
|
await page.goto('/logga-in')
|
|
await page.getByLabel('E-postadress').fill('admin@bilhalsning.se')
|
|
await page.getByLabel('Lösenord').fill('test1234')
|
|
await page.getByRole('button', { name: 'Logga in' }).click()
|
|
await page.waitForURL('/')
|
|
}
|
|
|
|
async function openAdmin(page: import('@playwright/test').Page) {
|
|
await page.goto('/admin')
|
|
await expect(page.locator('.admin__loading')).toBeHidden({ timeout: 30_000 })
|
|
}
|
|
|
|
function orderRowByPlate(page: import('@playwright/test').Page, plate: string) {
|
|
return page.locator('.admin__row').filter({
|
|
has: page.locator('.admin__plate', { hasText: plate }),
|
|
})
|
|
}
|
|
|
|
function orderRowByShortId(
|
|
page: import('@playwright/test').Page,
|
|
shortId: string,
|
|
) {
|
|
return page.locator('.admin__row', { hasText: shortId })
|
|
}
|
|
|
|
test.describe('Admin fulfillment flows', () => {
|
|
test.beforeEach(async ({ page }) => {
|
|
await loginAsAdmin(page)
|
|
await openAdmin(page)
|
|
})
|
|
|
|
test('can mark unpaid order as failed', async ({ page }) => {
|
|
await page.locator('#admin-order-search').fill(PENDING_ORDER_SHORT_ID)
|
|
const row = orderRowByShortId(page, PENDING_ORDER_SHORT_ID)
|
|
const select = row.locator('.admin__status-select')
|
|
await select.selectOption('failed')
|
|
await expect(select).toHaveValue('failed')
|
|
await expect(page.getByRole('alert')).not.toBeVisible()
|
|
})
|
|
|
|
test('can revert unpaid failed order to pending payment', async ({
|
|
page,
|
|
}) => {
|
|
await page.locator('#admin-order-search').fill(PENDING_ORDER_SHORT_ID)
|
|
const row = orderRowByShortId(page, PENDING_ORDER_SHORT_ID)
|
|
const select = row.locator('.admin__status-select')
|
|
await expect(select).toHaveValue('failed')
|
|
await select.selectOption('pending_payment')
|
|
await expect(select).toHaveValue('pending_payment')
|
|
})
|
|
|
|
test('can register shipment for processing order', async ({ page }) => {
|
|
const row = orderRowByPlate(page, PROCESSING_PLATE)
|
|
await row.click()
|
|
await page
|
|
.locator('.admin__tracking-input')
|
|
.fill('PN-E2E-FULFILLMENT-001')
|
|
await page.getByRole('button', { name: 'Registrera utskick' }).click()
|
|
await expect(row.locator('.admin__status-select')).toHaveValue('sent')
|
|
await expect(page.locator('.admin__tracking-link')).toBeVisible()
|
|
})
|
|
|
|
test('can mark sent order as delivered', async ({ page }) => {
|
|
await page.locator('#admin-order-search').fill(SENT_ORDER_SHORT_ID)
|
|
const row = orderRowByShortId(page, SENT_ORDER_SHORT_ID)
|
|
const select = row.locator('.admin__status-select')
|
|
if ((await select.inputValue()) !== 'delivered') {
|
|
await select.selectOption('delivered')
|
|
}
|
|
await expect(select).toHaveValue('delivered')
|
|
})
|
|
|
|
test('can mark delivered order as failed then back to sent when tracking exists', async ({
|
|
page,
|
|
}) => {
|
|
await page.locator('#admin-order-search').fill(SENT_ORDER_SHORT_ID)
|
|
const row = orderRowByShortId(page, SENT_ORDER_SHORT_ID)
|
|
const select = row.locator('.admin__status-select')
|
|
|
|
await select.selectOption('failed')
|
|
await expect(select).toHaveValue('failed')
|
|
|
|
await select.selectOption('sent')
|
|
await expect(select).toHaveValue('sent')
|
|
})
|
|
})
|