UNPKG

fabric

Version:

Object model for HTML5 canvas, and SVG-to-canvas parser. Backed by jsdom and node-canvas.

368 lines (348 loc) 10.3 kB
import { Canvas } from '../../canvas/Canvas'; import { ActiveSelection } from '../ActiveSelection'; import { Group } from '../Group'; import { FabricObject } from './FabricObject'; class TestObject extends FabricObject { id: string; constructor({ id }: { id: string }) { super(); this.id = id; } } class TestCollection extends Group { id: string; constructor({ id }: { id: string }) { super(); this.id = id; } } class TestCanvas extends Canvas { id: string; constructor({ id }: { id: string }) { super(); this.id = id; } } function prepareObjectsForTreeTesting() { return { object: new TestObject({ id: 'object' }), other: new TestObject({ id: 'other' }), a: new TestCollection({ id: 'a' }), b: new TestCollection({ id: 'b' }), c: new TestCollection({ id: 'c' }), canvas: new TestCanvas({ id: 'canvas' }), }; } describe('FabricObject stacking', () => { test('isDescendantOf', function () { const canvas = new Canvas(); const object = new FabricObject(); const parent = new Group([]); expect(typeof object.isDescendantOf === 'function').toBe(true); parent.canvas = canvas; object.parent = parent; expect(object.isDescendantOf(parent)).toBe(true); object.parent = new Group(); object.parent.parent = parent; expect(object.isDescendantOf(parent)).toBe(true); object.parent = undefined; expect(object.isDescendantOf(parent) === false).toBe(true); object.canvas = canvas; expect(object.isDescendantOf(object) === false).toBe(true); object.parent = parent; const activeSelection = new ActiveSelection([object], { canvas }); expect(object.group).toEqual(activeSelection); expect(object.parent).toEqual(parent); expect(object.canvas).toEqual(canvas); expect(object.isDescendantOf(parent)); expect(object.isDescendantOf(activeSelection)).toBe(true); delete object.parent; expect(!object.isDescendantOf(parent)); expect(object.isDescendantOf(activeSelection)).toBe(true); }); test('getAncestors return type', () => { const object = new FabricObject(); const parents: Group[] = object.getAncestors(); const isGroup = (a: unknown): a is Group => a instanceof Group; const ancestors = object.getAncestors(); const parentAncestors: Group[] = ancestors.filter(isGroup); expect(parents).toBeDefined(); expect(parentAncestors).toBeDefined(); }); test('getAncestors', () => { const canvas = new Canvas(); const object = new FabricObject(); const parent = new Group([]); const other = new Group(); expect(object.getAncestors()).toEqual([]); object.parent = parent; expect(object.getAncestors()).toEqual([parent]); expect<Group[]>(object.getAncestors()).toEqual([parent]); parent.canvas = canvas; expect(object.getAncestors()).toEqual([parent]); parent.parent = other; expect(object.getAncestors()).toEqual([parent, other]); other.canvas = canvas; expect(object.getAncestors()).toEqual([parent, other]); delete object.parent; expect(object.getAncestors()).toEqual([]); }); describe('findCommonAncestors', () => { const getId = (obj: unknown) => (obj as TestObject | TestCollection | TestCanvas).id; function findCommonAncestors( object: TestObject, other: TestObject, expected: ReturnType<typeof FabricObject.prototype.findCommonAncestors>, ) { const common = object.findCommonAncestors(other); expect(common.fork.map(getId)).toEqual(expected.fork.map(getId)); expect(common.otherFork.map(getId)).toEqual( expected.otherFork.map(getId), ); expect(common.common.map(getId)).toEqual(expected.common.map(getId)); const oppositeCommon = other.findCommonAncestors(object); expect(oppositeCommon.fork.map(getId)).toEqual( expected.otherFork.map(getId), ); expect(oppositeCommon.otherFork.map(getId)).toEqual( expected.fork.map(getId), ); expect(oppositeCommon.common.map(getId)).toEqual( expected.common.map(getId), ); } const { object, other, a, b, c, canvas } = prepareObjectsForTreeTesting(); it('should be a function', () => { expect(typeof object.findCommonAncestors).toBe('function'); }); it('_objects should be an array', () => { expect(Array.isArray(a._objects)).toBe(true); }); it('_objects should be different', () => { expect(a._objects).not.toBe(b._objects); }); // same object findCommonAncestors(object, object, { fork: [], otherFork: [], common: [object], }); // foreign objects findCommonAncestors(object, other, { fork: [object], otherFork: [other], common: [], }); // same level a.add(object, other); findCommonAncestors(object, other, { fork: [object], otherFork: [other], common: [a], }); findCommonAncestors(object, a, { fork: [object], otherFork: [], common: [a], }); findCommonAncestors(other, a, { fork: [other], otherFork: [], common: [a], }); findCommonAncestors(a, object, { fork: [], otherFork: [object], common: [a], }); findCommonAncestors(a, object, { fork: [], otherFork: [object], common: [a], }); // different level a.remove(object); b.add(object); a.add(b); findCommonAncestors(object, b, { fork: [object], otherFork: [], common: [b, a], }); findCommonAncestors(b, a, { fork: [b], otherFork: [], common: [a] }); findCommonAncestors(object, other, { fork: [object, b], otherFork: [other], common: [a], }); // with common ancestor expect(c.size()).toBe(0); c.add(a); expect(c.size()).toBe(1); findCommonAncestors(object, b, { fork: [object], otherFork: [], common: [b, a, c], }); findCommonAncestors(b, a, { fork: [b], otherFork: [], common: [a, c], }); findCommonAncestors(object, other, { fork: [object, b], otherFork: [other], common: [a, c], }); findCommonAncestors(object, c, { fork: [object, b, a], otherFork: [], common: [c], }); findCommonAncestors(other, c, { fork: [other, a], otherFork: [], common: [c], }); findCommonAncestors(b, c, { fork: [b, a], otherFork: [], common: [c], }); findCommonAncestors(a, c, { fork: [a], otherFork: [], common: [c] }); // deeper asymmetrical c.removeAll(); expect(c.size()).toBe(0); a.remove(other); c.add(other, a); findCommonAncestors(object, b, { fork: [object], otherFork: [], common: [b, a, c], }); findCommonAncestors(b, a, { fork: [b], otherFork: [], common: [a, c], }); findCommonAncestors(a, other, { fork: [a], otherFork: [other], common: [c], }); findCommonAncestors(object, other, { fork: [object, b, a], otherFork: [other], common: [c], }); findCommonAncestors(object, c, { fork: [object, b, a], otherFork: [], common: [c], }); findCommonAncestors(other, c, { fork: [other], otherFork: [], common: [c], }); findCommonAncestors(b, c, { fork: [b, a], otherFork: [], common: [c], }); findCommonAncestors(a, c, { fork: [a], otherFork: [], common: [c], }); // with canvas a.removeAll(); b.removeAll(); c.removeAll(); canvas.add(object, other); findCommonAncestors(object, other, { fork: [object], otherFork: [other], common: [], }); findCommonAncestors(object, other, { fork: [object], otherFork: [other], common: [], }); }); test('isInFrontOf', () => { const isInFrontOf = ( object: FabricObject, other: FabricObject, expected: boolean | undefined, ) => { const actual = object.isInFrontOf(other); expect(actual).toBe(expected); if (actual === expected && typeof expected === 'boolean') { const actual2 = other.isInFrontOf(object); expect(!expected).toBe(actual2); } }; const { object, other, a, b, c, canvas } = prepareObjectsForTreeTesting(); expect(typeof object.isInFrontOf).toBe('function'); expect(Array.isArray(a._objects)).toBe(true); expect(a._objects !== b._objects).toBe(true); // same object isInFrontOf(object, object, undefined); // foreign objects isInFrontOf(object, other, undefined); // same level a.add(object, other); isInFrontOf(object, other, false); isInFrontOf(object, a, true); isInFrontOf(other, a, true); // different level a.remove(object); b.add(object); a.add(b); isInFrontOf(object, b, true); isInFrontOf(b, a, true); isInFrontOf(object, other, true); // with common ancestor expect(c.size()).toBe(0); // 'c should be empty' c.add(a); expect(c.size()).toBe(1); // 'c should contain a' isInFrontOf(object, b, true); isInFrontOf(b, a, true); isInFrontOf(object, other, true); isInFrontOf(object, c, true); isInFrontOf(other, c, true); isInFrontOf(b, c, true); isInFrontOf(a, c, true); // deeper asymmetrical c.removeAll(); expect(c.size()).toBe(0); a.remove(other); c.add(other, a); isInFrontOf(object, b, true); isInFrontOf(b, a, true); isInFrontOf(a, other, true); isInFrontOf(object, other, true); isInFrontOf(object, c, true); isInFrontOf(other, c, true); isInFrontOf(b, c, true); isInFrontOf(a, c, true); // with canvas a.removeAll(); b.removeAll(); c.removeAll(); canvas.add(object, other); isInFrontOf(object, other, false); // parent precedes canvas when checking ancestor a.add(object); expect(other.canvas).toBe(canvas); expect(object.canvas).toBe(undefined); // because a is not on canvas isInFrontOf(object, other, undefined); canvas.insertAt(0, a); isInFrontOf(object, other, false); isInFrontOf(a, other, false); expect(object.canvas).toBe(canvas); // because a is now on a canvas }); });