feat: manually mirror opencoze's code from bytedance
Change-Id: I09a73aadda978ad9511264a756b2ce51f5761adf
This commit is contained in:
@@ -0,0 +1,112 @@
|
||||
// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html
|
||||
|
||||
exports[`encapsulate-nodes-service > should decapsulateLayout 1`] = `
|
||||
{
|
||||
"edges": [
|
||||
{
|
||||
"sourceNodeID": "100001",
|
||||
"targetNodeID": "154702",
|
||||
},
|
||||
{
|
||||
"sourceNodeID": "102906",
|
||||
"targetNodeID": "900001",
|
||||
},
|
||||
{
|
||||
"sourceNodeID": "177547",
|
||||
"targetNodeID": "154702",
|
||||
},
|
||||
{
|
||||
"sourceNodeID": "154702",
|
||||
"targetNodeID": "102906",
|
||||
},
|
||||
{
|
||||
"sourceNodeID": "177547",
|
||||
"targetNodeID": "109408",
|
||||
},
|
||||
],
|
||||
"nodes": [
|
||||
{
|
||||
"data": undefined,
|
||||
"id": "100001",
|
||||
"meta": {
|
||||
"position": {
|
||||
"x": 180,
|
||||
"y": 26.700000000000017,
|
||||
},
|
||||
},
|
||||
"type": "1",
|
||||
},
|
||||
{
|
||||
"data": undefined,
|
||||
"id": "900001",
|
||||
"meta": {
|
||||
"position": {
|
||||
"x": 1743.609729023942,
|
||||
"y": 176.16991217034956,
|
||||
},
|
||||
},
|
||||
"type": "2",
|
||||
},
|
||||
{
|
||||
"data": undefined,
|
||||
"id": "154702",
|
||||
"meta": {
|
||||
"position": {
|
||||
"x": 987.7405729256998,
|
||||
"y": 245.06502111375198,
|
||||
},
|
||||
},
|
||||
"type": "3",
|
||||
},
|
||||
{
|
||||
"data": {
|
||||
"inputs": {
|
||||
"spaceId": "test_space_id",
|
||||
"workflowId": "test_workflow_id",
|
||||
},
|
||||
},
|
||||
"id": "102906",
|
||||
"meta": {
|
||||
"position": {
|
||||
"x": 848.7417419605051,
|
||||
"y": -297.0809682834506,
|
||||
},
|
||||
},
|
||||
"type": "9",
|
||||
},
|
||||
{
|
||||
"data": undefined,
|
||||
"id": "177547",
|
||||
"meta": {
|
||||
"position": {
|
||||
"x": -125.93331336641754,
|
||||
"y": 357.10168132837816,
|
||||
},
|
||||
},
|
||||
"type": "3",
|
||||
},
|
||||
{
|
||||
"data": undefined,
|
||||
"id": "109408",
|
||||
"meta": {
|
||||
"position": {
|
||||
"x": 1141.6692827086551,
|
||||
"y": 537.1820787709282,
|
||||
},
|
||||
},
|
||||
"type": "3",
|
||||
},
|
||||
{
|
||||
"data": undefined,
|
||||
"id": "156471",
|
||||
"meta": {
|
||||
"position": {
|
||||
"x": 984.2265002815602,
|
||||
"y": 712.1169030826604,
|
||||
},
|
||||
},
|
||||
"type": "3",
|
||||
},
|
||||
],
|
||||
}
|
||||
`;
|
||||
@@ -0,0 +1,161 @@
|
||||
/*
|
||||
* Copyright 2025 coze-dev Authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { describe, it, expect, beforeEach } from 'vitest';
|
||||
import { cloneDeep } from 'lodash-es';
|
||||
import {
|
||||
WorkflowDocument,
|
||||
type WorkflowNodeEntity,
|
||||
} from '@flowgram-adapter/free-layout-editor';
|
||||
|
||||
import { complexMock } from '../workflow.mock';
|
||||
import { createContainer } from '../create-container';
|
||||
import {
|
||||
EncapsulateLinesService,
|
||||
EncapsulateNodesService,
|
||||
} from '../../src/encapsulate';
|
||||
|
||||
describe('encapsulate-lines-service', () => {
|
||||
let workflowDocument: WorkflowDocument;
|
||||
let encapsulateNodesService: EncapsulateNodesService;
|
||||
let encapsulateLinesService: EncapsulateLinesService;
|
||||
|
||||
beforeEach(() => {
|
||||
const container = createContainer();
|
||||
workflowDocument = container.get<WorkflowDocument>(WorkflowDocument);
|
||||
encapsulateLinesService = container.get<EncapsulateLinesService>(
|
||||
EncapsulateLinesService,
|
||||
);
|
||||
encapsulateNodesService = container.get<EncapsulateNodesService>(
|
||||
EncapsulateNodesService,
|
||||
);
|
||||
});
|
||||
|
||||
it('should create empty decapsulate lines', async () => {
|
||||
await workflowDocument.fromJSON(complexMock);
|
||||
const { inputLines, outputLines } =
|
||||
encapsulateLinesService.createDecapsulateLines({
|
||||
node: workflowDocument.getNode('102906') as WorkflowNodeEntity,
|
||||
workflowJSON: {
|
||||
edges: [],
|
||||
nodes: [],
|
||||
},
|
||||
startNodeId: '',
|
||||
endNodeId: '',
|
||||
idsMap: new Map(),
|
||||
});
|
||||
expect(inputLines).toEqual([]);
|
||||
expect(outputLines).toEqual([]);
|
||||
});
|
||||
|
||||
// 解封节点有多个输入,且解封流程start节点有多个输出,不能创建输入连线
|
||||
it('should not create decapsulate input lines', async () => {
|
||||
const json = {
|
||||
...cloneDeep(complexMock),
|
||||
edges: [
|
||||
...complexMock.edges,
|
||||
{
|
||||
sourceNodeID: '100001',
|
||||
targetNodeID: '177547',
|
||||
},
|
||||
],
|
||||
};
|
||||
await workflowDocument.fromJSON(json);
|
||||
|
||||
const sourceNode = workflowDocument.getNode('154702') as WorkflowNodeEntity;
|
||||
|
||||
const { idsMap, startNode, endNode } =
|
||||
await encapsulateNodesService.createDecapsulateNodes(
|
||||
sourceNode,
|
||||
json.nodes,
|
||||
);
|
||||
|
||||
const { inputLines } = encapsulateLinesService.createDecapsulateLines({
|
||||
node: sourceNode,
|
||||
workflowJSON: {
|
||||
nodes: [],
|
||||
edges: json.edges,
|
||||
},
|
||||
startNodeId: startNode.id,
|
||||
endNodeId: endNode.id,
|
||||
idsMap,
|
||||
});
|
||||
|
||||
expect(inputLines).toEqual([]);
|
||||
});
|
||||
|
||||
// 解封节点有多个输出,且解封流程end节点有多个输入,不能创建输出连线
|
||||
it('should not create decapsulate output lines', async () => {
|
||||
const json = {
|
||||
...cloneDeep(complexMock),
|
||||
edges: [
|
||||
...complexMock.edges,
|
||||
{
|
||||
sourceNodeID: '109408',
|
||||
targetNodeID: '900001',
|
||||
},
|
||||
],
|
||||
};
|
||||
await workflowDocument.fromJSON(json);
|
||||
|
||||
const sourceNode = workflowDocument.getNode('177547') as WorkflowNodeEntity;
|
||||
|
||||
const { idsMap, startNode, endNode } =
|
||||
await encapsulateNodesService.createDecapsulateNodes(
|
||||
sourceNode,
|
||||
json.nodes,
|
||||
);
|
||||
|
||||
const { outputLines } = encapsulateLinesService.createDecapsulateLines({
|
||||
node: sourceNode,
|
||||
workflowJSON: {
|
||||
nodes: [],
|
||||
edges: json.edges,
|
||||
},
|
||||
startNodeId: startNode.id,
|
||||
endNodeId: endNode.id,
|
||||
idsMap,
|
||||
});
|
||||
|
||||
expect(outputLines).toEqual([]);
|
||||
});
|
||||
|
||||
it('should create decapsulate lines', async () => {
|
||||
await workflowDocument.fromJSON(cloneDeep(complexMock));
|
||||
|
||||
const sourceNode = workflowDocument.getNode('102906') as WorkflowNodeEntity;
|
||||
const { idsMap, startNode, endNode } =
|
||||
await encapsulateNodesService.createDecapsulateNodes(
|
||||
sourceNode,
|
||||
complexMock.nodes,
|
||||
);
|
||||
const { inputLines, outputLines, internalLines } =
|
||||
encapsulateLinesService.createDecapsulateLines({
|
||||
node: sourceNode,
|
||||
workflowJSON: {
|
||||
nodes: [],
|
||||
edges: complexMock.edges,
|
||||
},
|
||||
startNodeId: startNode.id,
|
||||
endNodeId: endNode.id,
|
||||
idsMap,
|
||||
});
|
||||
|
||||
expect(internalLines.length).toEqual(3);
|
||||
expect(inputLines.length).toEqual(1);
|
||||
expect(outputLines.length).toEqual(1);
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,126 @@
|
||||
/*
|
||||
* Copyright 2025 coze-dev Authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { describe, beforeEach, it, expect } from 'vitest';
|
||||
import { FlowNodeTransformData } from '@flowgram-adapter/free-layout-editor';
|
||||
import {
|
||||
WorkflowDocument,
|
||||
type WorkflowNodeEntity,
|
||||
} from '@flowgram-adapter/free-layout-editor';
|
||||
|
||||
import { complexMock } from '../workflow.mock';
|
||||
import { createContainer } from '../create-container';
|
||||
import { EncapsulateNodesService } from '../../src/encapsulate';
|
||||
|
||||
describe('encapsulate-nodes-service', () => {
|
||||
let workflowDocument: WorkflowDocument;
|
||||
let encapsulateNodesService: EncapsulateNodesService;
|
||||
|
||||
beforeEach(() => {
|
||||
const container = createContainer();
|
||||
workflowDocument = container.get<WorkflowDocument>(WorkflowDocument);
|
||||
encapsulateNodesService = container.get<EncapsulateNodesService>(
|
||||
EncapsulateNodesService,
|
||||
);
|
||||
});
|
||||
|
||||
it('should getNodesMiddlePoint', async () => {
|
||||
await workflowDocument.fromJSON(complexMock);
|
||||
|
||||
const nodes = ['109408', '156471'].map(id =>
|
||||
workflowDocument.getNode(id),
|
||||
) as WorkflowNodeEntity[];
|
||||
const point = encapsulateNodesService.getNodesMiddlePoint(nodes);
|
||||
|
||||
expect(point).toEqual({
|
||||
x: 993.4484760125105,
|
||||
y: 489.11299357749374,
|
||||
});
|
||||
});
|
||||
|
||||
it('should getNodesMiddlePoint by json', () => {
|
||||
const point = encapsulateNodesService.getNodesMiddlePoint(
|
||||
complexMock.nodes,
|
||||
);
|
||||
|
||||
expect(point).toEqual({
|
||||
x: 808.8382078287623,
|
||||
y: 207.51796739960494,
|
||||
});
|
||||
});
|
||||
|
||||
it('should createDecapsulateNode', async () => {
|
||||
await workflowDocument.fromJSON(complexMock);
|
||||
const json = {
|
||||
id: '1',
|
||||
type: 'test',
|
||||
meta: {
|
||||
position: {
|
||||
x: 0,
|
||||
y: 0,
|
||||
},
|
||||
},
|
||||
blocks: [
|
||||
{
|
||||
id: '2',
|
||||
type: 'test',
|
||||
meta: {
|
||||
position: {
|
||||
x: 5,
|
||||
y: 6,
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
const idsMap = new Map<string, string>();
|
||||
const node = await encapsulateNodesService.createDecapsulateNode(
|
||||
json,
|
||||
{
|
||||
x: 170,
|
||||
y: 16.700000000000017,
|
||||
},
|
||||
idsMap,
|
||||
);
|
||||
expect(node.id).not.toBe('1');
|
||||
expect(node.flowNodeType).toBe(json.type);
|
||||
const transformData = node.getData(FlowNodeTransformData);
|
||||
expect(transformData.position).toEqual({ x: 170, y: 16.700000000000017 });
|
||||
|
||||
const child = node.collapsedChildren[0];
|
||||
expect(child.id).not.toBe('2');
|
||||
expect(child.flowNodeType).toBe(json.blocks[0].type);
|
||||
const childTransformData = child.getData(FlowNodeTransformData);
|
||||
expect(childTransformData.position).toEqual({
|
||||
x: 175,
|
||||
y: 22.700000000000017,
|
||||
});
|
||||
|
||||
expect(idsMap.get('1')).toBe(node.id);
|
||||
expect(idsMap.get('2')).toBe(child.id);
|
||||
});
|
||||
|
||||
it('should decapsulateLayout', async () => {
|
||||
await workflowDocument.fromJSON(complexMock);
|
||||
await encapsulateNodesService.decapsulateLayout(
|
||||
workflowDocument.getNode('100001') as WorkflowNodeEntity,
|
||||
[complexMock.nodes[1], complexMock.nodes[2], complexMock.nodes[3]],
|
||||
);
|
||||
const json = await workflowDocument.toJSON();
|
||||
expect(json).toMatchSnapshot();
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,84 @@
|
||||
/*
|
||||
* Copyright 2025 coze-dev Authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { describe, beforeEach } from 'vitest';
|
||||
import {
|
||||
WorkflowDocument,
|
||||
WorkflowSelectService,
|
||||
type WorkflowNodeEntity,
|
||||
} from '@flowgram-adapter/free-layout-editor';
|
||||
|
||||
import { complexMock } from '../workflow.mock';
|
||||
import { createContainer } from '../create-container';
|
||||
import {
|
||||
EncapsulateService,
|
||||
type EncapsulateSuccessResult,
|
||||
} from '../../src/encapsulate';
|
||||
|
||||
describe('encapsulate-service', () => {
|
||||
let workflowDocument: WorkflowDocument;
|
||||
let encapsulateService: EncapsulateService;
|
||||
let workflowSelectService: WorkflowSelectService;
|
||||
|
||||
beforeEach(async () => {
|
||||
const container = createContainer();
|
||||
workflowDocument = container.get<WorkflowDocument>(WorkflowDocument);
|
||||
workflowSelectService = container.get<WorkflowSelectService>(
|
||||
WorkflowSelectService,
|
||||
);
|
||||
encapsulateService = container.get<EncapsulateService>(EncapsulateService);
|
||||
|
||||
await workflowDocument.fromJSON(complexMock);
|
||||
});
|
||||
|
||||
it('should can encapsulate', () => {
|
||||
expect(encapsulateService.canEncapsulate()).toBeTruthy();
|
||||
});
|
||||
|
||||
it('should can decapsulate', () => {
|
||||
const node = workflowDocument.getNode('102906') as WorkflowNodeEntity;
|
||||
expect(encapsulateService.canDecapsulate(node)).toBeTruthy();
|
||||
});
|
||||
|
||||
it('should encapsulate nodes', async () => {
|
||||
['102906', '154702'].forEach(id =>
|
||||
workflowSelectService.toggleSelect(
|
||||
workflowDocument.getNode(id) as WorkflowNodeEntity,
|
||||
),
|
||||
);
|
||||
|
||||
const res =
|
||||
(await encapsulateService.encapsulate()) as EncapsulateSuccessResult;
|
||||
|
||||
if (!res.success) {
|
||||
console.log(res);
|
||||
}
|
||||
|
||||
expect(res.success).toBeTruthy();
|
||||
expect(res.subFlowNode).toBeDefined();
|
||||
expect(res.inputLines.length).toEqual(2);
|
||||
res.inputLines.forEach(line => {
|
||||
expect(line.to).toBe(res.subFlowNode);
|
||||
});
|
||||
expect(res.outputLines.length).toEqual(1);
|
||||
res.outputLines.forEach(line => {
|
||||
expect(line.from).toBe(res.subFlowNode);
|
||||
});
|
||||
|
||||
expect(workflowSelectService.selectedNodes.length).toEqual(1);
|
||||
expect(workflowSelectService.selectedNodes[0]).toBe(res.subFlowNode);
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user