add tests (!)

This commit is contained in:
Orion Reed 2024-12-05 21:55:57 -05:00
parent 8508607b6d
commit 632bd43dc1
4 changed files with 334 additions and 1 deletions

View File

@ -19,6 +19,7 @@
"@types/leaflet": "^1.9.14",
"@types/node": "^22.10.1",
"@webgpu/types": "^0.1.51",
"bun-types": "^1.1.38",
"typescript": "^5.7.2",
"vite": "^6.0.0"
}

View File

@ -0,0 +1,224 @@
import { expect, test, describe } from 'bun:test';
import { RotatedDOMRect } from '../common/rotated-dom-rect-2';
import { Vector } from '../common/Vector';
// Helper for comparing points with floating point values
const expectPointClose = (actual: { x: number; y: number }, expected: { x: number; y: number }) => {
expect(actual.x).toBeCloseTo(expected.x);
expect(actual.y).toBeCloseTo(expected.y);
};
describe('RotatedDOMRect', () => {
describe('constructor', () => {
test('initializes with default values', () => {
const rect = new RotatedDOMRect();
expect(rect.x).toBe(0);
expect(rect.y).toBe(0);
expect(rect.width).toBe(0);
expect(rect.height).toBe(0);
expect(rect.rotation).toBe(0);
});
test('initializes with custom values', () => {
const rect = new RotatedDOMRect({
x: 10,
y: 20,
width: 100,
height: 50,
rotation: Math.PI / 4,
});
expect(rect.x).toBe(10);
expect(rect.y).toBe(20);
expect(rect.width).toBe(100);
expect(rect.height).toBe(50);
expect(rect.rotation).toBe(Math.PI / 4);
});
});
describe('corner calculations', () => {
test('calculates corners for unrotated rectangle', () => {
const rect = new RotatedDOMRect({
x: 0,
y: 0,
width: 100,
height: 50,
rotation: 0,
});
expectPointClose(rect.topLeft, { x: -50, y: -25 });
expectPointClose(rect.topRight, { x: 50, y: -25 });
expectPointClose(rect.bottomLeft, { x: -50, y: 25 });
expectPointClose(rect.bottomRight, { x: 50, y: 25 });
});
test('calculates corners for 90-degree rotated rectangle', () => {
const rect = new RotatedDOMRect({
x: 0,
y: 0,
width: 100,
height: 100,
rotation: Math.PI / 2,
});
expectPointClose(rect.topLeft, { x: 50, y: -50 });
expectPointClose(rect.topRight, { x: 50, y: 50 });
expectPointClose(rect.bottomLeft, { x: -50, y: -50 });
expectPointClose(rect.bottomRight, { x: -50, y: 50 });
});
});
describe('bounds', () => {
test('calculates bounds for unrotated rectangle', () => {
const rect = new RotatedDOMRect({
x: 0,
y: 0,
width: 100,
height: 50,
rotation: 0,
});
expect(rect.getBounds()).toEqual({
x: -50,
y: -25,
width: 100,
height: 50,
});
});
test('calculates bounds for 45-degree rotated rectangle', () => {
const rect = new RotatedDOMRect({
x: 0,
y: 0,
width: 100,
height: 50,
rotation: Math.PI / 4,
});
const bounds = rect.getBounds();
const cos45 = Math.cos(Math.PI / 4);
const sin45 = Math.sin(Math.PI / 4);
const expectedWidth = Math.abs(100 * cos45) + Math.abs(50 * sin45);
const expectedHeight = Math.abs(100 * sin45) + Math.abs(50 * cos45);
expect(bounds.width).toBeCloseTo(expectedWidth);
expect(bounds.height).toBeCloseTo(expectedHeight);
expect(bounds.x).toBeCloseTo(-expectedWidth / 2);
expect(bounds.y).toBeCloseTo(-expectedHeight / 2);
});
});
describe('setters', () => {
test('updates corners when center is modified', () => {
const rect = new RotatedDOMRect({
width: 100,
height: 50,
});
rect.center = { x: 100, y: 100 };
expectPointClose(rect.topLeft, { x: 50, y: 75 });
expectPointClose(rect.bottomRight, { x: 150, y: 125 });
});
test('updates dimensions and rotation when setting topRight', () => {
const rect = new RotatedDOMRect({
x: 0,
y: 0,
width: 100,
height: 50,
});
expect(rect.width).toBe(100);
expect(rect.height).toBe(50);
expect(rect.rotation).toBe(0);
expectPointClose(rect.bottomLeft, { x: -50, y: 25 });
expectPointClose(rect.bottomRight, { x: 50, y: 25 });
expectPointClose(rect.center, { x: 0, y: 0 });
expectPointClose(rect.topLeft, { x: -50, y: -25 });
expectPointClose(rect.topRight, { x: 50, y: -25 });
rect.topRight = { x: 100, y: -50 };
expect(rect.width).toBe(150);
});
});
describe('corner setters', () => {
test('updates dimensions when setting bottomLeft', () => {
const rect = new RotatedDOMRect({
x: 0,
y: 0,
width: 100,
height: 50,
});
// Store original topRight position as it should remain fixed
const originalTopRight = { ...rect.topRight }; // (50, -25)
// Set new bottomLeft position
rect.bottomLeft = { x: -100, y: 100 };
// Verify topRight hasn't moved
expectPointClose(rect.topRight, originalTopRight);
// Verify bottomLeft is at new position
expectPointClose(rect.bottomLeft, { x: -100, y: 100 });
// Verify center is halfway between bottomLeft and topRight
expectPointClose(rect.center, {
x: (-100 + 50) / 2, // -25
y: (100 + -25) / 2, // 37.5
});
// Verify new dimensions
expect(rect.width).toBeCloseTo(150); // abs(-100 - 50) = 150
expect(rect.height).toBeCloseTo(125); // abs(100 - -25) = 125
});
test('maintains rectangle properties when setting corners', () => {
const rect = new RotatedDOMRect({
x: 0,
y: 0,
width: 100,
height: 50,
rotation: Math.PI / 6,
});
rect.bottomRight = { x: 75, y: 75 };
// After setting bottomRight, topLeft and bottomRight should be equidistant from center
const distanceToTopLeft = Vector.distance(rect.center, rect.topLeft);
const distanceToBottomRight = Vector.distance(rect.center, rect.bottomRight);
expect(distanceToTopLeft).toBeCloseTo(distanceToBottomRight);
});
});
describe('edge cases', () => {
test('handles zero dimensions', () => {
const rect = new RotatedDOMRect({
x: 10,
y: 10,
width: 0,
height: 0,
rotation: Math.PI / 4,
});
expect(rect.getBounds()).toEqual({
x: 10,
y: 10,
width: 0,
height: 0,
});
});
test('handles 360-degree rotation', () => {
const rect = new RotatedDOMRect({
width: 100,
height: 50,
rotation: Math.PI * 2,
});
// Should be equivalent to rotation: 0
expectPointClose(rect.topLeft, { x: -50, y: -25 });
expectPointClose(rect.topRight, { x: 50, y: -25 });
});
});
});

View File

@ -0,0 +1,108 @@
import { expect, test, describe } from 'bun:test';
import { Vector } from '../common/Vector';
describe('Vector', () => {
describe('basic operations', () => {
test('zero() returns zero vector', () => {
expect(Vector.zero()).toEqual({ x: 0, y: 0 });
});
test('add() combines two vectors', () => {
const a = { x: 1, y: 2 };
const b = { x: 3, y: 4 };
expect(Vector.add(a, b)).toEqual({ x: 4, y: 6 });
});
test('sub() subtracts vectors', () => {
const a = { x: 3, y: 4 };
const b = { x: 1, y: 2 };
expect(Vector.sub(a, b)).toEqual({ x: 2, y: 2 });
});
test('mult() multiplies vectors component-wise', () => {
const a = { x: 2, y: 3 };
const b = { x: 4, y: 5 };
expect(Vector.mult(a, b)).toEqual({ x: 8, y: 15 });
});
test('scale() multiplies vector by scalar', () => {
const v = { x: 2, y: 3 };
expect(Vector.scale(v, 2)).toEqual({ x: 4, y: 6 });
});
});
describe('vector properties', () => {
test('mag() calculates magnitude', () => {
const v = { x: 3, y: 4 };
expect(Vector.mag(v)).toBe(5);
});
test('normalized() returns unit vector', () => {
const v = { x: 3, y: 4 };
const normalized = Vector.normalized(v);
expect(normalized.x).toBeCloseTo(0.6);
expect(normalized.y).toBeCloseTo(0.8);
});
test('normalized() handles zero vector', () => {
const v = { x: 0, y: 0 };
expect(Vector.normalized(v)).toEqual({ x: 0, y: 0 });
});
});
describe('distance calculations', () => {
test('distance() calculates Euclidean distance', () => {
const a = { x: 0, y: 0 };
const b = { x: 3, y: 4 };
expect(Vector.distance(a, b)).toBe(5);
});
test('distanceSquared() calculates squared distance', () => {
const a = { x: 0, y: 0 };
const b = { x: 3, y: 4 };
expect(Vector.distanceSquared(a, b)).toBe(25);
});
});
describe('interpolation and rotation', () => {
test('lerp() interpolates between points', () => {
const a = { x: 0, y: 0 };
const b = { x: 10, y: 10 };
expect(Vector.lerp(a, b, 0.5)).toEqual({ x: 5, y: 5 });
});
test('rotate() rotates vector by angle', () => {
const v = { x: 1, y: 0 };
const rotated = Vector.rotate(v, Math.PI / 2);
expect(rotated.x).toBeCloseTo(0);
expect(rotated.y).toBeCloseTo(1);
});
test('rotateAround() rotates point around pivot', () => {
const point = { x: 2, y: 0 };
const pivot = { x: 0, y: 0 };
const rotated = Vector.rotateAround(point, pivot, Math.PI / 2);
expect(rotated.x).toBeCloseTo(0);
expect(rotated.y).toBeCloseTo(2);
});
});
describe('angle calculations', () => {
test('angle() calculates angle from x-axis', () => {
const v = { x: 1, y: 1 };
expect(Vector.angle(v)).toBeCloseTo(Math.PI / 4);
});
test('angleTo() calculates angle between vectors', () => {
const a = { x: 1, y: 0 };
const b = { x: 0, y: 1 };
expect(Vector.angleTo(b, a)).toBeCloseTo(Math.PI / 2);
});
test('angleFromOrigin() calculates angle relative to origin', () => {
const point = { x: 1, y: 1 };
const origin = { x: 0, y: 0 };
expect(Vector.angleFromOrigin(point, origin)).toBeCloseTo(Math.PI / 4);
});
});
});

View File

@ -11,7 +11,7 @@
"useDefineForClassFields": true,
"skipLibCheck": true,
"lib": ["DOM", "DOM.Iterable", "ESNext", "WebWorker"],
"types": ["@webgpu/types", "@types/node"]
"types": ["@webgpu/types", "@types/node", "bun-types"]
},
"include": ["src/**/*.ts", "demo/**/*.ts", "vite.config.ts"]
}