refactor: reorganize package structure and decouple framework packages (#338)

* refactor: reorganize package structure and decouple framework packages

## Package Structure Reorganization
- Reorganized 55 packages into categorized subdirectories:
  - packages/framework/ - Generic framework (Laya/Cocos compatible)
  - packages/engine/ - ESEngine core modules
  - packages/rendering/ - Rendering modules (WASM dependent)
  - packages/physics/ - Physics modules
  - packages/streaming/ - World streaming
  - packages/network-ext/ - Network extensions
  - packages/editor/ - Editor framework and plugins
  - packages/rust/ - Rust WASM engine
  - packages/tools/ - Build tools and SDK

## Framework Package Decoupling
- Decoupled behavior-tree and blueprint packages from ESEngine dependencies
- Created abstracted interfaces (IBTAssetManager, IBehaviorTreeAssetContent)
- ESEngine-specific code moved to esengine/ subpath exports
- Framework packages now usable with Cocos/Laya without ESEngine

## CI Configuration
- Updated CI to only type-check and lint framework packages
- Added type-check:framework and lint:framework scripts

## Breaking Changes
- Package import paths changed due to directory reorganization
- ESEngine integrations now use subpath imports (e.g., '@esengine/behavior-tree/esengine')

* fix: update es-engine file path after directory reorganization

* docs: update README to focus on framework over engine

* ci: only build framework packages, remove Rust/WASM dependencies

* fix: remove esengine subpath from behavior-tree and blueprint builds

ESEngine integration code will only be available in full engine builds.
Framework packages are now purely engine-agnostic.

* fix: move network-protocols to framework, build both in CI

* fix: update workflow paths from packages/core to packages/framework/core

* fix: exclude esengine folder from type-check in behavior-tree and blueprint

* fix: update network tsconfig references to new paths

* fix: add test:ci:framework to only test framework packages in CI

* fix: only build core and math npm packages in CI

* fix: exclude test files from CodeQL and fix string escaping security issue
This commit is contained in:
YHH
2025-12-26 14:50:35 +08:00
committed by GitHub
parent a84ff902e4
commit 155411e743
1936 changed files with 4147 additions and 11578 deletions

View File

@@ -0,0 +1,212 @@
import { CollisionDetector } from '../src/Collision/CollisionDetector';
import { Vector2 } from '../src/Vector2';
import { Rectangle } from '../src/Rectangle';
import { Circle } from '../src/Circle';
declare global {
var expectFloatsEqual: (actual: number, expected: number, epsilon?: number) => void;
}
describe('CollisionDetector', () => {
describe('点与几何体碰撞', () => {
test('点与圆形碰撞检测', () => {
const circle = new Circle(0, 0, 5);
const pointInside = new Vector2(3, 0);
const pointOutside = new Vector2(10, 0);
const pointOnBoundary = new Vector2(5, 0);
const collision1 = CollisionDetector.pointCircle(pointInside, circle);
expect(collision1.collided).toBe(true);
expect(collision1.penetration).toBe(2);
const collision2 = CollisionDetector.pointCircle(pointOutside, circle);
expect(collision2.collided).toBe(false);
const collision3 = CollisionDetector.pointCircle(pointOnBoundary, circle);
expect(collision3.collided).toBe(true);
expectFloatsEqual(collision3.penetration!, 0, 1e-10);
});
test('点与矩形碰撞检测', () => {
const rect = new Rectangle(10, 10, 20, 20);
const pointInside = new Vector2(15, 15);
const pointOutside = new Vector2(5, 5);
const collision1 = CollisionDetector.pointRect(pointInside, rect);
expect(collision1.collided).toBe(true);
expect(collision1.normal).toBeDefined();
const collision2 = CollisionDetector.pointRect(pointOutside, rect);
expect(collision2.collided).toBe(false);
});
});
describe('圆形碰撞检测', () => {
test('圆形与圆形碰撞检测', () => {
const circle1 = new Circle(0, 0, 5);
const circle2 = new Circle(8, 0, 5); // 相交
const circle3 = new Circle(15, 0, 5); // 不相交
const circle4 = new Circle(10, 0, 5); // 边界接触
const collision1 = CollisionDetector.circleCircle(circle1, circle2);
expect(collision1.collided).toBe(true);
expect(collision1.penetration).toBe(2);
const collision2 = CollisionDetector.circleCircle(circle1, circle3);
expect(collision2.collided).toBe(false);
const collision3 = CollisionDetector.circleCircle(circle1, circle4);
expect(collision3.collided).toBe(true);
expectFloatsEqual(collision3.penetration!, 0, 1e-10);
});
test('圆形与矩形碰撞检测', () => {
const circle = new Circle(15, 15, 5);
const rect1 = new Rectangle(10, 10, 20, 20); // 相交
const rect2 = new Rectangle(30, 30, 10, 10); // 不相交
const collision1 = CollisionDetector.circleRect(circle, rect1);
expect(collision1.collided).toBe(true);
const collision2 = CollisionDetector.circleRect(circle, rect2);
expect(collision2.collided).toBe(false);
});
});
describe('矩形碰撞检测', () => {
test('矩形与矩形碰撞检测', () => {
const rect1 = new Rectangle(10, 10, 20, 20);
const rect2 = new Rectangle(15, 15, 20, 20); // 相交
const rect3 = new Rectangle(40, 40, 10, 10); // 不相交
const collision1 = CollisionDetector.rectRect(rect1, rect2);
expect(collision1.collided).toBe(true);
expect(collision1.penetration).toBeGreaterThan(0);
expect(collision1.normal).toBeDefined();
const collision2 = CollisionDetector.rectRect(rect1, rect3);
expect(collision2.collided).toBe(false);
});
});
describe('射线投射', () => {
test('射线与圆形相交', () => {
const rayOrigin = new Vector2(-10, 0);
const rayDirection = new Vector2(1, 0);
const circle = new Circle(0, 0, 5);
const collision = CollisionDetector.rayCircle(rayOrigin, rayDirection, circle);
expect(collision.collided).toBe(true);
expect(collision.distance).toBe(5);
expect(collision.contactPoint!.x).toBe(-5);
expect(collision.contactPoint!.y).toBe(0);
});
test('射线与圆形不相交', () => {
const rayOrigin = new Vector2(-10, 10);
const rayDirection = new Vector2(1, 0);
const circle = new Circle(0, 0, 5);
const collision = CollisionDetector.rayCircle(rayOrigin, rayDirection, circle);
expect(collision.collided).toBe(false);
});
test('射线与矩形相交', () => {
const rayOrigin = new Vector2(-5, 15);
const rayDirection = new Vector2(1, 0);
const rect = new Rectangle(10, 10, 20, 20);
const collision = CollisionDetector.rayRect(rayOrigin, rayDirection, rect);
expect(collision.collided).toBe(true);
expect(collision.distance).toBe(15);
});
test('射线距离限制', () => {
const rayOrigin = new Vector2(-10, 0);
const rayDirection = new Vector2(1, 0);
const circle = new Circle(0, 0, 5);
const collision = CollisionDetector.rayCircle(rayOrigin, rayDirection, circle, 3);
expect(collision.collided).toBe(false);
});
});
describe('线段相交', () => {
test('线段与线段相交', () => {
const p1 = new Vector2(0, 0);
const p2 = new Vector2(10, 10);
const p3 = new Vector2(0, 10);
const p4 = new Vector2(10, 0);
const collision = CollisionDetector.lineSegmentLineSegment(p1, p2, p3, p4);
expect(collision.collided).toBe(true);
expect(collision.contactPoint!.x).toBe(5);
expect(collision.contactPoint!.y).toBe(5);
});
test('线段与线段不相交', () => {
const p1 = new Vector2(0, 0);
const p2 = new Vector2(5, 5);
const p3 = new Vector2(10, 0);
const p4 = new Vector2(15, 5);
const collision = CollisionDetector.lineSegmentLineSegment(p1, p2, p3, p4);
expect(collision.collided).toBe(false);
});
test('线段与圆形相交', () => {
const lineStart = new Vector2(-10, 0);
const lineEnd = new Vector2(10, 0);
const circle = new Circle(0, 0, 5);
const collision = CollisionDetector.lineSegmentCircle(lineStart, lineEnd, circle);
expect(collision.collided).toBe(true);
});
});
describe('快速排斥测试', () => {
test('AABB包围盒测试', () => {
const bounds1 = new Rectangle(10, 10, 20, 20);
const bounds2 = new Rectangle(15, 15, 20, 20);
const bounds3 = new Rectangle(40, 40, 10, 10);
expect(CollisionDetector.aabbTest(bounds1, bounds2)).toBe(true);
expect(CollisionDetector.aabbTest(bounds1, bounds3)).toBe(false);
});
test('圆形包围盒测试', () => {
const center1 = new Vector2(0, 0);
const center2 = new Vector2(8, 0);
const center3 = new Vector2(15, 0);
expect(CollisionDetector.circleTest(center1, 5, center2, 5)).toBe(true);
expect(CollisionDetector.circleTest(center1, 5, center3, 5)).toBe(false);
});
});
describe('边界情况', () => {
test('零半径圆形', () => {
const point = new Vector2(0, 0);
const circle = new Circle(0, 0, 0);
const collision = CollisionDetector.pointCircle(point, circle);
expect(collision.collided).toBe(true);
});
test('零面积矩形', () => {
const point = new Vector2(10, 10);
const rect = new Rectangle(10, 10, 0, 0);
const collision = CollisionDetector.pointRect(point, rect);
expect(collision.collided).toBe(true);
});
test('同心圆形', () => {
const circle1 = new Circle(0, 0, 5);
const circle2 = new Circle(0, 0, 3);
const collision = CollisionDetector.circleCircle(circle1, circle2);
expect(collision.collided).toBe(true);
});
});
});

View File

@@ -0,0 +1,292 @@
import { Rectangle } from '../src/Rectangle';
import { Vector2 } from '../src/Vector2';
declare global {
var expectFloatsEqual: (actual: number, expected: number, epsilon?: number) => void;
}
describe('Rectangle', () => {
describe('构造函数和基础属性', () => {
test('默认构造函数应创建空矩形', () => {
const rect = new Rectangle();
expect(rect.x).toBe(0);
expect(rect.y).toBe(0);
expect(rect.width).toBe(0);
expect(rect.height).toBe(0);
});
test('应正确设置矩形参数', () => {
const rect = new Rectangle(10, 20, 100, 50);
expect(rect.x).toBe(10);
expect(rect.y).toBe(20);
expect(rect.width).toBe(100);
expect(rect.height).toBe(50);
});
test('边界属性应正确计算', () => {
const rect = new Rectangle(10, 20, 100, 50);
expect(rect.left).toBe(10);
expect(rect.right).toBe(110);
expect(rect.top).toBe(20);
expect(rect.bottom).toBe(70);
});
test('中心属性应正确计算', () => {
const rect = new Rectangle(10, 20, 100, 50);
expect(rect.centerX).toBe(60);
expect(rect.centerY).toBe(45);
const center = rect.center;
expect(center.x).toBe(60);
expect(center.y).toBe(45);
});
test('角点属性应正确计算', () => {
const rect = new Rectangle(10, 20, 100, 50);
expect(rect.topLeft.x).toBe(10);
expect(rect.topLeft.y).toBe(20);
expect(rect.topRight.x).toBe(110);
expect(rect.topRight.y).toBe(20);
expect(rect.bottomLeft.x).toBe(10);
expect(rect.bottomLeft.y).toBe(70);
expect(rect.bottomRight.x).toBe(110);
expect(rect.bottomRight.y).toBe(70);
});
test('面积和周长应正确计算', () => {
const rect = new Rectangle(0, 0, 10, 5);
expect(rect.area).toBe(50);
expect(rect.perimeter).toBe(30);
});
});
describe('基础操作', () => {
test('set方法应正确设置值', () => {
const rect = new Rectangle();
rect.set(1, 2, 3, 4);
expect(rect.x).toBe(1);
expect(rect.y).toBe(2);
expect(rect.width).toBe(3);
expect(rect.height).toBe(4);
});
test('copy方法应正确复制', () => {
const rect1 = new Rectangle(1, 2, 3, 4);
const rect2 = new Rectangle();
rect2.copy(rect1);
expect(rect2.x).toBe(1);
expect(rect2.y).toBe(2);
expect(rect2.width).toBe(3);
expect(rect2.height).toBe(4);
});
test('clone方法应创建相同的新实例', () => {
const rect1 = new Rectangle(1, 2, 3, 4);
const rect2 = rect1.clone();
expect(rect2.x).toBe(1);
expect(rect2.y).toBe(2);
expect(rect2.width).toBe(3);
expect(rect2.height).toBe(4);
expect(rect2).not.toBe(rect1);
});
test('setCenter方法应正确设置中心点', () => {
const rect = new Rectangle(0, 0, 10, 10);
rect.setCenter(50, 60);
expect(rect.x).toBe(45);
expect(rect.y).toBe(55);
expect(rect.centerX).toBe(50);
expect(rect.centerY).toBe(60);
});
});
describe('变换操作', () => {
test('translate方法应正确平移', () => {
const rect = new Rectangle(10, 20, 30, 40);
rect.translate(5, 10);
expect(rect.x).toBe(15);
expect(rect.y).toBe(30);
expect(rect.width).toBe(30);
expect(rect.height).toBe(40);
});
test('scale方法应正确缩放', () => {
const rect = new Rectangle(10, 10, 20, 30);
const originalCenterX = rect.centerX;
const originalCenterY = rect.centerY;
rect.scale(2, 3);
expect(rect.width).toBe(40);
expect(rect.height).toBe(90);
expect(rect.centerX).toBe(originalCenterX);
expect(rect.centerY).toBe(originalCenterY);
});
test('inflate方法应正确扩展', () => {
const rect = new Rectangle(10, 10, 20, 30);
rect.inflate(5);
expect(rect.x).toBe(5);
expect(rect.y).toBe(5);
expect(rect.width).toBe(30);
expect(rect.height).toBe(40);
});
});
describe('包含检测', () => {
const rect = new Rectangle(10, 10, 20, 30);
test('containsPoint应正确检测点包含', () => {
const point1 = new Vector2(15, 15);
const point2 = new Vector2(5, 5);
const point3 = new Vector2(10, 10); // 边界点
expect(rect.containsPoint(point1)).toBe(true);
expect(rect.containsPoint(point2)).toBe(false);
expect(rect.containsPoint(point3)).toBe(true);
});
test('contains方法应正确检测坐标包含', () => {
expect(rect.contains(15, 15)).toBe(true);
expect(rect.contains(5, 5)).toBe(false);
});
test('containsRect应正确检测矩形包含', () => {
const inside = new Rectangle(12, 12, 5, 5);
const outside = new Rectangle(5, 5, 5, 5);
const overlapping = new Rectangle(8, 8, 5, 5);
expect(rect.containsRect(inside)).toBe(true);
expect(rect.containsRect(outside)).toBe(false);
expect(rect.containsRect(overlapping)).toBe(false);
});
});
describe('相交检测', () => {
const rect1 = new Rectangle(10, 10, 20, 20);
test('intersects应正确检测相交', () => {
const rect2 = new Rectangle(15, 15, 20, 20); // 相交
const rect3 = new Rectangle(40, 40, 10, 10); // 不相交
const rect4 = new Rectangle(30, 10, 10, 20); // 边界接触
expect(rect1.intersects(rect2)).toBe(true);
expect(rect1.intersects(rect3)).toBe(false);
expect(rect1.intersects(rect4)).toBe(false);
});
test('intersection应正确计算相交矩形', () => {
const rect2 = new Rectangle(15, 15, 20, 20);
const intersection = rect1.intersection(rect2);
expect(intersection.x).toBe(15);
expect(intersection.y).toBe(15);
expect(intersection.width).toBe(15);
expect(intersection.height).toBe(15);
});
test('union应正确计算并集矩形', () => {
const rect2 = new Rectangle(20, 20, 20, 20);
const union = rect1.union(rect2);
expect(union.x).toBe(10);
expect(union.y).toBe(10);
expect(union.right).toBe(40);
expect(union.bottom).toBe(40);
});
});
describe('距离计算', () => {
const rect = new Rectangle(10, 10, 20, 20);
test('distanceToPoint应正确计算点到矩形的距离', () => {
const insidePoint = new Vector2(15, 15);
const outsidePoint = new Vector2(40, 40);
expect(rect.distanceToPoint(insidePoint)).toBe(0);
expectFloatsEqual(rect.distanceToPoint(outsidePoint), Math.sqrt(200));
});
test('closestPointTo应返回最近点', () => {
const point = new Vector2(5, 25);
const closest = rect.closestPointTo(point);
expect(closest.x).toBe(10);
expect(closest.y).toBe(25);
});
});
describe('静态方法', () => {
test('fromCenter应从中心点创建矩形', () => {
const rect = Rectangle.fromCenter(50, 60, 20, 30);
expect(rect.centerX).toBe(50);
expect(rect.centerY).toBe(60);
expect(rect.width).toBe(20);
expect(rect.height).toBe(30);
});
test('fromPoints应从两点创建矩形', () => {
const p1 = new Vector2(10, 20);
const p2 = new Vector2(30, 15);
const rect = Rectangle.fromPoints(p1, p2);
expect(rect.x).toBe(10);
expect(rect.y).toBe(15);
expect(rect.width).toBe(20);
expect(rect.height).toBe(5);
});
test('fromPointArray应从点数组创建包围矩形', () => {
const points = [
new Vector2(5, 10),
new Vector2(15, 5),
new Vector2(20, 25),
new Vector2(8, 30)
];
const rect = Rectangle.fromPointArray(points);
expect(rect.x).toBe(5);
expect(rect.y).toBe(5);
expect(rect.right).toBe(20);
expect(rect.bottom).toBe(30);
});
});
describe('比较操作', () => {
test('equals应正确比较矩形', () => {
const rect1 = new Rectangle(1, 2, 3, 4);
const rect2 = new Rectangle(1, 2, 3, 4);
const rect3 = new Rectangle(1.001, 2, 3, 4);
expect(rect1.equals(rect2)).toBe(true);
expect(rect1.equals(rect3, 0.01)).toBe(true);
expect(rect1.equals(rect3, 0.0001)).toBe(false);
});
});
describe('字符串转换', () => {
test('toString应返回正确格式', () => {
const rect = new Rectangle(1.234, 2.567, 3.891, 4.012);
const str = rect.toString();
expect(str).toContain('Rectangle');
});
test('toArray应返回数组', () => {
const rect = new Rectangle(1, 2, 3, 4);
const arr = rect.toArray();
expect(arr).toEqual([1, 2, 3, 4]);
});
test('getVertices应返回四个顶点', () => {
const rect = new Rectangle(10, 20, 30, 40);
const vertices = rect.getVertices();
expect(vertices).toHaveLength(4);
expect(vertices[0].x).toBe(10); // topLeft
expect(vertices[0].y).toBe(20);
expect(vertices[1].x).toBe(40); // topRight
expect(vertices[1].y).toBe(20);
});
});
});

View File

@@ -0,0 +1,263 @@
import { Vector2 } from '../src/Vector2';
declare global {
var expectFloatsEqual: (actual: number, expected: number, epsilon?: number) => void;
}
describe('Vector2', () => {
describe('构造函数和基础属性', () => {
test('默认构造函数应创建零向量', () => {
const v = new Vector2();
expect(v.x).toBe(0);
expect(v.y).toBe(0);
});
test('应正确设置x和y值', () => {
const v = new Vector2(3, 4);
expect(v.x).toBe(3);
expect(v.y).toBe(4);
});
test('length属性应正确计算', () => {
const v = new Vector2(3, 4);
expect(v.length).toBe(5);
});
test('lengthSquared属性应正确计算', () => {
const v = new Vector2(3, 4);
expect(v.lengthSquared).toBe(25);
});
test('angle属性应正确计算', () => {
const v = new Vector2(1, 0);
expect(v.angle).toBe(0);
const v2 = new Vector2(0, 1);
expectFloatsEqual(v2.angle, Math.PI / 2);
});
});
describe('基础运算', () => {
test('set方法应正确设置值', () => {
const v = new Vector2();
v.set(5, 6);
expect(v.x).toBe(5);
expect(v.y).toBe(6);
});
test('copy方法应正确复制值', () => {
const v1 = new Vector2(1, 2);
const v2 = new Vector2(3, 4);
v2.copy(v1);
expect(v2.x).toBe(1);
expect(v2.y).toBe(2);
});
test('clone方法应创建相同的新实例', () => {
const v1 = new Vector2(1, 2);
const v2 = v1.clone();
expect(v2.x).toBe(1);
expect(v2.y).toBe(2);
expect(v2).not.toBe(v1);
});
test('add方法应正确相加', () => {
const v1 = new Vector2(1, 2);
const v2 = new Vector2(3, 4);
v1.add(v2);
expect(v1.x).toBe(4);
expect(v1.y).toBe(6);
});
test('subtract方法应正确相减', () => {
const v1 = new Vector2(5, 7);
const v2 = new Vector2(2, 3);
v1.subtract(v2);
expect(v1.x).toBe(3);
expect(v1.y).toBe(4);
});
test('multiply方法应正确数乘', () => {
const v = new Vector2(2, 3);
v.multiply(4);
expect(v.x).toBe(8);
expect(v.y).toBe(12);
});
test('divide方法应正确数除', () => {
const v = new Vector2(8, 12);
v.divide(4);
expect(v.x).toBe(2);
expect(v.y).toBe(3);
});
test('divide方法应在除以零时抛出错误', () => {
const v = new Vector2(1, 2);
expect(() => v.divide(0)).toThrow('不能除以零');
});
});
describe('向量运算', () => {
test('dot方法应正确计算点积', () => {
const v1 = new Vector2(1, 2);
const v2 = new Vector2(3, 4);
expect(v1.dot(v2)).toBe(11); // 1*3 + 2*4 = 11
});
test('cross方法应正确计算叉积', () => {
const v1 = new Vector2(1, 0);
const v2 = new Vector2(0, 1);
expect(v1.cross(v2)).toBe(1);
});
test('normalize方法应正确归一化向量', () => {
const v = new Vector2(3, 4);
v.normalize();
expectFloatsEqual(v.length, 1);
expectFloatsEqual(v.x, 0.6);
expectFloatsEqual(v.y, 0.8);
});
test('零向量归一化应保持不变', () => {
const v = new Vector2(0, 0);
v.normalize();
expect(v.x).toBe(0);
expect(v.y).toBe(0);
});
});
describe('几何运算', () => {
test('distanceTo方法应正确计算距离', () => {
const v1 = new Vector2(0, 0);
const v2 = new Vector2(3, 4);
expect(v1.distanceTo(v2)).toBe(5);
});
test('angleTo方法应正确计算夹角', () => {
const v1 = new Vector2(1, 0);
const v2 = new Vector2(0, 1);
expectFloatsEqual(v1.angleTo(v2), Math.PI / 2);
});
test('projectOnto方法应正确投影', () => {
const v1 = new Vector2(2, 2);
const v2 = new Vector2(1, 0);
const projected = v1.projectOnto(v2);
expect(projected.x).toBe(2);
expect(projected.y).toBe(0);
});
});
describe('变换操作', () => {
test('rotate方法应正确旋转向量顺时针', () => {
// Clockwise rotation: (1, 0) rotated 90° clockwise = (0, -1)
// 顺时针旋转:(1, 0) 顺时针旋转 90° = (0, -1)
const v = new Vector2(1, 0);
v.rotate(Math.PI / 2);
expectFloatsEqual(v.x, 0, 1e-10);
expectFloatsEqual(v.y, -1, 1e-10);
});
test('reflect方法应正确反射向量', () => {
const v = new Vector2(1, 1);
const normal = new Vector2(0, 1);
v.reflect(normal);
expectFloatsEqual(v.x, 1);
expectFloatsEqual(v.y, -1);
});
});
describe('插值和限制', () => {
test('lerp方法应正确插值', () => {
const v1 = new Vector2(0, 0);
const v2 = new Vector2(10, 10);
v1.lerp(v2, 0.5);
expect(v1.x).toBe(5);
expect(v1.y).toBe(5);
});
test('clampLength方法应正确限制长度', () => {
const v = new Vector2(6, 8); // 长度为10
v.clampLength(5);
expectFloatsEqual(v.length, 5);
});
});
describe('比较操作', () => {
test('equals方法应正确比较向量', () => {
const v1 = new Vector2(1, 2);
const v2 = new Vector2(1, 2);
const v3 = new Vector2(1.0001, 2);
expect(v1.equals(v2)).toBe(true);
expect(v1.equals(v3, 0.001)).toBe(true);
expect(v1.equals(v3, 0.00001)).toBe(false);
});
test('exactEquals方法应检查完全相等', () => {
const v1 = new Vector2(1, 2);
const v2 = new Vector2(1, 2);
const v3 = new Vector2(1.0001, 2);
expect(v1.exactEquals(v2)).toBe(true);
expect(v1.exactEquals(v3)).toBe(false);
});
});
describe('静态方法', () => {
test('Vector2.add应创建新的相加结果', () => {
const v1 = new Vector2(1, 2);
const v2 = new Vector2(3, 4);
const result = Vector2.add(v1, v2);
expect(result.x).toBe(4);
expect(result.y).toBe(6);
expect(v1.x).toBe(1); // 原向量不变
expect(v1.y).toBe(2);
});
test('Vector2.fromAngle应从角度创建单位向量', () => {
const v = Vector2.fromAngle(Math.PI / 2);
expectFloatsEqual(v.x, 0, 1e-10);
expectFloatsEqual(v.y, 1, 1e-10);
});
test('Vector2.fromPolar应从极坐标创建向量', () => {
const v = Vector2.fromPolar(5, 0);
expect(v.x).toBe(5);
expectFloatsEqual(v.y, 0, 1e-10);
});
});
describe('静态常量', () => {
test('静态常量应具有正确的值', () => {
expect(Vector2.ZERO.x).toBe(0);
expect(Vector2.ZERO.y).toBe(0);
expect(Vector2.ONE.x).toBe(1);
expect(Vector2.ONE.y).toBe(1);
expect(Vector2.RIGHT.x).toBe(1);
expect(Vector2.RIGHT.y).toBe(0);
});
});
describe('字符串转换', () => {
test('toString应返回正确格式', () => {
const v = new Vector2(1.2345, 2.6789);
const str = v.toString();
expect(str).toContain('1.234');
expect(str).toContain('2.679');
});
test('toArray应返回数组', () => {
const v = new Vector2(1, 2);
const arr = v.toArray();
expect(arr).toEqual([1, 2]);
});
test('toObject应返回对象', () => {
const v = new Vector2(1, 2);
const obj = v.toObject();
expect(obj).toEqual({ x: 1, y: 2 });
});
});
});

View File

@@ -0,0 +1,23 @@
/**
* Jest测试设置文件
* 用于配置全局测试环境
*/
// 设置测试超时
jest.setTimeout(10000);
// 全局数学常量和工具函数
(global as any).EPSILON = 1e-10;
// 浮点数比较助手函数
(global as any).expectFloatsEqual = (actual: number, expected: number, epsilon = (global as any).EPSILON) => {
expect(Math.abs(actual - expected)).toBeLessThan(epsilon);
};
// 声明全局类型扩展
declare global {
var EPSILON: number;
var expectFloatsEqual: (actual: number, expected: number, epsilon?: number) => void;
}
export {};