fabric
Version:
Object model for HTML5 canvas, and SVG-to-canvas parser. Backed by jsdom and node-canvas.
188 lines (179 loc) • 6.64 kB
text/typescript
import type { TModificationEvents } from '../EventTypeDefs';
import { ActiveSelection } from '../shapes/ActiveSelection';
import { Group } from '../shapes/Group';
import { FabricObject } from '../shapes/Object/FabricObject';
import { ActiveSelectionLayoutManager } from './ActiveSelectionLayoutManager';
describe('ActiveSelectionLayoutManager', () => {
describe('onBeforeLayout', () => {
describe('triggers', () => {
const triggers: ('modified' | TModificationEvents | 'changed')[] = [
'modified',
'moving',
'resizing',
'rotating',
'scaling',
'skewing',
'changed',
'modifyPoly',
'modifyPath',
];
it('should subscribe activeSelection that contains object', () => {
const manager = new ActiveSelectionLayoutManager();
const object = new FabricObject();
new Group([object], {
interactive: true,
subTargetCheck: true,
});
const as = new ActiveSelection([object], { layoutManager: manager });
const objectOn = jest.spyOn(object, 'on');
const objectOff = jest.spyOn(object, 'off');
const asOn = jest.spyOn(as, 'on');
manager.subscribeTargets({
targets: [object],
target: as,
});
expect(objectOn).not.toHaveBeenCalled();
expect(objectOff).not.toHaveBeenCalled();
expect(asOn).toHaveBeenCalledTimes(triggers.length);
expect(objectOff).not.toHaveBeenCalled();
});
it('a subscribed activeSelection should trigger layout on the object parent once per parent', () => {
const object = new FabricObject();
const object2 = new FabricObject();
const object3 = new FabricObject();
const object4 = new FabricObject();
const group = new Group([object, object2], {
interactive: true,
subTargetCheck: true,
});
const group2 = new Group([object3, object4], {
interactive: true,
subTargetCheck: true,
});
const as = new ActiveSelection([object, object2, object3, object4]);
const asPerformLayout = jest.spyOn(as.layoutManager, 'performLayout');
const groupPerformLayout = jest.spyOn(
group.layoutManager,
'performLayout',
);
const groupPerformLayout2 = jest.spyOn(
group2.layoutManager,
'performLayout',
);
groupPerformLayout.mockClear();
groupPerformLayout2.mockClear();
asPerformLayout.mockClear();
// group have registered the AS
expect(group.layoutManager['_subscriptions'].get(as)).toBeDefined();
expect(group2.layoutManager['_subscriptions'].get(as)).toBeDefined();
// the AS has registered nothing
expect(as.layoutManager['_subscriptions'].size).toBe(0);
const event = { foo: 'bar' };
triggers.forEach((trigger) => as.fire(trigger, event));
// the as does not need to perform a layout and indeed is not called
expect(asPerformLayout).not.toHaveBeenCalled();
expect(groupPerformLayout.mock.calls).toMatchObject([
[
{
e: event,
target: group,
trigger: 'modified',
type: 'object_modified',
},
],
...triggers.slice(1).map((trigger) => [
{
e: event,
target: group,
trigger,
type: 'object_modifying',
},
]),
]);
expect(groupPerformLayout2.mock.calls).toMatchObject([
[
{
e: event,
target: group2,
trigger: 'modified',
type: 'object_modified',
},
],
...triggers.slice(1).map((trigger) => [
{
e: event,
target: group2,
trigger,
type: 'object_modifying',
},
]),
]);
expect(groupPerformLayout).toHaveBeenCalledTimes(triggers.length);
expect(groupPerformLayout2).toHaveBeenCalledTimes(triggers.length);
// we remove a single object, but both group and group2 have multiple object in the as
// so with this removal nothing changes.
as.remove(object);
groupPerformLayout.mockClear();
groupPerformLayout2.mockClear();
asPerformLayout.mockClear();
triggers.forEach((trigger) => as.fire(trigger, event));
expect(asPerformLayout).not.toHaveBeenCalled();
expect(groupPerformLayout.mock.calls).toMatchObject([
[
{
e: event,
target: group,
trigger: 'modified',
type: 'object_modified',
},
],
...triggers.slice(1).map((trigger) => [
{
e: event,
target: group,
trigger,
type: 'object_modifying',
},
]),
]);
expect(groupPerformLayout2.mock.calls).toMatchObject([
[
{
e: event,
target: group2,
trigger: 'modified',
type: 'object_modified',
},
],
...triggers.slice(1).map((trigger) => [
{
e: event,
target: group2,
trigger,
type: 'object_modifying',
},
]),
]);
expect(groupPerformLayout).toHaveBeenCalledTimes(triggers.length);
expect(groupPerformLayout2).toHaveBeenCalledTimes(triggers.length);
groupPerformLayout.mockClear();
groupPerformLayout2.mockClear();
asPerformLayout.mockClear();
// now that we deselect object 2, the first group is out from the AS
as.remove(object2);
expect(group.layoutManager['_subscriptions'].get(as)).toBeUndefined();
expect(group2.layoutManager['_subscriptions'].get(as)).toBeDefined();
// now the active selection is empty and none of the groups is affected by the AS events
as.removeAll();
expect(group2.layoutManager['_subscriptions'].get(as)).toBeUndefined();
groupPerformLayout.mockClear();
groupPerformLayout2.mockClear();
asPerformLayout.mockClear();
triggers.forEach((trigger) => as.fire(trigger, event));
expect(groupPerformLayout).not.toHaveBeenCalled();
expect(groupPerformLayout2).not.toHaveBeenCalled();
expect(asPerformLayout).not.toHaveBeenCalled();
});
});
});
});