Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
30 changes: 14 additions & 16 deletions packages/core/src/plugins/with-selection.spec.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { fakeAsync, tick } from '@angular/core/testing';
import { PlaitBoard, PlaitElement, PlaitPointerType } from '../interfaces';
import { clearBoardElementHost, clearNodeWeakMap, createTestingBoard, fakeBoardElementHost, fakeNodeWeakMap } from '../testing';
import { setupTestingBoard, TestingBoardFixture } from '../testing';
import { Transforms } from '../transforms';
import { cacheSelectedElements, createG, getSelectedElements } from '../utils';
import { withOptions } from './with-options';
Expand All @@ -12,31 +12,29 @@ const children: PlaitElement[] = [
];

describe('withSelection', () => {
let board: PlaitBoard;
let activeHost: SVGGElement;
let drawSelectionRectangle: jasmine.Spy;
let fixture: TestingBoardFixture;

beforeEach(() => {
board = createTestingBoard([withOptions, withSelection], children);
fakeNodeWeakMap(board);
activeHost = fakeBoardElementHost(board).activeHost;
fixture = setupTestingBoard([withOptions, withSelection], children);
activeHost = PlaitBoard.getActiveHost(fixture.board);
drawSelectionRectangle = jasmine.createSpy('drawSelectionRectangle').and.callFake(() => createG());
board.drawSelectionRectangle = drawSelectionRectangle;
fixture.board.drawSelectionRectangle = drawSelectionRectangle;
});

afterEach(() => {
clearNodeWeakMap(board);
clearBoardElementHost(board);
fixture.destroy();
});

it('should refresh the multi-selection rectangle after viewport changes in hand mode', fakeAsync(() => {
cacheSelectedElements(board, children);
board.afterChange();
cacheSelectedElements(fixture.board, children);
fixture.board.afterChange();
const initialRectangle = activeHost.firstElementChild;
expect(activeHost.contains(initialRectangle)).toBeTrue();

board.pointer = PlaitPointerType.hand;
Transforms.setViewport(board, { zoom: 1, origination: [10, 10] });
fixture.board.pointer = PlaitPointerType.hand;
Transforms.setViewport(fixture.board, { zoom: 1, origination: [10, 10] });
tick();

expect(drawSelectionRectangle).toHaveBeenCalledTimes(2);
Expand All @@ -46,15 +44,15 @@ describe('withSelection', () => {
}));

it('should not update selected elements from selection operations in hand mode', fakeAsync(() => {
cacheSelectedElements(board, children);
board.pointer = PlaitPointerType.hand;
cacheSelectedElements(fixture.board, children);
fixture.board.pointer = PlaitPointerType.hand;

Transforms.setSelection(board, {
Transforms.setSelection(fixture.board, {
anchor: [100, 100],
focus: [100, 100]
});
tick();

expect(getSelectedElements(board)).toEqual(children);
expect(getSelectedElements(fixture.board)).toEqual(children);
}));
});
20 changes: 0 additions & 20 deletions packages/core/src/testing/core/create-board.ts

This file was deleted.

40 changes: 0 additions & 40 deletions packages/core/src/testing/core/fake-weak-map.ts

This file was deleted.

2 changes: 0 additions & 2 deletions packages/core/src/testing/core/index.ts

This file was deleted.

3 changes: 3 additions & 0 deletions packages/core/src/testing/fixture/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
export * from './mock-board';
export * from './mock-host';
export * from './mock-weak-map';
104 changes: 104 additions & 0 deletions packages/core/src/testing/fixture/mock-board.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
import { PlaitBoard, PlaitBoardOptions, PlaitElement, PlaitPlugin } from '../../interfaces';
import { createBoard } from '../../plugins/create-board';
import { cacheSelectedElements, KEY_TO_ELEMENT_MAP } from '../../utils';
import { IS_BOARD_ALIVE } from '../../utils/weak-maps';
import {
clearBoardElementHost,
clearBoardHost,
clearBoardRoughSVG,
fakeBoardElementHost,
fakeBoardHost,
fakeBoardRoughSVG
} from './mock-host';
import { clearNodeWeakMap, clearNodeWeakMapByNodes, fakeNodeWeakMap } from './mock-weak-map';

export interface TestingBoardFixture {
board: PlaitBoard;
destroy: () => void;
}

/**
* 1.create board instance
* 2.build fake node weak map
*/
export const createTestingBoard = (
plugins: PlaitPlugin[],
children: PlaitElement[],
options: PlaitBoardOptions = { readonly: false, hideScrollbar: true, disabledScrollOnNonFocus: false }
) => {
let board = createBoard(children, options);
plugins.forEach((plugin) => {
board = plugin(board);
});
KEY_TO_ELEMENT_MAP.set(board, new Map());
return board;
};

export interface SetupTestingBoardOptions {
boardOptions?: PlaitBoardOptions;
selectedElements?: any[];
withNodeWeakMap?: boolean;
withElementHost?: boolean;
withHost?: boolean;
withRoughSVG?: boolean;
alive?: boolean;
}

export const setupTestingBoard = (
plugins: PlaitPlugin[],
children: PlaitElement[],
options: SetupTestingBoardOptions = {}
): TestingBoardFixture => {
const {
boardOptions,
selectedElements,
withNodeWeakMap = true,
withElementHost = true,
withHost = true,
withRoughSVG = false,
alive = true
} = options;

const board = createTestingBoard(plugins as PlaitPlugin[], children as PlaitElement[], boardOptions);
const initialChildren = board.children;

if (withNodeWeakMap) {
fakeNodeWeakMap(board);
}
if (withElementHost) {
fakeBoardElementHost(board);
}
if (withHost) {
fakeBoardHost(board);
}
if (withRoughSVG) {
fakeBoardRoughSVG(board);
}
if (selectedElements) {
cacheSelectedElements(board, selectedElements as PlaitElement[]);
}
if (alive) {
IS_BOARD_ALIVE.set(board, true);
}

const destroy = () => {
if (withNodeWeakMap) {
clearNodeWeakMapByNodes(initialChildren);
clearNodeWeakMap(board);
}
if (withElementHost) {
clearBoardElementHost(board);
}
if (withHost) {
clearBoardHost(board);
}
if (withRoughSVG) {
clearBoardRoughSVG(board);
}
if (alive) {
IS_BOARD_ALIVE.delete(board);
}
};

return { board, destroy };
};
70 changes: 70 additions & 0 deletions packages/core/src/testing/fixture/mock-host.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
import { RoughSVG } from 'roughjs/bin/svg';
import { PlaitBoard } from '../../interfaces/board';
import { createG, createSVG } from '../../utils/dom/common';
import { BOARD_TO_ELEMENT_HOST, BOARD_TO_HOST, BOARD_TO_ROUGH_SVG } from '../../utils/weak-maps';

export const fakeBoardElementHost = (board: PlaitBoard) => {
const elementHost = {
lowerHost: createG(),
host: createG(),
upperHost: createG(),
topHost: createG(),
activeHost: createG(),
container: document.createElement('div'),
viewportContainer: document.createElement('div')
};
BOARD_TO_ELEMENT_HOST.set(board, elementHost);
return elementHost;
};

export const clearBoardElementHost = (board: PlaitBoard) => {
BOARD_TO_ELEMENT_HOST.delete(board);
};

export const fakeBoardHost = (board: PlaitBoard) => {
const host = createSVG();
host.setAttribute('viewBox', '0 0 1000 1000');
Object.defineProperty(host, 'getBoundingClientRect', {
value: () =>
({
x: 0,
y: 0,
width: 1000,
height: 1000,
top: 0,
right: 1000,
bottom: 1000,
left: 0
} as DOMRect)
});
BOARD_TO_HOST.set(board as PlaitBoard, host);
return host;
};

export const clearBoardHost = (board: PlaitBoard) => {
BOARD_TO_HOST.delete(board);
};

export const createTestingRoughSVG = () => {
const roughSVG = {
path: () => createG(),
polygon: () => createG(),
circle: () => createG(),
ellipse: () => createG(),
line: () => createG(),
curve: () => createG(),
rectangle: () => createG(),
linearPath: () => createG()
};
return roughSVG as unknown as RoughSVG;

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[P2] This object is cast to the full RoughSVG interface, but it is missing methods already used by repository drawing paths, including line, curve, and ellipse. A fixture exercising those paths will fail at runtime. Could we implement the used RoughSVG surface here, or narrow the declared mock type?

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in 2e45e4e: the shared RoughSVG fixture now implements the repository-used ellipse, line, and curve methods in addition to the existing surface, with fixture coverage.

};

export const fakeBoardRoughSVG = (board: PlaitBoard) => {
const roughSVG = createTestingRoughSVG();
BOARD_TO_ROUGH_SVG.set(board, roughSVG);
return roughSVG;
};

export const clearBoardRoughSVG = (board: PlaitBoard) => {
BOARD_TO_ROUGH_SVG.delete(board);
};
25 changes: 25 additions & 0 deletions packages/core/src/testing/fixture/mock-weak-map.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import { PlaitBoard } from '../../interfaces/board';
import { PlaitNode } from '../../interfaces/node';
import { NODE_TO_INDEX, NODE_TO_PARENT } from '../../utils/weak-maps';

export const fakeNodeWeakMap = (object: PlaitNode | PlaitBoard) => {
const children = object.children || [];
children.forEach((value, index: number) => {
NODE_TO_PARENT.set(value, object);
NODE_TO_INDEX.set(value, index);
fakeNodeWeakMap(value);
});
};

export const clearNodeWeakMap = (object: PlaitNode | PlaitBoard) => {
const children = object.children || [];
clearNodeWeakMapByNodes(children);
};

export const clearNodeWeakMapByNodes = (nodes: PlaitNode[]) => {
nodes.forEach((value) => {
NODE_TO_PARENT.delete(value);
NODE_TO_INDEX.delete(value);
clearNodeWeakMap(value);
});
};
4 changes: 2 additions & 2 deletions packages/core/src/testing/index.ts
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
export * from './core';
export * from './fake-events';
export * from './fixture';
export * from './mock-events';
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
*/

import { IS_MAC } from '../../utils/dom/environment';
import { ModifierKeys } from '../test-element';
import { ModifierKeys } from './test-element';

/** Used to generate unique IDs for events. */
let uniqueIds = 0;
Expand Down
Loading
Loading