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)); const root = path.resolve(here, '..', '..', '..', '..'); void test('SceneOverlayHost: один dim для materials + npcs в Control и Presentation', () => { const host = fs.readFileSync(path.join(here, 'SceneOverlayHost.tsx'), 'utf8'); const control = fs.readFileSync(path.join(root, 'app/renderer/control/ControlApp.tsx'), 'utf8'); const presentation = fs.readFileSync(path.join(root, 'app/renderer/shared/PresentationView.tsx'), 'utf8'); const css = fs.readFileSync( path.join(root, 'app/renderer/shared/materials/MaterialOverlay.module.css'), 'utf8', ); assert.ok(host.includes('styles.dim')); assert.ok(host.includes('hostHitThrough')); // Dim только при active; жёлтая рамка без затемнения. assert.match(host, /\{active \?
{ const material = fs.readFileSync( path.join(root, 'app/renderer/shared/materials/MaterialOverlay.tsx'), 'utf8', ); const npcs = fs.readFileSync(path.join(root, 'app/renderer/shared/npcs/NpcsSceneOverlay.tsx'), 'utf8'); assert.ok(material.includes('embedded')); assert.ok(material.includes('useSceneOverlayView')); assert.ok(npcs.includes('embedded')); assert.ok(npcs.includes('useSceneOverlayView')); // В embedded-ветке не рисуем второй dim. assert.match(material, /if \(embedded\) \{\s*return frame;/); assert.match(npcs, /if \(embedded\) \{\s*return <>\{frames\}<\/>;/); }); void test('overlays: layout IPC live через rAF coalesce, RO host coalesced', () => { const host = fs.readFileSync(path.join(here, 'SceneOverlayHost.tsx'), 'utf8'); const material = fs.readFileSync( path.join(root, 'app/renderer/shared/materials/MaterialOverlay.tsx'), 'utf8', ); const npcs = fs.readFileSync(path.join(root, 'app/renderer/shared/npcs/NpcsSceneOverlay.tsx'), 'utf8'); assert.ok(host.includes('requestAnimationFrame')); assert.ok(host.includes('prev.w === w && prev.h === h')); // Лайв: publishDraft шлёт onLayoutChange внутри rAF (не на каждый pointermove). assert.ok(material.includes('publishDraft')); assert.ok(material.includes('onLayoutChangeRef')); assert.match(material, /requestAnimationFrame\(\(\) => \{[\s\S]*?onLayoutChangeRef\.current\?\.\(pending\)/); assert.match( material, /if \(drag\.mode === 'move'\) \{[\s\S]*?publishDraft\(\{[\s\S]*?return;\s*\}/, ); assert.ok(npcs.includes('publishDraft')); assert.ok(npcs.includes('onLayoutChangeRef')); assert.match( npcs, /requestAnimationFrame\(\(\) => \{[\s\S]*?onLayoutChangeRef\.current\?\.\(item\.npcId, pending\)/, ); assert.match( npcs, /if \(drag\.mode === 'move'\) \{[\s\S]*?publishDraft\(\{[\s\S]*?return;\s*\}/, ); });