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});