manager.test.ts 7.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216
  1. /**
  2. * Copyright (c) 2025 Bytedance Ltd. and/or its affiliates
  3. * SPDX-License-Identifier: MIT
  4. */
  5. import { it, expect, beforeEach, describe, vi } from 'vitest';
  6. import { debounce } from 'lodash-es';
  7. import { interfaces } from 'inversify';
  8. import {
  9. delay,
  10. WorkflowDocument,
  11. WorkflowHoverService,
  12. WorkflowSelectService,
  13. } from '@flowgram.ai/free-layout-core';
  14. import { FlowNodeRenderData } from '@flowgram.ai/document';
  15. import {
  16. EntityManager,
  17. PipelineRegistry,
  18. PipelineRenderer,
  19. PlaygroundConfigEntity,
  20. } from '@flowgram.ai/core';
  21. import { StackingContextManager } from '../src/manager';
  22. import { createWorkflowContainer, workflowJSON } from './utils.mock';
  23. import { IStackingContextManager } from './type.mock';
  24. let container: interfaces.Container;
  25. let document: WorkflowDocument;
  26. let stackingContextManager: IStackingContextManager;
  27. beforeEach(() => {
  28. container = createWorkflowContainer();
  29. container.bind(StackingContextManager).to(StackingContextManager);
  30. document = container.get<WorkflowDocument>(WorkflowDocument);
  31. stackingContextManager = container.get<StackingContextManager>(
  32. StackingContextManager
  33. ) as unknown as IStackingContextManager;
  34. document.fromJSON(workflowJSON);
  35. });
  36. describe('StackingContextManager public methods', () => {
  37. it('should create instance', () => {
  38. const stackingContextManager = container.get<StackingContextManager>(StackingContextManager);
  39. expect(stackingContextManager.node).toMatchInlineSnapshot(`
  40. <div
  41. class="gedit-playground-layer gedit-flow-render-layer"
  42. />
  43. `);
  44. expect(stackingContextManager).not.toBeUndefined();
  45. });
  46. it('should execute init', () => {
  47. stackingContextManager.init();
  48. const pipelineRenderer = container.get<PipelineRenderer>(PipelineRenderer);
  49. expect(pipelineRenderer.node).toMatchInlineSnapshot(
  50. `
  51. <div
  52. class="gedit-playground-pipeline"
  53. >
  54. <div
  55. class="gedit-playground-layer gedit-flow-render-layer"
  56. />
  57. </div>
  58. `
  59. );
  60. expect(stackingContextManager.disposers).toHaveLength(4);
  61. });
  62. it('should execute ready', () => {
  63. stackingContextManager.compute = vi.fn();
  64. stackingContextManager.ready();
  65. expect(stackingContextManager.compute).toBeCalled();
  66. });
  67. it('should dispose', () => {
  68. expect(stackingContextManager.disposers).toHaveLength(0);
  69. stackingContextManager.init();
  70. expect(stackingContextManager.disposers).toHaveLength(4);
  71. const mockDispose = { dispose: vi.fn() };
  72. stackingContextManager.disposers.push(mockDispose);
  73. stackingContextManager.dispose();
  74. expect(mockDispose.dispose).toBeCalled();
  75. });
  76. });
  77. describe('StackingContextManager private methods', () => {
  78. it('should compute with debounce', async () => {
  79. const compute = vi.fn();
  80. vi.spyOn(stackingContextManager, 'compute').mockImplementation(debounce(compute, 10));
  81. stackingContextManager.compute();
  82. await delay(1);
  83. stackingContextManager.compute();
  84. await delay(1);
  85. stackingContextManager.compute();
  86. await delay(1);
  87. stackingContextManager.compute();
  88. expect(compute).toBeCalledTimes(0);
  89. await delay(20);
  90. expect(compute).toBeCalledTimes(1);
  91. });
  92. it('should get nodes and lines', async () => {
  93. const nodeIds = stackingContextManager.nodes.map((n) => n.id);
  94. const lineIds = stackingContextManager.lines.map((l) => l.id);
  95. expect(nodeIds).toEqual([
  96. 'root',
  97. 'start_0',
  98. 'condition_0',
  99. 'end_0',
  100. 'loop_0',
  101. 'break_0',
  102. 'variable_0',
  103. ]);
  104. expect(lineIds).toEqual([
  105. 'break_0_-variable_0_',
  106. 'start_0_-condition_0_',
  107. 'condition_0_if-end_0_',
  108. 'condition_0_else-end_0_',
  109. 'loop_0_-end_0_',
  110. 'start_0_-loop_0_',
  111. ]);
  112. });
  113. it('should generate context', async () => {
  114. const hoverService = container.get<WorkflowHoverService>(WorkflowHoverService);
  115. const selectService = container.get<WorkflowSelectService>(WorkflowSelectService);
  116. expect(stackingContextManager.context).toStrictEqual({
  117. hoveredEntityID: undefined,
  118. selectedIDs: new Set(),
  119. selectedNodes: [],
  120. sortNodes: stackingContextManager.options.sortNodes,
  121. });
  122. hoverService.updateHoveredKey('start_0');
  123. const breakNode = document.getNode('break_0')!;
  124. const variableNode = document.getNode('variable_0')!;
  125. selectService.selection = [breakNode, variableNode];
  126. expect(stackingContextManager.context.hoveredEntityID).toEqual('start_0');
  127. expect(stackingContextManager.context.selectedIDs).toEqual(new Set(['break_0', 'variable_0']));
  128. });
  129. it('should callback compute when onZoom trigger', () => {
  130. const entityManager = container.get<EntityManager>(EntityManager);
  131. const pipelineRegistry = container.get<PipelineRegistry>(PipelineRegistry);
  132. const compute = vi.spyOn(stackingContextManager, 'compute').mockImplementation(() => {});
  133. const playgroundConfig =
  134. entityManager.getEntity<PlaygroundConfigEntity>(PlaygroundConfigEntity)!;
  135. pipelineRegistry.ready();
  136. stackingContextManager.mountListener();
  137. playgroundConfig.updateConfig({
  138. zoom: 1.5,
  139. });
  140. expect(stackingContextManager.node.style.transform).toBe('scale(1.5)');
  141. playgroundConfig.updateConfig({
  142. zoom: 2,
  143. });
  144. expect(stackingContextManager.node.style.transform).toBe('scale(2)');
  145. playgroundConfig.updateConfig({
  146. zoom: 1,
  147. });
  148. expect(stackingContextManager.node.style.transform).toBe('scale(1)');
  149. expect(compute).toBeCalledTimes(3);
  150. });
  151. it('should callback compute when onHover trigger', () => {
  152. const hoverService = container.get<WorkflowHoverService>(WorkflowHoverService);
  153. const compute = vi.spyOn(stackingContextManager, 'compute').mockImplementation(() => {});
  154. stackingContextManager.mountListener();
  155. hoverService.updateHoveredKey('start_0');
  156. hoverService.updateHoveredKey('end_0');
  157. expect(compute).toBeCalledTimes(2);
  158. });
  159. it('should callback compute when onEntityChange trigger', () => {
  160. const entityManager = container.get<EntityManager>(EntityManager);
  161. const compute = vi.spyOn(stackingContextManager, 'compute').mockImplementation(() => {});
  162. const node = document.getNode('start_0')!;
  163. stackingContextManager.mountListener();
  164. entityManager.fireEntityChanged(node);
  165. expect(compute).toBeCalledTimes(1);
  166. });
  167. it('should callback compute when onSelect trigger', () => {
  168. const selectService = container.get<WorkflowSelectService>(WorkflowSelectService);
  169. const compute = vi.spyOn(stackingContextManager, 'compute').mockImplementation(() => {});
  170. stackingContextManager.mountListener();
  171. const breakNode = document.getNode('break_0')!;
  172. const variableNode = document.getNode('variable_0')!;
  173. selectService.selectNode(breakNode);
  174. selectService.selectNode(variableNode);
  175. expect(compute).toBeCalledTimes(2);
  176. });
  177. it('should mount listeners', () => {
  178. const hoverService = container.get<WorkflowHoverService>(WorkflowHoverService);
  179. const selectService = container.get<WorkflowSelectService>(WorkflowSelectService);
  180. const compute = vi.spyOn(stackingContextManager, 'compute').mockImplementation(() => {});
  181. stackingContextManager.mountListener();
  182. // onHover
  183. hoverService.updateHoveredKey('start_0');
  184. hoverService.updateHoveredKey('end_0');
  185. expect(compute).toBeCalledTimes(2);
  186. compute.mockReset();
  187. // select callback
  188. const breakNode = document.getNode('break_0')!;
  189. const variableNode = document.getNode('variable_0')!;
  190. selectService.selectNode(breakNode);
  191. selectService.selectNode(variableNode);
  192. expect(compute).toBeCalledTimes(2);
  193. });
  194. it('should trigger compute', async () => {
  195. stackingContextManager.ready();
  196. await delay(200);
  197. const node = document.getNode('loop_0')!;
  198. const nodeRenderData = node.getData<FlowNodeRenderData>(FlowNodeRenderData);
  199. const element = nodeRenderData.node;
  200. expect(element.style.zIndex).toBe('12');
  201. });
  202. });