Files
feuerwerk/tests/ui/svg-icons.test.ts
T

76 lines
3.3 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import { describe, expect, it } from 'vitest';
import { ASSET_TYPES } from '../../src/core/model/AssetTypes';
import { ASSET_ICON_SVG, LIST_ICON_SVG } from '../../src/editor/panels/assetIcons';
import { tintSvg } from '../../src/ui/svg';
import { SYMBOL_SVG } from '../../src/viewport/symbolIcons';
/** Opening tags as name + attributes; the files are flat, so no real XML parser is needed. */
function tags(svg: string): { name: string; attrs: Record<string, string> }[] {
return [...svg.matchAll(/<([a-z]+)([^>]*?)\/?>/g)].map(([, name, rest]) => ({
name: name!,
attrs: Object.fromEntries([...rest!.matchAll(/([a-zA-Z-]+)="([^"]*)"/g)].map(([, k, v]) => [k!, v!])),
}));
}
describe('icon files', () => {
it('has a scene view symbol for every symbol (#175)', () => {
expect(Object.keys(SYMBOL_SVG).sort()).toEqual([
'behavior',
'camera',
'container',
'particles',
'rigidbody',
]);
});
it('has an asset panel icon for every asset type, each its own picture (#176)', () => {
expect(Object.keys(ASSET_ICON_SVG).sort()).toEqual([...ASSET_TYPES].sort());
expect(new Set(Object.values(ASSET_ICON_SVG)).size).toBe(ASSET_TYPES.length);
});
const icons = [
...Object.entries(SYMBOL_SVG).map(([name, svg]) => [`symbol ${name}`, svg]),
...Object.entries(ASSET_ICON_SVG).map(([name, svg]) => [`asset ${name}`, svg]),
['list tab', LIST_ICON_SVG],
];
// Pixi draws the file's coordinates as they are and ignores the viewBox, so the viewBox only
// shows the file right elsewhere – centred on the origin, it matches what the editor draws.
it.each(icons)('%s is centred on its origin', (_, svg) => {
const root = tags(svg)[0]!;
expect(root.name).toBe('svg');
const [x, y, w, h] = root.attrs.viewBox!.split(' ').map(Number);
expect([x, y]).toEqual([-w! / 2, -h! / 2]);
expect([Number(root.attrs.width), Number(root.attrs.height)]).toEqual([w, h]);
});
it('draws the asset icons in the 16 units a list row and a button reserve', () => {
for (const svg of [...Object.values(ASSET_ICON_SVG), LIST_ICON_SVG])
expect(tags(svg)[0]!.attrs.viewBox).toBe('-8 -8 16 16');
});
it('gives the List tab a picture no asset type has', () => {
expect(Object.values(ASSET_ICON_SVG)).not.toContain(LIST_ICON_SVG);
});
it.each(icons)('%s uses only what the Pixi parser draws like a browser', (_, svg) => {
for (const { name, attrs } of tags(svg).slice(1)) {
// polygon and polyline read their points as whole numbers; groups would carry transforms,
// which the parser ignores.
expect(['path', 'circle', 'rect', 'ellipse', 'line']).toContain(name);
expect(attrs.transform).toBeUndefined();
// Without a fill a browser fills black, Pixi does not fill at all once there is a stroke.
expect(attrs.fill, `${name} states its fill`).toBeDefined();
for (const paint of [attrs.fill, attrs.stroke].filter((p) => p !== undefined))
expect(paint).toMatch(/^(none|currentColor|#[0-9a-f]{6})$/);
}
});
it('puts the theme colour where the file says currentColor', () => {
expect(tintSvg('<path fill="currentColor" stroke="currentColor"/>', 0x0000ff)).toBe(
'<path fill="#0000ff" stroke="#0000ff"/>',
);
for (const [, svg] of icons) expect(tintSvg(svg!, 0xb066ff)).not.toContain('currentColor');
});
});