CopyPasteSpec.js 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498
  1. /* global sinon */
  2. import {
  3. bootstrapDiagram,
  4. inject
  5. } from 'test/TestHelper';
  6. import { forEach } from 'min-dash';
  7. import copyPasteModule from 'lib/features/copy-paste';
  8. import selectionModule from 'lib/features/selection';
  9. import modelingModule from 'lib/features/modeling';
  10. import rulesModule from './rules';
  11. describe('features/copy-paste', function() {
  12. beforeEach(bootstrapDiagram({
  13. modules: [
  14. rulesModule,
  15. modelingModule,
  16. copyPasteModule,
  17. selectionModule
  18. ]
  19. }));
  20. function toObjectBranch(branch) {
  21. var newBranch = {};
  22. if (!branch) {
  23. return false;
  24. }
  25. forEach(branch, function(elem) {
  26. newBranch[elem.id] = elem;
  27. });
  28. return newBranch;
  29. }
  30. describe('basics', function() {
  31. var rootShape,
  32. parentShape,
  33. parentShape2,
  34. host,
  35. attacher,
  36. childShape,
  37. childShape2,
  38. connection;
  39. beforeEach(inject(function(elementFactory, canvas, modeling) {
  40. rootShape = elementFactory.createRoot({
  41. id: 'root'
  42. });
  43. canvas.setRootElement(rootShape);
  44. parentShape = elementFactory.createShape({
  45. id: 'parent',
  46. x: 600, y: 200,
  47. width: 600, height: 300
  48. });
  49. canvas.addShape(parentShape, rootShape);
  50. parentShape2 = elementFactory.createShape({
  51. id: 'parent2',
  52. x: 90, y: 15,
  53. width: 425, height: 300
  54. });
  55. canvas.addShape(parentShape2, rootShape);
  56. host = elementFactory.createShape({
  57. id: 'host',
  58. x: 300, y: 50,
  59. width: 100, height: 100
  60. });
  61. canvas.addShape(host, parentShape2);
  62. attacher = elementFactory.createShape({
  63. id: 'attacher',
  64. x: 375, y: 25,
  65. width: 50, height: 50
  66. });
  67. canvas.addShape(attacher, parentShape2);
  68. modeling.updateAttachment(attacher, host);
  69. childShape = elementFactory.createShape({
  70. id: 'childShape',
  71. x: 110, y: 110,
  72. width: 100, height: 100
  73. });
  74. canvas.addShape(childShape, parentShape2);
  75. childShape2 = elementFactory.createShape({
  76. id: 'childShape2',
  77. x: 400, y: 200,
  78. width: 100, height: 100
  79. });
  80. canvas.addShape(childShape2, parentShape2);
  81. connection = elementFactory.createConnection({
  82. id: 'connection',
  83. waypoints: [
  84. { x: 160, y: 160 },
  85. { x: 450, y: 250 }
  86. ],
  87. source: childShape,
  88. target: childShape2
  89. });
  90. canvas.addConnection(connection, parentShape2);
  91. }));
  92. describe('events', function() {
  93. it('should fire <elements.copy> when copying elements', inject(function(eventBus, clipboard, copyPaste) {
  94. eventBus.on('elements.copy', function(event) {
  95. var context = event.context,
  96. tree = context.tree;
  97. // then
  98. expect(toObjectBranch(tree[0])).to.have.keys('parent2');
  99. expect(toObjectBranch(tree[1])).to.have.keys('host', 'childShape', 'childShape2', 'connection');
  100. });
  101. // when
  102. copyPaste.copy([ parentShape2 ]);
  103. }));
  104. it('should have an empty clipboard on <elements.copy> when no element was allowed to be copied',
  105. inject(function(eventBus, clipboard, copyPaste) {
  106. // given
  107. var listener = sinon.spy();
  108. eventBus.on('elements.copy', listener);
  109. // when
  110. copyPaste.copy(attacher);
  111. // then
  112. expect(listener).to.have.been.called;
  113. expect(clipboard.isEmpty()).to.be.true;
  114. })
  115. );
  116. it('should fire "elements.copied" after elements have been copied',
  117. inject(function(eventBus, clipboard, copyPaste) {
  118. var called = false;
  119. eventBus.on('elements.copied', function(event) {
  120. var context = event.context,
  121. tree = context.tree;
  122. // then
  123. expect(clipboard.isEmpty()).to.be.false;
  124. expect(clipboard.get()).to.eql(tree);
  125. called = true;
  126. });
  127. // when
  128. copyPaste.copy([ parentShape2 ]);
  129. expect(called).to.be.true;
  130. })
  131. );
  132. });
  133. describe('copy', function() {
  134. it('should return copied elements', inject(function(copyPaste, clipboard) {
  135. // when
  136. var copyResult = copyPaste.copy([ parentShape2 ]);
  137. // then
  138. expect(copyResult).to.exist;
  139. expect(copyResult).to.equal(clipboard.get());
  140. }));
  141. it('should copy parent + children, when element is selected', inject(function(copyPaste, clipboard) {
  142. // when
  143. copyPaste.copy([ parentShape2 ]);
  144. var tree = clipboard.get();
  145. // then
  146. expect(toObjectBranch(tree[0])).to.have.keys('parent2');
  147. expect(toObjectBranch(tree[1])).to.have.keys('host', 'childShape', 'childShape2', 'connection');
  148. }));
  149. it('should copy attachers + connections', inject(function(copyPaste, clipboard) {
  150. // when
  151. copyPaste.copy([ host, childShape, childShape2 ]);
  152. var tree = clipboard.get();
  153. // then
  154. expect(toObjectBranch(tree[0])).to.have.keys('host', 'childShape', 'childShape2', 'connection');
  155. }));
  156. it('should not copy connection without target or source', inject(function(copyPaste, clipboard) {
  157. var tree;
  158. // when
  159. copyPaste.copy([ childShape2, connection ]);
  160. tree = clipboard.get();
  161. // then
  162. expect(toObjectBranch(tree[0])).not.to.have.keys('connection');
  163. expect(tree[1]).not.to.exist;
  164. // when
  165. copyPaste.copy([ childShape, connection ]);
  166. tree = clipboard.get();
  167. // then
  168. expect(toObjectBranch(tree[0])).not.to.have.keys('connection');
  169. expect(tree[1]).not.to.exist;
  170. }));
  171. it('should not copy attacher based on rules', inject(function(copyPaste, clipboard) {
  172. // given
  173. copyPaste.copy(attacher);
  174. var tree = clipboard.get();
  175. // then
  176. expect(tree).not.to.exist;
  177. }));
  178. });
  179. describe('paste', function() {
  180. it('should paste', inject(function(copyPaste) {
  181. // when
  182. copyPaste.copy([ host, childShape ]);
  183. copyPaste.paste({
  184. element: parentShape,
  185. point: {
  186. x: 900,
  187. y: 350
  188. }
  189. });
  190. // then
  191. expect(parentShape.children).to.have.length(2);
  192. }));
  193. describe('rule integration', function() {
  194. it('should exclude element based on rule', inject(function(copyPaste) {
  195. // given
  196. copyPaste.copy([ childShape, childShape2 ]);
  197. copyPaste.paste({
  198. element: parentShape,
  199. point: {
  200. x: 900,
  201. y: 350
  202. }
  203. });
  204. // then
  205. expect(parentShape.children).to.have.length(2);
  206. }));
  207. it('should reject overall paste based on rule', inject(function(copyPaste, clipboard, eventBus) {
  208. // given
  209. var listener = sinon.spy();
  210. eventBus.on('elements.paste.rejected', listener);
  211. // when
  212. copyPaste.copy([ childShape, childShape2 ]);
  213. copyPaste.paste({
  214. element: parentShape2,
  215. point: {
  216. x: 900,
  217. y: 350
  218. }
  219. });
  220. // then
  221. expect(listener).to.have.been.called;
  222. }));
  223. });
  224. describe('post-paste selection', function() {
  225. it('should select top-level element', inject(function(copyPaste, clipboard, selection) {
  226. var selectedElement;
  227. // when
  228. copyPaste.copy([ parentShape2 ]);
  229. copyPaste.paste({
  230. element: parentShape,
  231. point: {
  232. x: 900,
  233. y: 350
  234. }
  235. });
  236. selectedElement = selection.get();
  237. // then
  238. expect(selectedElement).to.have.length(1);
  239. }));
  240. it('should multiple top-level elements', inject(function(copyPaste, clipboard, selection) {
  241. var selectedElements;
  242. // when
  243. copyPaste.copy([ childShape, childShape2 ]);
  244. copyPaste.paste({
  245. element: parentShape,
  246. point: {
  247. x: 900,
  248. y: 350
  249. }
  250. });
  251. selectedElements = selection.get();
  252. // then
  253. expect(selectedElements).to.have.length(2);
  254. }));
  255. });
  256. describe('hints', function() {
  257. it('should create non-root elements with { root: false } hint', inject(function(copyPaste, eventBus) {
  258. // given
  259. var listener = sinon.spy(function(event) {
  260. var context = event.context;
  261. expect(context.hints).to.exist;
  262. if (context.parent === parentShape) {
  263. expect(context.hints.root).not.to.exist;
  264. } else {
  265. expect(context.hints.root).to.be.false;
  266. }
  267. });
  268. // when
  269. eventBus.on('commandStack.shape.create.preExecute', listener);
  270. copyPaste.copy([ parentShape2, childShape2 ]);
  271. copyPaste.paste({
  272. element: parentShape,
  273. point: {
  274. x: 900,
  275. y: 350
  276. }
  277. });
  278. // then
  279. expect(listener).to.have.been.called;
  280. }));
  281. });
  282. });
  283. });
  284. describe('#createTree', function() {
  285. var sB = { id: 'b', parent: { id: 'a' }, x: 0, y: 0, width: 100, height: 100 },
  286. sC = { id: 'c', parent: sB, x: 0, y: 0, width: 100, height: 100 },
  287. sD = { id: 'd', parent: sB, x: 0, y: 0, width: 100, height: 100 },
  288. sE = { id: 'e', parent: { id: 'y' }, x: 0, y: 0, width: 100, height: 100 },
  289. sF = { id: 'f', parent: sE, x: 0, y: 0, width: 100, height: 100 },
  290. sG = { id: 'g', parent: sF, x: 0, y: 0, width: 100, height: 100 },
  291. sW = { id: 'w', parent: { id: 'z' }, x: 0, y: 0, width: 100, height: 100 };
  292. var cA = { id: 'connA', parent: sB, source: sC, target: sD,
  293. waypoints: [ { x: 0, y: 0, original: { x: 50, y: 50 } } ] },
  294. cB = { id: 'connB', parent: { id: 'p' }, source: sC, target: sW,
  295. waypoints: [ { x: 0, y: 0 } ] };
  296. var host = { id: 'host', parent: { id: 't' }, x: 0, y: 0, width: 100, height: 100 },
  297. attacher = { id: 'attacher', parent: { id: 't' }, x: 0, y: 0, width: 100, height: 100 };
  298. sB.children = [ sC, sD, cA ];
  299. sF.children = [ sG ];
  300. sE.children = [ sF ];
  301. sC.outgoing = cA;
  302. sD.incoming = cA;
  303. host.attachers = [ attacher ];
  304. attacher.host = host;
  305. it('should create tree of shapes', inject(function(copyPaste) {
  306. // when
  307. var tree = copyPaste.createTree([ sE, sF, sG ]);
  308. var branchZero = toObjectBranch(tree[0]),
  309. branchOne = toObjectBranch(tree[1]),
  310. branchTwo = toObjectBranch(tree[2]);
  311. // then
  312. expect(branchZero.e).to.exist;
  313. expect(branchZero.e.parent).to.equal('y');
  314. expect(branchOne.f).to.exist;
  315. expect(branchOne.f.parent).to.equal('e');
  316. expect(branchTwo.g).to.exist;
  317. expect(branchTwo.g.parent).to.equal('f');
  318. }));
  319. it('should create a tree of shapes and connections', inject(function(copyPaste) {
  320. // when
  321. var tree = copyPaste.createTree([ sB, sC, sD, sE, sF, sG, cA, cB ]);
  322. var branchZero = toObjectBranch(tree[0]),
  323. branchOne = toObjectBranch(tree[1]),
  324. branchTwo = toObjectBranch(tree[2]);
  325. // then
  326. expect(branchZero).to.have.keys('b', 'e');
  327. expect(branchOne).to.have.keys('c', 'd', 'f', 'connA');
  328. expect(branchTwo).to.have.keys('g');
  329. expect(branchOne).not.to.have.keys('connB');
  330. expect(branchOne.c.parent).to.equal('b');
  331. expect(branchOne.connA.source).to.equal('c');
  332. expect(branchOne.connA.target).to.equal('d');
  333. }));
  334. it('should create a tree of everything', inject(function(copyPaste) {
  335. // when
  336. var tree = copyPaste.createTree([ sB, sC, sD, sE, sF, sG, cA, cB, host ]);
  337. var branchZero = toObjectBranch(tree[0]),
  338. branchOne = toObjectBranch(tree[1]),
  339. branchTwo = toObjectBranch(tree[2]);
  340. // then
  341. expect(branchZero).to.have.keys('b', 'e', 'host');
  342. expect(branchOne).to.have.keys('c', 'd', 'f', 'connA');
  343. expect(branchTwo).to.have.keys('g');
  344. }));
  345. });
  346. });