|
1 | | -# @flowiseai/agentflow Test Plan |
| 1 | +# @flowiseai/agentflow — Testing Guide |
2 | 2 |
|
3 | 3 | ## Running Tests |
4 | 4 |
|
5 | 5 | ```bash |
6 | | -# Fast run (no coverage) |
7 | | -pnpm test |
| 6 | +pnpm test # Fast run (no coverage) |
| 7 | +pnpm test:coverage # With coverage enforcement |
| 8 | +pnpm test:watch # Watch mode during development |
| 9 | +``` |
8 | 10 |
|
9 | | -# With coverage enforcement |
10 | | -pnpm test:coverage |
| 11 | +## Test Strategy |
11 | 12 |
|
12 | | -# Watch mode during development |
13 | | -pnpm test:watch |
14 | | -``` |
| 13 | +Tests are prioritized by impact. When modifying a file, add or update tests in the same PR. |
15 | 14 |
|
16 | | -## Test Coverage by Tier |
| 15 | +### Tier 1 — Core Logic (must test) |
17 | 16 |
|
18 | | -Add tests when actively working on these files. Each tier reflects impact and testability. |
| 17 | +Pure business logic in `core/`, `infrastructure/`, and critical hooks. These carry the highest risk — a bug here affects every user. Always test in the same PR when modifying. |
19 | 18 |
|
20 | | -### Tier 1 — Core Logic |
| 19 | +**What belongs here:** validation rules, node utilities, API clients, state management (reducers, context actions), flow data hooks (`useFlowHandlers`). |
21 | 20 |
|
22 | | -These modules carry the highest risk. Test in the same PR when modifying. |
| 21 | +### Tier 2 — Feature Hooks & Dialogs (test when changing) |
23 | 22 |
|
24 | | -<!-- prettier-ignore --> |
25 | | -| File | Key exports to test | Status | |
26 | | -| --- | --- | --- | |
27 | | -| `src/core/validation/` | `validateFlow`, `validateNode` — empty flows, missing/multiple starts, disconnected nodes, cycles, required inputs; `isValidConnectionAgentflowV2` — self-connections, cycle detection | ✅ Done | |
28 | | -| `src/core/utils/` | `getUniqueNodeId`, `getUniqueNodeLabel`, `initNode`, `generateExportFlowData` | ✅ Done | |
29 | | -| `src/core/node-catalog/` | `filterNodesByComponents`, `isAgentflowNode`, `groupNodesByCategory` | ✅ Done | |
30 | | -| `src/core/node-config/` | `getAgentflowIcon`, `getNodeColor` | ✅ Done | |
31 | | -| `src/core/theme/tokens.ts` | All design tokens — node colors, light/dark variants, spacing scale, semantic colors, ReactFlow colors, shadows, border radius, gradients | ✅ Done | |
32 | | -| `src/core/theme/cssVariables.ts` | `generateCSSVariables()` — valid CSS strings, all variables, correct light/dark values, proper formatting, consistency with tokens | ✅ Done | |
33 | | -| `src/core/theme/createAgentflowTheme.ts` | `createAgentflowTheme()` — MUI theme creation, palette mode, colors from tokens, custom card palette, spacing, border radius, consistency | ✅ Done | |
34 | | -| `src/infrastructure/api/client.ts` | `createApiClient` — headers, auth token, 401 interceptor | ✅ Done | |
35 | | -| `src/infrastructure/api/chatflows.ts` | All CRUD + `generateAgentflow` + `getChatModels`, FlowData serialization | ✅ Done | |
36 | | -| `src/infrastructure/api/nodes.ts` | `getAllNodes`, `getNodeByName`, `getNodeIconUrl` | ✅ Done | |
37 | | -| `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 | |
39 | | -| `src/useAgentflow.ts` | `getFlow()`, `toJSON()`, `validate()`, `addNode()`, `clear()`, `fitView()`, `getReactFlowInstance()`, instance stability | ✅ Done | |
40 | | -| `src/features/canvas/hooks/useFlowHandlers.ts` | `handleConnect`, `handleNodesChange`, `handleEdgesChange`, `handleAddNode` — synchronous `onFlowChange` callbacks, dirty tracking, viewport resolution, change filtering | ✅ Done | |
| 23 | +Feature-level hooks and dialog components that orchestrate UI behavior. Test when adding features or fixing bugs. |
41 | 24 |
|
42 | | -### Tier 2 — Feature Hooks & Dialogs |
| 25 | +**What belongs here:** search logic, drag-and-drop, node color calculations, dialog state machines, theme detection. |
43 | 26 |
|
44 | | -Test when adding features or fixing bugs in these areas. |
| 27 | +### Tier 3 — UI Components (test if logic exists) |
45 | 28 |
|
46 | | -<!-- prettier-ignore --> |
47 | | -| File | Key exports to test | Status | |
48 | | -| --- | --- | --- | |
49 | | -| `src/features/node-palette/search.ts` | `fuzzyScore`, `searchNodes`, `debounce` | ✅ Done | |
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, fallback to `node.data.inputs` when API schema unavailable, API schema priority over `data.inputs` | ✅ Done | |
| 29 | +Presentational components that are mostly JSX. Only add tests if the component contains meaningful business logic (e.g., an exported helper function). Pure styling components (`styled.ts`, `MainCard.tsx`, etc.) do not need tests. |
57 | 30 |
|
58 | | -### Tier 3 — UI Components |
| 31 | +## Writing Tests |
59 | 32 |
|
60 | | -Mostly JSX with minimal logic. Only add tests if business logic is introduced. |
| 33 | +### File Extension Convention |
61 | 34 |
|
62 | | -<!-- prettier-ignore --> |
63 | | -| File | Key exports to test | Status | |
64 | | -| --- | --- | --- | |
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 | |
67 | | -| `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 | |
| 35 | +The Jest config uses file extensions to select the test environment: |
68 | 36 |
|
69 | | -Files that are pure styling or data constants (`styled.ts`, `nodeIcons.ts`, `MainCard.tsx`, etc.) do not need dedicated tests. |
| 37 | +| Extension | Environment | When to use | |
| 38 | +| ----------- | ----------------------- | -------------------------------------------------------------------------- | |
| 39 | +| `.test.ts` | **node** (no DOM) | Pure logic — utilities, reducers, data transformations | |
| 40 | +| `.test.tsx` | **jsdom** (browser DOM) | Anything that renders React — `renderHook` with providers, component tests | |
70 | 41 |
|
71 | | -## Test Utilities |
| 42 | +Source files use `.tsx` only when they contain JSX syntax. A hook like `useAgentflow.ts` has no JSX, so it stays `.ts` even though its test is `.test.tsx` (because the test uses `renderHook` with a JSX wrapper). |
72 | 43 |
|
73 | | -### Factory Functions (`src/__test_utils__/factories.ts`) |
| 44 | +### Factory Functions |
74 | 45 |
|
75 | | -Use factory functions to create test fixtures with sensible defaults: |
| 46 | +Use factory functions from `@test-utils/factories` to create test fixtures with sensible defaults: |
76 | 47 |
|
77 | 48 | ```typescript |
78 | 49 | import { makeFlowNode, makeFlowEdge, makeNodeData } from '@test-utils/factories' |
79 | 50 |
|
80 | | -// Create test nodes |
81 | 51 | const node = makeFlowNode('node-1', { |
82 | 52 | type: 'agentflowNode', |
83 | 53 | data: { name: 'llmAgentflow', label: 'LLM' } |
84 | 54 | }) |
85 | 55 |
|
86 | | -// Create test edges |
87 | 56 | const edge = makeFlowEdge('node-1', 'node-2') |
88 | 57 |
|
89 | | -// Create node data (for palette/search tests) |
90 | 58 | const nodeData = makeNodeData({ name: 'llmAgentflow', label: 'LLM' }) |
91 | 59 | ``` |
92 | 60 |
|
93 | | -### Custom Jest Environment |
94 | | - |
95 | | -**File**: `src/__test_utils__/jest-environment-jsdom.js` |
| 61 | +### Mocking Patterns |
96 | 62 |
|
97 | | -Prevents the `canvas` native module from being loaded during jsdom initialization. The canvas package requires native compilation which fails in many environments, but it's only an optional dependency of jsdom and not needed for React component tests. |
| 63 | +**Mocking a module with `jest.mock`:** |
98 | 64 |
|
99 | | -This custom environment intercepts `require('canvas')` at the module level and returns a mock before jsdom tries to load the native binary. |
| 65 | +```typescript |
| 66 | +import { isValidConnectionAgentflowV2 } from '@/core' |
100 | 67 |
|
101 | | -### Module Mocks |
| 68 | +jest.mock('@/core', () => ({ |
| 69 | + isValidConnectionAgentflowV2: jest.fn(() => true), |
| 70 | + getUniqueNodeId: jest.fn(() => 'new-node-1') |
| 71 | +})) |
102 | 72 |
|
103 | | -**ReactFlow Mock** (`src/__mocks__/reactflow.tsx`): Provides mock implementations of ReactFlow components and hooks. |
| 73 | +// Override per-test: |
| 74 | +it('should reject invalid connection', () => { |
| 75 | + ;(isValidConnectionAgentflowV2 as jest.Mock).mockReturnValueOnce(false) |
| 76 | + // ... |
| 77 | +}) |
| 78 | +``` |
104 | 79 |
|
105 | | -Key features: |
| 80 | +**Mocking context hooks:** |
106 | 81 |
|
107 | | -- Uses `forwardRef` for MUI `styled()` compatibility (prevents emotion errors) |
108 | | -- Uses `useState` internally to maintain stable references (prevents infinite re-render loops) |
109 | | -- Exports all commonly used ReactFlow components (`Controls`, `MiniMap`, `Background`, etc.) |
110 | | -- Mocks hooks (`useNodesState`, `useEdgesState`, `useReactFlow`) |
| 82 | +```typescript |
| 83 | +const mockSetDirty = jest.fn() |
| 84 | + |
| 85 | +jest.mock('@/infrastructure/store', () => ({ |
| 86 | + useAgentflowContext: () => ({ |
| 87 | + state: { reactFlowInstance: null }, |
| 88 | + setDirty: mockSetDirty |
| 89 | + }) |
| 90 | +})) |
| 91 | +``` |
111 | 92 |
|
112 | | -**Axios Mock** (`src/__mocks__/axios.ts`): Prevents network errors by mocking all HTTP requests. Returns empty arrays/objects for all API calls to silence network warnings in tests. |
| 93 | +### Module Mocks |
113 | 94 |
|
114 | | -**CSS Mock** (`src/__mocks__/styleMock.js`): Empty object export for CSS imports. |
| 95 | +**ReactFlow** (`src/__mocks__/reactflow.tsx`): Mock implementations of ReactFlow components and hooks. Uses `forwardRef` for MUI `styled()` compatibility and `useState` internally for stable references. |
115 | 96 |
|
116 | | -## File Extension Convention |
| 97 | +**Axios** (`src/__mocks__/axios.ts`): Prevents network errors by mocking all HTTP methods. Returns empty arrays/objects by default. |
117 | 98 |
|
118 | | -The jest config uses file extensions to select the test environment: |
| 99 | +**CSS/SVG** (`src/__mocks__/styleMock.js`): Empty object export for CSS and SVG imports. |
119 | 100 |
|
120 | | -| Extension | Environment | When to use | |
121 | | -| ----------- | ----------------------- | -------------------------------------------------------------------------- | |
122 | | -| `.test.ts` | **node** (no DOM) | Pure logic — utilities, reducers, data transformations | |
123 | | -| `.test.tsx` | **jsdom** (browser DOM) | Anything that renders React — `renderHook` with providers, component tests | |
| 101 | +### Custom Jest Environment |
124 | 102 |
|
125 | | -**Source files** follow a different rule: use `.tsx` only when the file contains JSX syntax. A React hook like `useAgentflow.ts` has no JSX, so it stays `.ts` even though its test file is `.test.tsx` (because the test uses `renderHook` with a JSX wrapper). |
| 103 | +`src/__test_utils__/jest-environment-jsdom.js` intercepts `require('canvas')` and returns a mock before jsdom tries to load the native binary. This prevents build failures in environments without native canvas compilation. |
126 | 104 |
|
127 | 105 | ## Configuration |
128 | 106 |
|
129 | 107 | - **Jest config**: `jest.config.js` — two projects: `unit` (node env, `.test.ts`) and `components` (custom jsdom env, `.test.tsx`) |
130 | | -- **Test environment**: Component tests use custom jsdom environment (`src/__test_utils__/jest-environment-jsdom.js`) to handle canvas loading |
131 | | -- **Import aliases**: `@test-utils` maps to `src/__test_utils__` for convenient imports |
132 | | -- **Coverage thresholds**: uniform 80% floor (`branches`, `functions`, `lines`, `statements`) — see `coverageThreshold` in `jest.config.js` for the full list |
133 | | -- **Coverage exclusions**: |
134 | | - - `src/__test_utils__/**` — test utilities |
135 | | - - `src/__mocks__/**` — module mocks |
| 108 | +- **Import aliases**: `@test-utils` maps to `src/__test_utils__`, `@/` maps to `src/` |
| 109 | +- **Coverage thresholds**: 80% floor for `branches`, `functions`, `lines`, `statements` — see `coverageThreshold` in `jest.config.js` for per-path entries |
| 110 | +- **Coverage exclusions**: `src/__test_utils__/**`, `src/__mocks__/**` |
136 | 111 | - **CI**: `pnpm test:coverage` runs in GitHub Actions between lint and build |
137 | 112 | - **Reports**: `coverage/lcov-report/index.html` for detailed HTML report |
0 commit comments