svg.test.js
4.4 kB · javascript · 79 lines
1import { expect, test } from "bun:test";2import { svg } from "./index.js";34const ORANGE = [255, 143, 44, 255];5const pixels = { shape: [1, 2], colors: Uint8Array.from([255, 0, 0, 255, 0, 0, 255, 128]) };67async function body(draw, options) {8 const pen = svg(64, 48, [0, 0, 0, 0]);9 draw(pen);10 const text = await pen.text(options);11 return text.split("\n").slice(1, -2).join("\n");12}1314// VERBS1516test("svg opens on its size and floods its ground", async () => {17 const text = await svg(64, 48, [17, 17, 18, 255]).text();18 expect(text).toStartWith('<svg xmlns="http://www.w3.org/2000/svg" width="64" height="48" viewBox="0 0 64 48">\n');19 expect(text).toContain('<rect width="64" height="48" fill="#111112"/>');20});2122test("rect is a rect and alpha is its opacity", async () => {23 expect(await body((p) => p.rect(1, 2.5, 3, 4, ORANGE))).toBe('<rect x="1" y="2.5" width="3" height="4" fill="#ff8f2c"/>');24 expect(await body((p) => p.rect(1, 2, 3, 4, [255, 143, 44, 51]))).toBe('<rect x="1" y="2" width="3" height="4" fill="#ff8f2c" fill-opacity="0.2"/>');25});2627test("round_rect clamps its radius to half the short side", async () => {28 expect(await body((p) => p.round_rect(1, 2, 10, 20, 50, ORANGE))).toBe('<rect x="1" y="2" width="10" height="20" rx="5" ry="5" fill="#ff8f2c"/>');29});3031test("disc is a filled circle", async () => {32 expect(await body((p) => p.disc(30, 20, 9.5, ORANGE))).toBe('<circle cx="30" cy="20" r="9.5" fill="#ff8f2c"/>');33});3435test("ring is a stroked circle", async () => {36 expect(await body((p) => p.ring(30, 20, 9, 2, [255, 143, 44, 128]))).toBe('<circle cx="30" cy="20" r="9" fill="none" stroke="#ff8f2c" stroke-opacity="0.5019607843137255" stroke-width="2"/>');37});3839test("segment is a line with round caps", async () => {40 expect(await body((p) => p.segment([1, 2], [30, 40], 3, ORANGE))).toBe('<line x1="1" y1="2" x2="30" y2="40" fill="none" stroke="#ff8f2c" stroke-width="3" stroke-linecap="round" stroke-linejoin="round"/>');41 expect(await body((p) => p.segment([5, 6], [5, 6], 4, ORANGE))).toBe('<circle cx="5" cy="6" r="2" fill="#ff8f2c"/>');42});4344test("polyline is one stroke with round caps and joints", async () => {45 expect(await body((p) => p.polyline([[1, 2], [3, 4], [5, 6]], 2, ORANGE))).toBe('<polyline points="1,2 3,4 5,6" fill="none" stroke="#ff8f2c" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>');46});4748test("triangle is a three point polygon", async () => {49 expect(await body((p) => p.triangle([1, 2], [3, 4], [5, 0], ORANGE))).toBe('<polygon points="1,2 3,4 5,0" fill="#ff8f2c" fill-rule="evenodd"/>');50});5152test("polygon fills by the even-odd rule", async () => {53 expect(await body((p) => p.polygon([[0, 0], [10, 0], [10, 10], [0, 10]], ORANGE))).toBe('<polygon points="0,0 10,0 10,10 0,10" fill="#ff8f2c" fill-rule="evenodd"/>');54});5556test("arc sweeps clockwise in two arms, a full turn is a ring and none is a dot", async () => {57 expect(await body((p) => p.arc([20, 20], 10, [Math.PI / 2, 0], 2, ORANGE))).toBe(`<path d="M30,20 A10,10 0 0 1 ${20 + 10 * Math.cos(Math.PI / 4)},${20 + 10 * Math.sin(Math.PI / 4)} A10,10 0 0 1 ${20 + 10 * Math.cos(Math.PI / 2)},30" fill="none" stroke="#ff8f2c" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>`);58 expect(await body((p) => p.arc([20, 20], 10, [0, 7], 2, ORANGE))).toBe('<circle cx="20" cy="20" r="10" fill="none" stroke="#ff8f2c" stroke-width="2"/>');59 expect(await body((p) => p.arc([20, 20], 10, [0, 0], 2, ORANGE))).toBe('<circle cx="30" cy="20" r="1" fill="#ff8f2c"/>');60});6162test("image embeds the png its encoder returns, nearest sampled", async () => {63 const draw = (p) => p.image(4, 8, 32, 16, pixels);64 await expect(body(draw)).rejects.toThrow("png encoder");65 const bytes = await body(draw, { png: async () => Uint8Array.from([137, 80, 78, 71]) });66 expect(bytes).toBe('<image x="4" y="8" width="32" height="16" preserveAspectRatio="none" image-rendering="pixelated" href="data:image/png;base64,iVBORw=="/>');67 const url = await body(draw, { png: (seen) => `data:${seen.shape.join("x")}` });68 expect(url).toContain('href="data:1x2"');69});7071test("image keeps the pixels it was handed at the time it was drawn", async () => {72 const mine = { shape: [1, 1], colors: Uint8Array.from([1, 2, 3, 4]) };73 const pen = svg(8, 8);74 pen.image(0, 0, 8, 8, mine);75 mine.colors[0] = 99;76 let seen;77 await pen.text({ png: (p) => ((seen = Array.from(p.colors)), "data:") });78 expect(seen).toEqual([1, 2, 3, 4]);79});