SpaceToolSpec.js 20 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759
  1. import {
  2. bootstrapDiagram,
  3. inject
  4. } from 'test/TestHelper';
  5. import {
  6. createCanvasEvent as canvasEvent
  7. } from '../../../util/MockEvents';
  8. import spaceToolModule from 'lib/features/space-tool';
  9. import modelingModule from 'lib/features/modeling';
  10. import autoResizeModule from 'lib/features/auto-resize';
  11. import rulesModule from './rules';
  12. import autoResizeProviderModule from './auto-resize';
  13. import { isMac } from 'lib/util/Platform';
  14. var keyModifier = isMac() ? { metaKey: true } : { ctrlKey: true };
  15. import {
  16. query as domQuery
  17. } from 'min-dom';
  18. describe('features/space-tool', function() {
  19. describe('create/remove space', function() {
  20. beforeEach(bootstrapDiagram({
  21. modules: [
  22. spaceToolModule,
  23. modelingModule,
  24. rulesModule
  25. ]
  26. }));
  27. var childShape, childShape2, connection;
  28. beforeEach(inject(function(elementFactory, canvas) {
  29. childShape = elementFactory.createShape({
  30. id: 'child',
  31. x: 110, y: 110,
  32. width: 100, height: 100
  33. });
  34. canvas.addShape(childShape);
  35. childShape2 = elementFactory.createShape({
  36. id: 'child2',
  37. x: 400, y: 250,
  38. width: 100, height: 100
  39. });
  40. canvas.addShape(childShape2);
  41. connection = elementFactory.createConnection({
  42. id: 'connection',
  43. waypoints: [
  44. { x: 160, y: 160 },
  45. { x: 450, y: 300 }
  46. ],
  47. source: childShape,
  48. target: childShape2
  49. });
  50. canvas.addConnection(connection);
  51. }));
  52. describe('basics', function() {
  53. it('should expose spaceTool', inject(function(spaceTool) {
  54. expect(spaceTool).to.exist;
  55. }));
  56. it('should reactivate after usage', inject(function(canvas, spaceTool, dragging) {
  57. var context;
  58. // when
  59. spaceTool.activateSelection(canvasEvent({ x: 100, y: 225 }));
  60. dragging.move(canvasEvent({ x: 100, y: 250 }, keyModifier));
  61. dragging.end();
  62. context = dragging.context();
  63. // then space tool should still be active
  64. expect(context.prefix).to.eql('spaceTool');
  65. expect(context.active).to.be.true;
  66. expect(spaceTool.isActive()).to.be.true;
  67. }));
  68. it('should not be active on cancel', inject(function(canvas, spaceTool, dragging) {
  69. var context;
  70. // when
  71. spaceTool.activateSelection(canvasEvent({ x: 100, y: 225 }));
  72. dragging.move(canvasEvent({ x: 100, y: 250 }, keyModifier));
  73. dragging.end();
  74. dragging.cancel();
  75. context = dragging.context();
  76. // then
  77. expect(context).not.to.exist;
  78. expect(spaceTool.isActive()).not.to.exist;
  79. }));
  80. });
  81. describe('create space behaviour', function() {
  82. beforeEach(inject(function(dragging) {
  83. dragging.setOptions({ manual: true });
  84. }));
  85. it('should show crosshair once activated', inject(function(spaceTool, dragging, canvas, eventBus) {
  86. // given
  87. spaceTool.activateSelection(canvasEvent({ x: 30, y: 30 }));
  88. // when
  89. dragging.move(canvasEvent({ x: 50, y: 50 }));
  90. // then
  91. var spaceGroup = domQuery('.djs-crosshair-group', canvas.getLayer('space'));
  92. expect(spaceGroup).to.exist;
  93. }));
  94. it('should remove crosshair once deactivated', inject(function(spaceTool, dragging, canvas) {
  95. // given
  96. spaceTool.activateSelection(canvasEvent({ x: 30, y: 30 }));
  97. // when
  98. dragging.move(canvasEvent({ x: 50, y: 50 }));
  99. dragging.end();
  100. var spaceLayer = domQuery('.djs-crosshair-group', canvas.getLayer('space'));
  101. // then
  102. expect(spaceLayer).to.be.null;
  103. }));
  104. it('should move the *y axis* when doing a perfect diagonal', inject(function(spaceTool, dragging, canvas) {
  105. // given
  106. spaceTool.activateMakeSpace(canvasEvent({ x: 300, y: 225 }));
  107. // when
  108. dragging.move(canvasEvent({ x: 350, y: 275 }));
  109. dragging.end();
  110. expect(childShape.x).to.equal(110);
  111. expect(childShape.y).to.equal(110);
  112. expect(childShape2.x).to.equal(400);
  113. expect(childShape2.y).to.equal(300);
  114. expect(connection).to.have.waypoints([
  115. { x: 160, y: 160 },
  116. { x: 450, y: 350 }
  117. ]);
  118. }));
  119. it('should make space to the right and resize parent', inject(function(spaceTool, dragging) {
  120. // when
  121. spaceTool.activateMakeSpace(canvasEvent({ x: 300, y: 150 }));
  122. dragging.move(canvasEvent({ x: 350, y: 150 }));
  123. dragging.end();
  124. // then
  125. expect(childShape.x).to.equal(110);
  126. expect(childShape.y).to.equal(110);
  127. expect(childShape2.x).to.equal(450);
  128. expect(childShape2.y).to.equal(250);
  129. expect(connection).to.have.waypoints([
  130. { x: 160, y: 160 },
  131. { x: 500, y: 300 }
  132. ]);
  133. }));
  134. it('should round made space to pixel values', inject(function(spaceTool, dragging) {
  135. // when
  136. spaceTool.activateMakeSpace(canvasEvent({ x: 300, y: 150 }));
  137. dragging.move(canvasEvent({ x: 350.14, y: 149.8 }));
  138. dragging.end();
  139. // then
  140. expect(childShape.x).to.equal(110);
  141. expect(childShape.y).to.equal(110);
  142. expect(childShape2.x).to.equal(450);
  143. expect(childShape2.y).to.equal(250);
  144. expect(connection).to.have.waypoints([
  145. { x: 160, y: 160 },
  146. { x: 500, y: 300 }
  147. ]);
  148. }));
  149. it('should remove space from the left and resize parent', inject(function(spaceTool, dragging) {
  150. // when
  151. spaceTool.activateMakeSpace(canvasEvent({ x: 200, y: 150 }));
  152. dragging.move(canvasEvent({ x: 150, y: 150 }));
  153. dragging.end();
  154. // then
  155. expect(childShape.x).to.equal(110);
  156. expect(childShape.y).to.equal(110);
  157. expect(childShape2.x).to.equal(350);
  158. expect(childShape2.y).to.equal(250);
  159. expect(connection).to.have.waypoints([
  160. { x: 160, y: 160 },
  161. { x: 400, y: 300 }
  162. ]);
  163. }));
  164. it('should remove space from the top and resize parent', inject(function(spaceTool, dragging) {
  165. // when
  166. spaceTool.activateMakeSpace(canvasEvent({ x: 300,y: 150 }));
  167. dragging.move(canvasEvent({ x: 300, y: 120 }));
  168. dragging.end();
  169. // then
  170. expect(childShape.x).to.equal(110);
  171. expect(childShape.y).to.equal(110);
  172. expect(childShape2.x).to.equal(400);
  173. expect(childShape2.y).to.equal(220);
  174. expect(connection).to.have.waypoints([
  175. { x: 160, y: 160 },
  176. { x: 450, y: 270 }
  177. ]);
  178. }));
  179. it('should make space at the bottom and resize parent', inject(function(spaceTool, dragging) {
  180. // when
  181. spaceTool.activateMakeSpace(canvasEvent({ x: 300, y: 150 }));
  182. dragging.move(canvasEvent({ x: 300, y: 200 }));
  183. dragging.end();
  184. // then
  185. expect(childShape.x).to.equal(110);
  186. expect(childShape.y).to.equal(110);
  187. expect(childShape2.x).to.equal(400);
  188. expect(childShape2.y).to.equal(300);
  189. expect(connection).to.have.waypoints([
  190. { x: 160, y: 160 },
  191. { x: 450, y: 350 }
  192. ]);
  193. }));
  194. it('should remove space with objects to the right', inject(function(spaceTool, dragging) {
  195. // when
  196. spaceTool.activateMakeSpace(canvasEvent({ x: 300, y: 150 }));
  197. dragging.move(canvasEvent({ x: 350, y: 150 }, keyModifier));
  198. dragging.end();
  199. // then
  200. expect(childShape.x).to.equal(160);
  201. expect(childShape.y).to.equal(110);
  202. expect(childShape2.x).to.equal(400);
  203. expect(childShape2.y).to.equal(250);
  204. expect(connection).to.have.waypoints([
  205. { x: 210, y: 160 },
  206. { x: 450, y: 300 }
  207. ]);
  208. }));
  209. it('should add space with objects to the left', inject(function(spaceTool, dragging) {
  210. // when
  211. spaceTool.activateMakeSpace(canvasEvent({ x: 350, y: 150 }));
  212. dragging.move(canvasEvent({ x: 300, y: 150 }, keyModifier));
  213. dragging.end();
  214. // then
  215. expect(childShape.x).to.equal(60);
  216. expect(childShape.y).to.equal(110);
  217. expect(childShape2.x).to.equal(400);
  218. expect(childShape2.y).to.equal(250);
  219. expect(connection).to.have.waypoints([
  220. { x: 110, y: 160 },
  221. { x: 450, y: 300 }
  222. ]);
  223. }));
  224. it('should remove space with objects that are above', inject(function(spaceTool, dragging) {
  225. // when
  226. spaceTool.activateMakeSpace(canvasEvent({ x: 350, y: 230 }));
  227. dragging.move(canvasEvent({ x: 350, y: 280 }, keyModifier));
  228. dragging.end();
  229. // then
  230. expect(childShape.x).to.equal(110);
  231. expect(childShape.y).to.equal(160);
  232. expect(childShape2.x).to.equal(400);
  233. expect(childShape2.y).to.equal(250);
  234. expect(connection).to.have.waypoints([
  235. { x: 160, y: 210 },
  236. { x: 450, y: 300 }
  237. ]);
  238. }));
  239. it('should add space with objects that are below', inject(function(spaceTool, dragging) {
  240. // when
  241. spaceTool.activateMakeSpace(canvasEvent({ x: 350, y: 230 }));
  242. dragging.move(canvasEvent({ x: 350, y: 180 }, keyModifier));
  243. dragging.end();
  244. // then
  245. expect(childShape.x).to.equal(110);
  246. expect(childShape.y).to.equal(60);
  247. expect(childShape2.x).to.equal(400);
  248. expect(childShape2.y).to.equal(250);
  249. expect(connection).to.have.waypoints([
  250. { x: 160, y: 110 },
  251. { x: 450, y: 300 }
  252. ]);
  253. }));
  254. });
  255. });
  256. describe('resize containers', function() {
  257. beforeEach(bootstrapDiagram({
  258. modules: [
  259. spaceToolModule,
  260. modelingModule,
  261. rulesModule
  262. ]
  263. }));
  264. beforeEach(inject(function(dragging) {
  265. dragging.setOptions({ manual: true });
  266. }));
  267. var greatGrandParent, grandParent, parentShape, childShape, childShape2, connection,
  268. parentShape2, childShape3, childShape4, connection2;
  269. beforeEach(inject(function(elementFactory, canvas) {
  270. greatGrandParent = elementFactory.createShape({
  271. id: 'greatGrandParent',
  272. x: 100, y: 50,
  273. width: 400, height: 400
  274. });
  275. canvas.addShape(greatGrandParent);
  276. grandParent = elementFactory.createShape({
  277. id: 'grandParent',
  278. x: 125, y: 75,
  279. width: 350, height: 350
  280. });
  281. canvas.addShape(grandParent, greatGrandParent);
  282. parentShape = elementFactory.createShape({
  283. id: 'parent1',
  284. x: 200, y: 150,
  285. width: 200, height: 200
  286. });
  287. canvas.addShape(parentShape, grandParent);
  288. childShape = elementFactory.createShape({
  289. id: 'child',
  290. x: 225, y: 175,
  291. width: 50, height: 50
  292. });
  293. canvas.addShape(childShape, parentShape);
  294. childShape2 = elementFactory.createShape({
  295. id: 'child2',
  296. x: 325, y: 275,
  297. width: 50, height: 50
  298. });
  299. canvas.addShape(childShape2, parentShape);
  300. connection = elementFactory.createConnection({
  301. id: 'connection',
  302. waypoints: [
  303. { x: 250, y: 200 },
  304. { x: 350, y: 300 }
  305. ],
  306. source: childShape,
  307. target: childShape2
  308. });
  309. canvas.addConnection(connection);
  310. parentShape2 = elementFactory.createShape({
  311. id: 'parent2',
  312. x: 800, y: 200,
  313. width: 250, height: 150
  314. });
  315. canvas.addShape(parentShape2);
  316. childShape3 = elementFactory.createShape({
  317. id: 'child3',
  318. x: 990, y: 210,
  319. width: 50, height: 50
  320. });
  321. canvas.addShape(childShape3, parentShape2);
  322. childShape4 = elementFactory.createShape({
  323. id: 'child4',
  324. x: 810, y: 290,
  325. width: 50, height: 50
  326. });
  327. canvas.addShape(childShape4, parentShape2);
  328. connection2 = elementFactory.createConnection({
  329. id: 'connection2',
  330. waypoints: [
  331. { x: 1015, y: 235 },
  332. { x: 835, y: 315 }
  333. ],
  334. source: childShape3,
  335. target: childShape4
  336. });
  337. canvas.addConnection(connection2);
  338. }));
  339. it('should resize parents', inject(function(spaceTool, dragging) {
  340. // when
  341. spaceTool.activateMakeSpace(canvasEvent({ x: 275, y: 155 }));
  342. dragging.move(canvasEvent({ x: 400, y: 155 })); // x =/= 125
  343. dragging.end();
  344. // then
  345. expect(greatGrandParent.x).to.equal(100);
  346. expect(greatGrandParent.y).to.equal(50);
  347. expect(greatGrandParent.width).to.equal(525); // changes
  348. expect(greatGrandParent.height).to.equal(400);
  349. expect(grandParent.x).to.equal(125);
  350. expect(grandParent.y).to.equal(75);
  351. expect(grandParent.width).to.equal(475); // changes
  352. expect(grandParent.height).to.equal(350);
  353. expect(parentShape.x).to.equal(200);
  354. expect(parentShape.y).to.equal(150);
  355. expect(parentShape.width).to.equal(325); // changes
  356. expect(parentShape.height).to.equal(200);
  357. expect(childShape.x).to.equal(225);
  358. expect(childShape.y).to.equal(175);
  359. expect(childShape2.x).to.equal(450); // changes
  360. expect(childShape2.y).to.equal(275);
  361. expect(connection).to.have.waypoints([
  362. { x: 250, y: 200 },
  363. { x: 475, y: 300 } // changes
  364. ]);
  365. expect(parentShape2.x).to.equal(925); // changes
  366. expect(parentShape2.y).to.equal(200);
  367. expect(parentShape2.width).to.equal(250);
  368. expect(parentShape2.height).to.equal(150);
  369. expect(childShape3.x).to.equal(1115); // changes
  370. expect(childShape3.y).to.equal(210);
  371. expect(childShape4.x).to.equal(935); // changes
  372. expect(childShape4.y).to.equal(290);
  373. expect(connection2).to.have.waypoints([
  374. { x: 1140, y: 235 }, // changes
  375. { x: 960, y: 315 } // changes
  376. ]);
  377. }));
  378. it('should resize parents (inverted)', inject(function(spaceTool, dragging) {
  379. // when
  380. spaceTool.activateMakeSpace(canvasEvent({ x: 280, y: 155 }));
  381. dragging.move(canvasEvent({ x: 330, y: 155 }, keyModifier)); // x =/= 50
  382. dragging.end();
  383. // then
  384. expect(greatGrandParent.x).to.equal(150); // changes
  385. expect(greatGrandParent.y).to.equal(50);
  386. expect(greatGrandParent.width).to.equal(350);
  387. expect(greatGrandParent.height).to.equal(400);
  388. expect(grandParent.x).to.equal(175); // changes
  389. expect(grandParent.y).to.equal(75);
  390. expect(grandParent.width).to.equal(300);
  391. expect(grandParent.height).to.equal(350);
  392. expect(parentShape.x).to.equal(250); // changes
  393. expect(parentShape.y).to.equal(150);
  394. expect(parentShape.width).to.equal(150);
  395. expect(parentShape.height).to.equal(200);
  396. expect(childShape.x).to.equal(275); // changes
  397. expect(childShape.y).to.equal(175);
  398. }));
  399. it('should not remove space beyond max bounds (x axis)', inject(function(spaceTool, dragging) {
  400. // when
  401. spaceTool.activateMakeSpace(canvasEvent({ x: 280, y: 155 }));
  402. dragging.move(canvasEvent({ x: 550, y: 155 }, keyModifier));
  403. dragging.end();
  404. // then
  405. expect(greatGrandParent).to.have.bounds({ x: 100, y: 50, width: 400, height: 400 });
  406. expect(grandParent).to.have.bounds({ x: 125, y: 75, width: 350, height: 350 });
  407. expect(parentShape).to.have.bounds({ x: 200, y: 150, width: 200, height: 200 });
  408. expect(childShape).to.have.bounds({ x: 225, y: 175, width: 50, height: 50 });
  409. }));
  410. it('should not remove space beyond min bounds (x axis)', inject(function(spaceTool, dragging) {
  411. // when
  412. spaceTool.activateMakeSpace(canvasEvent({ x: 280, y: 155 }));
  413. dragging.move(canvasEvent({ x: 0, y: 155 }));
  414. dragging.end();
  415. // then
  416. expect(greatGrandParent).to.have.bounds({ x: 100, y: 50, width: 400, height: 400 });
  417. expect(grandParent).to.have.bounds({ x: 125, y: 75, width: 350, height: 350 });
  418. expect(parentShape).to.have.bounds({ x: 200, y: 150, width: 200, height: 200 });
  419. expect(childShape).to.have.bounds({ x: 225, y: 175, width: 50, height: 50 });
  420. }));
  421. it('should not remove space beyond max bounds (y axis)', inject(function(spaceTool, dragging) {
  422. // when
  423. spaceTool.activateMakeSpace(canvasEvent({ x: 280, y: 155 }));
  424. dragging.move(canvasEvent({ x: 280, y: 800 }, keyModifier));
  425. dragging.end();
  426. // then
  427. expect(greatGrandParent).to.have.bounds({ x: 100, y: 50, width: 400, height: 400 });
  428. expect(grandParent).to.have.bounds({ x: 125, y: 75, width: 350, height: 350 });
  429. expect(parentShape).to.have.bounds({ x: 200, y: 150, width: 200, height: 200 });
  430. expect(childShape).to.have.bounds({ x: 225, y: 175, width: 50, height: 50 });
  431. }));
  432. it('should not remove space beyond min bounds (y axis)', inject(function(spaceTool, dragging) {
  433. // when
  434. spaceTool.activateMakeSpace(canvasEvent({ x: 280, y: 155 }));
  435. dragging.move(canvasEvent({ x: 280, y: 0 }));
  436. dragging.end();
  437. // then
  438. expect(greatGrandParent).to.have.bounds({ x: 100, y: 50, width: 400, height: 400 });
  439. expect(grandParent).to.have.bounds({ x: 125, y: 75, width: 350, height: 350 });
  440. expect(parentShape).to.have.bounds({ x: 200, y: 150, width: 200, height: 200 });
  441. expect(childShape).to.have.bounds({ x: 225, y: 175, width: 50, height: 50 });
  442. }));
  443. });
  444. describe('redo / undo integration', function() {
  445. beforeEach(bootstrapDiagram({
  446. modules: [
  447. spaceToolModule,
  448. modelingModule,
  449. rulesModule
  450. ]
  451. }));
  452. beforeEach(inject(function(dragging) {
  453. dragging.setOptions({ manual: true });
  454. }));
  455. var shape1, shape2;
  456. beforeEach(inject(function(elementFactory, canvas) {
  457. shape1 = elementFactory.createShape({
  458. id: 'shape1',
  459. x: 50, y: 100,
  460. width: 100, height: 100
  461. });
  462. canvas.addShape(shape1);
  463. shape2 = elementFactory.createShape({
  464. id: 'shape2',
  465. x: 50, y: 250,
  466. width: 100, height: 100
  467. });
  468. canvas.addShape(shape2);
  469. }));
  470. it('should undo', inject(function(spaceTool, dragging, commandStack) {
  471. // given
  472. spaceTool.activateMakeSpace(canvasEvent({ x: 100, y: 225 }));
  473. dragging.move(canvasEvent({ x: 100, y: 250 }));
  474. dragging.end();
  475. // when
  476. commandStack.undo();
  477. // then
  478. expect(shape1.x).to.equal(50);
  479. expect(shape1.y).to.equal(100);
  480. expect(shape2.x).to.equal(50);
  481. expect(shape2.y).to.equal(250);
  482. }));
  483. it('should redo', inject(function(spaceTool, dragging, commandStack) {
  484. // given
  485. spaceTool.activateMakeSpace(canvasEvent({ x: 100, y: 225 }));
  486. dragging.move(canvasEvent({ x: 100, y: 250 }));
  487. dragging.end();
  488. commandStack.undo();
  489. // when
  490. commandStack.redo();
  491. // then
  492. expect(shape1.x).to.equal(50);
  493. expect(shape1.y).to.equal(100);
  494. expect(shape2.x).to.equal(50);
  495. expect(shape2.y).to.equal(275);
  496. }));
  497. });
  498. describe('integration', function() {
  499. beforeEach(bootstrapDiagram({
  500. modules: [
  501. spaceToolModule,
  502. modelingModule,
  503. autoResizeModule,
  504. autoResizeProviderModule
  505. ]
  506. }));
  507. beforeEach(inject(function(dragging) {
  508. dragging.setOptions({ manual: true });
  509. }));
  510. it('should not auto-resize', inject(function(elementFactory, canvas, spaceTool) {
  511. // given
  512. var parent = canvas.addShape(elementFactory.createShape({
  513. id: 'parent',
  514. x: 100, y: 50,
  515. width: 200, height: 200
  516. }));
  517. var outsideChild = canvas.addShape(elementFactory.createShape({
  518. id: 'child',
  519. x: 50, y: 50,
  520. width: 50, height: 50
  521. }), parent);
  522. // when
  523. spaceTool.makeSpace([ outsideChild ], [ ], { x: 20, y: 0 }, 'e');
  524. // then
  525. // expect parent with original bounds
  526. expect(parent).to.have.bounds({
  527. x: 100, y: 50,
  528. width: 200, height: 200
  529. });
  530. }));
  531. });
  532. });