rampensau
Version:
Color ramp generator using curves within the HSL color model
131 lines (114 loc) • 4.83 kB
text/typescript
import { describe, it, expect } from 'vitest';
import { shuffleArray, scaleSpreadArray, makeCurveEasings, pointOnCurve } from '../src/utils';
// Define lerp function locally for testing
const lerp = (amt: number, from: number, to: number): number => from + amt * (to - from);
describe('shuffleArray', () => {
it('should return an array with the same length', () => {
const original = [1, 2, 3, 4, 5];
const shuffled = shuffleArray(original);
expect(shuffled).toHaveLength(original.length);
});
it('should contain the same elements', () => {
const original = [1, 2, 3, 4, 5];
const shuffled = shuffleArray(original);
expect(shuffled).toEqual(expect.arrayContaining(original));
expect(original).toEqual(expect.arrayContaining(shuffled));
});
it('should shuffle the array (usually)', () => {
const original = Array.from({ length: 100 }, (_, i) => i);
const shuffled = shuffleArray(original);
// It's statistically improbable but possible for the array to remain unchanged
expect(shuffled).not.toEqual(original);
});
it('should use the provided random function', () => {
const original = [1, 2, 3];
// A deterministic "random" function that cycles through 0.5, 0.2, 0.8
const mockRndFn = (() => {
const sequence = [0.5, 0.2, 0.8];
let index = 0;
return () => sequence[index++ % sequence.length];
})();
const shuffled = shuffleArray(original, mockRndFn);
// With this mock, the shuffle should produce a predictable result
expect(shuffled).toEqual([3, 1, 2]);
});
});
describe('lerp', () => {
it('should interpolate correctly', () => {
expect(lerp(0, 0, 10)).toBe(0);
expect(lerp(1, 0, 10)).toBe(10);
expect(lerp(0.5, 0, 10)).toBe(5);
expect(lerp(0.25, 10, 20)).toBe(12.5);
expect(lerp(0.75, -10, 10)).toBe(5);
});
});
describe('scaleSpreadArray', () => {
it('should scale and spread the array correctly with lerp', () => {
const initial = [0, 10];
const targetSize = 5;
const expected = [0, 2.5, 5, 7.5, 10];
const result = scaleSpreadArray(initial, targetSize, lerp);
expect(result).toHaveLength(targetSize);
result.forEach((val, i) => expect(val).toBeCloseTo(expected[i] as number));
});
it('should handle arrays with more than two elements', () => {
const initial = [0, 10, 5];
const targetSize = 7;
// Expected: [0, 5, 10, 7.5, 5, ?, ?] - Calculation gets complex, focus on length and bounds
const result = scaleSpreadArray(initial, targetSize, lerp);
expect(result).toHaveLength(targetSize);
expect(result[0]).toBe(0);
expect(result[2]).toBe(6.666666666666666); // Second original element position
expect(result[4]).toBe(8.333333333333334); // Third original element position
});
it('should throw error if initial array is too small', () => {
expect(() => scaleSpreadArray([1], 8.333333333333334)).toThrow();
});
it('should throw error if target size is smaller than initial size', () => {
expect(() => scaleSpreadArray([1, 2, 3], 2)).toThrow();
});
});
describe('pointOnCurve', () => {
it('should return points for "lamé" curve', () => {
const poc = pointOnCurve('lamé', 0.5);
const point = poc(0.5);
expect(point.x).toBeGreaterThanOrEqual(0);
expect(point.x).toBeLessThanOrEqual(1);
expect(point.y).toBeGreaterThanOrEqual(0);
expect(point.y).toBeLessThanOrEqual(1);
});
it('should return points for "arc" curve', () => {
const poc = pointOnCurve('arc', 0.1);
const point = poc(0.2);
expect(point.x).toBeGreaterThanOrEqual(0);
expect(point.x).toBeLessThanOrEqual(1);
expect(point.y).toBeGreaterThanOrEqual(0);
expect(point.y).toBeLessThanOrEqual(1);
});
/*
it('should throw error for invalid curve method', () => {
// @ts-expect-error Testing invalid input
expect(() => pointOnCurve('invalidMethod', 0.5)).toThrow(
'pointOnCurve() curveAccent parameter is expected to be "lamé" | "arc" | "pow" | "powY" | "powX" or a function but `invalidMethod` given.'
);
});*/
});
describe('makeCurveEasings', () => {
it('should return sEasing and lEasing functions', () => {
const easings = makeCurveEasings('lamé', 0.5);
expect(easings.sEasing).toBeInstanceOf(Function);
expect(easings.lEasing).toBeInstanceOf(Function);
});
it('easing functions should return values between 0 and 1 for t between 0 and 1', () => {
const easings = makeCurveEasings('lamé', 0.5);
const tValues = [0, 0.25, 0.5, 0.75, 1];
tValues.forEach(t => {
const sVal = easings.sEasing(t);
const lVal = easings.lEasing(t);
expect(sVal).toBeGreaterThanOrEqual(0);
expect(sVal).toBeLessThanOrEqual(1);
expect(lVal).toBeGreaterThanOrEqual(0);
expect(lVal).toBeLessThanOrEqual(1);
});
});
});