UNPKG

sprotty

Version:

A next-gen framework for graphical views

173 lines (149 loc) 6.31 kB
/******************************************************************************** * Copyright (c) 2017-2018 TypeFox and others. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v. 2.0 which is available at * http://www.eclipse.org/legal/epl-2.0. * * This Source Code may also be made available under the following Secondary * Licenses when the conditions for such availability set forth in the Eclipse * Public License v. 2.0 are satisfied: GNU General Public License, version 2 * with the GNU Classpath Exception which is available at * https://www.gnu.org/software/classpath/license.html. * * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 ********************************************************************************/ import 'reflect-metadata'; import { expect, describe, it } from 'vitest'; import { Container } from 'inversify'; import { TYPES } from '../../base/types'; import { ConsoleLogger } from '../../utils/logging'; import { CommandExecutionContext } from '../../base/commands/command'; import { SModelRootImpl } from '../../base/model/smodel'; import { SGraphImpl, SNodeImpl } from '../../graph/sgraph'; import { AnimationFrameSyncer } from '../../base/animations/animation-frame-syncer'; import { ElementMove, MoveCommand } from './move'; import defaultModule from '../../base/di.config'; import { MoveAction, Point } from 'sprotty-protocol'; import { IModelFactory } from '../../base/model/smodel-factory'; import { registerModelElement } from '../../base/model/smodel-utils'; describe('move', () => { const container = new Container(); container.load(defaultModule); registerModelElement(container, 'graph', SGraphImpl); registerModelElement(container, 'node:circle', SNodeImpl); const graphFactory = container.get<IModelFactory>(TYPES.IModelFactory); const pointNW: Point = { x: 0, y: 0 }; const pointNE: Point = { x: 300, y: 1 }; const pointSW: Point = { x: 1, y: 300 }; const pointSE: Point = { x: 301, y: 301 }; // nodes start at pointNW const myNode0 = { id: 'node0', type: 'node:circle', x: pointNW.x, y: pointNW.y, selected: false }; const myNode1 = { id: 'node1', type: 'node:circle', x: pointNW.x, y: pointNW.y, selected: false }; const myNode2 = { id: 'node2', type: 'node:circle', x: pointNW.x, y: pointNW.y, selected: false }; // setup the GModel const model = graphFactory.createRoot({ id: 'model1', type: 'graph', children: [myNode0, myNode1, myNode2] }); // move each node to a different corner const moves: ElementMove[] = [ { elementId: myNode0.id, toPosition: { x: pointNE.x, y: pointNE.y } }, { elementId: myNode1.id, toPosition: { x: pointSW.x, y: pointSW.y } }, { elementId: myNode2.id, toPosition: { x: pointSE.x, y: pointSE.y } } ]; // create the action const moveAction = MoveAction.create(moves, { animate: false }); // create the command const cmd = new MoveCommand(moveAction); const context: CommandExecutionContext = { root: model, modelFactory: graphFactory, duration: 0, modelChanged: undefined!, logger: new ConsoleLogger(), syncer: new AnimationFrameSyncer() }; // global so we can carry-over the model, as it's updated, // from test case to test case (i,e, select, undo, redo, merge) let newModel: SModelRootImpl; function getNode(nodeId: string, root: SModelRootImpl) { return root.index.getById(nodeId) as SNodeImpl; } it('execute() works as expected', () => { // execute command newModel = cmd.execute(context) as SModelRootImpl; // node0 => PointNE expect(pointNE.x).equals(getNode('node0', newModel).bounds.x); expect(pointNE.y).equals(getNode('node0', newModel).bounds.y); // node1 => pointSW expect(pointSW.x).equals(getNode('node1', newModel).bounds.x); expect(pointSW.y).equals(getNode('node1', newModel).bounds.y); // node2 => PointSE expect(pointSE.x).equals(getNode('node2', newModel).bounds.x); expect(pointSE.y).equals(getNode('node2', newModel).bounds.y); }); // TODO: undo, redo, merge // note: not sure how to deal with promise returned by undo() // and redo()... // Should undo()/redo() check whether the move action wants // animation, and if not just return an updated model? let undoneModel: SModelRootImpl; it('undo() works as expected', async () => { // test 'undo' context.root = newModel; undoneModel = await cmd.undo(context); // confirm that each node is back at original // coordinates // node0, node1 and node2 => pointNW expect(pointNW.x).equals(getNode('node0', undoneModel).bounds.x); expect(pointNW.y).equals(getNode('node0', undoneModel).bounds.y); expect(pointNW.x).equals(getNode('node1', undoneModel).bounds.x); expect(pointNW.y).equals(getNode('node1', undoneModel).bounds.y); expect(pointNW.x).equals(getNode('node2', undoneModel).bounds.x); expect(pointNW.y).equals(getNode('node2', undoneModel).bounds.y); }); it('redo() works as expected', async () => { // test 'redo': context.root = undoneModel; const redoneModel = await cmd.redo(context); // confirm that each node is back where ordered to move // node0 => PointNE expect(pointNE.x).equals(getNode('node0', redoneModel).bounds.x); expect(pointNE.y).equals(getNode('node0', redoneModel).bounds.y); // node1 => pointSW expect(pointSW.x).equals(getNode('node1', redoneModel).bounds.x); expect(pointSW.y).equals(getNode('node1', redoneModel).bounds.y); // node2 => PointSE expect(pointSE.x).equals(getNode('node2', redoneModel).bounds.x); expect(pointSE.y).equals(getNode('node2', redoneModel).bounds.y); }); });