flow-document.test.ts 8.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287
  1. /**
  2. * Copyright (c) 2025 Bytedance Ltd. and/or its affiliates
  3. * SPDX-License-Identifier: MIT
  4. */
  5. import { beforeEach, describe, expect, it } from 'vitest';
  6. import { type FlowDocumentJSON, type FlowNodeJSON } from '../src/typings';
  7. import { FlowDocument } from '../src';
  8. import { baseMock, baseMockAddBranch, baseMockAddNode, baseMockNodeEnd } from './flow.mock';
  9. import { createDocumentContainer } from './flow-document-container.mock';
  10. describe('flow-document', () => {
  11. let container = createDocumentContainer();
  12. let document: FlowDocument;
  13. beforeEach(() => {
  14. container = createDocumentContainer();
  15. container.get(FlowDocument).fromJSON(baseMockAddNode);
  16. document = container.get<FlowDocument>(FlowDocument);
  17. });
  18. it('fromJSON', () => {
  19. document = container.get<FlowDocument>(FlowDocument);
  20. expect(document.root.childrenLength).toEqual(3);
  21. expect(document.root.children[0].parent).toEqual(document.root);
  22. expect(document.getNode('$blockOrderIcon$block_0')!.originParent).toEqual(
  23. document.getNode('dynamicSplit_0')
  24. );
  25. expect(document.getNode('$blockOrderIcon$block_0')!.parent).toEqual(
  26. document.getNode('block_0')
  27. );
  28. expect(document.toString()).toEqual(`root
  29. |-- start_0
  30. |-- dynamicSplit_0
  31. |---- $blockIcon$dynamicSplit_0
  32. |---- $inlineBlocks$dynamicSplit_0
  33. |------ block_0
  34. |-------- $blockOrderIcon$block_0
  35. |------ block_1
  36. |-------- $blockOrderIcon$block_1
  37. |-------- noop_0
  38. |-- end_0`);
  39. });
  40. it('fromJSON is equal toJSON', () => {
  41. function normalizeNode(node: FlowNodeJSON): FlowNodeJSON {
  42. return {
  43. ...node,
  44. type: node.type || 'block',
  45. blocks: node.blocks?.map((b) => normalizeNode(b)),
  46. };
  47. }
  48. function jsonEqual(from: FlowDocumentJSON, to: FlowDocumentJSON) {
  49. const nodes = to.nodes.map((node) => normalizeNode(node));
  50. expect(from.nodes).toEqual(nodes);
  51. }
  52. document.fromJSON(baseMock);
  53. jsonEqual(document.toJSON(), baseMock);
  54. document.fromJSON(baseMockAddNode);
  55. jsonEqual(document.toJSON(), baseMockAddNode);
  56. document.fromJSON(baseMockAddBranch);
  57. jsonEqual(document.toJSON(), baseMockAddBranch);
  58. // eslint-disable-next-line guard-for-in
  59. // for (const key in dataList) {
  60. // const json = (dataList as any)[key]
  61. // document.fromJSON(json)
  62. // jsonEqual(document.toJSON(), json)
  63. // }
  64. });
  65. it('fromJSON with cache', () => {
  66. // Step1: 导入初始结构
  67. document.fromJSON(baseMock);
  68. const originNodes = document.getAllNodes().slice();
  69. expect(document.size).toEqual(10);
  70. // Step2: 同一个数据,结构未变化,数据未变化
  71. document.fromJSON(baseMock);
  72. expect(document.size).toEqual(10);
  73. const nodes2 = document.getAllNodes().slice();
  74. expect(originNodes).toEqual(nodes2);
  75. // Step3: 添加一个节点,结构产生变化
  76. document.fromJSON(baseMockAddNode);
  77. expect(document.size).toEqual(11);
  78. const nodes3 = document.getAllNodes().slice();
  79. // 新添加的节点会放在最后边,前面所有的 instance 都不变
  80. expect(nodes3.slice(0, -1)).toEqual(originNodes);
  81. // Step4: 添加一个块
  82. document.fromJSON(baseMockAddBranch);
  83. expect(document.size).toEqual(13); // 会添加两个节点
  84. const nodes4 = document.getAllNodes().slice();
  85. expect(nodes4.slice(0, -3)).toEqual(originNodes);
  86. // Step5: 跑最初始的数据,结构变化,节点被删除了两个
  87. document.fromJSON(baseMock);
  88. expect(document.size).toEqual(10);
  89. const nodes5 = document.getAllNodes().slice();
  90. expect(nodes5).toEqual(originNodes);
  91. });
  92. // it('remove node from JSON', () => {
  93. // document.fromJSON(dataList['split-nested']);
  94. // expect(document.size).toBeGreaterThan(40);
  95. // document.fromJSON(dataList.empty);
  96. // expect(document.size).toEqual(3);
  97. // });
  98. it('add base node', () => {
  99. document.addFromNode('start_0', { id: 'new_noop', type: 'noop' });
  100. expect(document.toString()).toEqual(`root
  101. |-- start_0
  102. |-- new_noop
  103. |-- dynamicSplit_0
  104. |---- $blockIcon$dynamicSplit_0
  105. |---- $inlineBlocks$dynamicSplit_0
  106. |------ block_0
  107. |-------- $blockOrderIcon$block_0
  108. |------ block_1
  109. |-------- $blockOrderIcon$block_1
  110. |-------- noop_0
  111. |-- end_0`);
  112. expect(document.size).toEqual(12);
  113. });
  114. it('add split node', () => {
  115. document.addFromNode('dynamicSplit_0', {
  116. id: 'new_split',
  117. type: 'dynamicSplit',
  118. blocks: [{ id: 'b1' }, { id: 'b2' }],
  119. });
  120. expect(document.toString()).toEqual(`root
  121. |-- start_0
  122. |-- dynamicSplit_0
  123. |---- $blockIcon$dynamicSplit_0
  124. |---- $inlineBlocks$dynamicSplit_0
  125. |------ block_0
  126. |-------- $blockOrderIcon$block_0
  127. |------ block_1
  128. |-------- $blockOrderIcon$block_1
  129. |-------- noop_0
  130. |-- new_split
  131. |---- $blockIcon$new_split
  132. |---- $inlineBlocks$new_split
  133. |------ b1
  134. |-------- $blockOrderIcon$b1
  135. |------ b2
  136. |-------- $blockOrderIcon$b2
  137. |-- end_0`);
  138. expect(document.size).toEqual(18);
  139. document.addFromNode('block_1', {
  140. id: 'new_split2',
  141. type: 'dynamicSplit',
  142. blocks: [{ id: 'nb1' }, { id: 'nb2' }],
  143. });
  144. expect(document.toString()).toMatchSnapshot();
  145. expect(document.size).toEqual(25);
  146. });
  147. it('removeNode', () => {
  148. document.removeNode('dynamicSplit_0');
  149. expect(document.toString()).toEqual(`root
  150. |-- start_0
  151. |-- end_0`);
  152. expect(document.size).toEqual(3);
  153. });
  154. it('removeLeafBlock', () => {
  155. document.removeNode('noop_0');
  156. expect(document.toString()).toEqual(`root
  157. |-- start_0
  158. |-- dynamicSplit_0
  159. |---- $blockIcon$dynamicSplit_0
  160. |---- $inlineBlocks$dynamicSplit_0
  161. |------ block_0
  162. |-------- $blockOrderIcon$block_0
  163. |------ block_1
  164. |-------- $blockOrderIcon$block_1
  165. |-- end_0`);
  166. });
  167. it('removeBlockFistChild', () => {
  168. document = container.get<FlowDocument>(FlowDocument);
  169. document.removeNode('$blockOrderIcon$block_1');
  170. expect(document.toString()).toEqual(`root
  171. |-- start_0
  172. |-- dynamicSplit_0
  173. |---- $blockIcon$dynamicSplit_0
  174. |---- $inlineBlocks$dynamicSplit_0
  175. |------ block_0
  176. |-------- $blockOrderIcon$block_0
  177. |------ block_1
  178. |-------- noop_0
  179. |-- end_0`);
  180. });
  181. it('removeBlockWithChild', () => {
  182. document.removeNode('block_1');
  183. expect(document.toString()).toEqual(`root
  184. |-- start_0
  185. |-- dynamicSplit_0
  186. |---- $blockIcon$dynamicSplit_0
  187. |---- $inlineBlocks$dynamicSplit_0
  188. |------ block_0
  189. |-------- $blockOrderIcon$block_0
  190. |-- end_0`);
  191. });
  192. it('flow node type changed', () => {
  193. document.fromJSON({
  194. nodes: [
  195. {
  196. id: 'start_0',
  197. type: 'start',
  198. blocks: [],
  199. },
  200. {
  201. id: 'dynamicSplit_0',
  202. type: 'noop', // Change node type to noop
  203. },
  204. {
  205. id: 'end_0',
  206. type: 'dynamicSplit', // Change node type to dynamicSplit
  207. blocks: [{ id: 'block_0' }, { id: 'block_1' }],
  208. },
  209. ],
  210. });
  211. expect(document.toString()).toEqual(`root
  212. |-- start_0
  213. |-- dynamicSplit_0
  214. |-- end_0
  215. |---- $blockIcon$end_0
  216. |---- $inlineBlocks$end_0
  217. |------ block_0
  218. |-------- $blockOrderIcon$block_0
  219. |------ block_1
  220. |-------- $blockOrderIcon$block_1`);
  221. expect(document.size).toEqual(10);
  222. });
  223. /**
  224. * 分支移动
  225. */
  226. it('flow node move', () => {
  227. document.fromJSON({
  228. nodes: [
  229. {
  230. id: 'start_0',
  231. type: 'start',
  232. blocks: [],
  233. },
  234. {
  235. id: 'end_0',
  236. type: 'end',
  237. blocks: [],
  238. },
  239. {
  240. id: 'dynamicSplit_0',
  241. type: 'dynamicSplit',
  242. blocks: [{ id: 'block_1' }, { id: 'block_0' }], // swap blocks order
  243. },
  244. ],
  245. });
  246. expect(document.toString()).toEqual(`root
  247. |-- start_0
  248. |-- end_0
  249. |-- dynamicSplit_0
  250. |---- $blockIcon$dynamicSplit_0
  251. |---- $inlineBlocks$dynamicSplit_0
  252. |------ block_1
  253. |-------- $blockOrderIcon$block_1
  254. |------ block_0
  255. |-------- $blockOrderIcon$block_0`);
  256. });
  257. /**
  258. * 节点结束判断
  259. */
  260. it('flow is node end', () => {
  261. document.fromJSON(baseMockNodeEnd);
  262. expect(document.getNode('dynamicSplit_0')?.isNodeEnd).toBeTruthy();
  263. expect(document.getNode('block_0')?.isNodeEnd).toBeTruthy();
  264. expect(document.getNode('noop_0')?.isNodeEnd).toBeTruthy();
  265. });
  266. it('node registry add cache', () => {
  267. const registry1 = document.getNodeRegistry('start');
  268. const registry2 = document.getNodeRegistry('start');
  269. expect(registry1 === registry2).toBeTruthy();
  270. expect(
  271. document.getNode('block_0')!.getNodeRegistry() ===
  272. document.getNode('block_1')!.getNodeRegistry()
  273. ).toBeTruthy();
  274. expect(
  275. document.getNode('block_0')!.getNodeMeta() === document.getNode('block_1')!.getNodeMeta()
  276. ).toBeTruthy();
  277. });
  278. it('document is disposed and call toJSON should throw error', () => {
  279. document.dispose();
  280. expect(() => document.toJSON()).toThrowError(/disposed/);
  281. });
  282. });