GridSnappingSpec.js 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706
  1. import {
  2. bootstrapDiagram,
  3. getDiagramJS,
  4. inject
  5. } from 'test/TestHelper';
  6. import modelingModule from 'lib/features/modeling';
  7. import gridSnappingModule from 'lib/features/grid-snapping';
  8. import bendpointsModule from 'lib/features/bendpoints';
  9. import connectModule from 'lib/features/connect';
  10. import createModule from 'lib/features/create';
  11. import moveModule from 'lib/features/move';
  12. import resizeModule from 'lib/features/resize';
  13. import CroppingConnectionDocking from 'lib/layout/CroppingConnectionDocking';
  14. import { asTRBL } from 'lib/layout/LayoutUtil';
  15. var layoutModule = {
  16. connectionDocking: [ 'type', CroppingConnectionDocking ]
  17. };
  18. import {
  19. createCanvasEvent as canvasEvent
  20. } from '../../../util/MockEvents';
  21. var LOW_PRIORITY = 500;
  22. describe('features/grid-snapping', function() {
  23. describe('init', function() {
  24. it('should init as active by default', function() {
  25. // when
  26. bootstrapDiagram({
  27. modules: [
  28. modelingModule,
  29. gridSnappingModule,
  30. moveModule
  31. ]
  32. })();
  33. // then
  34. var gridSnapping = getDiagramJS().get('gridSnapping');
  35. expect(gridSnapping.isActive()).to.be.true;
  36. });
  37. it('should init as NOT active', function() {
  38. // when
  39. bootstrapDiagram({
  40. modules: [
  41. modelingModule,
  42. gridSnappingModule,
  43. moveModule
  44. ],
  45. gridSnapping: {
  46. active: false
  47. }
  48. })();
  49. // then
  50. var gridSnapping = getDiagramJS().get('gridSnapping');
  51. expect(gridSnapping.isActive()).to.be.false;
  52. });
  53. describe('api', function() {
  54. beforeEach(bootstrapDiagram({
  55. modules: [
  56. modelingModule,
  57. gridSnappingModule,
  58. moveModule
  59. ]
  60. }));
  61. it('#isActive', inject(function(gridSnapping) {
  62. // then
  63. expect(gridSnapping.isActive()).to.be.true;
  64. // when
  65. gridSnapping.setActive(false);
  66. // then
  67. expect(gridSnapping.isActive()).to.be.false;
  68. }));
  69. it('#setActive', inject(function(gridSnapping) {
  70. // when
  71. gridSnapping.setActive(false);
  72. // then
  73. expect(gridSnapping.isActive()).to.be.false;
  74. }));
  75. it('#toggleActive', inject(function(gridSnapping) {
  76. // when
  77. gridSnapping.toggleActive();
  78. // then
  79. expect(gridSnapping.isActive()).to.be.false;
  80. // when
  81. gridSnapping.toggleActive();
  82. // then
  83. expect(gridSnapping.isActive()).to.be.true;
  84. }));
  85. });
  86. });
  87. describe('snapping', function() {
  88. var rootShape,
  89. rootShapeGfx,
  90. shape1,
  91. shape2,
  92. newShape,
  93. connection,
  94. connectionGfx;
  95. beforeEach(bootstrapDiagram({
  96. modules: [
  97. modelingModule,
  98. gridSnappingModule,
  99. bendpointsModule,
  100. connectModule,
  101. createModule,
  102. moveModule,
  103. resizeModule,
  104. layoutModule
  105. ]
  106. }));
  107. beforeEach(inject(function(elementFactory, elementRegistry, canvas) {
  108. rootShape = elementFactory.createRoot({
  109. id: 'root'
  110. });
  111. canvas.setRootElement(rootShape);
  112. rootShapeGfx = elementRegistry.getGraphics(rootShape);
  113. shape1 = elementFactory.createShape({
  114. id: 'shape1',
  115. x: 100, y: 100, width: 100, height: 100
  116. });
  117. canvas.addShape(shape1, rootShape);
  118. shape2 = elementFactory.createShape({
  119. id: 'shape2',
  120. x: 250, y: 250, width: 100, height: 100
  121. });
  122. canvas.addShape(shape2, rootShape);
  123. var shape3 = elementFactory.createShape({
  124. id: 'shape3',
  125. x: 300, y: 100, width: 100, height: 100
  126. });
  127. canvas.addShape(shape3, rootShape);
  128. connection = elementFactory.createConnection({
  129. id: 'connection',
  130. source: shape1,
  131. target: shape3,
  132. waypoints: [
  133. { x: 150, y: 150 },
  134. { x: 350, y: 150 }
  135. ]
  136. });
  137. canvas.addConnection(connection, rootShape);
  138. connectionGfx = elementRegistry.getGraphics(connection);
  139. newShape = elementFactory.createShape({
  140. id: 'newShape',
  141. x: 0, y: 0, width: 100, height: 100
  142. });
  143. }));
  144. describe('<move>', function() {
  145. it('should snap', inject(function(dragging, eventBus, move) {
  146. // given
  147. var events = recordEvents(eventBus, [
  148. 'shape.move.move',
  149. 'shape.move.end'
  150. ]);
  151. move.start(canvasEvent({ x: 150, y: 150 }), shape1);
  152. // when
  153. dragging.hover({ element: rootShape, gfx: rootShapeGfx });
  154. dragging.move(canvasEvent({ x: 156, y: 153 }));
  155. dragging.move(canvasEvent({ x: 162, y: 156 }));
  156. dragging.move(canvasEvent({ x: 168, y: 159 }));
  157. dragging.move(canvasEvent({ x: 174, y: 162 }));
  158. dragging.move(canvasEvent({ x: 180, y: 165 }));
  159. dragging.end();
  160. // then
  161. expect(events.map(position)).to.eql([
  162. { x: 160, y: 150 }, // move
  163. { x: 160, y: 160 }, // move
  164. { x: 170, y: 160 }, // move
  165. { x: 170, y: 160 }, // move
  166. { x: 180, y: 170 }, // move
  167. { x: 180, y: 170 } // end
  168. ]);
  169. expect(shape1.x + shape1.width / 2).to.equal(180);
  170. expect(shape1.y + shape1.height / 2).to.equal(170);
  171. }));
  172. it('should NOT snap (cmd)', inject(function(dragging, eventBus, move) {
  173. // given
  174. var events = recordEvents(eventBus, [
  175. 'shape.move.move',
  176. 'shape.move.end'
  177. ]);
  178. var data = { ctrlKey: true };
  179. move.start(canvasEvent({ x: 150, y: 150 }, data), shape1);
  180. // when
  181. dragging.hover({ element: rootShape, gfx: rootShapeGfx });
  182. dragging.move(canvasEvent({ x: 156, y: 153 }, data));
  183. dragging.move(canvasEvent({ x: 162, y: 156 }, data));
  184. dragging.move(canvasEvent({ x: 168, y: 159 }, data));
  185. dragging.move(canvasEvent({ x: 174, y: 162 }, data));
  186. dragging.move(canvasEvent({ x: 180, y: 165 }, data));
  187. dragging.end();
  188. // then
  189. expect(events.map(position)).to.eql([
  190. { x: 156, y: 153 }, // move
  191. { x: 162, y: 156 }, // move
  192. { x: 168, y: 159 }, // move
  193. { x: 174, y: 162 }, // move
  194. { x: 180, y: 165 }, // move
  195. { x: 180, y: 165 } // end
  196. ]);
  197. expect(shape1.x + shape1.width / 2).to.equal(180);
  198. expect(shape1.y + shape1.height / 2).to.equal(165);
  199. }));
  200. });
  201. it('<create>', inject(function(create, dragging, eventBus) {
  202. // given
  203. var events = recordEvents(eventBus, [
  204. 'create.move',
  205. 'create.end'
  206. ]);
  207. create.start(canvasEvent({ x: 150, y: 250 }), newShape);
  208. // when
  209. dragging.hover({ element: rootShape, gfx: rootShapeGfx });
  210. dragging.move(canvasEvent({ x: 156, y: 253 }));
  211. dragging.move(canvasEvent({ x: 162, y: 256 }));
  212. dragging.move(canvasEvent({ x: 168, y: 259 }));
  213. dragging.move(canvasEvent({ x: 174, y: 262 }));
  214. dragging.move(canvasEvent({ x: 180, y: 265 }));
  215. dragging.end();
  216. // then
  217. expect(events.map(position)).to.eql([
  218. { x: 150, y: 250 }, // move (triggered on create.start thanks to autoActivate)
  219. { x: 160, y: 250 }, // move
  220. { x: 160, y: 260 }, // move
  221. { x: 170, y: 260 }, // move
  222. { x: 170, y: 260 }, // move
  223. { x: 180, y: 270 }, // move
  224. { x: 180, y: 270 } // end
  225. ]);
  226. expect(newShape.x + newShape.width / 2).to.equal(180);
  227. expect(newShape.y + newShape.height / 2).to.equal(270);
  228. }));
  229. it('<connect>', inject(function(connect, dragging, eventBus) {
  230. // given
  231. var events = recordEvents(eventBus, [
  232. 'connect.move',
  233. 'connect.end'
  234. ]);
  235. connect.start(canvasEvent({ x: 250, y: 250 }), shape1);
  236. // when
  237. dragging.move(canvasEvent({ x: 256, y: 253 }));
  238. dragging.move(canvasEvent({ x: 262, y: 256 }));
  239. dragging.move(canvasEvent({ x: 268, y: 259 }));
  240. dragging.move(canvasEvent({ x: 274, y: 262 }));
  241. dragging.move(canvasEvent({ x: 280, y: 265 }));
  242. dragging.hover({ element: shape2 });
  243. dragging.end();
  244. // then
  245. expect(events.map(position)).to.eql([
  246. { x: 260, y: 250 },
  247. { x: 260, y: 260 },
  248. { x: 270, y: 260 },
  249. { x: 270, y: 260 },
  250. { x: 280, y: 270 },
  251. { x: 280, y: 270 }
  252. ]);
  253. expect(shape1.outgoing[1].waypoints.map(position)).to.eql([
  254. { x: 150, y: 150 },
  255. { x: 280, y: 270 }
  256. ]);
  257. }));
  258. describe('<resize>', function() {
  259. var events;
  260. beforeEach(inject(function(eventBus) {
  261. events = recordEvents(eventBus, [
  262. 'resize.move',
  263. 'resize.end'
  264. ]);
  265. }));
  266. it('without constraints', inject(function(dragging, resize) {
  267. // given
  268. resize.activate(canvasEvent({ x: 100, y: 200 }), shape1, 'sw');
  269. // when
  270. dragging.move(canvasEvent({ x: 106, y: 203 }));
  271. dragging.move(canvasEvent({ x: 112, y: 206 }));
  272. dragging.move(canvasEvent({ x: 118, y: 209 }));
  273. dragging.move(canvasEvent({ x: 124, y: 212 }));
  274. dragging.move(canvasEvent({ x: 130, y: 215 }));
  275. dragging.end();
  276. // then
  277. expect(events.map(position)).to.eql([
  278. { x: 100, y: 200 }, // move (triggered on resize.activate thanks to autoActivate)
  279. { x: 110, y: 200 }, // move
  280. { x: 110, y: 210 }, // move
  281. { x: 120, y: 210 }, // move
  282. { x: 120, y: 210 }, // move
  283. { x: 130, y: 220 }, // move
  284. { x: 130, y: 220 } // end
  285. ]);
  286. expect(shape1.width).to.equal(70);
  287. expect(shape1.height).to.equal(120);
  288. }));
  289. it('with constraints (min)', inject(function(dragging, resize) {
  290. // given
  291. resize.activate(canvasEvent({ x: 100, y: 200 }), shape1, {
  292. direction: 'sw',
  293. resizeConstraints: {
  294. min: asTRBL({
  295. x: 125,
  296. y: 125,
  297. width: 70,
  298. height: 70
  299. })
  300. }
  301. });
  302. // when
  303. dragging.move(canvasEvent({ x: 112, y: 203 }));
  304. dragging.move(canvasEvent({ x: 124, y: 206 }));
  305. dragging.move(canvasEvent({ x: 136, y: 209 }));
  306. dragging.move(canvasEvent({ x: 148, y: 212 }));
  307. dragging.move(canvasEvent({ x: 160, y: 215 }));
  308. dragging.end();
  309. // then
  310. expect(events.map(position)).to.eql([
  311. { x: 100, y: 200 }, // move (triggered on resize.activate thanks to autoActivate)
  312. { x: 110, y: 200 }, // move
  313. { x: 120, y: 210 }, // move
  314. { x: 120, y: 210 }, // move
  315. { x: 120, y: 210 }, // move
  316. { x: 120, y: 220 }, // move
  317. { x: 120, y: 220 } // end
  318. ]);
  319. expect(shape1.width).to.equal(80);
  320. expect(shape1.height).to.equal(120);
  321. }));
  322. it('with constraints (max)', inject(function(dragging, resize) {
  323. // given
  324. resize.activate(canvasEvent({ x: 100, y: 200 }), shape1, {
  325. direction: 'sw',
  326. resizeConstraints: {
  327. max: asTRBL({
  328. x: 75,
  329. y: 75,
  330. width: 150,
  331. height: 150
  332. })
  333. }
  334. });
  335. // when
  336. dragging.move(canvasEvent({ x: 88, y: 203 }));
  337. dragging.move(canvasEvent({ x: 76, y: 206 }));
  338. dragging.move(canvasEvent({ x: 64, y: 209 }));
  339. dragging.move(canvasEvent({ x: 52, y: 212 }));
  340. dragging.move(canvasEvent({ x: 40, y: 215 }));
  341. dragging.end();
  342. // then
  343. expect(events.map(position)).to.eql([
  344. { x: 100, y: 200 }, // move (triggered on resize.activate thanks to autoActivate)
  345. { x: 90, y: 200 }, // move
  346. { x: 80, y: 210 }, // move
  347. { x: 80, y: 210 }, // move
  348. { x: 80, y: 210 }, // move
  349. { x: 80, y: 220 }, // move
  350. { x: 80, y: 220 } // end
  351. ]);
  352. expect(shape1.width).to.equal(120);
  353. expect(shape1.height).to.equal(120);
  354. }));
  355. });
  356. it('<bendpoint.move>', inject(function(bendpointMove, dragging, eventBus) {
  357. // given
  358. var events = recordEvents(eventBus, [
  359. 'bendpoint.move.move',
  360. 'bendpoint.move.end'
  361. ]);
  362. bendpointMove.start(canvasEvent({ x: 250, y: 150 }), connection, 1, true);
  363. dragging.hover({ element: connection, gfx: connectionGfx });
  364. // when
  365. dragging.move(canvasEvent({ x: 250, y: 162 }));
  366. dragging.move(canvasEvent({ x: 250, y: 174 }));
  367. dragging.move(canvasEvent({ x: 250, y: 186 }));
  368. dragging.move(canvasEvent({ x: 250, y: 198 }));
  369. dragging.move(canvasEvent({ x: 250, y: 210 }));
  370. dragging.end();
  371. // then
  372. expect(events.map(position)).to.eql([
  373. { x: 250, y: 160 }, // move
  374. { x: 250, y: 170 }, // move
  375. { x: 250, y: 190 }, // move
  376. { x: 250, y: 200 }, // move
  377. { x: 250, y: 210 }, // move
  378. { x: 250, y: 210 } // end
  379. ]);
  380. expect(shape1.outgoing[0].waypoints.map(position)).to.eql([
  381. { x: 200, y: 180 },
  382. { x: 250, y: 210 },
  383. { x: 300, y: 180 }
  384. ]);
  385. }));
  386. it('<connectionSegment.move>', inject(function(connectionSegmentMove, dragging, eventBus) {
  387. // given
  388. var events = recordEvents(eventBus, [
  389. 'connectionSegment.move.move',
  390. 'connectionSegment.move.end'
  391. ]);
  392. connectionSegmentMove.start(canvasEvent({ x: 250, y: 150 }), connection, 1);
  393. // when
  394. dragging.move(canvasEvent({ x: 250, y: 162 }));
  395. dragging.move(canvasEvent({ x: 250, y: 174 }));
  396. dragging.move(canvasEvent({ x: 250, y: 186 }));
  397. dragging.move(canvasEvent({ x: 250, y: 198 }));
  398. dragging.move(canvasEvent({ x: 250, y: 210 }));
  399. dragging.end();
  400. // then
  401. expect(events.map(position)).to.eql([
  402. { x: 250, y: 160 }, // move
  403. { x: 250, y: 170 }, // move
  404. { x: 250, y: 190 }, // move
  405. { x: 250, y: 200 }, // move
  406. { x: 250, y: 210 }, // move
  407. { x: 250, y: 210 } // end
  408. ]);
  409. expect(shape1.outgoing[0].waypoints.map(position)).to.eql([
  410. { x: 150, y: 200 },
  411. { x: 150, y: 210 },
  412. { x: 350, y: 210 },
  413. { x: 350, y: 200 }
  414. ]);
  415. }));
  416. });
  417. describe('grid', function() {
  418. it('should be visible by default', function() {
  419. // when
  420. bootstrapDiagram({
  421. modules: [
  422. modelingModule,
  423. gridSnappingModule,
  424. moveModule
  425. ]
  426. })();
  427. // then
  428. var grid = getDiagramJS().get('grid'),
  429. gfx = grid._getParent();
  430. expect(grid.isVisible()).to.be.true;
  431. expect(gfx.childNodes).to.have.length(1);
  432. });
  433. it('should NOT be visible (grid.visible = false)', function() {
  434. // when
  435. bootstrapDiagram({
  436. modules: [
  437. modelingModule,
  438. gridSnappingModule,
  439. moveModule
  440. ],
  441. grid: {
  442. visible: false
  443. }
  444. })();
  445. // then
  446. var grid = getDiagramJS().get('grid'),
  447. gfx = grid._getParent();
  448. expect(grid.isVisible()).to.be.false;
  449. expect(gfx.childNodes).to.have.length(0);
  450. });
  451. it('should NOT be visible (gridSnapping.active = false)', function() {
  452. // when
  453. bootstrapDiagram({
  454. modules: [
  455. modelingModule,
  456. gridSnappingModule,
  457. moveModule
  458. ],
  459. gridSnapping: {
  460. active: false
  461. }
  462. })();
  463. // then
  464. var grid = getDiagramJS().get('grid'),
  465. gfx = grid._getParent();
  466. expect(grid.isVisible()).to.be.false;
  467. expect(gfx.childNodes).to.have.length(0);
  468. });
  469. describe('api', function() {
  470. beforeEach(bootstrapDiagram({
  471. modules: [
  472. modelingModule,
  473. gridSnappingModule,
  474. moveModule
  475. ]
  476. }));
  477. it('#isVisible', inject(function(grid) {
  478. // then
  479. expect(grid.isVisible()).to.be.true;
  480. // when
  481. grid.setVisible(false);
  482. // then
  483. expect(grid.isVisible()).to.be.false;
  484. }));
  485. it('#setVisible', inject(function(grid) {
  486. // when
  487. grid.setVisible(false);
  488. // then
  489. var gfx = grid._getParent();
  490. expect(grid.isVisible()).to.be.false;
  491. expect(gfx.childNodes).to.have.length(0);
  492. }));
  493. it('#toggleVisible', inject(function(grid) {
  494. // when
  495. grid.toggleVisible();
  496. // then
  497. var gfx = grid._getParent();
  498. expect(grid.isVisible()).to.be.false;
  499. expect(gfx.childNodes).to.have.length(0);
  500. // when
  501. grid.toggleVisible();
  502. // then
  503. expect(grid.isVisible()).to.be.true;
  504. expect(gfx.childNodes).to.have.length(1);
  505. }));
  506. });
  507. });
  508. });
  509. // helpers //////////
  510. function recordEvents(eventBus, eventTypes) {
  511. var events = [];
  512. eventTypes.forEach(function(eventType) {
  513. eventBus.on(eventType, LOW_PRIORITY, function(event) {
  514. events.push(event);
  515. });
  516. });
  517. return events;
  518. }
  519. function position(event) {
  520. return {
  521. x: event.x,
  522. y: event.y
  523. };
  524. }