From 5b2a2917c14fc3f92c056b164db53c7a083883b1 Mon Sep 17 00:00:00 2001 From: Denozordec Date: Sun, 4 Oct 2026 10:51:59 +0700 Subject: [PATCH] =?UTF-8?q?fix(ui):=20=D0=BE=D0=B3=D1=80=D0=B0=D0=BD=D0=B8?= =?UTF-8?q?=D1=87=D0=B8=D1=82=D1=8C=20=D0=BE=D1=82=D0=BE=D0=B1=D1=80=D0=B0?= =?UTF-8?q?=D0=B6=D0=B5=D0=BD=D0=B8=D0=B5=20=D0=BA=D0=B0=D1=82=D0=B0=D0=BB?= =?UTF-8?q?=D0=BE=D0=B3=D0=B0=20BFD-=D1=81=D0=B5=D1=81=D1=81=D0=B8=D0=B9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- components/data-grids/ospf-bfd-data-grid.tsx | 27 +++++++--- docs/ux-ui-performance.md | 2 + docs/ux-ui-plan.md | 2 +- docs/ux-ui-progress.md | 8 +++ docs/ux-ui-stage-12.md | 9 ++++ docs/ux-ui/screens/large-ospf-bfd-320.png | Bin 0 -> 52962 bytes tests/e2e/large-ospf-bfd.spec.ts | 52 +++++++++++++++++++ 7 files changed, 93 insertions(+), 7 deletions(-) create mode 100644 docs/ux-ui/screens/large-ospf-bfd-320.png create mode 100644 tests/e2e/large-ospf-bfd.spec.ts diff --git a/components/data-grids/ospf-bfd-data-grid.tsx b/components/data-grids/ospf-bfd-data-grid.tsx index 6f40f914..691f2f08 100644 --- a/components/data-grids/ospf-bfd-data-grid.tsx +++ b/components/data-grids/ospf-bfd-data-grid.tsx @@ -1,8 +1,8 @@ "use client" -import { dataGridFeatures, type DataGridFeatures } from "@/components/reui/data-grid" +import { DataGridContainer, DataGridScrollArea, DataGridTable, dataGridFeatures, type DataGridFeatures } from "@/components/reui/data-grid" -import { useMemo } from "react" +import { useEffect, useMemo } from "react" import { type ColumnDef, useTable, @@ -76,6 +76,7 @@ function OspfBfdDataGrid({ sessions }: OspfBfdDataGridProps) { () => [ { id: "serverLabel", + size: 128, accessorKey: "serverLabel", header: ({ column }) => ( @@ -84,7 +85,7 @@ function OspfBfdDataGrid({ sessions }: OspfBfdDataGridProps) { const b = row.original return (
- {b.serverLabel} + {b.serverLabel} {b.multihop === null && Режим неизвестен} {b.multihop === true && ( @@ -254,6 +255,7 @@ function OspfBfdDataGrid({ sessions }: OspfBfdDataGridProps) { }, { id: "stateChanges", + size: 112, accessorKey: "stateChanges", header: ({ column }) => ( @@ -273,12 +275,20 @@ function OspfBfdDataGrid({ sessions }: OspfBfdDataGridProps) { const table = useTable({ features: dataGridFeatures, - manualPagination: true, + manualPagination: false, + autoResetPageIndex: false, + initialState: { pagination: { pageIndex: 0, pageSize: 50 }, columnPinning: { start: ["serverLabel"], end: [] } }, data: sessions, columns, getRowId: (row) => row.id, }) + const { pageIndex, pageSize } = table.state.pagination + useEffect(() => { + const lastPage = Math.max(0, Math.ceil(sessions.length / pageSize) - 1) + if (pageIndex > lastPage) table.setPageIndex(lastPage) + }, [sessions.length, pageIndex, pageSize, table]) + if (sessions.length === 0) { return ( 50 || pageSize !== 50} tableClassNames={{ headerRow: "border-b border-border", bodyRow: "group/row hover:bg-muted/30" }} - tableLayout={{ dense: true }} - /> + tableLayout={{ dense: true, columnsPinnable: true }} + > + + + + ) } diff --git a/docs/ux-ui-performance.md b/docs/ux-ui-performance.md index 5907a5fa..44d5d39a 100644 --- a/docs/ux-ui-performance.md +++ b/docs/ux-ui-performance.md @@ -76,3 +76,5 @@ Statistics pivot в backend/src/services/statistics-aggregate.ts огранич 12.3.21: history200 остаётся50 DOM rows/2367 nodes/1150ms dev ready. Strict DTO и null rates приняты от pure DB mapper до grid/KPI/card;60+5 browser и unit4/builds. Это не проверка реального PostgreSQL/RouterOS. 12.3.24:OSPF routes1000/200347 JSON bytes —50 DOM rows/1356 page nodes/930ms dev ready. Источник /ospf/catalog объединяет полный RouterOS ipRoutes без page/limit. Whole-catalog search/URL/reload/global Cost sort/page20/refresh/503 cache/1000→1/320px/видимость области/axe приняты;22+1 browser/type/lint/build. Полный DTO остаётся в памяти; скорость RouterOS не измерена. Осталось20 manualPagination:true specialized grids, включая demo/bounded источники; это не20 доказанных дефектов. + +12.3.25:BFD1000/323133 JSON bytes —50 DOM rows/1956 page nodes/983ms dev ready. Источник /ospf/catalog объединяет полный RouterOS bfdSessions без page/limit. Whole-catalog search/URL/reload/global Rx sort/page20/refresh/503 cache/1000→1/320px/pinning/axe приняты;12 browser/type/lint/build. Полный DTO остаётся в памяти; скорость RouterOS не измерялась. Осталось19 manualPagination:true specialized grids, включая demo/bounded источники. diff --git a/docs/ux-ui-plan.md b/docs/ux-ui-plan.md index 241b18d0..bf066272 100644 --- a/docs/ux-ui-plan.md +++ b/docs/ux-ui-plan.md @@ -4,7 +4,7 @@ ## Актуальная точка продолжения — 2026-10-04 -Этап 11Б завершён в границах локальной приёмки (11Б.7); внешний выпуск не проверен. 12.1 принят локально: доступность оболочки/клавиатура/health в описанных сценариях. 12.2.1 принят локально: strict BGP catalog/partial/nullable counters. 12.2.2 принят локально: strict traffic DTO и агрегация без выдуманных точек. 12.2.3 принят локально: observations для server traffic от backend до dashboard. 12.2.4 принят локально: strict servers/probes и unknown server status. 12.2.5 принят локально: реальные ping samples/unknown/ось RTT/Recharts. 12.2.6 принят локально: strict GRE/WireGuard/VXLAN и device failures. 12.2.7 принят локально: strict Internet path snapshot/fallback. 12.2.8 принят локально: подтверждение полного backend сбора. 12.2.9 принят локально: метрики участков и наблюдаемый WAN без эвристической подмены. 12.3.1 принят локально:1000 серверов/штатная пагинация. 12.3.2 принят локально: пагинация CompactDataGrid/1000 строк статистики. 12.3.3 принят локально: порционный DOM очереди проблем. 12.3.4 принят локально: большой BGP каталог/страница при обновлении. Production build и123 профильных browser приняты локально на3978976. 12.2.10 принят локально: обязательные RouterOS поля. 12.3.5 принят локально: страница CompactDataGrid сохраняется при опросе,89 browser и повторный production build прошли. 12.3.6 сохранён: baseline1000 speed probes (ограничение DOM пока не выполнено). 12.3.7 принят локально:50 строк speed-проб/сохранение страницы/глобальная сортировка/1000→1; каталог1001 устройства остаётся в DOM. 12.3.8 принят локально:50 строк ресурсов, URL/поиск/сохранение страницы/1000→1; исправлена pagination оболочки с children, production build и8 больших browser прошли. 12.3.9 принят локально:порционное раскрытие1000 предупреждений ресурсов/клавиатура/axe; исходная ошибка ESLint speedDraft подтверждена. 12.3.10 принят локально:недоступные интерфейсы очищаются в обработчиках,54 browser/полный ESLint/production build прошли. 12.3.11 принят локально:ServerTileRail50 устройств/URL/поиск/клавиатура/две площадки/mobile Sheet/axe обе темы;47 browser и production build прошли. 12.3.12 принят локально:BGP ручная страница при выбранной URL сессии/refresh/reload,16 browser/build прошли. 12.4.1 записан:main1091 сценарий/1088 passed/3 устаревших fixtures; оба исправленных файла6 passed. 12.4.2 принят локально:context205/24 файла и unit28 прошли. 12.3.13 принят локально:wide pivot51×16/ScrollArea/axe/24+2 browser/builds. 12.3.14 принят локально:history200/50 DOM/54+1 browser/build. 12.3.15 принят локально:live расписание1000/page20 error cache/15 browser. 12.3.16 принят локально:certificates1000/50 DOM/refresh деталей/19+3 browser/build. 12.3.17 принят локально:pinning идентификатора/видимость значений/29 browser/build. 12.3.18 принят локально:users1000/поиск всего списка/детали и ошибка refresh/32 browser/build. 12.3.19 принят локально:backups1000/все мобильные действия/возврат focus/39+4 browser/build. 12.3.20 принят локально:независимые speed GET/cache/freshness/58+4 browser/build. 12.3.21 принят локально:strict history DTO/null rates от DB до UI/60+5 browser/unit4/builds. 12.4.3 принят локально:users/backups delta context36 на a558412. 12.3.22 принят локально:shared speed-probes DTO/context8/60+6+47 browser/unit5/builds. 12.3.23 принят локально:strict write DTO/atomic replacement/confirmed scheduler warning/65 browser/HTTP DI JWT/builds. 12.3.24 принят локально:OSPF routes1000/50 DOM/page20/cache/320px/22+1 browser/build. Следующий блок12.3.25:BFD sessions большого каталога; далее network grids и полная main/context регрессия.12.5 отдельно. См. docs/ux-ui-stage-12.md. Внешние ограничения 11Б сохраняются. +Этап 11Б завершён в границах локальной приёмки (11Б.7); внешний выпуск не проверен. 12.1 принят локально: доступность оболочки/клавиатура/health в описанных сценариях. 12.2.1 принят локально: strict BGP catalog/partial/nullable counters. 12.2.2 принят локально: strict traffic DTO и агрегация без выдуманных точек. 12.2.3 принят локально: observations для server traffic от backend до dashboard. 12.2.4 принят локально: strict servers/probes и unknown server status. 12.2.5 принят локально: реальные ping samples/unknown/ось RTT/Recharts. 12.2.6 принят локально: strict GRE/WireGuard/VXLAN и device failures. 12.2.7 принят локально: strict Internet path snapshot/fallback. 12.2.8 принят локально: подтверждение полного backend сбора. 12.2.9 принят локально: метрики участков и наблюдаемый WAN без эвристической подмены. 12.3.1 принят локально:1000 серверов/штатная пагинация. 12.3.2 принят локально: пагинация CompactDataGrid/1000 строк статистики. 12.3.3 принят локально: порционный DOM очереди проблем. 12.3.4 принят локально: большой BGP каталог/страница при обновлении. Production build и123 профильных browser приняты локально на3978976. 12.2.10 принят локально: обязательные RouterOS поля. 12.3.5 принят локально: страница CompactDataGrid сохраняется при опросе,89 browser и повторный production build прошли. 12.3.6 сохранён: baseline1000 speed probes (ограничение DOM пока не выполнено). 12.3.7 принят локально:50 строк speed-проб/сохранение страницы/глобальная сортировка/1000→1; каталог1001 устройства остаётся в DOM. 12.3.8 принят локально:50 строк ресурсов, URL/поиск/сохранение страницы/1000→1; исправлена pagination оболочки с children, production build и8 больших browser прошли. 12.3.9 принят локально:порционное раскрытие1000 предупреждений ресурсов/клавиатура/axe; исходная ошибка ESLint speedDraft подтверждена. 12.3.10 принят локально:недоступные интерфейсы очищаются в обработчиках,54 browser/полный ESLint/production build прошли. 12.3.11 принят локально:ServerTileRail50 устройств/URL/поиск/клавиатура/две площадки/mobile Sheet/axe обе темы;47 browser и production build прошли. 12.3.12 принят локально:BGP ручная страница при выбранной URL сессии/refresh/reload,16 browser/build прошли. 12.4.1 записан:main1091 сценарий/1088 passed/3 устаревших fixtures; оба исправленных файла6 passed. 12.4.2 принят локально:context205/24 файла и unit28 прошли. 12.3.13 принят локально:wide pivot51×16/ScrollArea/axe/24+2 browser/builds. 12.3.14 принят локально:history200/50 DOM/54+1 browser/build. 12.3.15 принят локально:live расписание1000/page20 error cache/15 browser. 12.3.16 принят локально:certificates1000/50 DOM/refresh деталей/19+3 browser/build. 12.3.17 принят локально:pinning идентификатора/видимость значений/29 browser/build. 12.3.18 принят локально:users1000/поиск всего списка/детали и ошибка refresh/32 browser/build. 12.3.19 принят локально:backups1000/все мобильные действия/возврат focus/39+4 browser/build. 12.3.20 принят локально:независимые speed GET/cache/freshness/58+4 browser/build. 12.3.21 принят локально:strict history DTO/null rates от DB до UI/60+5 browser/unit4/builds. 12.4.3 принят локально:users/backups delta context36 на a558412. 12.3.22 принят локально:shared speed-probes DTO/context8/60+6+47 browser/unit5/builds. 12.3.23 принят локально:strict write DTO/atomic replacement/confirmed scheduler warning/65 browser/HTTP DI JWT/builds. 12.3.24 принят локально:OSPF routes1000/50 DOM/page20/cache/320px/22+1 browser/build. 12.3.25 принят локально:BFD1000/50 DOM/cache/320px/12 browser/build. Следующий блок12.4.4:общая context матрица213, затем12.4.5 main текущего HEAD и12.3.26 оставшиеся network grids; далее network grids и полная main/context регрессия.12.5 отдельно. См. docs/ux-ui-stage-12.md. Внешние ограничения 11Б сохраняются. Реальные pg_dump/pg_restore, PG rollback и остановка UDP worker требуют внешней приёмки. Датированные продолжения ниже — история; точный следующий шаг также указан в начале [журнала](ux-ui-progress.md). diff --git a/docs/ux-ui-progress.md b/docs/ux-ui-progress.md index b1f14e04..be63f368 100644 --- a/docs/ux-ui-progress.md +++ b/docs/ux-ui-progress.md @@ -36,6 +36,14 @@ +## 12.3.25 — большой каталог BFD-сессий (2026-10-04) + +Источник /ospf/catalog читает и объединяет полный RouterOS bfdSessions без pagination cap. OspfBfdDataGrid использует client pagination50, сохранение страницы при refresh и clamp при сокращении/поиске. Роутер закреплён штатным start/end pinning, горизонтальная прокрутка через DataGridScrollArea; последняя метрика полностью видима на320px. Метрики и режимы unknown/null остаются прежними, сетевые операции не изменены. + +1000 BFD/323133 JSON bytes:50 DOM rows/1956 page nodes/983ms dev ready.12 browser прошли:page20/refresh/global Rx sort/поиск по всему remoteAddr каталогу/URL/reload/503 cache/unknown/recovery1000→1/320px/axe, включая прежние null/zero и layout сценарии. Lint/typecheck и production build прошли, мобильный снимок просмотрен. Полные DTO остаются в памяти; реальный RouterOS опрос не проверен. Осталось19 manualPagination:true specialized grids, включая bounded/demo, это не19 доказанных дефектов. + +Следующий блок12.4.4: повтор общей context матрицы213 scenarios после последних delta. Затем12.4.5 общий main прогон текущего HEAD и12.3.26 оставшиеся network grids. Внешняя12.5 отдельно. Коммит локальный без push. + ## 12.3.24 — большой каталог OSPF-маршрутов (2026-10-04) Аудит источника: /ospf/catalog опрашивает полный RouterOS ipRoutes и объединяет маршруты устройств без page/limit. Grid переведён на штатную TanStack v9 client pagination50; сортировка применяется ко всему отфильтрованному каталогу. Страница сохраняется при refresh и ограничивается при сокращении списка/поиске. Назначение закреплено штатным columnPinning start/end, горизонтальная прокрутка использует DataGridScrollArea. Подобраны ширины, чтобы значение области не перекрывалось на320px; длинный префикс переносится. diff --git a/docs/ux-ui-stage-12.md b/docs/ux-ui-stage-12.md index 1e52f375..3d437730 100644 --- a/docs/ux-ui-stage-12.md +++ b/docs/ux-ui-stage-12.md @@ -353,3 +353,12 @@ Backend заменяет каталог в одной Drizzle transaction с pg_ Следующий блок12.3.25: BFD sessions большого каталога OSPF,50 DOM rows/поиск/sort/cache/320px. После него оставшиеся network grids и общая матрица12.4; внешняя12.5 отдельно. Коммит локальный без push. + +## 12.3.25 — большой каталог BFD-сессий (2026-10-04) + +Источник /ospf/catalog читает и объединяет полный RouterOS bfdSessions без pagination cap. OspfBfdDataGrid использует client pagination50, сохранение страницы при refresh и clamp при сокращении/поиске. Роутер закреплён штатным start/end pinning, горизонтальная прокрутка через DataGridScrollArea; последняя метрика полностью видима на320px. Метрики и режимы unknown/null остаются прежними, сетевые операции не изменены. + +1000 BFD/323133 JSON bytes:50 DOM rows/1956 page nodes/983ms dev ready.12 browser прошли:page20/refresh/global Rx sort/поиск по всему remoteAddr каталогу/URL/reload/503 cache/unknown/recovery1000→1/320px/axe, включая прежние null/zero и layout сценарии. Lint/typecheck и production build прошли, мобильный снимок просмотрен. Полные DTO остаются в памяти; реальный RouterOS опрос не проверен. Осталось19 manualPagination:true specialized grids, включая bounded/demo, это не19 доказанных дефектов. + +Следующий блок12.4.4: повтор общей context матрицы213 scenarios после последних delta. Затем12.4.5 общий main прогон текущего HEAD и12.3.26 оставшиеся network grids. Внешняя12.5 отдельно. Коммит локальный без push. + diff --git a/docs/ux-ui/screens/large-ospf-bfd-320.png b/docs/ux-ui/screens/large-ospf-bfd-320.png new file mode 100644 index 0000000000000000000000000000000000000000..2a2b0e14a0741fb7b18b3b8f874b99a8f8c2757c GIT binary patch literal 52962 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

S?O}uQpKM>ie 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 literal 0 HcmV?d00001 diff --git a/tests/e2e/large-ospf-bfd.spec.ts b/tests/e2e/large-ospf-bfd.spec.ts new file mode 100644 index 00000000..53a928a2 --- /dev/null +++ b/tests/e2e/large-ospf-bfd.spec.ts @@ -0,0 +1,52 @@ +import { test, expect } from "@playwright/test" +import { ospfCatalogSchema } from "../../lib/ospf-response" +import axe from "axe-core" +test("OSPF BFD1000: catalog search/sort, pagination/cache/refresh and320px", async ({ page }, info) => { + test.setTimeout(90000) + const server = { id: 1, name: "Роутер 1", host: "192.0.2.1", country: "RU", enabled: true, status: null } + let sessions = Array.from({ length: 1000 }, (_, i) => ({ id: "*" + (i + 1), serverId: 1, serverName: "Роутер 1", serverSite: "", serverCountry: "RU", localAddr: "192.0.2.1", remoteAddr: "10." + Math.floor(i / 256) + "." + (i % 256) + ".1", interface: "ether" + (i + 1), state: "Up", uptime: "1h", multihop: false, multiplier: 3, txInterval: 100, rxInterval: 100, holdTime: 300, packetsRx: i + 1, packetsTx: 0, stateChanges: i + 1 })) + let calls = 0, failed = false; const writes: string[] = [] + await page.route("**/health", r => r.fulfill({ json: { ok: true } })) + await page.route("**/api/**", r => { + const p = new URL(r.request().url()).pathname + if (r.request().method() !== "GET") writes.push(p) + if (p === "/api/ospf/catalog") { calls++; return r.fulfill(failed ? { status: 503, json: { error: "BFD catalog unavailable" } } : { json: ospfCatalogSchema.parse({ neighbors: [], interfaces: [], instances: [], routes: [], bfdSessions: sessions, failures: [] }) }) } + return r.fulfill({ json: p === "/api/auth/config" ? { required: false } : p === "/api/servers" ? [server] : {} }) + }) + const started = Date.now(); await page.goto("/ospf?tab=bfd&server=1&keep=1") + const rows = page.locator("tr[data-row-id]"); await expect(rows).toHaveCount(50) + const profile = { count: sessions.length, jsonBytes: Buffer.byteLength(JSON.stringify(sessions)), renderedRows: await rows.count(), nodes: await page.locator("*").count(), readyMs: Date.now() - started } + console.log("BFD profile", JSON.stringify(profile)); await info.attach("profile", { body: JSON.stringify(profile), contentType: "application/json" }) + const next = page.getByRole("button", { name: "Следующая страница", exact: true }) + for (let i=0;i<19;i++) await next.click() + await expect(next).toBeDisabled(); await expect(rows.first()).toHaveAttribute("data-row-id", "1-*951") + const before = calls; await page.getByRole("button", { name: "Обновить", exact: true }).click() + await expect.poll(() => calls).toBeGreaterThan(before); await expect(rows.first()).toHaveAttribute("data-row-id", "1-*951") + const search = page.getByPlaceholder("Поиск в текущем разделе OSPF…") + await search.fill(sessions[999].remoteAddr); await expect(rows).toHaveCount(1); await expect(rows.first()).toHaveAttribute("data-row-id", "1-*1000") + await expect(page).toHaveURL(url => url.searchParams.get("q") === sessions[999].remoteAddr && url.searchParams.get("keep") === "1") + await page.reload(); await expect(rows).toHaveCount(1); await search.fill(""); await expect(rows).toHaveCount(50) + await page.getByRole("table").getByRole("button", { name: "Пакеты Rx", exact: true }).click() + await expect(rows.first()).toHaveAttribute("data-row-id", "1-*1000") + await next.click(); const previous = await rows.first().getAttribute("data-row-id") + failed = true; await page.getByRole("button", { name: "Обновить", exact: true }).click() + const alert = page.getByRole("alert").filter({ hasText: "Не удалось загрузить OSPF" }) + await expect(alert).toContainText("Показаны последние полученные данные"); await expect(rows).toHaveCount(50); await expect(rows.first()).toHaveAttribute("data-row-id", previous!) + await expect(rows.first()).toContainText("Неизвестно") + await page.setViewportSize({ width: 320, height: 1000 }) + const scroll = page.locator('[data-slot="data-grid"] [data-slot="scroll-area-viewport"]') + await expect(scroll).toHaveCount(1); await scroll.evaluate(el => { el.scrollLeft = el.scrollWidth }) + await expect.poll(() => scroll.evaluate(el => el.scrollLeft)).toBeGreaterThan(0) + const cells = rows.first().getByRole("cell") + expect(await cells.first().evaluate(el => { const a=el.getBoundingClientRect(),v=el.closest('[data-slot="scroll-area-viewport"]')!.getBoundingClientRect();return a.left>=v.left-1&&a.right<=v.right+1 })).toBe(true) + expect(await cells.last().locator("span").evaluate(el => {const a=el.getBoundingClientRect(),v=el.closest('[data-slot="scroll-area-viewport"]')!.getBoundingClientRect(),p=el.closest("tr")!.querySelector("td")!.getBoundingClientRect();return a.left>=p.right-1&&a.right<=v.right+1})).toBe(true) + expect(await page.evaluate(() => document.documentElement.scrollWidth <= innerWidth)).toBe(true) + await page.addScriptTag({ content: axe.source }) + expect((await page.evaluate(async () => (window as unknown as { axe: typeof axe }).axe.run(document.querySelector('[data-slot="data-grid"]')!, { runOnly: { type: "tag", values: ["wcag2a", "wcag2aa", "wcag21a", "wcag21aa"] } }))).violations).toEqual([]) + await page.addStyleTag({ content: "nextjs-portal { display: none !important; }" }) + await page.getByRole("table").getByRole("button", { name: "Роутер", exact: true }).scrollIntoViewIfNeeded() + await page.screenshot({ path: info.outputPath("ospf-bfd-320.png"), animations: "disabled" }) + failed = false; sessions = [sessions[999]]; await alert.getByRole("button", { name: "Повторить загрузку OSPF" }).click() + await expect(rows).toHaveCount(1); await expect(alert).toHaveCount(0); await expect(rows.first()).toHaveAttribute("data-row-id", "1-*1000") + expect(writes).toEqual([]) +})