DFiP_Budget_planing/perf/e2e/tests/scroll-lib.spec.ts
Andrey Epifancev 29867535b0 perf: полный прогон run-all.sh и правки по ревью
- perf/run-all.sh: масштабы 1 и 2, шаги reset -> gen -> verify -> stand ->
  preflight -> scroll -> coauthor -> grow -> modals -> modals_profiling ->
  memory -> memtrend -> [control4x] -> bench -> aggregate; лог и
  run-all.status.json, возобновление (PERF_STEPS, PERF_SKIP_RESET),
  сухой режим с оценкой длительности, PERF_S2_REPEATS/PERF_S2_TARGETS,
  контроль CPU 4x (PERF_CONTROL_TARGETS).
- Основная метрика: main_thread_ms и main_thread_ms_per_step в скролле
  (CDP TaskDuration), frame_excess_ms; в summary.md первыми строками.
- SQL-статистика по шагам: PERF_SQLSTATS_TAG -> sql/<шаг>/ (sqlstats --out).
- netlog: тела /auth/ не пишутся, поля password/token/secret вырезаются.
- scroll.spec: уникальные имена целей (лист ССП с суффиксом /ssp, год в
  отчёте проекта) - иначе Playwright не запускал файл из-за дублей.
- PERF_TRACE=0 для прогонов с одним повтором.
- README: scroll-coauthor, run-all, возобновление, без CPU 4/6 в матрицах.
- design.md: уточнена формулировка R-2, решения Task 10a.

Refs #202

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-26 03:01:33 +04:00

59 lines
2.5 KiB
TypeScript

import { test, expect } from '@playwright/test';
import path from 'node:path';
import { scrollThrough } from '../lib/scroll';
import { PROBE_SCRIPT, startProbe, perStep } from '../lib/probe';
const fx = (n: string) => 'file://' + path.resolve(__dirname, '../fixtures', n);
test('scrollThrough: длинный список y проходится до конца', async ({ page }) => {
await page.goto(fx('long-list.html'));
const r = await scrollThrough(page, '#box', 'y');
expect(r.complete).toBe(true);
expect(r.steps).toBeGreaterThanOrEqual(100);
// вернулись к началу
expect(await page.evaluate(() => document.querySelector('#box')!.scrollTop)).toBeLessThanOrEqual(2);
});
test('scrollThrough: ось x', async ({ page }) => {
await page.goto(fx('long-list.html'));
const r = await scrollThrough(page, '#box', 'x');
expect(r.complete).toBe(true);
});
test('scrollThrough: при исчерпании maxSteps complete === false', async ({ page }) => {
await page.goto(fx('clipped-list.html'));
const r = await scrollThrough(page, '#box', 'y', 200, 10);
expect(r.complete).toBe(false);
expect(r.steps).toBeLessThanOrEqual(10);
});
test('scrollThrough: контейнер без переполнения - complete === false, reason no_overflow, 0 шагов', async ({ page }) => {
await page.goto(fx('no-overflow.html'));
for (const axis of ['y', 'x'] as const) {
const r = await scrollThrough(page, '#box', axis);
expect(r.complete).toBe(false);
expect(r.steps).toBe(0);
expect(r.reason).toBe('no_overflow');
}
});
test('scrollThrough: ось x на длинном по высоте, но не по ширине контейнере - no_overflow', async ({ page }) => {
await page.goto(fx('clipped-list.html'));
const r = await scrollThrough(page, '#box', 'x');
expect(r.complete).toBe(false);
expect(r.reason).toBe('no_overflow');
});
test('scroll + probe: main_thread_ms_per_step = main_thread_ms / steps', async ({ page }) => {
await page.addInitScript(PROBE_SCRIPT);
await page.goto(fx('long-list.html'));
const p = await startProbe(page);
const r = await scrollThrough(page, '#box', 'y');
const m = await p.stop();
expect(r.steps).toBeGreaterThan(0);
expect(m.main_thread_ms).toBeGreaterThan(0);
const ps = perStep(m, r.steps);
expect(ps.main_thread_ms_per_step).toBeCloseTo(m.main_thread_ms / r.steps, 6);
expect(perStep(m, 0)).toEqual({}); // нет шагов (фильтр, no_overflow) - метрики на шаг нет
});