141 lines
4.8 KiB
TypeScript
141 lines
4.8 KiB
TypeScript
import { describe, expect, it } from "@suaveplan/testing/runner";
|
|
import * as fc from "fast-check";
|
|
import { compressSvg } from "../src/core/compress-svg/compress-svg.js";
|
|
import { parseSvg, serializeSvg } from "../src/parser/svg/svg.js";
|
|
import { PRESET_DEFAULT } from "../src/presets/index.js";
|
|
|
|
const SIMPLE_SVGS = [
|
|
'<svg xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="50" r="40"/></svg>',
|
|
'<svg><rect width="100" height="100"/></svg>',
|
|
'<svg><g><path d="M 10 20 L 30 40"/></g></svg>',
|
|
'<svg><text x="10" y="20">hello</text></svg>',
|
|
"<svg><g><g><rect/></g></g></svg>",
|
|
];
|
|
|
|
describe("property-based: compression idempotency", () => {
|
|
it("compress(compress(svg)) === compress(svg) for all sample SVGs", {
|
|
timeout: 30000,
|
|
}, () => {
|
|
fc.assert(
|
|
fc.property(fc.constantFrom(...SIMPLE_SVGS), (svg) => {
|
|
const first = compressSvg(svg, { plugins: PRESET_DEFAULT });
|
|
const second = compressSvg(first.data, {
|
|
plugins: PRESET_DEFAULT,
|
|
});
|
|
expect(second.data).toBe(first.data);
|
|
return true;
|
|
}),
|
|
{ numRuns: 10 }
|
|
);
|
|
});
|
|
});
|
|
|
|
describe("property-based: parse ↔ serialize round-trip", () => {
|
|
it("serializeSvg(parseSvg(svg)) is re-parseable for sample SVGs", {
|
|
timeout: 30000,
|
|
}, () => {
|
|
fc.assert(
|
|
fc.property(fc.constantFrom(...SIMPLE_SVGS), (svg) => {
|
|
const node = parseSvg(svg);
|
|
const serialized = serializeSvg(node);
|
|
const reparsed = parseSvg(serialized);
|
|
expect(reparsed.type).toBe("element");
|
|
return true;
|
|
}),
|
|
{ numRuns: 10 }
|
|
);
|
|
});
|
|
|
|
it("parse → serialize preserves element names", { timeout: 30000 }, () => {
|
|
fc.assert(
|
|
fc.property(fc.constantFrom(...SIMPLE_SVGS), (svg) => {
|
|
const node = parseSvg(svg);
|
|
const serialized = serializeSvg(node);
|
|
const reparsed = parseSvg(serialized);
|
|
expect(reparsed.name).toBe(node.name);
|
|
return true;
|
|
}),
|
|
{ numRuns: 10 }
|
|
);
|
|
});
|
|
});
|
|
|
|
describe("property-based: ratio invariants", () => {
|
|
it("ratio is always > 0 for valid SVG", { timeout: 30000 }, () => {
|
|
fc.assert(
|
|
fc.property(fc.constantFrom(...SIMPLE_SVGS), (svg) => {
|
|
const result = compressSvg(svg);
|
|
expect(result.ratio).toBeGreaterThan(0);
|
|
return true;
|
|
}),
|
|
{ numRuns: 10 }
|
|
);
|
|
});
|
|
|
|
it("originalSize matches byte length of input", { timeout: 30000 }, () => {
|
|
fc.assert(
|
|
fc.property(fc.constantFrom(...SIMPLE_SVGS), (svg) => {
|
|
const result = compressSvg(svg);
|
|
const expected = new TextEncoder().encode(svg).byteLength;
|
|
expect(result.originalSize).toBe(expected);
|
|
return true;
|
|
}),
|
|
{ numRuns: 10 }
|
|
);
|
|
});
|
|
|
|
it("compressedSize matches byte length of output", {
|
|
timeout: 30000,
|
|
}, () => {
|
|
fc.assert(
|
|
fc.property(fc.constantFrom(...SIMPLE_SVGS), (svg) => {
|
|
const result = compressSvg(svg);
|
|
const expected = new TextEncoder().encode(
|
|
result.data
|
|
).byteLength;
|
|
expect(result.compressedSize).toBe(expected);
|
|
return true;
|
|
}),
|
|
{ numRuns: 10 }
|
|
);
|
|
});
|
|
|
|
it("ratio equals compressedSize / originalSize", { timeout: 30000 }, () => {
|
|
fc.assert(
|
|
fc.property(fc.constantFrom(...SIMPLE_SVGS), (svg) => {
|
|
const result = compressSvg(svg);
|
|
expect(result.ratio).toBeCloseTo(
|
|
result.compressedSize / result.originalSize,
|
|
5
|
|
);
|
|
return true;
|
|
}),
|
|
{ numRuns: 10 }
|
|
);
|
|
});
|
|
});
|
|
|
|
describe("property-based: passCount invariants", () => {
|
|
it("passCount >= 1 for all valid SVGs", { timeout: 30000 }, () => {
|
|
fc.assert(
|
|
fc.property(fc.constantFrom(...SIMPLE_SVGS), (svg) => {
|
|
const result = compressSvg(svg);
|
|
expect(result.passCount).toBeGreaterThanOrEqual(1);
|
|
return true;
|
|
}),
|
|
{ numRuns: 10 }
|
|
);
|
|
});
|
|
|
|
it("passCount === 1 when multipass: false", { timeout: 30000 }, () => {
|
|
fc.assert(
|
|
fc.property(fc.constantFrom(...SIMPLE_SVGS), (svg) => {
|
|
const result = compressSvg(svg, { multipass: false });
|
|
expect(result.passCount).toBe(1);
|
|
return true;
|
|
}),
|
|
{ numRuns: 10 }
|
|
);
|
|
});
|
|
});
|