181 lines
5.5 KiB
TypeScript
181 lines
5.5 KiB
TypeScript
// File: components/workflows/__tests__/visual-builder.test.ts
|
|
// Change Log:
|
|
// - 2026-06-14: สร้างใหม่สำหรับ Phase 3 Coverage
|
|
|
|
import { describe, it, expect } from 'vitest';
|
|
|
|
// Mock ReactFlow to avoid dependency issues
|
|
vi.mock('reactflow', () => ({
|
|
ReactFlow: () => null,
|
|
Controls: () => null,
|
|
Background: () => null,
|
|
Panel: () => null,
|
|
useNodesState: () => [[], () => {}, () => {}],
|
|
useEdgesState: () => [[], () => {}, () => {}],
|
|
addEdge: (params: any, edges: any) => [...edges, params],
|
|
useReactFlow: () => ({ fitView: () => {} }),
|
|
MarkerType: { ArrowClosed: 'arrowclosed' },
|
|
ReactFlowProvider: ({ children }: { children: React.ReactNode }) => children,
|
|
}));
|
|
|
|
// Import helper functions after mocking
|
|
import { createNode, createEdge, parseDSL } from '../visual-builder';
|
|
|
|
describe('visual-builder helper functions', () => {
|
|
describe('createNode', () => {
|
|
it('ควรสร้าง node ปกติ', () => {
|
|
const node = createNode('TestNode', 100);
|
|
|
|
expect(node.id).toBe('TestNode');
|
|
expect(node.type).toBe('default');
|
|
expect(node.data.label).toBe('TestNode\n(No Role)');
|
|
expect(node.data.name).toBe('TestNode');
|
|
});
|
|
|
|
it('ควรสร้าง start node เมื่อ isStart=true', () => {
|
|
const node = createNode('Start', 100, { isStart: true });
|
|
|
|
expect(node.type).toBe('input');
|
|
expect(node.data.type).toBe('START');
|
|
expect(node.style?.background).toBe('#10b981');
|
|
});
|
|
|
|
it('ควรสร้าง end node เมื่อ isEnd=true', () => {
|
|
const node = createNode('End', 100, { isEnd: true });
|
|
|
|
expect(node.type).toBe('output');
|
|
expect(node.data.type).toBe('END');
|
|
expect(node.style?.background).toBe('#ef4444');
|
|
});
|
|
|
|
it('ควรสร้าง condition node เมื่อ isCondition=true', () => {
|
|
const node = createNode('Condition', 100, { isCondition: true });
|
|
|
|
expect(node.style?.background).toBe('#fef3c7');
|
|
expect(node.style?.borderStyle).toBe('dashed');
|
|
});
|
|
|
|
it('ควรใส่ role ใน label เมื่อมี role', () => {
|
|
const node = createNode('Task', 100, { role: 'Manager' });
|
|
|
|
expect(node.data.label).toBe('Task\n(Manager)');
|
|
expect(node.data.role).toBe('Manager');
|
|
});
|
|
});
|
|
|
|
describe('createEdge', () => {
|
|
it('ควรสร้าง edge ระหว่าง source และ target', () => {
|
|
const edge = createEdge('node1', 'node2', 'TRANSITION');
|
|
|
|
expect(edge.source).toBe('node1');
|
|
expect(edge.target).toBe('node2');
|
|
expect(edge.label).toBe('TRANSITION');
|
|
expect(edge.id).toBe('e-node1-TRANSITION-node2');
|
|
});
|
|
|
|
it('ควรมี markerEnd', () => {
|
|
const edge = createEdge('node1', 'node2', 'TRANSITION');
|
|
|
|
expect(edge.markerEnd).toBeDefined();
|
|
});
|
|
});
|
|
|
|
describe('parseDSL', () => {
|
|
it('ควร return empty nodes/edges เมื่อ DSL เป็น empty string', () => {
|
|
const result = parseDSL('');
|
|
|
|
expect(result.nodes).toEqual([]);
|
|
expect(result.edges).toEqual([]);
|
|
});
|
|
|
|
it('ควร return empty nodes/edges เมื่อ JSON parse fail', () => {
|
|
const result = parseDSL('invalid json');
|
|
|
|
expect(result.nodes).toEqual([]);
|
|
expect(result.edges).toEqual([]);
|
|
});
|
|
|
|
it('ควร parse DSL ที่มี states array', () => {
|
|
const dsl = JSON.stringify({
|
|
states: [
|
|
{ name: 'Start', type: 'START', initial: true },
|
|
{ name: 'Review', role: 'Manager' },
|
|
],
|
|
});
|
|
|
|
const result = parseDSL(dsl);
|
|
|
|
expect(result.nodes.length).toBe(2);
|
|
expect(result.nodes[0].data.name).toBe('Start');
|
|
expect(result.nodes[1].data.name).toBe('Review');
|
|
});
|
|
|
|
it('ควร parse DSL ที่มี states object', () => {
|
|
const dsl = JSON.stringify({
|
|
initialState: 'Start',
|
|
states: {
|
|
Start: { initial: true },
|
|
End: { terminal: true },
|
|
},
|
|
});
|
|
|
|
const result = parseDSL(dsl);
|
|
|
|
expect(result.nodes.length).toBe(2);
|
|
expect(result.nodes[0].data.name).toBe('Start');
|
|
expect(result.nodes[1].data.name).toBe('End');
|
|
});
|
|
|
|
it('ควรสร้าง edges จาก transitions', () => {
|
|
const dsl = JSON.stringify({
|
|
states: [
|
|
{ name: 'Start', on: { SUBMIT: { to: 'Review' } } },
|
|
{ name: 'Review' },
|
|
],
|
|
});
|
|
|
|
const result = parseDSL(dsl);
|
|
|
|
expect(result.edges.length).toBe(1);
|
|
expect(result.edges[0].source).toBe('Start');
|
|
expect(result.edges[0].target).toBe('Review');
|
|
});
|
|
|
|
it('ควร handle dslDefinition field', () => {
|
|
const dsl = JSON.stringify({
|
|
dslDefinition: JSON.stringify({
|
|
states: [{ name: 'Start' }],
|
|
}),
|
|
});
|
|
|
|
const result = parseDSL(dsl);
|
|
|
|
expect(result.nodes.length).toBe(1);
|
|
});
|
|
|
|
it('ควร handle role จาก require.role', () => {
|
|
const dsl = JSON.stringify({
|
|
states: [
|
|
{ name: 'Review', on: { SUBMIT: { require: { role: 'Manager' } } } },
|
|
],
|
|
});
|
|
|
|
const result = parseDSL(dsl);
|
|
|
|
expect(result.nodes[0].data.role).toBe('Manager');
|
|
});
|
|
|
|
it('ควร handle role array จาก require.role', () => {
|
|
const dsl = JSON.stringify({
|
|
states: [
|
|
{ name: 'Review', on: { SUBMIT: { require: { role: ['Manager', 'Lead'] } } } },
|
|
],
|
|
});
|
|
|
|
const result = parseDSL(dsl);
|
|
|
|
expect(result.nodes[0].data.role).toBe('Manager, Lead');
|
|
});
|
|
});
|
|
});
|