fix(ui): снимать статус live при отсутствии обновлений

This commit is contained in:
Denozordec committed 2026-09-28 11:35:29 +07:00
1 parent 5734340db2
commit f50a29f116
8 files changed
+103 -5

No files matched your search

+25
View File
@@ -0,0 +1,25 @@
import type { Page } from '@playwright/test'
// Keep a response open; the browser test controls sample delivery independently of HTTP routes.
export async function installOpenLiveStream(page: Page, path: string, sample: unknown) {
await page.clock.install()
await page.addInitScript(({ path, sample }) => {
const originalFetch = window.fetch.bind(window)
window.fetch = async (input, init) => {
const url = new URL(input instanceof Request ? input.url : String(input), location.href)
if (url.pathname !== path) return originalFetch(input, init)
const stream = new ReadableStream<Uint8Array>({
start(controller) {
const send = () => controller.enqueue(new TextEncoder().encode('event: sample\ndata: ' + JSON.stringify(sample) + '\n\n'))
Object.assign(window, { sendLiveSample: send })
send()
},
})
return new Response(stream, { headers: { 'Content-Type': 'text/event-stream' } })
}
}, { path, sample })
}
export async function resumeLiveStream(page: Page) {
await page.evaluate(() => (window as unknown as { sendLiveSample: () => void }).sendLiveSample())
}
+22
View File
@@ -1,3 +1,4 @@
import { installOpenLiveStream, resumeLiveStream } from "./live-stream-fixture"
import { test, expect, type Page } from '@playwright/test'
async function setup(page: Page) {
@@ -319,3 +320,24 @@ test('Flow: время получения принадлежит текущем
await expect(page.getByRole('alert').filter({ hasText: 'Аналитика недоступна' })).toBeVisible()
await expect(receipt).toHaveCount(0)
})
test('Flow: молчащий SSE сохраняет данные и восстанавливает live после sample', async ({ page }) => {
await installOpenLiveStream(page, '/api/traffic/flow/live', { bpsNow: 1000000, bytes: 200, packets: 2, conversations: 1, uniqueSrc: 1, uniqueDst: 1, topProto: 'TCP', rxSeries: [1], txSeries: [2], applications: [], protocols: [], sources: [], destinations: [], interfaces: [], conversationsList: [], ifaces: [], live: true })
await setup(page)
const receipt = page.getByLabel('Получение аналитики потоков').locator('time')
await expect(page.getByText('live', { exact: true }).filter({ visible: true })).toHaveCount(3)
const stamp = await receipt.getAttribute('datetime')
await page.clock.fastForward(15_001)
await expect(page.getByText('Нет обновлений live более 15 секунд', { exact: true })).toBeVisible()
await expect(page.getByText('live', { exact: true })).toHaveCount(0)
await expect(page.getByLabel('Скорость потоков')).toContainText('1.00 Мбит/с')
await expect(receipt).toHaveAttribute('datetime', stamp!)
await resumeLiveStream(page)
await expect(page.getByText('live', { exact: true }).filter({ visible: true })).toHaveCount(3)
await expect(page.getByText('Нет обновлений live более 15 секунд', { exact: true })).toHaveCount(0)
await page.getByRole('button', { name: '15м', exact: true }).click()
await expect(page.getByText('Категория 15m', { exact: true }).filter({ visible: true })).toBeVisible()
await page.clock.fastForward(15_001)
await expect(page.getByText('Нет обновлений live более 15 секунд', { exact: true })).toHaveCount(0)
})
+17 -1
View File
@@ -1,6 +1,7 @@
import { installOpenLiveStream, resumeLiveStream } from "./live-stream-fixture"
import { test, expect } from '@playwright/test'
for (const variant of ['closed', 'invalid', 'http-error'] as const) {
for (const variant of ['closed', 'invalid', 'http-error', 'silent'] as const) {
test(`Трафик: SSE ${variant} не выдаёт старое измерение за live`, async ({ page }) => {
const server = { id: '1', name: 'Тестовый сервер', site: 'TEST', country: 'RU', status: 'online', rxNow: 2, txNow: 3, rxPeak: 5, txPeak: 6, rxTotal: 100, txTotal: 200, sessions: 1, rxSeries: [1, 2], txSeries: [2, 3] }
await page.route('**/health', route => route.fulfill({ json: { ok: true } }))
@@ -22,11 +23,26 @@ for (const variant of ['closed', 'invalid', 'http-error'] as const) {
: path === '/api/servers' ? [] : {}
return route.fulfill({ json })
})
if (variant === 'silent') await installOpenLiveStream(page, '/api/traffic/servers/1/live', { rxMbps: 17, txMbps: 19, at: '2026-09-23T12:00:00Z' })
const received = new Date('2026-09-28T06:00:00Z')
await page.clock.setFixedTime(received)
await page.goto('/traffic')
const summary = page.getByLabel('Скорость выбранного сервера')
await expect(summary).toBeVisible()
if (variant === 'silent') {
await expect(summary.getByText('live', { exact: true }).filter({ visible: true })).toHaveCount(2)
const stamp = await page.getByLabel('Последнее измерение сервера').locator('time').first().getAttribute('datetime')
await page.clock.fastForward(15_001)
await expect(summary.getByText('live', { exact: true })).toHaveCount(0)
await expect(summary).toContainText('17.0')
await expect(page.getByLabel('Последнее измерение сервера').locator('time').first()).toHaveAttribute('datetime', stamp!)
await resumeLiveStream(page)
await expect(summary.getByText('live', { exact: true }).filter({ visible: true })).toHaveCount(2)
await page.getByRole('group', { name: 'Разделы трафика' }).getByRole('button', { name: 'Клиенты', exact: true }).click()
await page.clock.fastForward(15_001)
await expect(page.getByLabel('Последнее измерение сервера')).toHaveCount(0)
return
}
const hint = variant === 'http-error' ? 'История · live недоступен' : 'Последнее live-измерение · поток недоступен'
await expect(summary.getByText(hint, { exact: true }).filter({ visible: true })).toHaveCount(2)
await expect(summary.getByText('live', { exact: true })).toHaveCount(0)