seedmarkfile
seedmark/tests/helpers.js/

tests/helpers.js raw
1import assert from 'node:assert/strict';
2// Small strict validator for the intentionally tiny emitted SVG vocabulary.
3export function validate(svg) {
4 const stack = [];
5 const tags = new Set(['svg', 'g', 'rect', 'circle', 'ellipse', 'path', 'polygon', 'line']);
6 let consumed = '';
7 for (const match of svg.matchAll(/<\/?([a-z]+)([^<>]*?)\/?\s*>/g)) {
8 consumed += match[0];
9 assert(tags.has(match[1]), `unexpected tag ${match[1]}`);
10 if (match[0].startsWith('</')) assert.equal(stack.pop(), match[1]);
11 else {
12 const attrs = match[2];
13 const parsed = [...attrs.matchAll(/\s+([a-zA-Z-]+)="([^"<>]*)"/g)];
14 assert.equal(parsed.map(a => a[0]).join(''), attrs);
15 assert.equal(new Set(parsed.map(a => a[1])).size, parsed.length);
16 for (const [, key, value] of parsed) {
17 assert(!/^(?:on|id$|href$|style$)/i.test(key));
18 assert(!/NaN|Infinity|undefined|url\(|[&<>]/.test(value));
19 if (['x', 'y', 'cx', 'cy', 'r', 'rx', 'ry', 'width', 'height', 'stroke-width'].includes(key)) assert(Number.isFinite(Number(value)));
20 }
21 if (!match[0].endsWith('/>')) stack.push(match[1]);
22 }
23 }
24 assert.equal(stack.length, 0);
25 assert.equal(consumed, svg);
26 assert.match(svg, /^<svg xmlns="http:\/\/www.w3.org\/2000\/svg"/);
27 assert.match(svg, /viewBox="0 0 64 64"/);
28}

About

ownerba1b5beed5b5
visibilitypublic
headc808a940bc19

Maintainers

  • ba1b5beed5b5 owner

Clone

  • https://012.run/npub1hgd4hmk4kkukjx7yf402adl7msw0ssnmqmt0y6sgz0j8yjjcj9nqh9znde/seedmark.git

Refs