canvas.test.js
4.1 kB · javascript · 108 lines
1import { afterAll, beforeAll, expect, test } from "bun:test";2import { canvas } from "./index.js";34const ORANGE = [255, 143, 44, 255];5const STROKE = [["strokeStyle", "rgba(255,143,44,1)"], ["lineWidth", 2], ["lineCap", "round"], ["lineJoin", "round"], ["stroke"]];6const FILL = [["fillStyle", "rgba(255,143,44,1)"], ["fill", "evenodd"]];7const globals = {};89class Surface {10 constructor(w, h) {11 this.size = [w, h];12 }1314 getContext() {15 return { putImageData: (image) => (this.image = image) };16 }17}1819class Image {20 constructor(data, w, h) {21 Object.assign(this, { data, w, h });22 }23}2425beforeAll(() => {26 globals.OffscreenCanvas = globalThis.OffscreenCanvas;27 globals.ImageData = globalThis.ImageData;28 globalThis.OffscreenCanvas = Surface;29 globalThis.ImageData = Image;30});3132afterAll(() => {33 globalThis.OffscreenCanvas = globals.OffscreenCanvas;34 globalThis.ImageData = globals.ImageData;35});3637function record(draw) {38 const calls = [];39 const state = { imageSmoothingEnabled: true };40 const ctx = new Proxy(state, {41 get: (_, key) => (key in state ? state[key] : (...args) => calls.push([key, ...args])),42 set: (_, key, value) => ((state[key] = value), calls.push([key, value]), true),43 });44 const pen = canvas(ctx, 64, 48, [0, 0, 0, 0]);45 calls.length = 0;46 draw(pen);47 return calls;48}4950// VERBS5152test("canvas clears and floods its ground", () => {53 const calls = [];54 const ctx = new Proxy({}, { get: (_, key) => (...args) => calls.push([key, ...args]), set: (_, key, value) => (calls.push([key, value]), true) });55 canvas(ctx, 64, 48, [17, 17, 18, 255]);56 expect(calls).toEqual([["clearRect", 0, 0, 64, 48], ["fillStyle", "rgba(17,17,18,1)"], ["fillRect", 0, 0, 64, 48]]);57});5859test("rect is fillRect", () => {60 expect(record((p) => p.rect(1, 2, 3, 4, [255, 143, 44, 51]))).toEqual([["fillStyle", "rgba(255,143,44,0.2)"], ["fillRect", 1, 2, 3, 4]]);61});6263test("round_rect is roundRect with its radius clamped", () => {64 expect(record((p) => p.round_rect(1, 2, 10, 20, 50, ORANGE))).toEqual([["beginPath"], ["roundRect", 1, 2, 10, 20, 5], ...FILL]);65});6667test("disc is a filled full arc", () => {68 expect(record((p) => p.disc(30, 20, 9, ORANGE))).toEqual([["beginPath"], ["arc", 30, 20, 9, 0, 2 * Math.PI], ...FILL]);69});7071test("ring is a stroked full arc", () => {72 expect(record((p) => p.ring(30, 20, 9, 2, ORANGE))).toEqual([["beginPath"], ["arc", 30, 20, 9, 0, 2 * Math.PI], ...STROKE]);73});7475test("segment is a stroked line with round caps", () => {76 expect(record((p) => p.segment([1, 2], [30, 40], 2, ORANGE))).toEqual([["beginPath"], ["moveTo", 1, 2], ["lineTo", 30, 40], ...STROKE]);77});7879test("polyline is one stroked path", () => {80 expect(record((p) => p.polyline([[1, 2], [3, 4], [5, 6]], 2, ORANGE))).toEqual([["beginPath"], ["moveTo", 1, 2], ["lineTo", 3, 4], ["lineTo", 5, 6], ...STROKE]);81});8283test("triangle is a closed filled path", () => {84 expect(record((p) => p.triangle([1, 2], [3, 4], [5, 0], ORANGE))).toEqual([["beginPath"], ["moveTo", 1, 2], ["lineTo", 3, 4], ["lineTo", 5, 0], ["closePath"], ...FILL]);85});8687test("polygon fills by the even-odd rule", () => {88 const calls = record((p) => p.polygon([[0, 0], [10, 0], [10, 10], [0, 10]], ORANGE));89 expect(calls.at(-1)).toEqual(["fill", "evenodd"]);90 expect(calls.filter(([key]) => key === "lineTo").length).toBe(3);91});9293test("arc is a clockwise stroked arc from its lower angle", () => {94 expect(record((p) => p.arc([20, 20], 10, [2, 0.5], 2, ORANGE))).toEqual([["beginPath"], ["arc", 20, 20, 10, 0.5, 2], ...STROKE]);95});9697test("image draws the pixels unsmoothed onto its box", () => {98 const pixels = { shape: [1, 2], colors: Uint8Array.from([255, 0, 0, 255, 0, 0, 255, 128]) };99 const calls = record((p) => p.image(4, 8, 32, 16, pixels));100 expect(calls.map(([key]) => key)).toEqual(["imageSmoothingEnabled", "drawImage", "imageSmoothingEnabled"]);101 expect(calls[0][1]).toBe(false);102 const [, surface, ...box] = calls[1];103 expect(box).toEqual([4, 8, 32, 16]);104 expect(surface.size).toEqual([2, 1]);105 expect(surface.image.data).toBeInstanceOf(Uint8ClampedArray);106 expect(Array.from(surface.image.data)).toEqual(Array.from(pixels.colors));107 expect(calls[2][1]).toBe(true);108});