@iress-oss/ids-mcp-server 0.0.1 → 5.14.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE.txt +201 -0
- package/README.md +29 -159
- package/dist/componentHandlers.js +241 -0
- package/dist/componentHandlers.test.js +380 -0
- package/{build → dist}/config.js +5 -5
- package/dist/index.js +53 -0
- package/{build → dist}/iressHandlers.js +52 -46
- package/dist/iressHandlers.test.js +316 -0
- package/{build → dist}/resourceHandlers.js +23 -22
- package/dist/resourceHandlers.test.js +352 -0
- package/{build → dist}/searchHandlers.js +107 -92
- package/dist/searchHandlers.test.js +524 -0
- package/{build → dist}/toolHandler.js +13 -13
- package/dist/toolHandler.test.js +369 -0
- package/dist/tools.js +165 -0
- package/{build → dist}/utils.js +11 -15
- package/dist/utils.test.js +286 -0
- package/{docs/ids → generated/docs}/components-autocomplete-docs.md +3 -3
- package/{docs/ids → generated/docs}/components-autocomplete-recipes-docs.md +17 -51
- package/{docs/ids → generated/docs}/components-card-recipes-docs.md +1 -1
- package/{docs/ids → generated/docs}/components-checkbox-docs.md +6 -19
- package/{docs/ids → generated/docs}/components-checkboxgroup-docs.md +18 -18
- package/{docs/ids → generated/docs}/components-checkboxgroup-recipes-docs.md +9 -9
- package/{docs/ids → generated/docs}/components-col-docs.md +1 -1
- package/{docs/ids → generated/docs}/components-combobox-docs.md +4 -4
- package/{docs/ids → generated/docs}/components-container-docs.md +8 -42
- package/{docs/ids → generated/docs}/components-filter-docs.md +14 -67
- package/{docs/ids → generated/docs}/components-form-docs.md +341 -335
- package/{docs/ids → generated/docs}/components-form-recipes-docs.md +198 -1
- package/{docs/ids → generated/docs}/components-hide-docs.md +16 -70
- package/{docs/ids → generated/docs}/components-icon-docs.md +4 -4
- package/{docs/ids → generated/docs}/components-input-recipes-docs.md +2 -2
- package/{docs/ids → generated/docs}/components-inputcurrency-recipes-docs.md +6 -40
- package/{docs/ids → generated/docs}/components-modal-docs.md +3 -113
- package/generated/docs/components-popover-docs.md +464 -0
- package/{docs/ids → generated/docs}/components-radiogroup-docs.md +21 -21
- package/{docs/ids → generated/docs}/components-richselect-docs.md +149 -111
- package/{docs/ids → generated/docs}/components-row-docs.md +4 -4
- package/{docs/ids → generated/docs}/components-skeleton-docs.md +3 -3
- package/{docs/ids → generated/docs}/components-skeleton-recipes-docs.md +1 -1
- package/{docs/ids → generated/docs}/components-skiplink-docs.md +1 -1
- package/{docs/ids → generated/docs}/components-slideout-docs.md +3 -113
- package/{docs/ids → generated/docs}/components-table-ag-grid-docs.md +109 -137
- package/{docs/ids → generated/docs}/components-table-docs.md +92 -597
- package/{docs/ids → generated/docs}/components-tabset-docs.md +2 -2
- package/{docs/ids → generated/docs}/components-tag-docs.md +1 -1
- package/{docs/ids → generated/docs}/components-toaster-docs.md +5 -5
- package/{docs/ids → generated/docs}/extensions-editor-docs.md +4 -4
- package/generated/docs/foundations-accessibility-docs.md +62 -0
- package/{docs/ids → generated/docs}/foundations-colours-docs.md +1 -1
- package/generated/docs/foundations-consistency-docs.md +52 -0
- package/generated/docs/foundations-content-docs.md +23 -0
- package/generated/docs/foundations-introduction-docs.md +17 -0
- package/generated/docs/foundations-principles-docs.md +70 -0
- package/{docs/ids → generated/docs}/foundations-typography-docs.md +7 -2
- package/generated/docs/foundations-user-experience-docs.md +63 -0
- package/generated/docs/foundations-visual-design-docs.md +46 -0
- package/{docs/ids → generated/docs}/get-started-develop-docs.md +3 -3
- package/generated/docs/guidelines.md +812 -0
- package/{docs/ids → generated/docs}/introduction-docs.md +4 -4
- package/{docs/ids → generated/docs}/patterns-loading-docs.md +332 -2
- package/generated/docs/resources-migration-guides-from-v4-to-v5-docs.md +437 -0
- package/generated/docs/themes-available-themes-docs.md +66 -0
- package/generated/docs/themes-tokens-docs.md +1200 -0
- package/generated/docs/versions-docs.md +17 -0
- package/package.json +42 -14
- package/LICENSE +0 -193
- package/build/componentHandlers.js +0 -205
- package/build/index.js +0 -51
- package/build/tools.js +0 -165
- package/docs/api-reference.md +0 -0
- package/docs/best-practices.md +0 -0
- package/docs/configuration.md +0 -0
- package/docs/examples.md +0 -0
- package/docs/guidelines.md +0 -269
- package/docs/ids/components-popover-docs.md +0 -4
- package/docs/ids/resources-migration-guides-from-v4-to-v5-docs.md +0 -639
- package/docs/ids/themes-available-themes-docs.md +0 -74
- package/docs/ids/themes-tokens-docs.md +0 -4580
- package/docs/ids/versions-docs.md +0 -27
- package/docs/tutorials/basic-integration.md +0 -0
- /package/{build → dist}/types.js +0 -0
- /package/{docs/ids → generated/docs}/components-alert-docs.md +0 -0
- /package/{docs/ids → generated/docs}/components-badge-docs.md +0 -0
- /package/{docs/ids → generated/docs}/components-button-docs.md +0 -0
- /package/{docs/ids → generated/docs}/components-button-recipes-docs.md +0 -0
- /package/{docs/ids → generated/docs}/components-buttongroup-docs.md +0 -0
- /package/{docs/ids → generated/docs}/components-card-docs.md +0 -0
- /package/{docs/ids → generated/docs}/components-divider-docs.md +0 -0
- /package/{docs/ids → generated/docs}/components-expander-docs.md +0 -0
- /package/{docs/ids → generated/docs}/components-field-docs.md +0 -0
- /package/{docs/ids → generated/docs}/components-inline-docs.md +0 -0
- /package/{docs/ids → generated/docs}/components-input-docs.md +0 -0
- /package/{docs/ids → generated/docs}/components-inputcurrency-docs.md +0 -0
- /package/{docs/ids → generated/docs}/components-label-docs.md +0 -0
- /package/{docs/ids → generated/docs}/components-menu-docs.md +0 -0
- /package/{docs/ids → generated/docs}/components-menu-menuitem-docs.md +0 -0
- /package/{docs/ids → generated/docs}/components-navbar-docs.md +0 -0
- /package/{docs/ids → generated/docs}/components-navbar-recipes-docs.md +0 -0
- /package/{docs/ids → generated/docs}/components-panel-docs.md +0 -0
- /package/{docs/ids → generated/docs}/components-placeholder-docs.md +0 -0
- /package/{docs/ids → generated/docs}/components-popover-recipes-docs.md +0 -0
- /package/{docs/ids → generated/docs}/components-progress-docs.md +0 -0
- /package/{docs/ids → generated/docs}/components-radio-docs.md +0 -0
- /package/{docs/ids → generated/docs}/components-readonly-docs.md +0 -0
- /package/{docs/ids → generated/docs}/components-select-docs.md +0 -0
- /package/{docs/ids → generated/docs}/components-slider-docs.md +0 -0
- /package/{docs/ids → generated/docs}/components-spinner-docs.md +0 -0
- /package/{docs/ids → generated/docs}/components-stack-docs.md +0 -0
- /package/{docs/ids → generated/docs}/components-tabset-tab-docs.md +0 -0
- /package/{docs/ids → generated/docs}/components-text-docs.md +0 -0
- /package/{docs/ids → generated/docs}/components-toaster-toast-docs.md +0 -0
- /package/{docs/ids → generated/docs}/components-toggle-docs.md +0 -0
- /package/{docs/ids → generated/docs}/components-tooltip-docs.md +0 -0
- /package/{docs/ids → generated/docs}/components-validationmessage-docs.md +0 -0
- /package/{docs/ids → generated/docs}/contact-us-docs.md +0 -0
- /package/{docs/ids → generated/docs}/extensions-editor-recipes-docs.md +0 -0
- /package/{docs/ids → generated/docs}/frequently-asked-questions-docs.md +0 -0
- /package/{docs/ids → generated/docs}/get-started-using-storybook-docs.md +0 -0
- /package/{docs/ids → generated/docs}/resources-changelog-docs.md +0 -0
- /package/{docs/ids → generated/docs}/resources-code-katas-docs.md +0 -0
- /package/{docs/ids → generated/docs}/themes-introduction-docs.md +0 -0
|
@@ -0,0 +1,524 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Tests for search handlers functionality
|
|
3
|
+
*/
|
|
4
|
+
import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest';
|
|
5
|
+
import { handleGetUsageExamples, handleSearchIdsDocs, handleGetDesignTokens, handleGetDesignGuidelines, } from './searchHandlers.js';
|
|
6
|
+
import * as utils from './utils.js';
|
|
7
|
+
// Mock the utils module
|
|
8
|
+
vi.mock('./utils.js');
|
|
9
|
+
vi.mock('./config.js', () => ({
|
|
10
|
+
DOCS_DIR: '/mock/docs/dir',
|
|
11
|
+
}));
|
|
12
|
+
const mockUtils = vi.mocked(utils);
|
|
13
|
+
describe('searchHandlers', () => {
|
|
14
|
+
beforeEach(() => {
|
|
15
|
+
vi.clearAllMocks();
|
|
16
|
+
});
|
|
17
|
+
afterEach(() => {
|
|
18
|
+
vi.restoreAllMocks();
|
|
19
|
+
});
|
|
20
|
+
describe('handleGetUsageExamples', () => {
|
|
21
|
+
it('should return usage examples for a valid component', () => {
|
|
22
|
+
const mockMarkdownFiles = [
|
|
23
|
+
'components-button-docs.md',
|
|
24
|
+
'components-input-docs.md',
|
|
25
|
+
'recipes-button-variations.md',
|
|
26
|
+
];
|
|
27
|
+
const mockContent = `
|
|
28
|
+
# Button Component
|
|
29
|
+
|
|
30
|
+
## Basic Usage
|
|
31
|
+
|
|
32
|
+
\`\`\`jsx
|
|
33
|
+
<Button variant="primary">Click me</Button>
|
|
34
|
+
\`\`\`
|
|
35
|
+
|
|
36
|
+
## Advanced Example
|
|
37
|
+
|
|
38
|
+
\`\`\`javascript
|
|
39
|
+
const MyButton = () => (
|
|
40
|
+
<Button size="large" onClick={handleClick}>
|
|
41
|
+
Advanced Button
|
|
42
|
+
</Button>
|
|
43
|
+
);
|
|
44
|
+
\`\`\`
|
|
45
|
+
|
|
46
|
+
<Button disabled>Disabled Button</Button>
|
|
47
|
+
`;
|
|
48
|
+
mockUtils.getMarkdownFiles.mockReturnValue(mockMarkdownFiles);
|
|
49
|
+
mockUtils.readFileContent.mockReturnValue(mockContent);
|
|
50
|
+
const result = handleGetUsageExamples({
|
|
51
|
+
component: 'Button',
|
|
52
|
+
});
|
|
53
|
+
expect(result.content).toHaveLength(1);
|
|
54
|
+
expect(result.content[0].type).toBe('text');
|
|
55
|
+
expect(result.content[0].text).toContain('**Button Usage Examples**');
|
|
56
|
+
expect(result.content[0].text).toContain('```jsx');
|
|
57
|
+
expect(result.content[0].text).toContain('<Button variant="primary">');
|
|
58
|
+
expect(result.isError).toBeUndefined();
|
|
59
|
+
});
|
|
60
|
+
it('should find component files including recipes', () => {
|
|
61
|
+
const mockMarkdownFiles = [
|
|
62
|
+
'components-input-docs.md',
|
|
63
|
+
'recipes-button-variations.md', // This should match for 'button'
|
|
64
|
+
'recipes-other-component.md',
|
|
65
|
+
];
|
|
66
|
+
const mockContent = `
|
|
67
|
+
# Button Recipes
|
|
68
|
+
|
|
69
|
+
\`\`\`jsx
|
|
70
|
+
<Button variant="recipe">Recipe Button</Button>
|
|
71
|
+
\`\`\`
|
|
72
|
+
`;
|
|
73
|
+
mockUtils.getMarkdownFiles.mockReturnValue(mockMarkdownFiles);
|
|
74
|
+
mockUtils.readFileContent.mockReturnValue(mockContent);
|
|
75
|
+
const result = handleGetUsageExamples({
|
|
76
|
+
component: 'Button',
|
|
77
|
+
});
|
|
78
|
+
expect(result.content[0].text).toContain('**Button Usage Examples**');
|
|
79
|
+
expect(result.content[0].text).toContain('Recipe Button');
|
|
80
|
+
// Should have processed the recipes file
|
|
81
|
+
expect(mockUtils.readFileContent).toHaveBeenCalledWith(expect.stringContaining('recipes-button-variations.md'));
|
|
82
|
+
});
|
|
83
|
+
it('should filter examples by pattern when provided', () => {
|
|
84
|
+
const mockMarkdownFiles = ['components-button-docs.md'];
|
|
85
|
+
const mockContent = `
|
|
86
|
+
\`\`\`jsx
|
|
87
|
+
<Button variant="primary">Primary</Button>
|
|
88
|
+
\`\`\`
|
|
89
|
+
|
|
90
|
+
\`\`\`jsx
|
|
91
|
+
<Button variant="secondary">Secondary</Button>
|
|
92
|
+
\`\`\`
|
|
93
|
+
|
|
94
|
+
\`\`\`jsx
|
|
95
|
+
<Button size="large">Large Button</Button>
|
|
96
|
+
\`\`\`
|
|
97
|
+
`;
|
|
98
|
+
mockUtils.getMarkdownFiles.mockReturnValue(mockMarkdownFiles);
|
|
99
|
+
mockUtils.readFileContent.mockReturnValue(mockContent);
|
|
100
|
+
const result = handleGetUsageExamples({
|
|
101
|
+
component: 'Button',
|
|
102
|
+
pattern: 'primary',
|
|
103
|
+
});
|
|
104
|
+
expect(result.content[0].text).toContain('(primary pattern)');
|
|
105
|
+
expect(result.content[0].text).toContain('variant="primary"');
|
|
106
|
+
expect(result.content[0].text).not.toContain('variant="secondary"');
|
|
107
|
+
});
|
|
108
|
+
it('should return appropriate message when no component files found', () => {
|
|
109
|
+
mockUtils.getMarkdownFiles.mockReturnValue([
|
|
110
|
+
'components-input-docs.md',
|
|
111
|
+
'components-select-docs.md',
|
|
112
|
+
]);
|
|
113
|
+
const result = handleGetUsageExamples({
|
|
114
|
+
component: 'NonExistent',
|
|
115
|
+
});
|
|
116
|
+
expect(result.content[0].text).toContain('No examples found for "NonExistent"');
|
|
117
|
+
expect(result.content[0].text).toContain('Try: input, select');
|
|
118
|
+
});
|
|
119
|
+
it('should handle file reading errors gracefully', () => {
|
|
120
|
+
const mockMarkdownFiles = ['components-button-docs.md'];
|
|
121
|
+
mockUtils.getMarkdownFiles.mockReturnValue(mockMarkdownFiles);
|
|
122
|
+
mockUtils.readFileContent.mockImplementation(() => {
|
|
123
|
+
throw new Error('File read error');
|
|
124
|
+
});
|
|
125
|
+
const consoleSpy = vi
|
|
126
|
+
.spyOn(console, 'error')
|
|
127
|
+
.mockImplementation(() => { });
|
|
128
|
+
const result = handleGetUsageExamples({
|
|
129
|
+
component: 'Button',
|
|
130
|
+
});
|
|
131
|
+
expect(consoleSpy).toHaveBeenCalledWith(expect.stringContaining('Error reading file'), expect.any(Error));
|
|
132
|
+
expect(result.content[0].text).toContain('No usage examples found');
|
|
133
|
+
consoleSpy.mockRestore();
|
|
134
|
+
});
|
|
135
|
+
it('should handle multiple file reading errors', () => {
|
|
136
|
+
const mockMarkdownFiles = [
|
|
137
|
+
'components-button-docs.md',
|
|
138
|
+
'recipes-button-variations.md',
|
|
139
|
+
];
|
|
140
|
+
mockUtils.getMarkdownFiles.mockReturnValue(mockMarkdownFiles);
|
|
141
|
+
mockUtils.readFileContent.mockImplementation(() => {
|
|
142
|
+
throw new Error('Multiple file read errors');
|
|
143
|
+
});
|
|
144
|
+
const consoleSpy = vi
|
|
145
|
+
.spyOn(console, 'error')
|
|
146
|
+
.mockImplementation(() => { });
|
|
147
|
+
const result = handleGetUsageExamples({
|
|
148
|
+
component: 'Button',
|
|
149
|
+
});
|
|
150
|
+
expect(consoleSpy).toHaveBeenCalledTimes(2);
|
|
151
|
+
expect(consoleSpy).toHaveBeenCalledWith('Error reading file components-button-docs.md:', expect.any(Error));
|
|
152
|
+
expect(consoleSpy).toHaveBeenCalledWith('Error reading file recipes-button-variations.md:', expect.any(Error));
|
|
153
|
+
expect(result.content[0].text).toContain('No usage examples found');
|
|
154
|
+
consoleSpy.mockRestore();
|
|
155
|
+
});
|
|
156
|
+
it('should validate input parameters using zod schema', () => {
|
|
157
|
+
expect(() => handleGetUsageExamples({})).toThrow();
|
|
158
|
+
expect(() => handleGetUsageExamples({ component: 123 })).toThrow();
|
|
159
|
+
expect(() => handleGetUsageExamples({ component: 'Button', pattern: 123 })).toThrow();
|
|
160
|
+
});
|
|
161
|
+
});
|
|
162
|
+
describe('handleSearchIdsDocs', () => {
|
|
163
|
+
it('should find and return search matches with context', () => {
|
|
164
|
+
const mockMarkdownFiles = [
|
|
165
|
+
'components-button-docs.md',
|
|
166
|
+
'foundations-colors-docs.md',
|
|
167
|
+
];
|
|
168
|
+
const mockButtonContent = `
|
|
169
|
+
# Button Component
|
|
170
|
+
The Button component is used for actions.
|
|
171
|
+
It supports various variants.
|
|
172
|
+
Use primary for main actions.
|
|
173
|
+
`;
|
|
174
|
+
const mockColorsContent = `
|
|
175
|
+
# Colors Foundation
|
|
176
|
+
Primary colors define the brand.
|
|
177
|
+
Secondary colors provide support.
|
|
178
|
+
Use primary sparingly.
|
|
179
|
+
`;
|
|
180
|
+
mockUtils.getMarkdownFiles.mockReturnValue(mockMarkdownFiles);
|
|
181
|
+
mockUtils.readFileContent
|
|
182
|
+
.mockReturnValueOnce(mockButtonContent)
|
|
183
|
+
.mockReturnValueOnce(mockColorsContent);
|
|
184
|
+
const result = handleSearchIdsDocs({
|
|
185
|
+
query: 'primary',
|
|
186
|
+
case_sensitive: false,
|
|
187
|
+
});
|
|
188
|
+
expect(result.content[0].text).toContain('Found 3 matches');
|
|
189
|
+
expect(result.content[0].text).toContain('**button:5**');
|
|
190
|
+
expect(result.content[0].text).toContain('**colors:3**');
|
|
191
|
+
expect(result.content[0].text).toContain('Use primary for main actions');
|
|
192
|
+
expect(result.content[0].text).toContain('Primary colors define the brand');
|
|
193
|
+
});
|
|
194
|
+
it('should respect case sensitivity when specified', () => {
|
|
195
|
+
const mockMarkdownFiles = ['components-button-docs.md'];
|
|
196
|
+
const mockContent = `
|
|
197
|
+
Primary button example
|
|
198
|
+
primary button example
|
|
199
|
+
PRIMARY button example
|
|
200
|
+
`;
|
|
201
|
+
mockUtils.getMarkdownFiles.mockReturnValue(mockMarkdownFiles);
|
|
202
|
+
mockUtils.readFileContent.mockReturnValue(mockContent);
|
|
203
|
+
const caseSensitiveResult = handleSearchIdsDocs({
|
|
204
|
+
query: 'Primary',
|
|
205
|
+
case_sensitive: true,
|
|
206
|
+
});
|
|
207
|
+
const caseInsensitiveResult = handleSearchIdsDocs({
|
|
208
|
+
query: 'Primary',
|
|
209
|
+
case_sensitive: false,
|
|
210
|
+
});
|
|
211
|
+
expect(caseSensitiveResult.content[0].text).toContain('Found 1 matches');
|
|
212
|
+
expect(caseInsensitiveResult.content[0].text).toContain('Found 3 matches');
|
|
213
|
+
});
|
|
214
|
+
it('should return no matches message when query not found', () => {
|
|
215
|
+
const mockMarkdownFiles = ['components-button-docs.md'];
|
|
216
|
+
const mockContent = 'Button component documentation without the search term.';
|
|
217
|
+
mockUtils.getMarkdownFiles.mockReturnValue(mockMarkdownFiles);
|
|
218
|
+
mockUtils.readFileContent.mockReturnValue(mockContent);
|
|
219
|
+
const result = handleSearchIdsDocs({
|
|
220
|
+
query: 'nonexistent',
|
|
221
|
+
case_sensitive: false,
|
|
222
|
+
});
|
|
223
|
+
expect(result.content[0].text).toContain('No matches found for "nonexistent"');
|
|
224
|
+
});
|
|
225
|
+
it('should limit results to 15 matches', () => {
|
|
226
|
+
const mockMarkdownFiles = ['components-button-docs.md'];
|
|
227
|
+
// Create content with 20 lines containing "test"
|
|
228
|
+
const mockContent = Array.from({ length: 20 }, (_, i) => `Line ${i + 1} with test content`).join('\n');
|
|
229
|
+
mockUtils.getMarkdownFiles.mockReturnValue(mockMarkdownFiles);
|
|
230
|
+
mockUtils.readFileContent.mockReturnValue(mockContent);
|
|
231
|
+
const result = handleSearchIdsDocs({
|
|
232
|
+
query: 'test',
|
|
233
|
+
case_sensitive: false,
|
|
234
|
+
});
|
|
235
|
+
// Count the number of **button:** entries in the result
|
|
236
|
+
const matches = (result.content[0].text.match(/\*\*button:\d+\*\*/g) ?? []).length;
|
|
237
|
+
expect(matches).toBe(15);
|
|
238
|
+
});
|
|
239
|
+
it('should use default case_sensitive value when not provided', () => {
|
|
240
|
+
const mockMarkdownFiles = ['components-button-docs.md'];
|
|
241
|
+
const mockContent = 'Primary button and primary action';
|
|
242
|
+
mockUtils.getMarkdownFiles.mockReturnValue(mockMarkdownFiles);
|
|
243
|
+
mockUtils.readFileContent.mockReturnValue(mockContent);
|
|
244
|
+
const result = handleSearchIdsDocs({ query: 'PRIMARY' });
|
|
245
|
+
expect(result.content[0].text).toContain('Found 1 matches');
|
|
246
|
+
});
|
|
247
|
+
it('should handle file reading errors during search', () => {
|
|
248
|
+
const mockMarkdownFiles = [
|
|
249
|
+
'components-button-docs.md',
|
|
250
|
+
'foundations-colors-docs.md',
|
|
251
|
+
];
|
|
252
|
+
mockUtils.getMarkdownFiles.mockReturnValue(mockMarkdownFiles);
|
|
253
|
+
mockUtils.readFileContent.mockImplementation((filePath) => {
|
|
254
|
+
if (filePath.includes('button')) {
|
|
255
|
+
return 'Button content with search term';
|
|
256
|
+
}
|
|
257
|
+
throw new Error('File read error for colors');
|
|
258
|
+
});
|
|
259
|
+
const consoleSpy = vi
|
|
260
|
+
.spyOn(console, 'error')
|
|
261
|
+
.mockImplementation(() => { });
|
|
262
|
+
const result = handleSearchIdsDocs({
|
|
263
|
+
query: 'search',
|
|
264
|
+
case_sensitive: false,
|
|
265
|
+
});
|
|
266
|
+
expect(consoleSpy).toHaveBeenCalledWith('Error reading file foundations-colors-docs.md:', expect.any(Error));
|
|
267
|
+
expect(result.content[0].text).toContain('Found 1 matches');
|
|
268
|
+
expect(result.content[0].text).toContain('Button content with search term');
|
|
269
|
+
consoleSpy.mockRestore();
|
|
270
|
+
});
|
|
271
|
+
});
|
|
272
|
+
describe('handleGetDesignTokens', () => {
|
|
273
|
+
it('should return all design tokens when type is "all"', () => {
|
|
274
|
+
const mockMarkdownFiles = [
|
|
275
|
+
'foundations-colors-docs.md',
|
|
276
|
+
'foundations-spacing-docs.md',
|
|
277
|
+
'foundations-typography-docs.md',
|
|
278
|
+
];
|
|
279
|
+
const mockColorsContent = `
|
|
280
|
+
# Colors
|
|
281
|
+
## Primary Colors
|
|
282
|
+
Use these CSS variables: --iress-color-primary, --iress-color-secondary
|
|
283
|
+
`;
|
|
284
|
+
const mockSpacingContent = `
|
|
285
|
+
# Spacing
|
|
286
|
+
## Base Spacing
|
|
287
|
+
Available variables: --iress-space-sm, --iress-space-md, --iress-space-lg
|
|
288
|
+
`;
|
|
289
|
+
const mockTypographyContent = `
|
|
290
|
+
# Typography
|
|
291
|
+
## Font Sizes
|
|
292
|
+
Typography tokens: --iress-font-size-sm, --iress-font-size-lg
|
|
293
|
+
`;
|
|
294
|
+
mockUtils.getMarkdownFiles.mockReturnValue(mockMarkdownFiles);
|
|
295
|
+
mockUtils.readFileContent
|
|
296
|
+
.mockReturnValueOnce(mockColorsContent)
|
|
297
|
+
.mockReturnValueOnce(mockSpacingContent)
|
|
298
|
+
.mockReturnValueOnce(mockTypographyContent);
|
|
299
|
+
const result = handleGetDesignTokens({ type: 'all' });
|
|
300
|
+
expect(result.content[0].text).toContain('**IDS Design Tokens**');
|
|
301
|
+
expect(result.content[0].text).toContain('**colors**');
|
|
302
|
+
expect(result.content[0].text).toContain('**spacing**');
|
|
303
|
+
expect(result.content[0].text).toContain('**typography**');
|
|
304
|
+
expect(result.content[0].text).toContain('--iress-color-primary');
|
|
305
|
+
expect(result.content[0].text).toContain('--iress-space-sm');
|
|
306
|
+
expect(result.content[0].text).toContain('--iress-font-size-sm');
|
|
307
|
+
});
|
|
308
|
+
it('should filter tokens by specific type', () => {
|
|
309
|
+
const mockMarkdownFiles = [
|
|
310
|
+
'foundations-colors-docs.md',
|
|
311
|
+
'foundations-spacing-docs.md',
|
|
312
|
+
];
|
|
313
|
+
const mockColorsContent = `
|
|
314
|
+
# Colors
|
|
315
|
+
CSS Variables: --iress-color-primary, --iress-color-secondary
|
|
316
|
+
`;
|
|
317
|
+
mockUtils.getMarkdownFiles.mockReturnValue(mockMarkdownFiles);
|
|
318
|
+
mockUtils.readFileContent.mockReturnValue(mockColorsContent);
|
|
319
|
+
const result = handleGetDesignTokens({ type: 'colors' });
|
|
320
|
+
expect(result.content[0].text).toContain('**IDS Design Tokens (colors)**');
|
|
321
|
+
expect(result.content[0].text).toContain('--iress-color-primary');
|
|
322
|
+
// Should only process the colors file
|
|
323
|
+
expect(mockUtils.readFileContent).toHaveBeenCalledTimes(1);
|
|
324
|
+
});
|
|
325
|
+
it('should handle case when no foundation files match the type', () => {
|
|
326
|
+
mockUtils.getMarkdownFiles.mockReturnValue([
|
|
327
|
+
'components-button-docs.md',
|
|
328
|
+
'foundations-other-docs.md',
|
|
329
|
+
]);
|
|
330
|
+
const result = handleGetDesignTokens({ type: 'colors' });
|
|
331
|
+
expect(result.content[0].text).toContain('No design token information found for colors');
|
|
332
|
+
expect(result.content[0].text).toContain('Available foundations: other');
|
|
333
|
+
});
|
|
334
|
+
it('should use default type "all" when not specified', () => {
|
|
335
|
+
const mockMarkdownFiles = ['foundations-colors-docs.md'];
|
|
336
|
+
const mockContent = 'Colors with --iress-color-primary';
|
|
337
|
+
mockUtils.getMarkdownFiles.mockReturnValue(mockMarkdownFiles);
|
|
338
|
+
mockUtils.readFileContent.mockReturnValue(mockContent);
|
|
339
|
+
const result = handleGetDesignTokens({});
|
|
340
|
+
expect(result.content[0].text).toContain('**IDS Design Tokens**');
|
|
341
|
+
expect(result.content[0].text).not.toContain('(colors)');
|
|
342
|
+
});
|
|
343
|
+
it('should extract token sections and CSS variables correctly', () => {
|
|
344
|
+
const mockMarkdownFiles = ['foundations-tokens-docs.md'];
|
|
345
|
+
const mockContent = `
|
|
346
|
+
# Design Tokens
|
|
347
|
+
|
|
348
|
+
## Primary Colors
|
|
349
|
+
Main brand colors
|
|
350
|
+
|
|
351
|
+
### Secondary Colors
|
|
352
|
+
Supporting colors
|
|
353
|
+
|
|
354
|
+
CSS Variables available:
|
|
355
|
+
- --iress-primary-100
|
|
356
|
+
- --iress-primary-200
|
|
357
|
+
- --iress-secondary-100
|
|
358
|
+
`;
|
|
359
|
+
mockUtils.getMarkdownFiles.mockReturnValue(mockMarkdownFiles);
|
|
360
|
+
mockUtils.readFileContent.mockReturnValue(mockContent);
|
|
361
|
+
const result = handleGetDesignTokens({ type: 'all' });
|
|
362
|
+
expect(result.content[0].text).toContain('## Primary Colors');
|
|
363
|
+
expect(result.content[0].text).toContain('### Secondary Colors');
|
|
364
|
+
expect(result.content[0].text).toContain('--iress-primary-');
|
|
365
|
+
expect(result.content[0].text).toContain('--iress-secondary-');
|
|
366
|
+
});
|
|
367
|
+
it('should handle file reading errors during token extraction', () => {
|
|
368
|
+
const mockMarkdownFiles = [
|
|
369
|
+
'foundations-colors-docs.md',
|
|
370
|
+
'foundations-spacing-docs.md',
|
|
371
|
+
];
|
|
372
|
+
mockUtils.getMarkdownFiles.mockReturnValue(mockMarkdownFiles);
|
|
373
|
+
mockUtils.readFileContent.mockImplementation((filePath) => {
|
|
374
|
+
if (filePath.includes('colors')) {
|
|
375
|
+
return 'Colors with --iress-color-primary token';
|
|
376
|
+
}
|
|
377
|
+
throw new Error('File read error for spacing');
|
|
378
|
+
});
|
|
379
|
+
const consoleSpy = vi
|
|
380
|
+
.spyOn(console, 'error')
|
|
381
|
+
.mockImplementation(() => { });
|
|
382
|
+
const result = handleGetDesignTokens({ type: 'all' });
|
|
383
|
+
expect(consoleSpy).toHaveBeenCalledWith('Error reading file foundations-spacing-docs.md:', expect.any(Error));
|
|
384
|
+
expect(result.content[0].text).toContain('**IDS Design Tokens**');
|
|
385
|
+
expect(result.content[0].text).toContain('**colors**');
|
|
386
|
+
expect(result.content[0].text).toContain('--iress-color-primary');
|
|
387
|
+
consoleSpy.mockRestore();
|
|
388
|
+
});
|
|
389
|
+
});
|
|
390
|
+
describe('handleGetDesignGuidelines', () => {
|
|
391
|
+
it('should return full guidelines when no section or query specified', () => {
|
|
392
|
+
const mockGuidelinesContent = `
|
|
393
|
+
# Design Guidelines
|
|
394
|
+
|
|
395
|
+
## Core Design Principles
|
|
396
|
+
1. Consistency
|
|
397
|
+
2. Accessibility
|
|
398
|
+
|
|
399
|
+
## Visual Design Standards
|
|
400
|
+
Typography and color usage
|
|
401
|
+
|
|
402
|
+
## Component Guidelines
|
|
403
|
+
How to use components
|
|
404
|
+
`;
|
|
405
|
+
mockUtils.readFileContent.mockReturnValue(mockGuidelinesContent);
|
|
406
|
+
const result = handleGetDesignGuidelines({});
|
|
407
|
+
expect(result.content[0].text).toContain('**IDS Design Guidelines**');
|
|
408
|
+
expect(result.content[0].text).toContain('# Design Guidelines');
|
|
409
|
+
expect(result.content[0].text).toContain('## Core Design Principles');
|
|
410
|
+
expect(result.content[0].text).toContain('## Visual Design Standards');
|
|
411
|
+
});
|
|
412
|
+
it('should filter by section when specified', () => {
|
|
413
|
+
const mockGuidelinesContent = `
|
|
414
|
+
# Design Guidelines
|
|
415
|
+
|
|
416
|
+
## Core Design Principles
|
|
417
|
+
1. Consistency is key
|
|
418
|
+
2. Accessibility first
|
|
419
|
+
|
|
420
|
+
## Visual Design Standards
|
|
421
|
+
Typography and color usage
|
|
422
|
+
|
|
423
|
+
## Component Guidelines
|
|
424
|
+
How to use components properly
|
|
425
|
+
`;
|
|
426
|
+
mockUtils.readFileContent.mockReturnValue(mockGuidelinesContent);
|
|
427
|
+
const result = handleGetDesignGuidelines({
|
|
428
|
+
section: 'Core Design Principles',
|
|
429
|
+
});
|
|
430
|
+
expect(result.content[0].text).toContain('**IDS Design Guidelines - Core Design Principles**');
|
|
431
|
+
expect(result.content[0].text).toContain('1. Consistency is key');
|
|
432
|
+
expect(result.content[0].text).toContain('2. Accessibility first');
|
|
433
|
+
expect(result.content[0].text).not.toContain('## Visual Design Standards');
|
|
434
|
+
});
|
|
435
|
+
it('should filter by query when specified', () => {
|
|
436
|
+
const mockGuidelinesContent = `
|
|
437
|
+
# Design Guidelines
|
|
438
|
+
|
|
439
|
+
Accessibility is important for all users.
|
|
440
|
+
Make sure colors meet accessibility standards.
|
|
441
|
+
Typography should be accessible.
|
|
442
|
+
|
|
443
|
+
Design should be consistent across components.
|
|
444
|
+
Use consistent spacing and layout.
|
|
445
|
+
`;
|
|
446
|
+
mockUtils.readFileContent.mockReturnValue(mockGuidelinesContent);
|
|
447
|
+
const result = handleGetDesignGuidelines({
|
|
448
|
+
query: 'accessibility',
|
|
449
|
+
});
|
|
450
|
+
expect(result.content[0].text).toContain('**IDS Design Guidelines (filtered by: accessibility)**');
|
|
451
|
+
expect(result.content[0].text).toContain('Accessibility is important');
|
|
452
|
+
expect(result.content[0].text).toContain('colors meet accessibility standards');
|
|
453
|
+
expect(result.content[0].text).toContain('Typography should be accessible');
|
|
454
|
+
expect(result.content[0].text).not.toContain('consistent spacing');
|
|
455
|
+
});
|
|
456
|
+
it('should handle both section and query filters together', () => {
|
|
457
|
+
const mockGuidelinesContent = `
|
|
458
|
+
# Design Guidelines
|
|
459
|
+
|
|
460
|
+
## Accessibility Guidelines
|
|
461
|
+
Accessibility is crucial for inclusive design.
|
|
462
|
+
Colors must meet WCAG standards.
|
|
463
|
+
Typography should be legible.
|
|
464
|
+
|
|
465
|
+
## Design Principles
|
|
466
|
+
Consistency in design elements.
|
|
467
|
+
Accessibility as a core principle.
|
|
468
|
+
`;
|
|
469
|
+
mockUtils.readFileContent.mockReturnValue(mockGuidelinesContent);
|
|
470
|
+
const result = handleGetDesignGuidelines({
|
|
471
|
+
section: 'Accessibility',
|
|
472
|
+
query: 'colors',
|
|
473
|
+
});
|
|
474
|
+
expect(result.content[0].text).toContain('**IDS Design Guidelines - Accessibility (filtered by: colors)**');
|
|
475
|
+
expect(result.content[0].text).toContain('Colors must meet WCAG standards');
|
|
476
|
+
// The filterContentByQuery function includes context lines, so we should expect the typography line to be included
|
|
477
|
+
expect(result.content[0].text).toContain('Typography should be legible');
|
|
478
|
+
});
|
|
479
|
+
it('should return error when section not found', () => {
|
|
480
|
+
const mockGuidelinesContent = `
|
|
481
|
+
# Design Guidelines
|
|
482
|
+
|
|
483
|
+
## Core Principles
|
|
484
|
+
Basic principles
|
|
485
|
+
`;
|
|
486
|
+
mockUtils.readFileContent.mockReturnValue(mockGuidelinesContent);
|
|
487
|
+
const result = handleGetDesignGuidelines({
|
|
488
|
+
section: 'NonExistent Section',
|
|
489
|
+
});
|
|
490
|
+
expect(result.content[0].text).toContain('Section "NonExistent Section" not found');
|
|
491
|
+
expect(result.content[0].text).toContain('Available sections include:');
|
|
492
|
+
});
|
|
493
|
+
it('should return error when query not found', () => {
|
|
494
|
+
const mockGuidelinesContent = `
|
|
495
|
+
# Design Guidelines
|
|
496
|
+
|
|
497
|
+
Basic design information without the search term.
|
|
498
|
+
`;
|
|
499
|
+
mockUtils.readFileContent.mockReturnValue(mockGuidelinesContent);
|
|
500
|
+
const result = handleGetDesignGuidelines({
|
|
501
|
+
query: 'nonexistent',
|
|
502
|
+
});
|
|
503
|
+
expect(result.content[0].text).toContain('No guidelines found matching "nonexistent"');
|
|
504
|
+
expect(result.content[0].text).toContain('Try searching for terms like:');
|
|
505
|
+
});
|
|
506
|
+
it('should handle file reading errors', () => {
|
|
507
|
+
mockUtils.readFileContent.mockImplementation(() => {
|
|
508
|
+
throw new Error('File not found');
|
|
509
|
+
});
|
|
510
|
+
const consoleSpy = vi
|
|
511
|
+
.spyOn(console, 'error')
|
|
512
|
+
.mockImplementation(() => { });
|
|
513
|
+
const result = handleGetDesignGuidelines({});
|
|
514
|
+
expect(result.content[0].text).toContain('Error reading design guidelines');
|
|
515
|
+
expect(consoleSpy).toHaveBeenCalledWith('Error reading guidelines:', expect.any(Error));
|
|
516
|
+
consoleSpy.mockRestore();
|
|
517
|
+
});
|
|
518
|
+
it('should handle empty file content', () => {
|
|
519
|
+
mockUtils.readFileContent.mockReturnValue('');
|
|
520
|
+
const result = handleGetDesignGuidelines({});
|
|
521
|
+
expect(result.content[0].text).toContain('Design guidelines file not found');
|
|
522
|
+
});
|
|
523
|
+
});
|
|
524
|
+
});
|
|
@@ -1,29 +1,29 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Main tool handler dispatcher
|
|
3
3
|
*/
|
|
4
|
-
import { handleFindComponent, handleGetComponentProps, handleListComponents, } from
|
|
5
|
-
import { handleGetUsageExamples, handleSearchIdsDocs, handleGetDesignTokens, handleGetDesignGuidelines, } from
|
|
6
|
-
import { handleGetIressComponentInfo, handleAnalyzeComponentMentions, } from
|
|
4
|
+
import { handleFindComponent, handleGetComponentProps, handleListComponents, } from './componentHandlers.js';
|
|
5
|
+
import { handleGetUsageExamples, handleSearchIdsDocs, handleGetDesignTokens, handleGetDesignGuidelines, } from './searchHandlers.js';
|
|
6
|
+
import { handleGetIressComponentInfo, handleAnalyzeComponentMentions, } from './iressHandlers.js';
|
|
7
7
|
export function handleToolCall(request) {
|
|
8
|
-
const { name, arguments: args } = request.params;
|
|
8
|
+
const { name, arguments: args = {} } = request.params;
|
|
9
9
|
switch (name) {
|
|
10
|
-
case
|
|
10
|
+
case 'find_component':
|
|
11
11
|
return handleFindComponent(args);
|
|
12
|
-
case
|
|
12
|
+
case 'get_component_props':
|
|
13
13
|
return handleGetComponentProps(args);
|
|
14
|
-
case
|
|
14
|
+
case 'get_usage_examples':
|
|
15
15
|
return handleGetUsageExamples(args);
|
|
16
|
-
case
|
|
16
|
+
case 'search_ids_docs':
|
|
17
17
|
return handleSearchIdsDocs(args);
|
|
18
|
-
case
|
|
18
|
+
case 'list_components':
|
|
19
19
|
return handleListComponents(args);
|
|
20
|
-
case
|
|
20
|
+
case 'get_design_tokens':
|
|
21
21
|
return handleGetDesignTokens(args);
|
|
22
|
-
case
|
|
22
|
+
case 'get_iress_component_info':
|
|
23
23
|
return handleGetIressComponentInfo(args);
|
|
24
|
-
case
|
|
24
|
+
case 'analyze_component_mentions':
|
|
25
25
|
return handleAnalyzeComponentMentions(args);
|
|
26
|
-
case
|
|
26
|
+
case 'get_design_guidelines':
|
|
27
27
|
return handleGetDesignGuidelines(args);
|
|
28
28
|
default:
|
|
29
29
|
throw new Error(`Unknown tool: ${name}`);
|