fabric
Version:
Object model for HTML5 canvas, and SVG-to-canvas parser. Backed by jsdom and node-canvas.
160 lines (133 loc) • 5.25 kB
text/typescript
import { describe, expect, it } from 'vitest';
import { Line } from './Line';
import { FabricObject } from './Object/Object';
import { createReferenceObject, createSVGElement } from '../../test/utils';
describe('Line', () => {
const LINE_OBJECT = createReferenceObject('Line', {
left: 12,
top: 13,
width: 2,
height: 2,
x1: -1,
y1: -1,
x2: 1,
y2: 1,
});
it('initializes constructor correctly', () => {
expect(Line).toBeTruthy();
const line = new Line([10, 11, 20, 21]);
expect(line).toBeInstanceOf(Line);
expect(line).toBeInstanceOf(FabricObject);
expect(line.constructor).toHaveProperty('type', 'Line');
expect(line.get('x1')).toBe(10);
expect(line.get('y1')).toBe(11);
expect(line.get('x2')).toBe(20);
expect(line.get('y2')).toBe(21);
const lineWithoutPoints = new Line();
expect(lineWithoutPoints.get('x1')).toBe(0);
expect(lineWithoutPoints.get('y1')).toBe(0);
expect(lineWithoutPoints.get('x2')).toBe(0);
expect(lineWithoutPoints.get('y2')).toBe(0);
});
it('has complexity function', () => {
const line = new Line();
expect(line.complexity).toBeTypeOf('function');
});
it('generates SVG correctly', () => {
const line = new Line([11, 12, 13, 14]);
const EXPECTED_SVG =
'<g transform="matrix(1 0 0 1 12 13)" >\n<line style="stroke: none; stroke-width: 1; stroke-dasharray: none; stroke-linecap: butt; stroke-dashoffset: 0; stroke-linejoin: miter; stroke-miterlimit: 4; fill: rgb(0,0,0); fill-rule: nonzero; opacity: 1;" x1="-1" y1="-1" x2="1" y2="1" />\n</g>\n';
expect(line.toSVG()).toEqual(EXPECTED_SVG);
});
it('converts to object correctly', () => {
const line = new Line([11, 12, 13, 14]);
expect(line.toObject).toBeTypeOf('function');
expect(line.toObject()).toEqual(LINE_OBJECT);
});
it('creates from object correctly', async () => {
expect(Line.fromObject).toBeTypeOf('function');
const line = await Line.fromObject(LINE_OBJECT);
expect(line).toBeInstanceOf(Line);
expect(line.toObject()).toEqual(LINE_OBJECT);
});
it('creates from SVG element correctly', async () => {
expect(Line.fromElement).toBeTypeOf('function');
// TODO: should fromElement also accept SVGElement since test is doing it?
const lineEl = createSVGElement('line', {
x1: 11,
x2: 34,
y1: 23,
y2: 7,
stroke: 'ff5555',
'stroke-width': 2,
'stroke-dasharray': '5, 2',
'stroke-linecap': 'round',
'stroke-linejoin': 'bevel',
'stroke-miterlimit': 5,
});
const oLine = await Line.fromElement(lineEl as unknown as HTMLElement);
expect(oLine).toBeInstanceOf(Line);
expect(oLine.get('x1')).toBe(11);
expect(oLine.get('y1')).toBe(23);
expect(oLine.get('x2')).toBe(34);
expect(oLine.get('y2')).toBe(7);
expect(oLine.get('stroke')).toBe('ff5555');
expect(oLine.get('strokeWidth')).toBe(2);
expect(oLine.get('strokeDashArray')).toEqual([5, 2]);
expect(oLine.get('strokeLineCap')).toBe('round');
expect(oLine.get('strokeLineJoin')).toBe('bevel');
expect(oLine.get('strokeMiterLimit')).toBe(5);
const lineElWithMissingAttributes = createSVGElement('line', {
x1: 10,
y1: 20,
});
const oLine2 = await Line.fromElement(
lineElWithMissingAttributes as unknown as HTMLElement,
);
expect(oLine2.get('x2')).toBe(0);
expect(oLine2.get('y2')).toBe(0);
});
it('allows straight lines to have 0 width or height', () => {
const line1 = new Line([10, 10, 100, 10]);
const line2 = new Line([10, 10, 10, 100]);
expect(line1.get('height')).toBe(0);
expect(line2.get('width')).toBe(0);
});
it('updates width/height when changing x/y coordinates', () => {
const line = new Line([50, 50, 100, 100]);
expect(line.width).toBe(50);
line.set({ x1: 75, y1: 75, x2: 175, y2: 175 });
expect(line.width).toBe(100);
expect(line.height).toBe(100);
});
it('parses stroke width from style attribute', async () => {
const lineEl = createSVGElement('line');
lineEl.setAttribute('style', 'stroke-width:4');
const oLine = await Line.fromElement(lineEl as unknown as HTMLElement);
expect(oLine.strokeWidth).toBe(4);
});
describe('line positioning', () => {
(['left', 'center', 'right'] as const).forEach((originX) => {
(['top', 'center', 'bottom'] as const).forEach((originY) => {
[0, 7].forEach((strokeWidth) => {
[0, 33, 90].forEach((angle) => {
(['butt', 'round', 'square'] as const).forEach((strokeLineCap) => {
it(`positions on center regardless of strokeWidth or origin (${originX}/${originY} stroke:${strokeWidth} angle:${angle} cap:${strokeLineCap})`, () => {
const line = new Line([1, 1, 15, 7], {
strokeWidth,
originX,
originY,
angle,
strokeLineCap,
});
const center = line.getCenterPoint();
expect(Math.round(center.x)).toBe(8);
expect(Math.round(center.y)).toBe(4);
});
});
});
});
});
});
});
});