OpenCut/packages/effects/tests/color-tone-effects.test.ts

181 lines
6.1 KiB
TypeScript
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.

import { describe, test, expect } from "bun:test";
import { EffectCategory } from "../src/categories";
import type { EffectDefinition } from "../src/types";
import { brightnessEffectDefinition } from "../src/effects/brightness";
import { contrastEffectDefinition } from "../src/effects/contrast";
import { saturationEffectDefinition } from "../src/effects/saturation";
import { hueShiftEffectDefinition } from "../src/effects/hue-shift";
import { temperatureEffectDefinition } from "../src/effects/temperature";
import { exposureEffectDefinition } from "../src/effects/exposure";
import { gammaEffectDefinition } from "../src/effects/gamma";
import { colorBalanceEffectDefinition } from "../src/effects/color-balance";
const defaultUniformArgs = { width: 1920, height: 1080 };
/** Helper: validate common structure of all color/tone effects */
function validateColorEffect(def: EffectDefinition, expectedType: string) {
test(`${expectedType}: has correct type`, () => {
expect(def.type).toBe(expectedType);
});
test(`${expectedType}: is COLOR_TONE category`, () => {
expect(def.category).toBe(EffectCategory.COLOR_TONE);
});
test(`${expectedType}: has single-pass WebGL renderer`, () => {
expect(def.renderer.type).toBe("webgl");
expect(def.renderer.passes).toHaveLength(1);
});
test(`${expectedType}: has at least one param`, () => {
expect(def.params.length).toBeGreaterThanOrEqual(1);
});
test(`${expectedType}: has keywords`, () => {
expect(def.keywords.length).toBeGreaterThan(0);
});
test(`${expectedType}: uniforms factory returns object`, () => {
const defaults: Record<string, number | string | boolean> = {};
for (const p of def.params) {
defaults[p.key] = p.default;
}
const result = def.renderer.passes[0].uniforms({
effectParams: defaults,
...defaultUniformArgs,
});
expect(typeof result).toBe("object");
});
}
describe("Brightness", () => {
validateColorEffect(brightnessEffectDefinition, "brightness");
test("default intensity is 0 (no change)", () => {
expect(brightnessEffectDefinition.params[0].default).toBe(0);
});
test("uniforms map intensity to -1..1 range", () => {
const pass = brightnessEffectDefinition.renderer.passes[0];
const at100 = pass.uniforms({ effectParams: { intensity: 100 }, ...defaultUniformArgs });
const atNeg100 = pass.uniforms({ effectParams: { intensity: -100 }, ...defaultUniformArgs });
expect(at100.u_intensity).toBe(1);
expect(atNeg100.u_intensity).toBe(-1);
});
});
describe("Contrast", () => {
validateColorEffect(contrastEffectDefinition, "contrast");
test("uniforms map 0 → 1.0 (no change)", () => {
const pass = contrastEffectDefinition.renderer.passes[0];
const result = pass.uniforms({ effectParams: { intensity: 0 }, ...defaultUniformArgs });
expect(result.u_contrast).toBe(1);
});
test("uniforms map 100 → 2.0 (max contrast)", () => {
const pass = contrastEffectDefinition.renderer.passes[0];
const result = pass.uniforms({ effectParams: { intensity: 100 }, ...defaultUniformArgs });
expect(result.u_contrast).toBe(2);
});
});
describe("Saturation", () => {
validateColorEffect(saturationEffectDefinition, "saturation");
test("uniforms map -100 → 0.0 (grayscale)", () => {
const pass = saturationEffectDefinition.renderer.passes[0];
const result = pass.uniforms({ effectParams: { intensity: -100 }, ...defaultUniformArgs });
expect(result.u_saturation).toBe(0);
});
});
describe("Hue Shift", () => {
validateColorEffect(hueShiftEffectDefinition, "hue-shift");
test("param range is 0..360 degrees", () => {
const param = hueShiftEffectDefinition.params[0];
if (param.type === "number") {
expect(param.min).toBe(0);
expect(param.max).toBe(360);
}
});
test("uniforms convert degrees to 0..1 range", () => {
const pass = hueShiftEffectDefinition.renderer.passes[0];
const at180 = pass.uniforms({ effectParams: { degrees: 180 }, ...defaultUniformArgs });
expect(at180.u_hueShift).toBe(0.5);
});
});
describe("Temperature", () => {
validateColorEffect(temperatureEffectDefinition, "temperature");
test("uniforms produce small shift values", () => {
const pass = temperatureEffectDefinition.renderer.passes[0];
const result = pass.uniforms({ effectParams: { warmth: 100 }, ...defaultUniformArgs });
expect(result.u_temperature).toBe(0.25);
});
});
describe("Exposure", () => {
validateColorEffect(exposureEffectDefinition, "exposure");
test("param range is -3..3 stops", () => {
const param = exposureEffectDefinition.params[0];
if (param.type === "number") {
expect(param.min).toBe(-3);
expect(param.max).toBe(3);
expect(param.step).toBe(0.1);
}
});
test("uniforms pass stops directly", () => {
const pass = exposureEffectDefinition.renderer.passes[0];
const result = pass.uniforms({ effectParams: { stops: 1.5 }, ...defaultUniformArgs });
expect(result.u_exposure).toBe(1.5);
});
});
describe("Gamma", () => {
validateColorEffect(gammaEffectDefinition, "gamma");
test("default gamma is 1.0 (linear)", () => {
expect(gammaEffectDefinition.params[0].default).toBe(1.0);
});
test("uniforms pass value directly", () => {
const pass = gammaEffectDefinition.renderer.passes[0];
const result = pass.uniforms({ effectParams: { value: 2.2 }, ...defaultUniformArgs });
expect(result.u_gamma).toBe(2.2);
});
});
describe("Color Balance", () => {
validateColorEffect(colorBalanceEffectDefinition, "color-balance");
test("has 9 params (3 channels × 3 regions)", () => {
expect(colorBalanceEffectDefinition.params).toHaveLength(9);
});
test("all params default to 0", () => {
for (const param of colorBalanceEffectDefinition.params) {
expect(param.default).toBe(0);
}
});
test("uniforms produce 3 vec3 arrays", () => {
const defaults: Record<string, number> = {};
for (const p of colorBalanceEffectDefinition.params) {
defaults[p.key] = 50;
}
const pass = colorBalanceEffectDefinition.renderer.passes[0];
const result = pass.uniforms({ effectParams: defaults, ...defaultUniformArgs });
expect(Array.isArray(result.u_shadows)).toBe(true);
expect(Array.isArray(result.u_midtones)).toBe(true);
expect(Array.isArray(result.u_highlights)).toBe(true);
expect((result.u_shadows as number[]).length).toBe(3);
});
});