fabric
Version:
Object model for HTML5 canvas, and SVG-to-canvas parser. Backed by jsdom and node-canvas.
789 lines (696 loc) • 26.5 kB
text/typescript
import { FabricObject } from '../shapes/Object/FabricObject';
import { Gradient } from './Gradient';
import type {
GradientUnits,
RadialGradientCoords,
SVGOptions,
} from './typedefs';
import { classRegistry } from '../ClassRegistry';
import { describe, expect, it, test } from 'vitest';
import { StaticCanvas } from '../canvas/StaticCanvas';
import { createSVGElement } from '../../test/utils';
function createLinearGradient(units: GradientUnits = 'pixels', id?: string) {
return new Gradient({
type: 'linear',
id,
gradientUnits: units,
coords: {
x1: 0,
y1: 10,
x2: 100,
y2: 200,
},
colorStops: [
{ offset: 0, color: 'rgba(255,0,0,0)' },
{ offset: 1, color: 'green' },
],
});
}
function createRadialGradient(units: GradientUnits = 'pixels') {
return new Gradient({
type: 'radial',
gradientUnits: units,
coords: {
x1: 0,
y1: 10,
x2: 100,
y2: 200,
r1: 0,
r2: 50,
},
colorStops: [
{ offset: 0, color: 'red' },
{ offset: 1, color: 'rgba(0,255,0,0)' },
],
});
}
function createRadialGradientWithInternalRadius() {
return new Gradient({
type: 'radial',
coords: {
x1: 0,
y1: 10,
x2: 100,
y2: 200,
r1: 10,
r2: 50,
},
colorStops: [
{ offset: 0, color: 'red' },
{ offset: 1, color: 'rgba(0,255,0,0)' },
],
});
}
function createRadialGradientSwapped() {
return new Gradient({
type: 'radial',
coords: {
x1: 0,
y1: 10,
x2: 100,
y2: 200,
r1: 50,
r2: 10,
},
colorStops: [
{ offset: 0, color: 'red' },
{ offset: 1, color: 'rgba(0,255,0,0)' },
],
});
}
const SVG_LINEAR =
'<linearGradient id="SVGID" gradientUnits="userSpaceOnUse" gradientTransform="matrix(1 0 0 1 -50 -50)" x1="0" y1="10" x2="100" y2="200">\n<stop offset="0%" style="stop-color:rgba(255,0,0,0);"/>\n<stop offset="100%" style="stop-color:green;"/>\n</linearGradient>\n';
const SVG_RADIAL =
'<radialGradient id="SVGID" gradientUnits="userSpaceOnUse" gradientTransform="matrix(1 0 0 1 -50 -50)" cx="100" cy="200" r="50" fx="0" fy="10">\n<stop offset="0%" style="stop-color:red;"/>\n<stop offset="100%" style="stop-color:rgba(0,255,0,0);"/>\n</radialGradient>\n';
const SVG_INTERNALRADIUS =
'<radialGradient id="SVGID" gradientUnits="userSpaceOnUse" gradientTransform="matrix(1 0 0 1 -50 -50)" cx="100" cy="200" r="50" fx="0" fy="10">\n<stop offset="20%" style="stop-color:red;"/>\n<stop offset="100%" style="stop-color:rgba(0,255,0,0);"/>\n</radialGradient>\n';
const SVG_SWAPPED =
'<radialGradient id="SVGID" gradientUnits="userSpaceOnUse" gradientTransform="matrix(1 0 0 1 -50 -50)" cx="0" cy="10" r="50" fx="100" fy="200">\n<stop offset="20%" style="stop-color:rgba(0,255,0,0);"/>\n<stop offset="100%" style="stop-color:red;"/>\n</radialGradient>\n';
const SVG_LINEAR_PERCENTAGE =
'<linearGradient id="SVGID" gradientUnits="objectBoundingBox" gradientTransform="matrix(1 0 0 1 0 0)" x1="0" y1="10" x2="100" y2="200">\n<stop offset="0%" style="stop-color:rgba(255,0,0,0);"/>\n<stop offset="100%" style="stop-color:green;"/>\n</linearGradient>\n';
const SVG_RADIAL_PERCENTAGE =
'<radialGradient id="SVGID" gradientUnits="objectBoundingBox" gradientTransform="matrix(1 0 0 1 0 0)" cx="100" cy="200" r="50" fx="0" fy="10">\n<stop offset="0%" style="stop-color:red;"/>\n<stop offset="100%" style="stop-color:rgba(0,255,0,0);"/>\n</radialGradient>\n';
describe('Gradient', () => {
function fromElement(
gradientDef: SVGGradientElement,
obj: FabricObject,
options: Partial<SVGOptions> = {},
) {
return Gradient.fromElement(gradientDef, obj, {
width: 0,
height: 0,
viewBoxHeight: 0,
viewBoxWidth: 0,
opacity: 1,
...options,
} as SVGOptions);
}
test('constructor linearGradient', () => {
const gradient = createLinearGradient();
expect(gradient instanceof Gradient).toBe(true);
});
test('constructor radialGradient', () => {
const gradient = createRadialGradient();
expect(gradient instanceof Gradient).toBe(true);
});
test('properties linearGradient', () => {
const gradient = createLinearGradient();
expect(gradient.coords.x1).toBe(0);
expect(gradient.coords.y1).toBe(10);
expect(gradient.coords.x2).toBe(100);
expect(gradient.coords.y2).toBe(200);
expect(gradient.type).toBe('linear');
expect(gradient.colorStops[0].offset).toBe(0);
expect(gradient.colorStops[0].color).toBe('rgba(255,0,0,0)');
expect(gradient.colorStops[1].offset).toBe(1);
expect(gradient.colorStops[1].color).toBe('green');
});
test('toSVG', () => {
const gradient = createLinearGradient();
expect(gradient.toSVG, 'toSVG function exists').toBeTruthy();
});
describe('SVG exports', () => {
test('toSVG linear', () => {
const gradient = createLinearGradient();
const baseObj = new FabricObject({ width: 100, height: 100 });
expect(gradient.toSVG(baseObj)).toEqualSVG(SVG_LINEAR);
});
test('toSVG radial', () => {
const gradient = createRadialGradient();
const baseObj = new FabricObject({ width: 100, height: 100 });
expect(gradient.toSVG(baseObj)).toEqualSVG(SVG_RADIAL);
});
test('toSVG linear sanitizes coord injection', () => {
const gradient = new Gradient({
type: 'linear',
coords: {
x1: '0" /><script>alert(1)</script>' as unknown as number,
y1: '0" /><script>alert(1)</script>' as unknown as number,
x2: '0" /><script>alert(1)</script>' as unknown as number,
y2: '0" /><script>alert(1)</script>' as unknown as number,
},
colorStops: [
{ offset: 0, color: 'rgba(255,0,0,0)' },
{ offset: 1, color: 'green' },
],
});
const baseObj = new FabricObject({ width: 100, height: 100 });
const svg = gradient.toSVG(baseObj);
expect(svg).not.toContain('<script>');
});
test('toSVG radial sanitizes coord injection', () => {
const gradient = new Gradient({
type: 'radial',
coords: {
x1: '0" /><script>alert(1)</script>' as unknown as number,
y1: '0" /><script>alert(1)</script>' as unknown as number,
x2: '0" /><script>alert(1)</script>' as unknown as number,
y2: '0" /><script>alert(1)</script>' as unknown as number,
r1: '0" /><script>alert(1)</script>' as unknown as number,
r2: '50" /><script>alert(1)</script>' as unknown as number,
},
colorStops: [
{ offset: 0, color: 'red' },
{ offset: 1, color: 'rgba(0,255,0,0)' },
],
});
const baseObj = new FabricObject({ width: 100, height: 100 });
const svg = gradient.toSVG(baseObj);
expect(svg).not.toContain('<script>');
});
test('toSVG radial with r1 > 0', () => {
const gradient = createRadialGradientWithInternalRadius();
const obj = new FabricObject({ width: 100, height: 100 });
expect(gradient.toSVG(obj)).toEqualSVG(SVG_INTERNALRADIUS);
});
test('toSVG radial with r1 > 0 swapped', () => {
const gradient = createRadialGradientSwapped();
const obj = new FabricObject({ width: 100, height: 100 });
const gradientColorStops = JSON.stringify(gradient.colorStops);
expect(gradient.toSVG(obj), 'it exports as expected').toEqualSVG(
SVG_SWAPPED,
);
const gradientColorStopsAfterExport = JSON.stringify(gradient.colorStops);
expect(
gradient.toSVG(obj),
'it exports as expected a second time',
).toEqualSVG(SVG_SWAPPED);
expect(gradientColorStops, 'colorstops do not change').toBe(
gradientColorStopsAfterExport,
);
});
test('toSVG linear objectBoundingBox', () => {
const gradient = createLinearGradient('percentage');
const obj = new FabricObject({ width: 100, height: 100 });
expect(gradient.toSVG(obj)).toEqualSVG(SVG_LINEAR_PERCENTAGE);
});
test('toSVG radial objectBoundingBox', () => {
const gradient = createRadialGradient('percentage');
const obj = new FabricObject({ width: 100, height: 100 });
expect(gradient.toSVG(obj)).toEqualSVG(SVG_RADIAL_PERCENTAGE);
});
});
test('properties radialGradient', () => {
const gradient = createRadialGradient();
expect(gradient.coords.x1).toBe(0);
expect(gradient.coords.y1).toBe(10);
expect(gradient.coords.x2).toBe(100);
expect(gradient.coords.y2).toBe(200);
expect(gradient.coords.r1).toBe(0);
expect(gradient.coords.r2).toBe(50);
expect(gradient.type, 'radial');
expect(gradient.colorStops[0].offset).toBe(0);
expect(gradient.colorStops[0].color).toBe('red');
expect(gradient.colorStops[1].offset).toBe(1);
expect(gradient.colorStops[1].color).toBe('rgba(0,255,0,0)');
});
test('toObject linearGradient', () => {
const gradient = createLinearGradient();
gradient.gradientTransform = [1, 0, 0, 1, 50, 50];
expect(typeof gradient.toObject === 'function');
const object = gradient.toObject();
expect(object.coords).toEqual(gradient.coords);
expect(object.coords, 'coords are not referenced').not.toBe(
gradient.coords,
);
expect(object.gradientUnits).toBe(gradient.gradientUnits);
expect(object.type).toBe(gradient.type);
expect(object.gradientTransform).toEqual(gradient.gradientTransform);
expect(object.gradientTransform, 'matrix is not referenced').not.toBe(
gradient.gradientTransform,
);
expect(object.colorStops).toEqual(gradient.colorStops);
expect(object.colorStops, 'colorStops are not referenced').not.toBe(
gradient.colorStops,
);
});
test('toObject with custom props', () => {
const gradient = createLinearGradient('pixels', 'myId');
const object = gradient.toObject(['id']) as { id: string };
expect(object.id).toMatch(/^myId_\d+$/);
});
test('toObject radialGradient', () => {
const gradient = createRadialGradient();
const object = gradient.toObject();
expect(object.coords).toEqual(gradient.coords);
expect(object.coords).not.toBe(gradient.coords);
expect(object.type).toBe(gradient.type);
expect(object.colorStops).not.toBe(gradient.colorStops);
expect(object.colorStops).toEqual(gradient.colorStops);
});
test('toLive linearGradient', () => {
const canvas = new StaticCanvas(undefined, {
enableRetinaScaling: false,
});
const gradient = createLinearGradient();
const gradientHTML = canvas.contextContainer.createLinearGradient(
0,
0,
1,
1,
);
expect(gradient.toLive).toBeTruthy();
const gradientCtx = gradient.toLive(canvas.contextContainer);
expect(gradientCtx.toString(), 'The type match').toEqual(
gradientHTML.toString(),
);
});
test('toLive radialGradient', () => {
const canvas = new StaticCanvas(undefined, {
enableRetinaScaling: false,
});
const gradient = createRadialGradient();
const gradientHTML = canvas.contextContainer.createRadialGradient(
0,
0,
1,
1,
2,
2,
);
const gradientCtx = gradient.toLive(canvas.contextContainer);
expect(gradientCtx.toString(), 'is a gradient for canvas radial').toEqual(
gradientHTML.toString(),
);
});
test('registered in class registry', () => {
expect(classRegistry.getClass('gradient')).toEqual(Gradient);
expect(classRegistry.getClass('linear')).toEqual(Gradient);
expect(classRegistry.getClass('radial')).toEqual(Gradient);
});
test('fromElement linearGradient', () => {
expect(typeof Gradient.fromElement === 'function').toBeTruthy();
const element = createSVGElement('linearGradient');
element.appendChild(
createSVGElement('stop', { offset: '0%', 'stop-color': 'white' }),
);
element.appendChild(
createSVGElement('stop', {
offset: '100%',
'stop-color': 'black',
'stop-opacity': 0,
}),
);
const object = new FabricObject({ width: 100, height: 100 });
const gradient = fromElement(element, object, { opacity: '' });
expect(gradient instanceof Gradient).toBeTruthy();
expect(gradient.type).toEqual('linear');
expect(gradient.coords.x1).toEqual(0);
expect(gradient.coords.y1).toEqual(0);
expect(gradient.coords.x2).toEqual(1);
expect(gradient.coords.y2).toEqual(0);
expect(gradient.gradientUnits).toEqual('percentage');
expect(gradient.colorStops[0].offset).toEqual(1);
expect(gradient.colorStops[1].offset).toEqual(0);
expect(gradient.colorStops[0].color).toEqual('rgba(0,0,0,0)');
expect(gradient.colorStops[1].color).toEqual('rgba(255,255,255,1)');
});
test('fromElement linearGradient with floats percentage - objectBoundingBox', () => {
const element = createSVGElement('linearGradient', {
gradientUnits: 'objectBoundingBox',
x1: '10%',
y1: '0.2%',
x2: 200,
y2: '20%',
});
element.appendChild(
createSVGElement('stop', { offset: '0%', 'stop-color': 'white' }),
);
element.appendChild(
createSVGElement('stop', {
offset: '100%',
'stop-color': 'black',
'stop-opacity': 0,
}),
);
const object = new FabricObject({ width: 200, height: 200 });
const gradient = fromElement(element, object, { opacity: '' });
expect(gradient instanceof Gradient).toBeTruthy();
expect(gradient.coords.x1).toEqual(0.1);
expect(gradient.coords.y1).toEqual(0.002);
expect(gradient.coords.x2).toEqual(200);
expect(gradient.coords.y2).toEqual(0.2);
expect(gradient.gradientUnits).toEqual('percentage');
});
test('fromElement linearGradient with floats percentage - userSpaceOnUse', () => {
const element = createSVGElement('linearGradient', {
gradientUnits: 'userSpaceOnUse',
x1: '10%',
y1: '0.2%',
x2: 200,
y2: '20%',
});
element.appendChild(
createSVGElement('stop', { offset: '0%', 'stop-color': 'white' }),
);
element.appendChild(
createSVGElement('stop', {
offset: '100%',
'stop-color': 'black',
'stop-opacity': 0,
}),
);
const object = new FabricObject({
left: 10,
top: 15,
width: 200,
height: 200,
});
const gradient = fromElement(element, object, {
opacity: '',
viewBoxWidth: 400,
viewBoxHeight: 300,
});
expect(gradient instanceof Gradient).toBeTruthy();
expect(gradient.gradientUnits).toEqual('pixels');
expect(gradient.offsetX).toEqual(-10);
expect(gradient.offsetY).toEqual(-15);
expect(gradient.coords.x1).toEqual(40);
expect(gradient.coords.y1).toEqual(0.6);
expect(gradient.coords.x2).toEqual(200);
expect(gradient.coords.y2).toEqual(60);
});
test('fromElement linearGradient with Infinity', () => {
const element = createSVGElement('linearGradient', {
x1: '-Infinity',
x2: 'Infinity',
y1: 'Infinity',
y2: '-Infinity',
});
element.appendChild(
createSVGElement('stop', { offset: '0%', 'stop-color': 'white' }),
);
element.appendChild(
createSVGElement('stop', {
offset: '100%',
'stop-color': 'black',
'stop-opacity': 0,
}),
);
const object = new FabricObject({
width: 100,
height: 300,
top: 20,
left: 30,
});
const gradient = fromElement(element, object, { opacity: '' });
expect(gradient instanceof Gradient).toBeTruthy();
expect(gradient.coords.x1).toEqual(0);
expect(gradient.coords.y1).toEqual(1);
expect(gradient.coords.x2).toEqual(1);
expect(gradient.coords.y2).toEqual(0);
expect(gradient.colorStops[0].offset).toEqual(1);
expect(gradient.colorStops[1].offset).toEqual(0);
expect(gradient.colorStops[0].color).toEqual('rgba(0,0,0,0)');
expect(gradient.colorStops[1].color).toEqual('rgba(255,255,255,1)');
});
test('fromElement without stop', () => {
const element = createSVGElement('linearGradient');
element.appendChild(createSVGElement('stop', { 'stop-color': 'white' }));
element.appendChild(
createSVGElement('stop', {
offset: '100%',
'stop-color': 'black',
'stop-opacity': 0,
}),
);
const object = new FabricObject({ width: 100, height: 100 });
const gradient = fromElement(element, object, { opacity: '' });
expect(gradient instanceof Gradient).toBeTruthy();
expect(gradient.colorStops[0].offset).toEqual(1);
expect(gradient.colorStops[1].offset).toEqual(0);
});
describe('fromElement with x1,x2,y1,2 linear', () => {
const element = createSVGElement('linearGradient', {
x1: '30%',
x2: '20%',
y1: '0.1',
y2: 'Infinity',
});
const object = new FabricObject({ width: 200, height: 200 });
const gradient = fromElement(element, object, { opacity: '' });
expect(gradient.coords.x1).toEqual(0.3);
expect(gradient.coords.y1).toEqual(0.1);
expect(gradient.coords.x2).toEqual(0.2);
expect(gradient.coords.y2).toEqual(1);
it('top and left do not change the output', () => {
const object = new FabricObject({
width: 200,
height: 200,
top: 50,
left: 10,
});
const gradient = fromElement(element, object, { opacity: '' });
expect(gradient.coords.x1).toEqual(0.3);
expect(gradient.coords.y1).toEqual(0.1);
expect(gradient.coords.x2).toEqual(0.2);
expect(gradient.coords.y2).toEqual(1);
});
});
describe('fromElement with x1,x2,y1,2 radial', () => {
const element = createSVGElement('radialGradient', {
fx: '30%',
fy: '20%',
cx: '0.1',
cy: 1,
r: '100%',
});
let object = new FabricObject({ width: 200, height: 200 });
let gradient = fromElement(element, object, { opacity: '' });
it('should not change with width height', () => {
const coords = gradient.coords as RadialGradientCoords<number>;
expect(coords.x1).toEqual(0.3);
expect(coords.y1).toEqual(0.2);
expect(coords.x2).toEqual(0.1);
expect(coords.y2).toEqual(1);
expect(coords.r1).toEqual(0);
expect(coords.r2).toEqual(1);
});
it('should not change with top left', () => {
object = new FabricObject({ width: 200, height: 200, top: 10, left: 10 });
gradient = fromElement(element, object, { opacity: '' });
const coords = gradient.coords as RadialGradientCoords<number>;
expect(coords.x1).toEqual(0.3);
expect(coords.y1).toEqual(0.2);
expect(coords.x2).toEqual(0.1);
expect(coords.y2).toEqual(1);
expect(coords.r1).toEqual(0);
expect(coords.r2).toEqual(1);
});
});
describe('fromElement with x1,x2,y1,2 radial userSpaceOnUse', () => {
const element = createSVGElement('radialGradient', {
fx: 30,
fy: 20,
cx: 15,
cy: 18,
r: 100,
gradientUnits: 'userSpaceOnUse',
});
it('should not change with width height', () => {
const object = new FabricObject({ width: 200, height: 200 });
const gradient = fromElement(element, object, { opacity: '' });
const coords = gradient.coords as RadialGradientCoords<number>;
expect(coords.x1).toEqual(30);
expect(coords.y1).toEqual(20);
expect(coords.x2).toEqual(15);
expect(coords.y2).toEqual(18);
expect(coords.r1).toEqual(0);
expect(coords.r2).toEqual(100);
});
it('should not change with top left', () => {
const object = new FabricObject({
width: 200,
height: 200,
top: 50,
left: 60,
});
const gradient = fromElement(element, object, { opacity: '' });
const coords = gradient.coords as RadialGradientCoords<number>;
expect(coords.x1).toEqual(30);
expect(coords.y1).toEqual(20);
expect(coords.x2).toEqual(15);
expect(coords.y2).toEqual(18);
expect(coords.r1).toEqual(0);
expect(coords.r2).toEqual(100);
});
});
describe('fromElement with x1,x2,y1,2 linear userSpaceOnUse', () => {
const element = createSVGElement('linearGradient', {
x1: 30,
y1: 20,
x2: 15,
y2: 18,
gradientUnits: 'userSpaceOnUse',
});
it('should not change with width height', () => {
const object = new FabricObject({ width: 200, height: 200 });
const gradient = fromElement(element, object, { opacity: '' });
expect(gradient.coords.x1).toEqual(30);
expect(gradient.coords.y1).toEqual(20);
expect(gradient.coords.x2).toEqual(15);
expect(gradient.coords.y2).toEqual(18);
});
it('should not change with top left', () => {
const object = new FabricObject({
width: 200,
height: 200,
top: 40,
left: 40,
});
const gradient = fromElement(element, object, { opacity: '' });
expect(gradient.coords.x1).toEqual(30);
expect(gradient.coords.y1).toEqual(20);
expect(gradient.coords.x2).toEqual(15);
expect(gradient.coords.y2).toEqual(18);
});
});
test('fromElement radialGradient defaults', () => {
const element = createSVGElement('radialGradient');
element.appendChild(
createSVGElement('stop', { offset: '0%', 'stop-color': 'white' }),
);
element.appendChild(
createSVGElement('stop', { offset: '100%', 'stop-color': 'black' }),
);
const object = new FabricObject({ width: 100, height: 100 });
const gradient = fromElement(element, object, {}) as Gradient<'radial'>;
expect(gradient instanceof Gradient).toBeTruthy();
expect(gradient.coords.x1).toEqual(0.5);
expect(gradient.coords.y1).toEqual(0.5);
expect(gradient.coords.x2).toEqual(0.5);
expect(gradient.coords.y2).toEqual(0.5);
expect(gradient.coords.r1).toEqual(0);
expect(gradient.coords.r2).toEqual(0.5);
expect(gradient.colorStops[0].offset).toEqual(1);
expect(gradient.colorStops[1].offset).toEqual(0);
expect(gradient.colorStops[0].color).toEqual('rgba(0,0,0,1)');
expect(gradient.colorStops[1].color).toEqual('rgba(255,255,255,1)');
});
test('fromElement radialGradient with transform', () => {
const element = createSVGElement('radialGradient', {
gradientTransform:
'matrix(3.321 -0.6998 0.4077 1.9347 -440.9168 -408.0598)',
});
element.appendChild(
createSVGElement('stop', { offset: '0%', 'stop-color': 'white' }),
);
element.appendChild(
createSVGElement('stop', { offset: '100%', 'stop-color': 'black' }),
);
const object = new FabricObject({ width: 100, height: 100 });
const gradient = fromElement(element, object, {});
expect(gradient.gradientTransform).toEqual([
3.321, -0.6998, 0.4077, 1.9347, -440.9168, -408.0598,
]);
});
test('fromElement linearGradient colorStop attributes/styles', () => {
const stop1 = createSVGElement('stop', {
offset: '0%',
'stop-color': '',
'stop-opacity': '',
});
const stop2 = createSVGElement('stop', {
offset: '0.5',
style: 'stop-color: black; stop-opacity:;',
'stop-color': 'white',
});
const stop3 = createSVGElement('stop', {
offset: '75%',
style: 'stop-color:; stop-opacity:;',
'stop-opacity': '0.9',
'stop-color': 'blue',
});
const stop4 = createSVGElement('stop', {
offset: '100%',
style: 'stop-color: red; stop-opacity: 0.5;',
'stop-opacity': '0.9',
});
const element = createSVGElement('linearGradient');
element.appendChild(stop1);
element.appendChild(stop2);
element.appendChild(stop3);
element.appendChild(stop4);
const object = new FabricObject({ width: 100, height: 100 });
const gradient = fromElement(element, object, { opacity: '' });
expect(gradient instanceof Gradient).toBeTruthy();
expect(gradient.coords.x1).toEqual(0);
expect(gradient.coords.y1).toEqual(0);
expect(gradient.coords.x2).toEqual(1);
expect(gradient.coords.y2).toEqual(0);
expect(gradient.colorStops[0].offset).toEqual(1);
expect(gradient.colorStops[1].offset).toEqual(0.75);
expect(gradient.colorStops[2].offset).toEqual(0.5);
expect(gradient.colorStops[3].offset).toEqual(0);
expect(gradient.colorStops[0].color).toEqual('rgba(255,0,0,0.5)');
expect(gradient.colorStops[1].color).toEqual('rgba(0,0,255,0.9)');
expect(gradient.colorStops[2].color).toEqual('rgba(0,0,0,1)');
expect(gradient.colorStops[3].color).toEqual('rgba(0,0,0,1)');
});
test('fromElement radialGradient colorStop attributes/styles', () => {
const stop1 = createSVGElement('stop', {
offset: '0%',
'stop-color': '',
'stop-opacity': '',
});
const stop2 = createSVGElement('stop', {
offset: '0.5',
style: 'stop-color: black; stop-opacity:;',
'stop-color': 'white',
});
const stop3 = createSVGElement('stop', {
offset: '75%',
style: 'stop-color:; stop-opacity:;',
'stop-opacity': '0.9',
'stop-color': 'blue',
});
const stop4 = createSVGElement('stop', {
offset: '100%',
style: 'stop-color: red; stop-opacity: 0.5;',
'stop-opacity': '0.9',
});
const element = createSVGElement('radialGradient');
element.appendChild(stop1);
element.appendChild(stop2);
element.appendChild(stop3);
element.appendChild(stop4);
const object = new FabricObject({ width: 100, height: 100 });
const gradient = fromElement(element, object, { opacity: '' });
expect(gradient instanceof Gradient).toBeTruthy();
expect(gradient.colorStops[0].offset).toEqual(1);
expect(gradient.colorStops[1].offset).toEqual(0.75);
expect(gradient.colorStops[2].offset).toEqual(0.5);
expect(gradient.colorStops[3].offset).toEqual(0);
expect(gradient.colorStops[0].color).toEqual('rgba(255,0,0,0.5)');
expect(gradient.colorStops[1].color).toEqual('rgba(0,0,255,0.9)');
expect(gradient.colorStops[2].color).toEqual('rgba(0,0,0,1)');
expect(gradient.colorStops[3].color).toEqual('rgba(0,0,0,1)');
});
describe('Attrivbute injection', () => {
it('id injection', () => {
const gradient = new Gradient({ id: 'malicious"><script>' });
const svg = gradient.toSVG({} as any);
expect(svg).toContain('id="SVGID_malicious"><script>');
});
});
});