test(s8-2): telegram stage e2e
Some checks are pending
CI / Backend (.NET 8) (push) Waiting to run
CI / Web (React + Vite) (push) Waiting to run
CI / POS (WPF, Windows) (push) Waiting to run

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
nns 2026-05-31 19:57:03 +05:00
parent 3088237ea7
commit 749829c12f
2 changed files with 65 additions and 1 deletions

View file

@ -15,7 +15,7 @@
## Чек-лист
- [x] **1. P2-7 SignalR real-time** — Hub `/hubs/notifications` с группами per-org. События `SalePosted`/`SupplyPosted`/`LowStock`. JWT через query `?access_token=` (для WebSocket). Дашборд: live-индикатор Wifi, оптимистическое приращение «Выручка сегодня», toast.info на SupplyPosted, toast.error на LowStock. Тесты: SignalRNotificationsTests (multi-tenant 1/1) + `stage-ui-signalr.spec.ts` (1/1 ✓). Nginx `/hubs/` с upgrade-хедерами и 24h read_timeout.
- [ ] **2. P2-14 Telegram-бот владельца** — Hangfire `OwnerDailySummaryJob` 09:00 МСК, ежедневная сводка (выручка вчера, продажи, топ-3, low-stock). Привязка через deep-link → `Organization.OwnerTelegramChatId`. UI в OrgSettings.
- [x] **2. P2-14 Telegram-бот владельца** — миграция `Phase9a_OwnerTelegramChatId`. `TelegramBotClient` (sendMessage HTML), Disabled-mode когда `Telegram__BotToken` пустой. `OwnerDailySummaryJob.RunAsync` — cron `0 6 * * *` UTC = 09:00 МСК. Сводка: выручка вчера, чеков, средний чек, топ-3 по выручке, low-stock 5. `TelegramBindingController` (status/bind/unbind). UI секция в OrganizationSettings с deep-link + пошаговой инструкцией. Тесты: `TelegramOwnerSummaryTests` (рендер ✓), `stage-ui-telegram.spec.ts` (3/3 ✓ на стейдже).
- [ ] **3. P2-6a Локализация UI (en)** — react-i18next, `ru.json` + `en.json`, language switcher в header. kz TODO. На стейдже smoke — все страницы переключаются.
- [ ] **4. P2-15 MinIO/S3 для uploads**`Minio` SDK, bucket `food-market-uploads`, авто-создание на старте, миграция existing volume. `Storage:Type=Local|Minio` с fallback на Local. Тесты + UI upload картинки.

View file

@ -0,0 +1,64 @@
/**
* Sprint 8 пункт 2 Telegram binding endpoints на стейдже.
* Бот может быть отключён (Telegram__BotToken не задан) это нормально,
* UI должен показать сообщение «бот не настроен». Тестируем endpoint и UI.
*/
import { test, expect, request as apiRequest } from '@playwright/test'
import { apiSignup, attachSession, watchPage, expectNoErrors } from '../lib/ui.js'
const BASE = process.env.E2E_ADMIN_URL ?? 'https://test.admin.food-market.kz'
test.describe('Telegram binding', () => {
test('S8-2.1 GET /api/organization/telegram/status работает', async () => {
const sess = await apiSignup('tg21')
const ctx = await apiRequest.newContext({
baseURL: BASE, ignoreHTTPSErrors: true,
extraHTTPHeaders: { Authorization: `Bearer ${sess.accessToken}` },
})
const r = await ctx.get('/api/organization/telegram/status')
expect(r.status()).toBe(200)
const body = await r.json() as { botEnabled: boolean; chatId: number | null }
// С стейджа токен не задан — бот disabled.
expect(typeof body.botEnabled).toBe('boolean')
expect(body.chatId).toBeNull()
await ctx.dispose()
})
test('S8-2.2 UI секция Telegram рендерится в OrganizationSettings', async ({ page }) => {
const sess = await apiSignup('tg22')
const errs = watchPage(page)
await attachSession(page, sess, '/settings/organization')
await page.waitForLoadState('networkidle')
// Заголовок секции
await expect(page.getByText(/Telegram владельца/i).first()).toBeVisible({ timeout: 8_000 })
// Если бот отключён — показывается соответствующее сообщение
const botDisabled = await page.locator('text=/бот не настроен|нет токена/i').count()
const showsBindForm = await page.locator('text=/Ваш chat_id/i').count()
// Один из двух режимов должен быть видим
expect(botDisabled + showsBindForm).toBeGreaterThan(0)
expectNoErrors(errs, 'telegram section')
})
test('S8-2.3 PUT bind с bot disabled → 400 с читаемым сообщением', async () => {
const sess = await apiSignup('tg23')
const ctx = await apiRequest.newContext({
baseURL: BASE, ignoreHTTPSErrors: true,
extraHTTPHeaders: { Authorization: `Bearer ${sess.accessToken}` },
})
const r = await ctx.put('/api/organization/telegram/bind', {
data: { chatId: 123456 },
failOnStatusCode: false,
})
// Бот выключен → 400
if (r.status() === 400) {
const body = await r.json() as { error: string }
expect(body.error).toMatch(/Telegram|бот.*не настроен|токен/i)
} else if (r.status() === 204) {
// если бот включён и chatId валидный — тоже OK
expect(r.status()).toBe(204)
} else {
throw new Error(`unexpected status ${r.status()}`)
}
await ctx.dispose()
})
})