Skip to content

Commit 86e9fae

Browse files
refactor(agentflow): enhance ESLint configuration and improve test coverage (#5866)
* refactor(agentflow): enhance ESLint configuration and improve test coverage - Updated ESLint configuration to include TypeScript resolver for improved import handling. - Added restrictions on importing from features to enforce architectural boundaries. - Enhanced test coverage for various components, including ConnectionLine and NodeOutputHandles. - Updated architecture documentation to reflect recent changes in file structure and component organization. * fix build error (missing import)
1 parent f0affbd commit 86e9fae

21 files changed

Lines changed: 1676 additions & 191 deletions

packages/agentflow/.eslintrc.js

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,11 @@ module.exports = {
2727
settings: {
2828
react: {
2929
version: 'detect'
30+
},
31+
'import/resolver': {
32+
typescript: {
33+
project: './tsconfig.json'
34+
}
3035
}
3136
},
3237
plugins: ['react', 'react-hooks', '@typescript-eslint', 'unused-imports', 'jsx-a11y', 'simple-import-sort', 'import'],
@@ -75,6 +80,21 @@ module.exports = {
7580
'import/newline-after-import': 'error',
7681
'import/no-duplicates': 'error',
7782
'prettier/prettier': 'error',
83+
// Ban @/features alias — features use relative imports internally, and no other
84+
// layer should import from features (enforced by import/no-restricted-paths below).
85+
// This catches any remaining edge case where the alias would bypass zone checks.
86+
'no-restricted-imports': [
87+
'error',
88+
{
89+
patterns: [
90+
{
91+
group: ['@/features', '@/features/*'],
92+
message:
93+
'@/features alias is not allowed. Features use relative imports internally; other layers cannot import from features.'
94+
}
95+
]
96+
}
97+
],
7898
// Architectural boundary enforcement
7999
'import/no-restricted-paths': [
80100
'error',

packages/agentflow/ARCHITECTURE.md

Lines changed: 25 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ src/
3131
atoms/
3232
├── MainCard.tsx # Styled card wrapper
3333
├── NodeInputHandler.tsx # Form input for node properties
34+
├── ... # Other UI primitives
3435
└── index.ts # Central export
3536
```
3637

@@ -57,41 +58,35 @@ features/
5758
│ ├── containers/ # Smart components with state/logic
5859
│ │ ├── AgentFlowNode.tsx
5960
│ │ ├── AgentFlowEdge.tsx
60-
│ │ ├── StickyNote.tsx
61-
│ │ ├── IterationNode.tsx
61+
│ │ ├── ... # Other node containers
6262
│ │ └── index.ts
6363
│ ├── components/ # Presentational components
64-
│ │ ├── AgentflowHeader.tsx
6564
│ │ ├── ConnectionLine.tsx
66-
│ │ ├── NodeToolbarActions.tsx
67-
│ │ ├── NodeStatusIndicator.tsx
68-
│ │ ├── NodeInfoDialog.tsx
69-
│ │ ├── NodeModelConfigs.tsx
65+
│ │ ├── NodeOutputHandles.tsx
66+
│ │ ├── ... # Other presentational components
7067
│ │ └── index.ts
7168
│ ├── hooks/ # Canvas-related hooks
72-
│ │ ├── useFlowNodes.ts
7369
│ │ ├── useFlowHandlers.ts
7470
│ │ ├── useDragAndDrop.ts
71+
│ │ ├── ... # Other canvas hooks
7572
│ │ └── index.ts
7673
│ ├── styled.ts # Styled components
77-
│ ├── nodeIcons.tsx # Icon utilities
78-
│ ├── canvas.css # Co-located styles
7974
│ └── index.ts # Public API
8075
8176
├── node-palette/ # Add nodes drawer
8277
│ ├── AddNodesDrawer.tsx
83-
│ ├── StyledFab.tsx
8478
│ ├── search.ts # Feature-specific utility (private)
85-
│ └── index.ts # Exports: AddNodesDrawer, StyledFab
79+
│ ├── ...
80+
│ └── index.ts
8681
8782
├── generator/ # AI flow generation
8883
│ ├── GenerateFlowDialog.tsx
89-
│ ├── SuggestionChips.tsx
90-
│ └── index.ts # Exports: GenerateFlowDialog
84+
│ ├── ...
85+
│ └── index.ts
9186
9287
└── node-editor/ # Node property editing
9388
├── EditNodeDialog.tsx
94-
└── index.ts # Exports: EditNodeDialog
89+
└── index.ts
9590
```
9691

9792
**Rules:**
@@ -117,19 +112,21 @@ core/
117112
│ └── index.ts
118113
├── node-config/ # Node configuration (icons, colors, default types)
119114
│ ├── nodeIcons.ts # AGENTFLOW_ICONS, DEFAULT_AGENTFLOW_NODES
120-
│ ├── nodeIconUtils.ts # getAgentflowIcon, getNodeColor
121-
│ └── index.ts
115+
│ └── ...
122116
├── node-catalog/ # Node catalog and filtering logic
123-
│ ├── nodeFilters.ts # filterNodesByComponents, isAgentflowNode, groupNodesByCategory
124-
│ └── index.ts
117+
│ ├── nodeFilters.ts # filterNodesByComponents, isAgentflowNode
118+
│ └── ...
119+
├── theme/ # Design tokens and MUI theming
120+
│ ├── tokens.ts # Color palettes, spacing, shadows
121+
│ ├── createAgentflowTheme.ts
122+
│ └── ...
125123
├── validation/ # Flow validation logic
126124
│ ├── flowValidation.ts # validateFlow, validateNode
127-
│ └── index.ts
125+
│ └── ...
128126
├── utils/ # Generic utilities
129-
│ ├── nodeFactory.ts # initNode, getUniqueNodeId, getUniqueNodeLabel
130-
│ ├── connectionValidation.ts # isValidConnectionAgentflowV2
131-
│ ├── flowExport.ts # generateExportFlowData
132-
│ └── index.ts
127+
│ ├── nodeFactory.ts # initNode, getUniqueNodeId
128+
│ ├── connectionValidation.ts
129+
│ └── ...
133130
└── index.ts # Barrel export (use sparingly)
134131
```
135132

@@ -153,15 +150,13 @@ core/
153150
infrastructure/
154151
├── api/ # API client layer (network requests)
155152
│ ├── client.ts # Axios factory
156-
│ ├── nodes.ts # Nodes API endpoints
157-
│ ├── chatflows.ts # Chatflows API endpoints
153+
│ ├── ... # Endpoint modules (nodes, chatflows, etc.)
158154
│ └── index.ts
159155
160156
└── store/ # State management
161157
├── AgentflowContext.tsx # Flow state context
162-
├── ApiContext.tsx # API client context
163-
├── ConfigContext.tsx # Configuration context
164-
├── useFlowInstance.ts # ReactFlow instance hook
158+
├── agentflowReducer.ts # Reducer for flow state actions
159+
├── ... # Other contexts (ApiContext, ConfigContext)
165160
└── index.ts
166161
```
167162

@@ -352,6 +347,6 @@ import { useFlowHandlers } from '@features/canvas/hooks/useFlowHandlers'
352347
| Type | Convention | Example |
353348
| ----------- | ------------------------- | ------------------------------------- |
354349
| Component | PascalCase.tsx | `AgentFlowNode.tsx` |
355-
| Hook | camelCase.ts (use prefix) | `useFlowInstance.ts` |
350+
| Hook | camelCase.ts (use prefix) | `useFlowHandlers.ts` |
356351
| Logic/Types | camelCase.ts | `flowValidation.ts`, `nodeFilters.ts` |
357352
| Styles | kebab-case (co-located) | `canvas.css` |

packages/agentflow/README.md

Lines changed: 9 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -117,14 +117,15 @@ export default function App() {
117117

118118
### Imperative Methods (via `ref`)
119119

120-
| Method | Return Type | Description |
121-
| ------------------- | ------------------ | -------------------------------- |
122-
| `getFlow()` | `FlowData` | Get current flow data |
123-
| `toJSON()` | `string` | Export flow as JSON string |
124-
| `validate()` | `ValidationResult` | Validate the current flow |
125-
| `fitView()` | `void` | Fit all nodes into view |
126-
| `clear()` | `void` | Remove all nodes and edges |
127-
| `addNode(nodeData)` | `void` | Add a node (`Partial<FlowNode>`) |
120+
| Method | Return Type | Description |
121+
| ------------------------ | ------------------------- | ------------------------------------- |
122+
| `getFlow()` | `FlowData` | Get current flow data |
123+
| `toJSON()` | `string` | Export flow as JSON string |
124+
| `validate()` | `ValidationResult` | Validate the current flow |
125+
| `fitView()` | `void` | Fit all nodes into view |
126+
| `clear()` | `void` | Remove all nodes and edges |
127+
| `addNode(nodeData)` | `void` | Add a node (`Partial<FlowNode>`) |
128+
| `getReactFlowInstance()` | `ReactFlowInstance\|null` | Get the underlying ReactFlow instance |
128129

129130
### Design Note
130131

packages/agentflow/TESTS.md

Lines changed: 12 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@ These modules carry the highest risk. Test in the same PR when modifying.
3535
| `src/infrastructure/api/chatflows.ts` | All CRUD + `generateAgentflow` + `getChatModels`, FlowData serialization | ✅ Done |
3636
| `src/infrastructure/api/nodes.ts` | `getAllNodes`, `getNodeByName`, `getNodeIconUrl` | ✅ Done |
3737
| `src/infrastructure/store/AgentflowContext.tsx` | `agentflowReducer` (all actions), `normalizeNodes`, `deleteNode()`, `duplicateNode()`, `openEditDialog()`, `closeEditDialog()`, `setNodes()`, `setEdges()`, `updateNodeData()`, `deleteEdge()`, state synchronization with local setters. E2E: composite workflow (add→connect→edit→save), load→modify→save roundtrip, multi-edge from single node, rapid connect/disconnect cycles, edge deletion | ✅ Done |
38+
| `src/infrastructure/store/ApiContext.tsx` | `ApiProvider` — client creation, memoization, `useApiContext` error boundary | ✅ Done |
3839
| `src/useAgentflow.ts` | `getFlow()`, `toJSON()`, `validate()`, `addNode()`, `clear()`, `fitView()`, `getReactFlowInstance()`, instance stability | ✅ Done |
3940
| `src/features/canvas/hooks/useFlowHandlers.ts` | `handleConnect`, `handleNodesChange`, `handleEdgesChange`, `handleAddNode` — synchronous `onFlowChange` callbacks, dirty tracking, viewport resolution, change filtering | ✅ Done |
4041

@@ -46,28 +47,23 @@ Test when adding features or fixing bugs in these areas.
4647
| File | Key exports to test | Status |
4748
| --- | --- | --- |
4849
| `src/features/node-palette/search.ts` | `fuzzyScore`, `searchNodes`, `debounce` | ✅ Done |
49-
| `src/features/canvas/hooks/useFlowNodes.ts` | `useFlowNodes()` — category filtering, component whitelist, error states | ⬜ Not yet |
50-
| `src/features/canvas/hooks/useDragAndDrop.ts` | `useDragAndDrop()` — JSON parse error handling, node init on drop | ⬜ Not yet |
51-
| `src/features/canvas/hooks/useNodeColors.ts` | `useNodeColors()` — color calculations for selected/hover/dark mode | ⬜ Not yet |
52-
| `src/infrastructure/store/ConfigContext.tsx` | `ConfigProvider` — theme detection (light/dark/system), media query listener | ⬜ Not yet |
53-
| `src/features/generator/GenerateFlowDialog.tsx` | Dialog state machine — API call flow, error handling, progress state | ⬜ Not yet |
54-
| `src/features/node-editor/EditNodeDialog.tsx` | Label editing — keyboard handling (Enter/Escape), node data updates | ⬜ Not yet |
50+
| `src/features/canvas/hooks/useFlowNodes.ts` | `useFlowNodes()` — category filtering, component whitelist, error states | ✅ Done |
51+
| `src/features/canvas/hooks/useDragAndDrop.ts` | `useDragAndDrop()` — JSON parse error handling, node init on drop | ✅ Done |
52+
| `src/features/canvas/hooks/useNodeColors.ts` | `useNodeColors()` — color calculations for selected/hover/dark mode | ✅ Done |
53+
| `src/infrastructure/store/ConfigContext.tsx` | `ConfigProvider` — theme detection (light/dark/system), media query listener | ✅ Done |
54+
| `src/features/generator/GenerateFlowDialog.tsx` | Dialog state machine — API call flow, error handling, progress state | ✅ Done |
55+
| `src/features/node-editor/EditNodeDialog.tsx` | Label editing — keyboard handling (Enter/Escape), node data updates | ✅ Done |
56+
| `src/features/canvas/hooks/useOpenNodeEditor.ts` | `openNodeEditor()` — node/schema lookup, inputValues initialization, early returns | ✅ Done |
5557

5658
### Tier 3 — UI Components
5759

5860
Mostly JSX with minimal logic. Only add tests if business logic is introduced.
5961

6062
<!-- prettier-ignore -->
61-
| File | When to add tests | Status |
63+
| File | Key exports to test | Status |
6264
| --- | --- | --- |
63-
| `src/features/node-palette/AddNodesDrawer.tsx` | If category grouping or drag serialization logic changes | ⬜ Not yet |
64-
| `src/features/canvas/components/NodeOutputHandles.tsx` | Has `getMinimumNodeHeight()` — test if calculation logic changes | ⬜ Not yet |
65-
| `src/features/canvas/containers/AgentFlowNode.tsx` | If warning state or color logic becomes more complex | ⬜ Not yet |
66-
| `src/features/canvas/containers/AgentFlowEdge.tsx` | If edge deletion or interaction logic changes | ⬜ Not yet |
67-
| `src/features/canvas/containers/IterationNode.tsx` | If resize or dimension calculation logic changes | ⬜ Not yet |
68-
| `src/atoms/NodeInputHandler.tsx` | If input rendering or position calculation logic changes | ⬜ Not yet |
69-
| `src/features/canvas/components/ConnectionLine.tsx` | If edge label determination logic becomes more complex | ⬜ Not yet |
70-
| `src/features/canvas/components/NodeStatusIndicator.tsx` | If status-to-color/icon mapping expands | ⬜ Not yet |
65+
| `src/features/canvas/components/NodeOutputHandles.tsx` | `getMinimumNodeHeight()` — linear scaling, MIN_NODE_HEIGHT floor | ✅ Done |
66+
| `src/features/canvas/components/ConnectionLine.tsx` | Edge label visibility per node type, label content (condition index, humanInput proceed/reject), edge color from AGENTFLOW_ICONS | ✅ Done |
7167
| `src/Agentflow.tsx` | Integration test — dark mode, ThemeProvider, CSS variables, header rendering, keyboard shortcuts (Cmd+S / Ctrl+S save), generate flow dialog, imperative ref | ✅ Done |
7268

7369
Files that are pure styling or data constants (`styled.ts`, `nodeIcons.ts`, `MainCard.tsx`, etc.) do not need dedicated tests.
@@ -133,14 +129,7 @@ The jest config uses file extensions to select the test environment:
133129
- **Jest config**: `jest.config.js` — two projects: `unit` (node env, `.test.ts`) and `components` (custom jsdom env, `.test.tsx`)
134130
- **Test environment**: Component tests use custom jsdom environment (`src/__test_utils__/jest-environment-jsdom.js`) to handle canvas loading
135131
- **Import aliases**: `@test-utils` maps to `src/__test_utils__` for convenient imports
136-
- **Coverage thresholds**: uniform 80% floor (`branches`, `functions`, `lines`, `statements`) enforced per-path:
137-
- `./src/*.ts` (root-level modules like `useAgentflow.ts`)
138-
- `./src/Agentflow.tsx`
139-
- `./src/core/`
140-
- `./src/features/canvas/hooks/useFlowHandlers.ts`
141-
- `./src/features/node-palette/search.ts`
142-
- `./src/infrastructure/api/`
143-
- `./src/infrastructure/store/AgentflowContext.tsx`
132+
- **Coverage thresholds**: uniform 80% floor (`branches`, `functions`, `lines`, `statements`) — see `coverageThreshold` in `jest.config.js` for the full list
144133
- **Coverage exclusions**:
145134
- `src/__test_utils__/**` — test utilities
146135
- `src/__mocks__/**` — module mocks

packages/agentflow/jest.config.js

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -38,10 +38,14 @@ module.exports = {
3838
'./src/*.ts': { branches: 80, functions: 80, lines: 80, statements: 80 },
3939
'./src/Agentflow.tsx': { branches: 80, functions: 80, lines: 80, statements: 80 },
4040
'./src/core/': { branches: 80, functions: 80, lines: 80, statements: 80 },
41-
'./src/features/canvas/hooks/useFlowHandlers.ts': { branches: 80, functions: 80, lines: 80, statements: 80 },
41+
'./src/features/canvas/components/ConnectionLine.tsx': { branches: 80, functions: 80, lines: 80, statements: 80 },
42+
// Only getMinimumNodeHeight() is tested; the component is Tier 3 UI with no business logic
43+
'./src/features/canvas/components/NodeOutputHandles.tsx': { branches: 0, functions: 10, lines: 30, statements: 30 },
44+
'./src/features/canvas/hooks/': { branches: 80, functions: 80, lines: 80, statements: 80 },
45+
'./src/features/generator/GenerateFlowDialog.tsx': { branches: 80, functions: 80, lines: 80, statements: 80 },
46+
'./src/features/node-editor/': { branches: 80, functions: 80, lines: 80, statements: 80 },
4247
'./src/features/node-palette/search.ts': { branches: 80, functions: 80, lines: 80, statements: 80 },
43-
'./src/infrastructure/api/': { branches: 80, functions: 80, lines: 80, statements: 80 },
44-
'./src/infrastructure/store/AgentflowContext.tsx': { branches: 80, functions: 80, lines: 80, statements: 80 }
48+
'./src/infrastructure/': { branches: 80, functions: 80, lines: 80, statements: 80 }
4549
},
4650
projects: [
4751
// .test.ts → node (fast, no DOM)

packages/agentflow/package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,7 @@
8585
"@typescript-eslint/eslint-plugin": "^8.18.2",
8686
"@typescript-eslint/parser": "^8.18.2",
8787
"@vitejs/plugin-react": "^4.2.0",
88+
"eslint-import-resolver-typescript": "4.4.4",
8889
"eslint-plugin-import": "^2.29.0",
8990
"eslint-plugin-simple-import-sort": "^12.0.0",
9091
"jest": "^29.7.0",

packages/agentflow/src/__test_utils__/jest-environment-jsdom.js

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,18 @@ Module.prototype.require = function (id) {
3030
toDataURL: () => ''
3131
}),
3232
createImageData: () => ({ data: [] }),
33-
loadImage: () => Promise.resolve({})
33+
loadImage: () => Promise.resolve({}),
34+
Image: class Image {
35+
constructor() {
36+
this.src = ''
37+
this.width = 0
38+
this.height = 0
39+
this.onload = null
40+
this.onerror = null
41+
this.naturalWidth = 0
42+
this.naturalHeight = 0
43+
}
44+
}
3445
}
3546
}
3647
return originalRequire.apply(this, arguments)

packages/agentflow/src/core/types/index.ts

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,6 @@ import type { ReactFlowInstance } from 'reactflow'
33

44
import type { AxiosInstance } from 'axios'
55

6-
import { EditNodeDialogProps } from '@/features/node-editor/EditNodeDialog'
7-
86
// ============================================================================
97
// Flow Data Types
108
// ============================================================================
@@ -267,14 +265,21 @@ export interface ConfigContextValue {
267265
readOnly: boolean
268266
}
269267

268+
/** Props passed to the edit node dialog (defined here to avoid core → features import) */
269+
export interface EditDialogProps {
270+
inputParams?: InputParam[]
271+
data?: NodeData
272+
disabled?: boolean
273+
}
274+
270275
export interface AgentflowState {
271276
nodes: FlowNode[]
272277
edges: FlowEdge[]
273278
chatflow: FlowConfig | null
274279
isDirty: boolean
275280
reactFlowInstance: ReactFlowInstance | null
276281
editingNodeId: string | null
277-
editDialogProps: EditNodeDialogProps['dialogProps'] | null
282+
editDialogProps: EditDialogProps | null
278283
}
279284

280285
export type AgentflowAction =
@@ -283,7 +288,7 @@ export type AgentflowAction =
283288
| { type: 'SET_CHATFLOW'; payload: FlowConfig | null }
284289
| { type: 'SET_DIRTY'; payload: boolean }
285290
| { type: 'SET_REACTFLOW_INSTANCE'; payload: ReactFlowInstance | null }
286-
| { type: 'OPEN_EDIT_DIALOG'; payload: { nodeId: string; dialogProps: EditNodeDialogProps['dialogProps'] } }
291+
| { type: 'OPEN_EDIT_DIALOG'; payload: { nodeId: string; dialogProps: EditDialogProps } }
287292
| { type: 'CLOSE_EDIT_DIALOG' }
288293
| { type: 'RESET' }
289294

0 commit comments

Comments
 (0)