Files
DndGamePlayer/app/renderer/shared/effects/PxiEffectsOverlay.pointer.test.ts
     Фонтош Иван Сергеевич 48a9c933af fix(effects): снизить лаги сцены на Mac
Pixi режется по contentRect, пульт идёт в preview (12 FPS, 0.5 DPR), штампы тумана/огня/дождя прореживаются, draft темноты и маркера не чаще 20 Гц.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-09-07 17:13:37 +08:00

65 lines
3.2 KiB
TypeScript

import assert from 'node:assert/strict';
import fs from 'node:fs';
import path from 'node:path';
import test from 'node:test';
import { fileURLToPath } from 'node:url';
const here = path.dirname(fileURLToPath(import.meta.url));
void test('PxiEffectsOverlay: canvas не перехватывает указатель в режиме без interactive', () => {
const src = fs.readFileSync(path.join(here, 'PxiEffectsOverlay.tsx'), 'utf8');
assert.ok(src.includes("app.canvas.style.pointerEvents = interactive ? 'auto' : 'none'"));
});
void test('PxiEffectsOverlay: lazy WebGL until effects need it', () => {
const src = fs.readFileSync(path.join(here, 'PxiEffectsOverlay.tsx'), 'utf8');
assert.ok(src.includes('effectsNeedWebGl'));
assert.ok(src.includes('gpuBoot'));
assert.match(src, /canvas\.style\.display = 'none'/);
assert.match(src, /const dpr = useMemo\(\(\) => 1/);
});
void test('PxiEffectsOverlay: imperative setDraft для кисти без React state', () => {
const src = fs.readFileSync(path.join(here, 'PxiEffectsOverlay.tsx'), 'utf8');
assert.ok(src.includes('setDraft'));
assert.ok(src.includes('useImperativeHandle'));
assert.ok(src.includes('mergeDraftState'));
});
void test('PxiEffectsOverlay: lazy VFX packs + idle ticker stop', () => {
const src = fs.readFileSync(path.join(here, 'PxiEffectsOverlay.tsx'), 'utf8');
assert.ok(src.includes('ensureVfxPacksForState'));
assert.ok(src.includes('collectNeededVfxPacks'));
assert.ok(src.includes('syncTickerForState'));
assert.ok(src.includes('app.ticker.stop'));
// После clear нужен явный render, иначе «очистить» замораживает кадр вместо исчезновения.
assert.match(src, /app\.render\?\.\(\);[\s\S]*?app\.ticker\.stop/);
// Eager preload всех наборов при init убран.
assert.doesNotMatch(
src,
/syncNodes\([\s\S]*?stateRef\.current[\s\S]*?\);\s*void preloadLightningVfxFrameTextures\(pixi\);\s*void preloadElectricAccentFrameTextures\(pixi\);\s*void preloadFogVfxFrameTextures\(pixi\);/,
);
assert.ok(src.includes('Lazy VFX'));
});
void test('PxiEffectsOverlay: эффекты перекладываются при смене viewport (зум/пан)', () => {
const src = fs.readFileSync(path.join(here, 'PxiEffectsOverlay.tsx'), 'utf8');
assert.ok(src.includes('relayoutInstanceNode'));
assert.ok(src.includes('instanceContentSig'));
assert.ok(src.includes('viewportSig'));
assert.match(src, /\$\{contentSig\}@\$\{viewportSig\(viewport\)\}/);
});
void test('PxiEffectsOverlay: canvas по contentRect + preview profile', () => {
const src = fs.readFileSync(path.join(here, 'PxiEffectsOverlay.tsx'), 'utf8');
const css = fs.readFileSync(path.join(here, 'PxiEffectsOverlay.module.css'), 'utf8');
assert.ok(css.includes('.hostBound'));
assert.ok(css.includes('.hostFill'));
assert.match(src, /perfProfile\s*=\s*'live'/);
assert.match(src, /resolution:\s*preview\s*\?\s*0\.5\s*:\s*dpr/);
assert.match(src, /maxFPS\s*=\s*preview\s*\?\s*12\s*:\s*32/);
assert.ok(src.includes('decimateFieldStampPoints'));
assert.ok(src.includes('fieldStampPoints'));
assert.match(src, /viewportRef\.current = \{ x: 0, y: 0, w: viewport\.w/);
});