From ccbc1aa029a99e2dc1f72ee3f2c76598d28aa971 Mon Sep 17 00:00:00 2001 From: Denozordec Date: Thu, 24 Sep 2026 15:51:32 +0700 Subject: [PATCH] =?UTF-8?q?fix(ui):=20=D0=BF=D0=BE=D1=8F=D1=81=D0=BD=D1=8F?= =?UTF-8?q?=D1=82=D1=8C=20=D0=BE=D1=82=D1=81=D1=83=D1=82=D1=81=D1=82=D0=B2?= =?UTF-8?q?=D0=B8=D0=B5=20=D0=BE=D1=82=D0=B4=D0=B5=D0=BB=D1=8C=D0=BD=D0=BE?= =?UTF-8?q?=D0=B3=D0=BE=20=D0=BD=D0=B0=D0=BF=D1=80=D0=B0=D0=B2=D0=BB=D0=B5?= =?UTF-8?q?=D0=BD=D0=B8=D1=8F=20=D1=82=D1=80=D0=B0=D1=84=D0=B8=D0=BA=D0=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- components/reui-kit/traffic-rx-tx-chart.tsx | 13 +++++++----- docs/ux-ui-progress.md | 7 +++++++ tests/e2e/traffic-details.spec.ts | 23 +++++++++++++++++++-- 3 files changed, 36 insertions(+), 7 deletions(-) diff --git a/components/reui-kit/traffic-rx-tx-chart.tsx b/components/reui-kit/traffic-rx-tx-chart.tsx index 7f6f039d..65283329 100644 --- a/components/reui-kit/traffic-rx-tx-chart.tsx +++ b/components/reui-kit/traffic-rx-tx-chart.tsx @@ -82,11 +82,14 @@ export function TrafficRxTxChart({ const data = toChartData(rx, tx, rangeMinutes) const tickEvery = Math.max(1, Math.ceil(data.length / 6)) - const hasData = data.some(point => point.rx !== null || point.tx !== null) + const hasRx = data.some(point => point.rx !== null) + const hasTx = data.some(point => point.tx !== null) + const hasData = hasRx || hasTx const chart = !hasData ? (

Нет исторических измерений за выбранный период

) : (
+ {(!hasRx || !hasTx) &&

{!hasRx ? "Нет измерений RX за выбранный период" : "Нет измерений TX за выбранный период"}

} } /> - - } + {hasTx && + />}
diff --git a/docs/ux-ui-progress.md b/docs/ux-ui-progress.md index 94f88fcb..f434e99b 100644 --- a/docs/ux-ui-progress.md +++ b/docs/ux-ui-progress.md @@ -29,6 +29,13 @@ | 11Б | Оповещения, сбор данных, интеграции, релизы и настройки | Не начат | | 12 | Итоговая доступность, адаптивность, производительность и регрессия | Не начат | +## Блок 8.4Е — отдельные отсутствующие направления RX/TX (2026-09-24) + +- Общий график поясняет отсутствие RX или TX, если второе направление содержит измерения. Линия отсутствующего направления не создаётся; подсказка содержит только доступное направление. +- 7 E2E деталей прошли, включая два новых сценария: одна серия отсутствует, остаётся одна линия, при наведении нет отсутствующего значения и виден доступный показатель. Это проверка полностью отсутствующего направления; разрывы внутри ряда ещё не проверялись отдельным E2E. +- Дополнительно прошли 24 E2E дашборда/flow, покрывающие потребителей общего графика. Lint графика/теста и diff-check чистые. Production build/TypeScript прошли (34 страницы). +- Далее: мини-графики карточек, разрывы внутри исторического ряда и свежесть; смена backend/сервера и поздний SSE, затем приёмка этапа 8. Реальные IPFIX/RouterOS не проверены. Использовано 20% пятичасового и 81% недельного лимита; локальный коммит без push. + ## Блок 8.4Д — пустая история RX/TX (2026-09-24) - Live-преобразования серверов, клиентов и интерфейсов сохраняют пустые массивы истории вместо заполнения 60 нулями. Общий TrafficRxTxChart показывает «Нет исторических измерений за выбранный период», если обе серии не содержат валидных точек. diff --git a/tests/e2e/traffic-details.spec.ts b/tests/e2e/traffic-details.spec.ts index 7a2a1ddf..bbbcba2e 100644 --- a/tests/e2e/traffic-details.spec.ts +++ b/tests/e2e/traffic-details.spec.ts @@ -1,7 +1,7 @@ import { test, expect, type Page } from '@playwright/test' async function setup(page: Page) { - const control = { holdRange: '', failIface: false, series: undefined as number[] | undefined, release: () => {} } + const control = { holdRange: '', failIface: false, series: undefined as number[] | undefined, txSeries: undefined as number[] | undefined, release: () => {} } const gate = new Promise(resolve => { control.release = resolve }) 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 } })) @@ -12,7 +12,7 @@ async function setup(page: Page) { const range = url.searchParams.get('range'), iface = url.searchParams.get('iface') if (control.holdRange && range === control.holdRange) await gate if (control.failIface && iface === 'ether2') return route.fulfill({ status: 500, json: { error: 'Интерфейс временно недоступен' } }) - return route.fulfill({ json: { server: { ...server, rxSeries: control.series ?? server.rxSeries, txSeries: control.series ?? server.txSeries, name: `Детали ${range} ${iface}`, rxNow: range === '24h' ? 24 : 15 } } }) + return route.fulfill({ json: { server: { ...server, rxSeries: control.series ?? server.rxSeries, txSeries: control.txSeries ?? control.series ?? server.txSeries, name: `Детали ${range} ${iface}`, rxNow: range === '24h' ? 24 : 15 } } }) } const json = path === '/api/auth/config' ? { required: false } : path === '/api/traffic/servers' ? { servers: [server] } @@ -85,3 +85,22 @@ test('Трафик: пустая история отличается от нул await expect(page.getByRole('region', { name: 'История RX/TX' })).toBeVisible() await expect(page.getByText('Нет исторических измерений за выбранный период', { exact: true })).toHaveCount(0) }) + + +for (const missing of ['RX', 'TX'] as const) { + test('Трафик: отсутствие ' + missing + ' не подменяется нулевой линией', async ({ page }) => { + const control = await setup(page) + control.series = missing === 'RX' ? [] : [2, 3] + control.txSeries = missing === 'TX' ? [] : [2, 3] + await page.getByRole('button', { name: '24ч', exact: true }).click() + const chart = page.getByRole('region', { name: 'История RX/TX' }) + await expect(chart.getByRole('status')).toHaveText('Нет измерений ' + missing + ' за выбранный период') + await expect(chart.locator('.recharts-line-curve')).toHaveCount(1) + const plot = chart.locator('.recharts-wrapper') + await plot.scrollIntoViewIfNeeded() + const box = await plot.boundingBox() + await page.mouse.move(box!.x + box!.width - 12, box!.y + box!.height / 2) + await expect(chart.getByText(missing + ':', { exact: true })).toHaveCount(0) + await expect(chart.getByText((missing === 'RX' ? 'TX' : 'RX') + ':', { exact: true })).toBeVisible() + }) +}