feat: introduce local-first SVG workbench
This commit is contained in:
@@ -0,0 +1,112 @@
|
||||
import { afterEach, beforeEach, describe, expect, it } from "vitest";
|
||||
import {
|
||||
defaultSvgLimits,
|
||||
utf8ByteLength,
|
||||
type SvgResourceLimits,
|
||||
} from "../../src/app/limits";
|
||||
import { parseSvgSource } from "../../src/document/source-parser";
|
||||
|
||||
const mutableLimits = defaultSvgLimits as SvgResourceLimits;
|
||||
let original: SvgResourceLimits;
|
||||
|
||||
beforeEach(() => {
|
||||
original = { ...defaultSvgLimits };
|
||||
Object.assign(mutableLimits, {
|
||||
maximumAttributeLength: 30,
|
||||
maximumPathCommandsPerPath: 2,
|
||||
maximumPathCommandsTotal: 3,
|
||||
maximumCssRules: 1,
|
||||
maximumReferences: 1,
|
||||
maximumAnimations: 1,
|
||||
maximumFilterPrimitives: 1,
|
||||
maximumTextLength: 3,
|
||||
maximumEmbeddedResourceBytes: 10,
|
||||
});
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
Object.assign(mutableLimits, original);
|
||||
});
|
||||
|
||||
describe("configured SVG resource limits", () => {
|
||||
it("counts UTF-8 bytes without allocating an encoded copy", () => {
|
||||
expect(utf8ByteLength("Aé🙂\ud800")).toBe(10);
|
||||
});
|
||||
|
||||
it("rejects a hard-limit document before XML or semantic processing", () => {
|
||||
const result = parseSvgSource(
|
||||
'<svg xmlns="http://www.w3.org/2000/svg"/>',
|
||||
0,
|
||||
{ ...original, sourceHardBytes: 8, sourceSoftBytes: 4 },
|
||||
);
|
||||
|
||||
expect(result.valid).toBe(false);
|
||||
expect(result.semantic).toBeNull();
|
||||
expect(result.diagnostics).toEqual([
|
||||
expect.objectContaining({ code: "source-hard-limit" }),
|
||||
]);
|
||||
});
|
||||
|
||||
it("reports every bounded resource class with source-preserving diagnostics", () => {
|
||||
const result = parseSvgSource(
|
||||
`<svg xmlns="http://www.w3.org/2000/svg" data-long="1234567890123456789012345678901">
|
||||
<style>.a{} .b{}</style>
|
||||
<defs><filter id="f"><feBlend/><feGaussianBlur/></filter></defs>
|
||||
<path d="M0 0L1 1L2 2L3 3" fill="url(#a)" stroke="url(#b)"/>
|
||||
<animate attributeName="opacity"/><set attributeName="fill"/>
|
||||
<text>long</text>
|
||||
<image href="data:image/png;base64,abcdef"/>
|
||||
</svg>`,
|
||||
1,
|
||||
);
|
||||
const codes = result.diagnostics.map((diagnostic) => diagnostic.code);
|
||||
|
||||
expect(result.valid).toBe(false);
|
||||
expect(result.semantic).not.toBeNull();
|
||||
expect(codes).toEqual(
|
||||
expect.arrayContaining([
|
||||
"attribute-length-limit",
|
||||
"path-command-per-element-limit",
|
||||
"path-command-limit",
|
||||
"css-rule-limit",
|
||||
"reference-limit",
|
||||
"animation-limit",
|
||||
"filter-primitive-limit",
|
||||
"text-length-limit",
|
||||
"total-text-limit",
|
||||
"embedded-resource-limit",
|
||||
]),
|
||||
);
|
||||
});
|
||||
|
||||
it("does not count exponent markers as path commands", () => {
|
||||
Object.assign(mutableLimits, {
|
||||
maximumPathCommandsPerPath: 10,
|
||||
maximumPathCommandsTotal: 10,
|
||||
maximumTextLength: original.maximumTextLength,
|
||||
maximumAttributeLength: original.maximumAttributeLength,
|
||||
});
|
||||
const result = parseSvgSource(
|
||||
'<svg xmlns="http://www.w3.org/2000/svg"><path d="M1e2 1e-2L3e1 4e0"/></svg>',
|
||||
1,
|
||||
);
|
||||
expect(result.semantic?.metrics.pathCommandCount).toBe(2);
|
||||
});
|
||||
|
||||
it("bounds aggregate embedded data even when each resource is below the limit", () => {
|
||||
const perResource = "data:image/png;base64,aaaaaaaaaaaaaaaaaaaaaaaa";
|
||||
const result = parseSvgSource(
|
||||
`<svg xmlns="http://www.w3.org/2000/svg"><image href="${perResource}"/><image href="${perResource}"/></svg>`,
|
||||
2,
|
||||
{
|
||||
...original,
|
||||
maximumEmbeddedResourceBytes: 60,
|
||||
},
|
||||
);
|
||||
const codes = result.diagnostics.map((diagnostic) => diagnostic.code);
|
||||
|
||||
expect(codes).not.toContain("embedded-resource-limit");
|
||||
expect(codes).toContain("embedded-resource-total-limit");
|
||||
expect(result.valid).toBe(false);
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user