/** * rbxl-lua-tween.test.js — тесты TweenService. */ import { LuaFactory } from 'wasmoon'; import { registerRobloxApi } from '../src/engine/roblox-shim.js'; import { RobloxScheduler } from '../src/engine/roblox-scheduler.js'; import { RobloxTweenManager } from '../src/engine/roblox-tween.js'; const SCENE = { primitives: { 1: { id: 1, type: 'cube', name: 'Movable', x: 0, y: 5, z: 0, sx: 1, sy: 1, sz: 1, color: '#ffffff', material: 'glossy', anchored: false, canCollide: true, opacity: 1 }, }, }; async function run(luaSource, ticks = []) { const factory = new LuaFactory(); const lua = await factory.createEngine(); const sent = []; const send = (cmd, payload) => sent.push({ cmd, payload }); registerRobloxApi(lua, { getSceneSnap: () => SCENE, targetPrimitiveId: 1, send }); const sched = new RobloxScheduler(lua); sched.install(); const tweenMgr = new RobloxTweenManager(); tweenMgr.install(lua); await sched.spawnMain(luaSource); for (const dt of ticks) { await sched.tick(dt); tweenMgr.tick(dt); } lua.global.close(); return { logs: sent.filter(s => s.cmd === 'log').map(s => s.payload), partSets: sent.filter(s => s.cmd === 'partSet').map(s => s.payload) }; } const TESTS = [ { name: 'TweenInfo создаётся', lua: ` local info = TweenInfo.new(2, Enum.EasingStyle.Linear, Enum.EasingDirection.Out) print("time:", info.Time, "style:", info.EasingStyle) `, ticks: [], expectLogs: [{ level: 'info', text: 'time:\t2\tstyle:\tLinear' }], }, { name: 'TweenService:Create + Play (Linear)', lua: ` local TS = game:GetService("TweenService") local p = workspace:FindFirstChild("Movable") local info = TweenInfo.new(1, Enum.EasingStyle.Linear, Enum.EasingDirection.Out) local tw = TS:Create(p, info, { Position = Vector3.new(10, 5, 0) }) tw:Play() print("started") `, ticks: [0.5, 0.5, 0.1], // больше 1 сек — должен завершиться // Ожидаем что хотя бы один partSet с prop=position expectPartSet: { primId: 1, prop: 'position' }, }, ]; (async () => { let passed = 0, failed = 0; for (const t of TESTS) { try { const r = await run(t.lua, t.ticks); let ok = true; let reason = ''; for (const exp of (t.expectLogs || [])) { const found = r.logs.find(l => l.level === exp.level && l.text === exp.text); if (!found) { ok = false; reason = `missing log: ${exp.text}`; break; } } if (t.expectPartSet) { const found = r.partSets.find(p => p.primId === t.expectPartSet.primId && p.prop === t.expectPartSet.prop); if (!found) { ok = false; reason = `missing partSet: ${JSON.stringify(t.expectPartSet)}; got: ${JSON.stringify(r.partSets.slice(0,3))}`; } } if (ok) { console.log(`✓ ${t.name}`); passed++; } else { console.log(`✗ ${t.name} — ${reason}`); failed++; } } catch (e) { console.log(`✗ ${t.name} — exception: ${e}`); failed++; } } console.log(`\n${passed} passed, ${failed} failed`); process.exit(failed > 0 ? 1 : 0); })();