diff --git a/app/(main)/traffic/page.tsx b/app/(main)/traffic/page.tsx index a12551b2..8fb12402 100644 --- a/app/(main)/traffic/page.tsx +++ b/app/(main)/traffic/page.tsx @@ -762,8 +762,8 @@ const toLiveServer = (s: LiveTrafficServer): ServerTraffic => { rxTotal: s.rxTotal, txTotal: s.txTotal, sessions: s.sessions, - rxSeries: s.rxSeries.length ? s.rxSeries : Array(60).fill(0), - txSeries: s.txSeries.length ? s.txSeries : Array(60).fill(0), + rxSeries: s.rxSeries, + txSeries: s.txSeries, boundIfaces: [], } } @@ -771,8 +771,8 @@ const toLiveServer = (s: LiveTrafficServer): ServerTraffic => { const toLiveBound = (c: BoundIfaceTraffic): BoundIfaceTraffic => ({ ...c, interfaceType: (["ether", "gre", "wg", "other"].includes(c.interfaceType) ? c.interfaceType : "other") as InterfaceType, - rxSeries: c.rxSeries.length ? c.rxSeries : Array(60).fill(0), - txSeries: c.txSeries.length ? c.txSeries : Array(60).fill(0), + rxSeries: c.rxSeries, + txSeries: c.txSeries, }) const toLiveUser = (u: UserTraffic & { interfaces?: BoundIfaceTraffic[] }): UserTraffic => { @@ -781,8 +781,8 @@ const toLiveUser = (u: UserTraffic & { interfaces?: BoundIfaceTraffic[] }): User ...u, role: ROLE_LABEL[u.role as keyof typeof ROLE_LABEL] ?? u.role, interfaces: ifaces, - rxSeries: u.rxSeries.length ? u.rxSeries : Array(60).fill(0), - txSeries: u.txSeries.length ? u.txSeries : Array(60).fill(0), + rxSeries: u.rxSeries, + txSeries: u.txSeries, } } diff --git a/components/reui-kit/traffic-rx-tx-chart.tsx b/components/reui-kit/traffic-rx-tx-chart.tsx index 8ed4d624..7f6f039d 100644 --- a/components/reui-kit/traffic-rx-tx-chart.tsx +++ b/components/reui-kit/traffic-rx-tx-chart.tsx @@ -60,8 +60,8 @@ function toChartData(rx: number[], tx: number[], rangeMinutes: number) { const minutesAgo = Math.round((1 - i / denom) * rangeMinutes) return { time: formatRangeAgo(minutesAgo), - rx: rx[i] ?? 0, - tx: tx[i] ?? 0, + rx: Number.isFinite(rx[i]) && rx[i] >= 0 ? rx[i] : null, + tx: Number.isFinite(tx[i]) && tx[i] >= 0 ? tx[i] : null, } }) } @@ -82,8 +82,11 @@ export function TrafficRxTxChart({ const data = toChartData(rx, tx, rangeMinutes) const tickEvery = Math.max(1, Math.ceil(data.length / 6)) - const chart = ( -
+ const hasData = data.some(point => point.rx !== null || point.tx !== null) + const chart = !hasData ? ( +

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

+ ) : ( +
{} } + const control = { holdRange: '', failIface: false, series: 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, name: `Детали ${range} ${iface}`, rxNow: range === '24h' ? 24 : 15 } } }) + return route.fulfill({ json: { server: { ...server, rxSeries: control.series ?? server.rxSeries, 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] } @@ -72,3 +72,16 @@ for (const width of [320, 390]) { expect(await page.evaluate(() => document.documentElement.scrollWidth <= window.innerWidth + 1)).toBe(true) }) } + + +test('Трафик: пустая история отличается от нулевой', async ({ page }) => { + const control = await setup(page) + control.series = [] + await page.getByRole('button', { name: '24ч', exact: true }).click() + await expect(page.getByText('Нет исторических измерений за выбранный период', { exact: true })).toBeVisible() + await expect(page.getByRole('region', { name: 'История RX/TX' })).toHaveCount(0) + control.series = [0, 0] + await page.getByRole('button', { name: '15м', exact: true }).click() + await expect(page.getByRole('region', { name: 'История RX/TX' })).toBeVisible() + await expect(page.getByText('Нет исторических измерений за выбранный период', { exact: true })).toHaveCount(0) +})