test(e2e): браузерный тест колокольчика — фоновый джоб кладёт агрегат минусов
Some checks are pending
Auto-tag / Create date-tag (push) Waiting to run
CI / Backend (.NET 8) (push) Waiting to run
CI / Web (React + Vite) (push) Waiting to run
CI / POS (WPF, Windows) (push) Waiting to run

Второй spec: seed + списания в минус, интервал 1 мин, ждём cron-джоб (*/5),
открываем колокольчик → видно «N товаров ушли в минус» + «Пересчитать». Проходит
на стенде (~3.4 мин ожидания джоба).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
nns 2026-10-09 04:01:14 +05:00
parent 560d52e8cd
commit ff42db7e97

View file

@ -64,4 +64,45 @@ test.describe('negative-stock admin UI', () => {
// В таблице есть хотя бы одна строка с отрицательным остатком (красная).
await expect(page.locator('tbody tr').first()).toBeVisible({ timeout: 15_000 })
})
test('колокольчик: фоновый джоб кладёт агрегат «N товаров ушли в минус»', async ({ page }) => {
test.setTimeout(480_000) // ждём cron-джоб (*/5), до ~6.5 мин
const sess = await apiSignup('negbell')
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 })
// интервал 1 мин — чтобы джоб разослал на ближайшем тике
const s = (await (await ctx.get('/api/organization/settings')).json())
await ctx.put('/api/organization/settings', { data: { ...s, negStockNotifyEnabled: true, negStockNotifyIntervalMinutes: 1 } })
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
for (const row of stock.filter((x: { quantity: number }) => x.quantity > 0).slice(0, 2)) {
const loss = await ctx.post('/api/inventory/losses', {
data: { date: new Date().toISOString(), storeId, currencyId, reason: 0, notes: 'bell e2e',
lines: [{ productId: row.productId, quantity: Number(row.quantity) + 3, unitCost: 10 }] },
})
await ctx.post(`/api/inventory/losses/${(await loss.json()).id}/post`, { data: {} })
}
// Ждём, пока cron-джоб создаст персистентное уведомление.
let unread = 0
for (let i = 0; i < 40 && unread === 0; i++) {
unread = (await (await ctx.get('/api/notifications/count')).json()).unread
if (unread === 0) await page.waitForTimeout(10_000)
}
expect(unread, 'джоб создал уведомление').toBeGreaterThan(0)
await ctx.dispose()
// Открываем колокольчик — видно агрегат и кнопку «Пересчитать».
await attachSession(page, sess, '/dashboard')
await page.getByRole('button', { name: /Уведомления/ }).first().click()
await expect(page.getByText(/ушл.* в минус/i).first()).toBeVisible({ timeout: 10_000 })
await expect(page.getByRole('button', { name: /Пересчитать/ }).first()).toBeVisible()
})
})