player/tests/rbxl-lua-tween.test.js
min f34320db91
All checks were successful
CI / Lint (pull_request) Successful in 54s
CI / Build (pull_request) Successful in 1m33s
CI / Secret scan (pull_request) Successful in 20s
CI / PR size check (pull_request) Successful in 6s
CI / Deploy to S1 + S2 (pull_request) Has been skipped
feat(rbxl-import): Lua-runtime (wasmoon) для Roblox-скриптов
Часть тест-фичи импорта Roblox-карт (см. rublox/studio rbxl-importer/).

Что добавлено:
- wasmoon (Lua 5.4 WASM) как dep.
- RobloxLuaWorker.js — Worker-хост Lua-VM.
- RobloxLuaSandbox.js — main-side обёртка (по аналогии с ScriptSandbox).
- roblox-shim.js — math (Vector3/Color3/CFrame/UDim2),
  Instance прокси (game/workspace/script/GetService/IsA),
  Part свойства (Position/Color/Material/Anchored/CanCollide),
  RBXScriptSignal (Touched/Heartbeat/Stepped/Connect/Wait).
- roblox-scheduler.js — корутины + wait/task.wait/task.delay/task.spawn,
  автоматический fire Heartbeat/Stepped/RenderStepped на tick.
- roblox-tween.js — TweenService с 10 easing-функциями
  (Linear, Quad, Cubic, Quart, Quint, Sine, Bounce, Elastic, Back, Exponential).
- roblox-services.js — Players/LocalPlayer/Character/Humanoid
  (Health, WalkSpeed, JumpPower, TakeDamage, Died, LoadAnimation),
  UserInputService, RemoteEvent (FireServer/FireClient),
  RemoteFunction, DataStoreService, HttpService.
- roblox-physics.js — BodyVelocity/BodyGyro/BodyPosition/BodyForce/
  BodyAngularVelocity/AlignPosition/LinearVelocity.

Интеграция в GameRuntime:
- В start() проверяется script.kind === 'roblox-lua' →
  _startRobloxLuaScript() запускает RobloxLuaSandbox.
- _handleRobloxLuaCommand() мапит IPC команды (partSet/partVel/playerCmd)
  на PrimitiveManager и game.player API.
- _buildRobloxLuaSceneSnap() готовит snap для workspace:GetChildren.

Тесты: **36/36 passed**.
- mvp (9): math, Instance proxy, Part, IsA.
- wait (5): корутины, wait/task.wait/task.delay.
- tween (2): TweenInfo + Linear easing.
- services (8): Humanoid, DataStore, HttpService, RemoteEvent.
- integration (12): KillBrick, WalkSpeed, Tween-door, BodyVelocity конвейер,
  leaderstats, Checkpoint, циклы с wait, task.spawn, Color/Material,
  RemoteEvent client→server, Heartbeat, Vector3.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-06-07 18:23:32 +03:00

90 lines
3.4 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/**
* 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);
})();