Something went wrong. Try again.
This repository has no description
Something went wrong. Try again.
TypeScript
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239import { describe, it, expect, beforeEach } from "vitest"import { createLayer, nextLayerId, makeRegistry, LAYER_TYPES,} from "./layers"import type { Layer } from "./layers"import { FieldKey } from "./brands"import { addLayer, removeLayer, reorderLayer, updateLayerParam, toggleLayerVisibility, replaceChain,} from "./chain"import { renderExposure, renderContrast, renderShadows, renderHighlights, renderWhiteBalance, renderSaturation, renderGrain, renderVignette, renderChromaticAberration, renderClarity, renderLut,} from "./shaders"
// ---- helpers ----
const registry = makeRegistry({ exposure: renderExposure, contrast: renderContrast, shadows: renderShadows, highlights: renderHighlights, whiteBalance: renderWhiteBalance, saturation: renderSaturation, grain: renderGrain, vignette: renderVignette, chromaticAberration: renderChromaticAberration, clarity: renderClarity, lut: renderLut,})
/** Read a numeric field from a layer, typed as unknown→number. */function field(layer: Layer, key: FieldKey): number { const record: Record<string, unknown> = layer const value = record[key] return typeof value === "number" ? value : NaN}
describe("createLayer", () => { it("creates every layer type with its registry defaults", () => { // Defaults are a product contract (e.g. vignette size 0.6, LUT amount 1 // = full apply), so every type is pinned, not just a sample. LAYER_TYPES // drives the loop, so a type missing from `defaults` fails the test. const defaults: Record<string, Record<string, number>> = { exposure: { stops: 0 }, contrast: { amount: 0 }, shadows: { amount: 0 }, highlights: { amount: 0 }, whiteBalance: { temp: 0, tint: 0 }, saturation: { amount: 0 }, grain: { texture: 0, size: 0, blur: 0 }, vignette: { amount: 0, size: 0.6 }, chromaticAberration: { amount: 0 }, clarity: { amount: 0 }, lut: { amount: 1 }, } for (const type of LAYER_TYPES) { const layer = createLayer(type, registry) expect(layer.type).toBe(type) expect(layer.visible).toBe(true) for (const [key, expected] of Object.entries(defaults[type]!)) { expect(field(layer, FieldKey(key))).toBe(expected) } } })
it("assigns unique ids", () => { const a = createLayer("exposure", registry) const b = createLayer("contrast", registry) expect(a.id).not.toBe(b.id) })
it("throws on an unknown type", () => { // An unknown type key reaches the registry lookup as a runtime string; // Object.create(null) supplies an arbitrary key without an assertion. expect(() => createLayer(Object.create(null), registry)).toThrow() })})
describe("chain operations", () => { let chain: Layer[]
beforeEach(() => { chain = [] })
describe("addLayer", () => { it("appends a layer to an empty chain", () => { const result = addLayer(chain, "exposure", registry) expect(result).toHaveLength(1) expect(result[0]!.type).toBe("exposure") })
it("appends to end of non-empty chain", () => { chain = addLayer(chain, "exposure", registry) const result = addLayer(chain, "contrast", registry) expect(result).toHaveLength(2) expect(result[0]!.type).toBe("exposure") expect(result[1]!.type).toBe("contrast") })
it("does not mutate the original chain", () => { const result = addLayer(chain, "exposure", registry) expect(chain).toHaveLength(0) expect(result).toHaveLength(1) }) })
describe("removeLayer", () => { it("removes a layer by id", () => { chain = addLayer(chain, "exposure", registry) const id = chain[0]!.id const result = removeLayer(chain, id) expect(result).toHaveLength(0) })
it("removes only the matching layer", () => { chain = addLayer(chain, "exposure", registry) chain = addLayer(chain, "contrast", registry) const id = chain[0]!.id const result = removeLayer(chain, id) expect(result).toHaveLength(1) expect(result[0]!.type).toBe("contrast") })
it("returns same chain if id not found", () => { chain = addLayer(chain, "exposure", registry) // A fresh id from the counter is not in the chain const result = removeLayer(chain, nextLayerId()) expect(result).toHaveLength(1) }) })
describe("reorderLayer", () => { it("moves a layer from first to last", () => { chain = addLayer(chain, "exposure", registry) chain = addLayer(chain, "contrast", registry) chain = addLayer(chain, "saturation", registry) const id = chain[0]!.id const result = reorderLayer(chain, id, 2) expect(result[0]!.type).toBe("contrast") expect(result[1]!.type).toBe("saturation") expect(result[2]!.type).toBe("exposure") })
it("moves a layer from last to first", () => { chain = addLayer(chain, "exposure", registry) chain = addLayer(chain, "contrast", registry) chain = addLayer(chain, "saturation", registry) const id = chain[2]!.id const result = reorderLayer(chain, id, 0) expect(result[0]!.type).toBe("saturation") expect(result[1]!.type).toBe("exposure") expect(result[2]!.type).toBe("contrast") })
it("returns unchanged for invalid index", () => { chain = addLayer(chain, "exposure", registry) const id = chain[0]!.id const result = reorderLayer(chain, id, 99) expect(result[0]!.type).toBe("exposure") }) })
describe("updateLayerParam", () => { it("updates a parameter on a layer", () => { chain = addLayer(chain, "exposure", registry) const result = updateLayerParam(chain, { type: "exposure", patch: { stops: 1.5 }, }) expect(field(result[0]!, FieldKey("stops"))).toBe(1.5) })
it("returns unchanged if type not found", () => { chain = addLayer(chain, "exposure", registry) const result = updateLayerParam(chain, { type: "contrast", patch: { amount: 0.5 }, }) expect(result).toHaveLength(1) expect(result[0]!.type).toBe("exposure") })
it("does not mutate original", () => { chain = addLayer(chain, "exposure", registry) const result = updateLayerParam(chain, { type: "exposure", patch: { stops: 1.5 }, }) expect(field(chain[0]!, FieldKey("stops"))).toBe(0) expect(field(result[0]!, FieldKey("stops"))).toBe(1.5) }) })
describe("toggleLayerVisibility", () => { it("toggles visible from true to false", () => { chain = addLayer(chain, "exposure", registry) const id = chain[0]!.id const result = toggleLayerVisibility(chain, id) expect(result[0]!.visible).toBe(false) })
it("toggles visible from false to true", () => { chain = addLayer(chain, "exposure", registry) const id = chain[0]!.id chain = toggleLayerVisibility(chain, id) const result = toggleLayerVisibility(chain, id) expect(result[0]!.visible).toBe(true) }) })
describe("replaceChain", () => { it("replaces the entire chain", () => { chain = addLayer(chain, "exposure", registry) const newLayers = addLayer([], "saturation", registry) const result = replaceChain(chain, newLayers) expect(result).toHaveLength(1) expect(result[0]!.type).toBe("saturation") }) })})