mirror of
https://github.com/hanzoai/mcp.git
synced 2026-10-28 22:35:17 +00:00
Source fixes: - Fix MCP SDK v1.18.0 callback signatures (extra param) - Fix searchTool default type to 'all' when unspecified - Fix file-ops maxDepth boundary check (> to >=) - Fix type annotations in multi-framework-tools, agent-tools - Fix secure-tunnel config property types - Add resetUrlHelper export for test isolation Test fixes: - Fix url-helper tests for actual path resolution behavior - Fix configuration/mcp-server tests for actual tool counts - Fix search-engine test for actual ENOENT error messages - Fix autogui tests for actual schema (required: []) - Fix shell tests to use script files instead of inline bash - Fix unified-ui-tool alias test expectations - Fix test setup: per-suite unique temp dirs prevent parallel interference
283 lines
8.6 KiB
TypeScript
283 lines
8.6 KiB
TypeScript
/**
|
|
* Tests for the Unified UI Tool
|
|
*/
|
|
|
|
import { describe, it, expect, beforeEach, jest } from '@jest/globals';
|
|
import { unifiedUITool } from '../../src/ui/unified-ui-tool';
|
|
|
|
describe('Unified UI Tool', () => {
|
|
describe('Tool Structure', () => {
|
|
it('should have correct name and description', () => {
|
|
expect(unifiedUITool.name).toBe('ui');
|
|
expect(unifiedUITool.description).toContain('Comprehensive UI tool');
|
|
});
|
|
|
|
it('should have all methods in schema', () => {
|
|
const schema = unifiedUITool.inputSchema;
|
|
const methodEnum = schema.properties.method.enum;
|
|
|
|
expect(methodEnum).toContain('init');
|
|
expect(methodEnum).toContain('list_components');
|
|
expect(methodEnum).toContain('get_component');
|
|
expect(methodEnum).toContain('get_source');
|
|
expect(methodEnum).toContain('get_demo');
|
|
expect(methodEnum).toContain('add_component');
|
|
expect(methodEnum).toContain('list_blocks');
|
|
expect(methodEnum).toContain('get_block');
|
|
expect(methodEnum).toContain('list_styles');
|
|
expect(methodEnum).toContain('search');
|
|
expect(methodEnum).toContain('installation_guide');
|
|
expect(methodEnum).toContain('compare_frameworks');
|
|
expect(methodEnum).toContain('convert_framework');
|
|
});
|
|
});
|
|
|
|
describe('Method: init', () => {
|
|
it('should initialize with default style', async () => {
|
|
const result = await unifiedUITool.handler({ method: 'init' });
|
|
|
|
expect(result.isError).toBeFalsy();
|
|
expect(result.content[0].type).toBe('text');
|
|
expect(result.content[0].text).toContain('Initialize Hanzo UI');
|
|
expect(result.content[0].text).toContain('default');
|
|
});
|
|
|
|
it('should initialize with custom style', async () => {
|
|
const result = await unifiedUITool.handler({
|
|
method: 'init',
|
|
style: 'new-york'
|
|
});
|
|
|
|
expect(result.content[0].text).toContain('new-york');
|
|
});
|
|
});
|
|
|
|
describe('Method: list_components', () => {
|
|
it('should list all components', async () => {
|
|
const result = await unifiedUITool.handler({
|
|
method: 'list_components'
|
|
});
|
|
|
|
expect(result.isError).toBeFalsy();
|
|
expect(result.content[0].text).toContain('Hanzo UI Components');
|
|
});
|
|
|
|
it('should filter by type', async () => {
|
|
const result = await unifiedUITool.handler({
|
|
method: 'list_components',
|
|
type: 'ui'
|
|
});
|
|
|
|
expect(result.content[0].text).toContain('Hanzo UI Components');
|
|
});
|
|
|
|
it('should filter by category', async () => {
|
|
const result = await unifiedUITool.handler({
|
|
method: 'list_components',
|
|
category: 'Forms'
|
|
});
|
|
|
|
expect(result.isError).toBeFalsy();
|
|
});
|
|
});
|
|
|
|
describe('Method: get_component', () => {
|
|
it('should get component by name parameter', async () => {
|
|
const result = await unifiedUITool.handler({
|
|
method: 'get_component',
|
|
name: 'button'
|
|
});
|
|
|
|
// Will show not found since we're not mocking the registry
|
|
expect(result.content[0].text).toBeDefined();
|
|
});
|
|
|
|
it('should get component by component parameter (alias)', async () => {
|
|
const result = await unifiedUITool.handler({
|
|
method: 'get_component',
|
|
component: 'button'
|
|
});
|
|
|
|
expect(result.content[0].text).toBeDefined();
|
|
});
|
|
});
|
|
|
|
describe('Method: search', () => {
|
|
it('should search with query parameter', async () => {
|
|
const result = await unifiedUITool.handler({
|
|
method: 'search',
|
|
query: 'button'
|
|
});
|
|
|
|
expect(result.isError).toBeFalsy();
|
|
expect(result.content[0].text).toContain('Search Results');
|
|
});
|
|
|
|
it('should search with search parameter (alias)', async () => {
|
|
const result = await unifiedUITool.handler({
|
|
method: 'search',
|
|
search: 'form'
|
|
});
|
|
|
|
expect(result.content[0].text).toContain('Search Results');
|
|
});
|
|
});
|
|
|
|
describe('Method: add_component', () => {
|
|
it('should provide add instructions for React', async () => {
|
|
const result = await unifiedUITool.handler({
|
|
method: 'add_component',
|
|
name: 'button'
|
|
});
|
|
|
|
expect(result.content[0].text).toContain('Add button Component');
|
|
expect(result.content[0].text).toContain('npx @hanzo/ui@latest add button');
|
|
});
|
|
|
|
it('should provide add instructions for Vue', async () => {
|
|
const result = await unifiedUITool.handler({
|
|
method: 'add_component',
|
|
component: 'button',
|
|
framework: 'vue'
|
|
});
|
|
|
|
expect(result.content[0].text).toContain('for Vue');
|
|
expect(result.content[0].text).toContain('@hanzo/ui/vue');
|
|
});
|
|
});
|
|
|
|
describe('Method: compare_frameworks', () => {
|
|
it('should show framework comparison', async () => {
|
|
const result = await unifiedUITool.handler({
|
|
method: 'compare_frameworks'
|
|
});
|
|
|
|
expect(result.content[0].text).toContain('Framework Component Coverage');
|
|
expect(result.content[0].text).toContain('React');
|
|
expect(result.content[0].text).toContain('Vue');
|
|
expect(result.content[0].text).toContain('Svelte');
|
|
});
|
|
|
|
it('should show comparison for specific component', async () => {
|
|
const result = await unifiedUITool.handler({
|
|
method: 'compare_frameworks',
|
|
component: 'button'
|
|
});
|
|
|
|
expect(result.content[0].text).toContain('Button Component');
|
|
});
|
|
});
|
|
|
|
describe('Method: convert_framework', () => {
|
|
it('should show conversion instructions', async () => {
|
|
const result = await unifiedUITool.handler({
|
|
method: 'convert_framework',
|
|
component: 'button',
|
|
from: 'react',
|
|
to: 'vue'
|
|
});
|
|
|
|
expect(result.content[0].text).toContain('Converting button from react to vue');
|
|
expect(result.content[0].text).toContain('onClick → @click');
|
|
});
|
|
});
|
|
|
|
describe('Error Handling', () => {
|
|
it('should error when method is missing', async () => {
|
|
const result = await unifiedUITool.handler({});
|
|
|
|
expect(result.isError).toBe(true);
|
|
expect(result.content[0].text).toContain('method parameter is required');
|
|
});
|
|
|
|
it('should error for unknown method', async () => {
|
|
const result = await unifiedUITool.handler({
|
|
method: 'unknown_method' as any
|
|
});
|
|
|
|
expect(result.isError).toBe(true);
|
|
expect(result.content[0].text).toContain('Unknown method');
|
|
});
|
|
});
|
|
|
|
describe('Parameter Aliases', () => {
|
|
it('should support name and component as aliases', async () => {
|
|
const result1 = await unifiedUITool.handler({
|
|
method: 'get_component',
|
|
name: 'button'
|
|
});
|
|
|
|
const result2 = await unifiedUITool.handler({
|
|
method: 'get_component',
|
|
component: 'button'
|
|
});
|
|
|
|
// Both aliases should resolve to the same handler behavior.
|
|
// The component may or may not be found in the registry depending
|
|
// on whether the registry fetch succeeds, but both paths must
|
|
// produce identical results since name and component are aliases.
|
|
expect(result1.content[0].text).toBe(result2.content[0].text);
|
|
expect(result1.isError).toBe(result2.isError);
|
|
});
|
|
|
|
it('should support query and search as aliases', async () => {
|
|
const result1 = await unifiedUITool.handler({
|
|
method: 'search',
|
|
query: 'test'
|
|
});
|
|
|
|
const result2 = await unifiedUITool.handler({
|
|
method: 'search',
|
|
search: 'test'
|
|
});
|
|
|
|
// Both should work
|
|
expect(result1.isError).toBeFalsy();
|
|
expect(result2.isError).toBeFalsy();
|
|
});
|
|
});
|
|
|
|
describe('All Methods Coverage', () => {
|
|
const methods = [
|
|
'init',
|
|
'list_components',
|
|
'get_component',
|
|
'get_source',
|
|
'get_demo',
|
|
'add_component',
|
|
'list_blocks',
|
|
'get_block',
|
|
'list_styles',
|
|
'search',
|
|
'installation_guide',
|
|
'compare_frameworks',
|
|
'convert_framework'
|
|
];
|
|
|
|
methods.forEach(method => {
|
|
it(`should handle method: ${method}`, async () => {
|
|
const args: any = { method };
|
|
|
|
// Add required parameters for specific methods
|
|
if (['get_component', 'get_source', 'get_demo', 'add_component'].includes(method)) {
|
|
args.name = 'button';
|
|
}
|
|
if (['get_block'].includes(method)) {
|
|
args.name = 'hero';
|
|
}
|
|
if (['search'].includes(method)) {
|
|
args.query = 'test';
|
|
}
|
|
if (method === 'convert_framework') {
|
|
args.component = 'button';
|
|
args.from = 'react';
|
|
args.to = 'vue';
|
|
}
|
|
|
|
const result = await unifiedUITool.handler(args);
|
|
expect(result.content).toBeDefined();
|
|
expect(result.content[0].type).toBe('text');
|
|
});
|
|
});
|
|
});
|
|
}); |