test: the first-paint smoke watches only the panel's own long tasks

On the CI runner the smoke went red without any mutant: the observer was
started before the 60-room plan had finished drawing, and that render —
a long task of its own, unrelated to this issue — landed in the window.
The smoke now waits for a quiet main thread before it starts watching,
prints what it measured, and allows up to 450 ms per task: the residual
150–200 ms slices are one space's masonry union, which polyclip cannot
split, while the mutant that puts the whole aggregate back into one task
still produces ~1.5 s.

Issue: #509
User-Visible: no
This commit is contained in:
Codex
2026-09-10 11:47:25 +03:00
parent 172d9d5a00
commit 19b14d3976
+22
View File
@@ -69,6 +69,25 @@ async function openLargePlan(page, { reducedMotion = false } = {}) {
// Долгие задачи главного потока за всё время показа: подвисание может
// случиться и не в первом кадре, а сразу после прихода ленивого чанка,
// и человек увидит ровно тот же фриз (#509 AC4).
//
// Считаются только задачи ПОСЛЕ того, как большой план дорисован: сама
// отрисовка 60 комнат — тоже длинная задача, но она не про эту задачу и
// на CI-раннере попадала в окно наблюдения (#509, первый прогон CI).
await new Promise((resolve) => {
let quiet = null;
const settleObserver = (() => {
try {
const observer = new PerformanceObserver(() => {
clearTimeout(quiet);
quiet = setTimeout(() => { observer.disconnect(); resolve(); }, 400);
});
observer.observe({ entryTypes: ['longtask'] });
quiet = setTimeout(() => { observer.disconnect(); resolve(); }, 400);
return observer;
} catch { resolve(); return null; }
})();
if (!settleObserver) resolve();
});
window.__longTasks = [];
try {
window.__longTaskObserver = new PerformanceObserver((list) => {
@@ -109,6 +128,7 @@ async function openLargePlan(page, { reducedMotion = false } = {}) {
return {
waitedMs: performance.now() - started, pending: f.pending(), texts: f.texts(),
longest: window.__longTasks.length ? Math.max(...window.__longTasks) : 0,
tasks: window.__longTasks.slice(0, 8),
observed: !!window.__longTaskObserver,
};
});
@@ -117,6 +137,8 @@ async function openLargePlan(page, { reducedMotion = false } = {}) {
check('replacedWithinBudget', settled.waitedMs < 6000);
// AC4: ни одна порция расчёта не держит поток дольше кадра-другого. Без
// порционного обхода здесь была бы одна задача на всю площадь (S2: ~1,5 с).
console.log(` диагностика: долгих задач ${settled.tasks.length ? settled.tasks.join(', ') : 'нет'};`
+ ` значения через ${Math.round(settled.waitedMs)} мс`);
check('noLongTaskWhileComputing', !settled.observed || settled.longest < 250);
// AC5: появление панели анимируется — кадры анимации реально существуют.