mirror of
https://gitee.com/ByteDance/flowgram.ai.git
synced 2025-07-07 17:43:29 +08:00
* feat(demo): create group tool * feat(demo): create group shortcut * refactor(core): split group service, controller, utils files * feat(history): free history add group operations * feat(demo): group node render * feat(demo): group node registry * refactor(stack): remove layer computing config * feat(stack): line stackIndex cannot be recalculated by default * feat(demo): group title & color palette acess form * feat(demo): ungroup button & shortcut * feat(demo): create group & ungroup operation register to free history service * refactor(group): group shortcuts move to group-plugin * refactor(group): group node render move to group-plugin * fix(group): undo/redo of create node or ungroup not work * perf(history): free history remove async operation * feat(group): trigger select box inside group * fix(group): container inside group * fix(group): auto layout should not be affected by group node * feat(container): support multi-layer nested containers * fix(group): group css variables overwrite each other * fix(container): node move in or out group shouldn't clear lines * feat(demo): node should follow mouse after move out container button clicked * feat(container): disable group move to non-group container node * fix(container): cross-level node moving causing coord offset * feat(demo): comment node support more button * fix(demo): comment in container fromJSON * feat(container): node into container show move out tips * feat(group): node into group show move out tips * feat(group): delete group when blocks is empty * refactor(group): createFreeGroupPlugin move to container-plugin * refactor(demo): replace disablePorts with defaultPorts * fix(demo): react warning * refactor(group): group plugin built-in GroupNodeRegistry * refactor(group): create free-group-plugin * fix(ci): lock & ts-check & test errors
212 lines
7.7 KiB
TypeScript
212 lines
7.7 KiB
TypeScript
import { it, expect, beforeEach, describe, vi } from 'vitest';
|
|
import { debounce } from 'lodash';
|
|
import { interfaces } from 'inversify';
|
|
import {
|
|
delay,
|
|
WorkflowDocument,
|
|
WorkflowHoverService,
|
|
WorkflowSelectService,
|
|
} from '@flowgram.ai/free-layout-core';
|
|
import { FlowNodeRenderData } from '@flowgram.ai/document';
|
|
import {
|
|
EntityManager,
|
|
PipelineRegistry,
|
|
PipelineRenderer,
|
|
PlaygroundConfigEntity,
|
|
} from '@flowgram.ai/core';
|
|
|
|
import { StackingContextManager } from '../src/manager';
|
|
import { createWorkflowContainer, workflowJSON } from './utils.mock';
|
|
import { IStackingContextManager } from './type.mock';
|
|
|
|
let container: interfaces.Container;
|
|
let document: WorkflowDocument;
|
|
let stackingContextManager: IStackingContextManager;
|
|
|
|
beforeEach(async () => {
|
|
container = createWorkflowContainer();
|
|
container.bind(StackingContextManager).to(StackingContextManager);
|
|
document = container.get<WorkflowDocument>(WorkflowDocument);
|
|
stackingContextManager = container.get<StackingContextManager>(
|
|
StackingContextManager
|
|
) as unknown as IStackingContextManager;
|
|
await document.fromJSON(workflowJSON);
|
|
});
|
|
|
|
describe('StackingContextManager public methods', () => {
|
|
it('should create instance', () => {
|
|
const stackingContextManager = container.get<StackingContextManager>(StackingContextManager);
|
|
expect(stackingContextManager.node).toMatchInlineSnapshot(`
|
|
<div
|
|
class="gedit-playground-layer gedit-flow-render-layer"
|
|
/>
|
|
`);
|
|
expect(stackingContextManager).not.toBeUndefined();
|
|
});
|
|
it('should execute init', () => {
|
|
stackingContextManager.init();
|
|
const pipelineRenderer = container.get<PipelineRenderer>(PipelineRenderer);
|
|
expect(pipelineRenderer.node).toMatchInlineSnapshot(
|
|
`
|
|
<div
|
|
class="gedit-playground-pipeline"
|
|
>
|
|
<div
|
|
class="gedit-playground-layer gedit-flow-render-layer"
|
|
/>
|
|
</div>
|
|
`
|
|
);
|
|
expect(stackingContextManager.disposers).toHaveLength(4);
|
|
});
|
|
it('should execute ready', () => {
|
|
stackingContextManager.compute = vi.fn();
|
|
stackingContextManager.ready();
|
|
expect(stackingContextManager.compute).toBeCalled();
|
|
});
|
|
it('should dispose', () => {
|
|
expect(stackingContextManager.disposers).toHaveLength(0);
|
|
stackingContextManager.init();
|
|
expect(stackingContextManager.disposers).toHaveLength(4);
|
|
const mockDispose = { dispose: vi.fn() };
|
|
stackingContextManager.disposers.push(mockDispose);
|
|
stackingContextManager.dispose();
|
|
expect(mockDispose.dispose).toBeCalled();
|
|
});
|
|
});
|
|
|
|
describe('StackingContextManager private methods', () => {
|
|
it('should compute with debounce', async () => {
|
|
const compute = vi.fn();
|
|
vi.spyOn(stackingContextManager, 'compute').mockImplementation(debounce(compute, 10));
|
|
stackingContextManager.compute();
|
|
await delay(1);
|
|
stackingContextManager.compute();
|
|
await delay(1);
|
|
stackingContextManager.compute();
|
|
await delay(1);
|
|
stackingContextManager.compute();
|
|
expect(compute).toBeCalledTimes(0);
|
|
await delay(20);
|
|
expect(compute).toBeCalledTimes(1);
|
|
});
|
|
|
|
it('should get nodes and lines', async () => {
|
|
const nodeIds = stackingContextManager.nodes.map((n) => n.id);
|
|
const lineIds = stackingContextManager.lines.map((l) => l.id);
|
|
expect(nodeIds).toEqual([
|
|
'root',
|
|
'start_0',
|
|
'condition_0',
|
|
'end_0',
|
|
'loop_0',
|
|
'break_0',
|
|
'variable_0',
|
|
]);
|
|
expect(lineIds).toEqual([
|
|
'break_0_-variable_0_',
|
|
'start_0_-condition_0_',
|
|
'condition_0_if-end_0_',
|
|
'condition_0_else-end_0_',
|
|
'loop_0_-end_0_',
|
|
'start_0_-loop_0_',
|
|
]);
|
|
});
|
|
|
|
it('should generate context', async () => {
|
|
const hoverService = container.get<WorkflowHoverService>(WorkflowHoverService);
|
|
const selectService = container.get<WorkflowSelectService>(WorkflowSelectService);
|
|
expect(stackingContextManager.context).toStrictEqual({
|
|
hoveredEntity: undefined,
|
|
hoveredEntityID: undefined,
|
|
selectedEntities: [],
|
|
selectedIDs: [],
|
|
});
|
|
hoverService.updateHoveredKey('start_0');
|
|
const breakNode = document.getNode('break_0')!;
|
|
const variableNode = document.getNode('variable_0')!;
|
|
selectService.selection = [breakNode, variableNode];
|
|
expect(stackingContextManager.context.hoveredEntityID).toEqual('start_0');
|
|
expect(stackingContextManager.context.selectedIDs).toEqual(['break_0', 'variable_0']);
|
|
});
|
|
|
|
it('should callback compute when onZoom trigger', () => {
|
|
const entityManager = container.get<EntityManager>(EntityManager);
|
|
const pipelineRegistry = container.get<PipelineRegistry>(PipelineRegistry);
|
|
const compute = vi.spyOn(stackingContextManager, 'compute').mockImplementation(() => {});
|
|
const playgroundConfig =
|
|
entityManager.getEntity<PlaygroundConfigEntity>(PlaygroundConfigEntity)!;
|
|
pipelineRegistry.ready();
|
|
stackingContextManager.mountListener();
|
|
playgroundConfig.updateConfig({
|
|
zoom: 1.5,
|
|
});
|
|
expect(stackingContextManager.node.style.transform).toBe('scale(1.5)');
|
|
playgroundConfig.updateConfig({
|
|
zoom: 2,
|
|
});
|
|
expect(stackingContextManager.node.style.transform).toBe('scale(2)');
|
|
playgroundConfig.updateConfig({
|
|
zoom: 1,
|
|
});
|
|
expect(stackingContextManager.node.style.transform).toBe('scale(1)');
|
|
expect(compute).toBeCalledTimes(3);
|
|
});
|
|
|
|
it('should callback compute when onHover trigger', () => {
|
|
const hoverService = container.get<WorkflowHoverService>(WorkflowHoverService);
|
|
const compute = vi.spyOn(stackingContextManager, 'compute').mockImplementation(() => {});
|
|
stackingContextManager.mountListener();
|
|
hoverService.updateHoveredKey('start_0');
|
|
hoverService.updateHoveredKey('end_0');
|
|
expect(compute).toBeCalledTimes(2);
|
|
});
|
|
|
|
it('should callback compute when onEntityChange trigger', () => {
|
|
const entityManager = container.get<EntityManager>(EntityManager);
|
|
const compute = vi.spyOn(stackingContextManager, 'compute').mockImplementation(() => {});
|
|
const node = document.getNode('start_0')!;
|
|
stackingContextManager.mountListener();
|
|
entityManager.fireEntityChanged(node);
|
|
expect(compute).toBeCalledTimes(1);
|
|
});
|
|
|
|
it('should callback compute when onSelect trigger', () => {
|
|
const selectService = container.get<WorkflowSelectService>(WorkflowSelectService);
|
|
const compute = vi.spyOn(stackingContextManager, 'compute').mockImplementation(() => {});
|
|
stackingContextManager.mountListener();
|
|
const breakNode = document.getNode('break_0')!;
|
|
const variableNode = document.getNode('variable_0')!;
|
|
selectService.selectNode(breakNode);
|
|
selectService.selectNode(variableNode);
|
|
expect(compute).toBeCalledTimes(2);
|
|
});
|
|
|
|
it('should mount listeners', () => {
|
|
const hoverService = container.get<WorkflowHoverService>(WorkflowHoverService);
|
|
const selectService = container.get<WorkflowSelectService>(WorkflowSelectService);
|
|
const compute = vi.spyOn(stackingContextManager, 'compute').mockImplementation(() => {});
|
|
stackingContextManager.mountListener();
|
|
// onHover
|
|
hoverService.updateHoveredKey('start_0');
|
|
hoverService.updateHoveredKey('end_0');
|
|
expect(compute).toBeCalledTimes(2);
|
|
compute.mockReset();
|
|
// select callback
|
|
const breakNode = document.getNode('break_0')!;
|
|
const variableNode = document.getNode('variable_0')!;
|
|
selectService.selectNode(breakNode);
|
|
selectService.selectNode(variableNode);
|
|
expect(compute).toBeCalledTimes(2);
|
|
});
|
|
|
|
it('should trigger compute', async () => {
|
|
stackingContextManager.ready();
|
|
await delay(200);
|
|
const node = document.getNode('loop_0')!;
|
|
const nodeRenderData = node.getData<FlowNodeRenderData>(FlowNodeRenderData);
|
|
const element = nodeRenderData.node;
|
|
expect(element.style.zIndex).toBe('12');
|
|
});
|
|
});
|