test(e2e): браузерный spec отрицательных остатков — баннер + «Пересчитать» + фильтр
Playwright stage-ui-negative-stock: signup+seed+списания в минус (API), затем в браузере проверяет постоянный баннер, навигацию «Пересчитать эти товары» → черновик инвентаризации, фильтр «Только минусы». Проходит на стенде (12с). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
parent
7e4cdf4c4f
commit
608d940028
67
tests/e2e/scenarios/stage-ui-negative-stock.spec.ts
Normal file
67
tests/e2e/scenarios/stage-ui-negative-stock.spec.ts
Normal file
|
|
@ -0,0 +1,67 @@
|
||||||
|
/**
|
||||||
|
* E2E (браузер): отрицательные остатки в админке.
|
||||||
|
* - постоянный баннер «N товаров в минусе» виден на дашборде;
|
||||||
|
* - кнопка «Пересчитать эти товары» создаёт черновик инвентаризации и
|
||||||
|
* открывает его (навигация на /inventory/inventories/<id>);
|
||||||
|
* - фильтр «Только минусы» в остатках показывает только отрицательные.
|
||||||
|
*
|
||||||
|
* Данные готовим через API (быстрее формы): signup + seed-demo + списания в
|
||||||
|
* минус. Проводятся списания больше остатка — разрешено (решение владельца),
|
||||||
|
* создаёт предупреждения.
|
||||||
|
*/
|
||||||
|
import { test, expect } from '@playwright/test'
|
||||||
|
import { apiSignup, attachSession, watchPage } from '../lib/ui.js'
|
||||||
|
import { request as apiRequest } from '@playwright/test'
|
||||||
|
|
||||||
|
const BASE = process.env.E2E_ADMIN_URL ?? 'https://test.admin.food-market.kz'
|
||||||
|
|
||||||
|
test.describe('negative-stock admin UI', () => {
|
||||||
|
test('баннер + «Пересчитать» + фильтр «Только минусы»', async ({ page }) => {
|
||||||
|
test.setTimeout(120_000)
|
||||||
|
|
||||||
|
// 1. Орг + демо-данные.
|
||||||
|
const sess = await apiSignup('negstock')
|
||||||
|
const ctx = await apiRequest.newContext({
|
||||||
|
baseURL: BASE, ignoreHTTPSErrors: true, timeout: 60_000,
|
||||||
|
extraHTTPHeaders: { Authorization: `Bearer ${sess.accessToken}` },
|
||||||
|
})
|
||||||
|
await ctx.post('/api/admin/seed-demo', { timeout: 60_000 })
|
||||||
|
|
||||||
|
// 2. Уводим пару товаров в минус через списания.
|
||||||
|
const storeId = (await (await ctx.get('/api/catalog/stores?pageSize=50')).json()).items[0].id
|
||||||
|
const currencyId = (await (await ctx.get('/api/catalog/currencies?pageSize=50')).json())
|
||||||
|
.items.find((c: { code: string }) => c.code === 'KZT').id
|
||||||
|
const stock = (await (await ctx.get(`/api/inventory/stock?storeId=${storeId}&pageSize=200`)).json()).items
|
||||||
|
const positives = stock.filter((x: { quantity: number }) => x.quantity > 0).slice(0, 2)
|
||||||
|
expect(positives.length, 'в демо есть товары с остатком').toBeGreaterThan(0)
|
||||||
|
for (const row of positives) {
|
||||||
|
const loss = await ctx.post('/api/inventory/losses', {
|
||||||
|
data: {
|
||||||
|
date: new Date().toISOString(), storeId, currencyId, reason: 0, notes: 'e2e минус',
|
||||||
|
lines: [{ productId: row.productId, quantity: Number(row.quantity) + 3, unitCost: 10 }],
|
||||||
|
},
|
||||||
|
})
|
||||||
|
const lid = (await loss.json()).id
|
||||||
|
await ctx.post(`/api/inventory/losses/${lid}/post`, { data: {} })
|
||||||
|
}
|
||||||
|
const cnt = await (await ctx.get('/api/negative-stock/count')).json()
|
||||||
|
expect(cnt.total, 'есть минусы').toBeGreaterThan(0)
|
||||||
|
await ctx.dispose()
|
||||||
|
|
||||||
|
// 3. Дашборд: постоянный баннер виден.
|
||||||
|
watchPage(page)
|
||||||
|
await attachSession(page, sess, '/dashboard')
|
||||||
|
const banner = page.getByText(/в минусе — нужна инвентаризация/i)
|
||||||
|
await expect(banner).toBeVisible({ timeout: 15_000 })
|
||||||
|
|
||||||
|
// 4. «Пересчитать эти товары» → черновик инвентаризации.
|
||||||
|
await page.getByRole('button', { name: /Пересчитать эти товары/i }).click()
|
||||||
|
await expect(page).toHaveURL(/\/inventory\/inventories\/[0-9a-f-]+/i, { timeout: 20_000 })
|
||||||
|
|
||||||
|
// 5. Фильтр «Только минусы» в остатках.
|
||||||
|
await page.goto('/inventory/stock?negativeOnly=true', { waitUntil: 'domcontentloaded' })
|
||||||
|
await expect(page.getByText('Только минусы')).toBeVisible({ timeout: 10_000 })
|
||||||
|
// В таблице есть хотя бы одна строка с отрицательным остатком (красная).
|
||||||
|
await expect(page.locator('tbody tr').first()).toBeVisible({ timeout: 15_000 })
|
||||||
|
})
|
||||||
|
})
|
||||||
Loading…
Reference in a new issue