From ff42db7e971d73a499e5021d76c037895fc5c9ac Mon Sep 17 00:00:00 2001 From: nns Date: Fri, 9 Oct 2026 04:01:14 +0500 Subject: [PATCH] =?UTF-8?q?test(e2e):=20=D0=B1=D1=80=D0=B0=D1=83=D0=B7?= =?UTF-8?q?=D0=B5=D1=80=D0=BD=D1=8B=D0=B9=20=D1=82=D0=B5=D1=81=D1=82=20?= =?UTF-8?q?=D0=BA=D0=BE=D0=BB=D0=BE=D0=BA=D0=BE=D0=BB=D1=8C=D1=87=D0=B8?= =?UTF-8?q?=D0=BA=D0=B0=20=E2=80=94=20=D1=84=D0=BE=D0=BD=D0=BE=D0=B2=D1=8B?= =?UTF-8?q?=D0=B9=20=D0=B4=D0=B6=D0=BE=D0=B1=20=D0=BA=D0=BB=D0=B0=D0=B4?= =?UTF-8?q?=D1=91=D1=82=20=D0=B0=D0=B3=D1=80=D0=B5=D0=B3=D0=B0=D1=82=20?= =?UTF-8?q?=D0=BC=D0=B8=D0=BD=D1=83=D1=81=D0=BE=D0=B2?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Второй spec: seed + списания в минус, интервал 1 мин, ждём cron-джоб (*/5), открываем колокольчик → видно «N товаров ушли в минус» + «Пересчитать». Проходит на стенде (~3.4 мин ожидания джоба). Co-Authored-By: Claude Opus 4.8 --- .../scenarios/stage-ui-negative-stock.spec.ts | 41 +++++++++++++++++++ 1 file changed, 41 insertions(+) diff --git a/tests/e2e/scenarios/stage-ui-negative-stock.spec.ts b/tests/e2e/scenarios/stage-ui-negative-stock.spec.ts index 566e9d7..b8d9502 100644 --- a/tests/e2e/scenarios/stage-ui-negative-stock.spec.ts +++ b/tests/e2e/scenarios/stage-ui-negative-stock.spec.ts @@ -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() + }) })