- Implemented functionality to install skills from GitHub URLs. - Updated API to handle new installation requests from GitHub. - Enhanced UI to allow users to input GitHub skill URLs for installation. - Added translations for new GitHub installation features in English, Thai, and Chinese. - Created tests for the new skill installation service and API routes to ensure proper functionality.
153 lines
4.1 KiB
TypeScript
153 lines
4.1 KiB
TypeScript
// @vitest-environment node
|
|
import { beforeEach, describe, expect, it, vi } from 'vitest';
|
|
|
|
const mocks = vi.hoisted(() => ({
|
|
install: vi.fn(),
|
|
getAllSkillConfigs: vi.fn(),
|
|
updateSkillConfig: vi.fn(),
|
|
openPath: vi.fn(),
|
|
MockSkillInstallServiceError: class MockSkillInstallServiceError extends Error {
|
|
status: number;
|
|
code: string;
|
|
|
|
constructor(message: string, status: number, code: string) {
|
|
super(message);
|
|
this.status = status;
|
|
this.code = code;
|
|
}
|
|
},
|
|
}));
|
|
|
|
vi.mock('@service/skill-install-service', () => ({
|
|
SkillInstallService: class {
|
|
install = mocks.install;
|
|
},
|
|
SkillInstallServiceError: mocks.MockSkillInstallServiceError,
|
|
}));
|
|
|
|
vi.mock('../electron/utils/skill-config', () => ({
|
|
getAllSkillConfigs: mocks.getAllSkillConfigs,
|
|
updateSkillConfig: mocks.updateSkillConfig,
|
|
}));
|
|
|
|
vi.mock('../electron/utils/paths', () => ({
|
|
getOpenClawConfigDir: () => 'C:/Users/Administrator/.openclaw',
|
|
}));
|
|
|
|
vi.mock('electron', () => ({
|
|
shell: {
|
|
openPath: mocks.openPath,
|
|
},
|
|
}));
|
|
|
|
import { normalizeRequest } from '../electron/api/route-utils';
|
|
import { handleSkillRoutes } from '../electron/api/routes/skills';
|
|
|
|
const ctx = {
|
|
gatewayManager: null,
|
|
providerApiService: null,
|
|
mainWindow: null,
|
|
clawHubService: {
|
|
getMarketplaceCapability: vi.fn(),
|
|
search: vi.fn(),
|
|
install: vi.fn(),
|
|
uninstall: vi.fn(),
|
|
listInstalled: vi.fn(),
|
|
openSkillReadme: vi.fn(),
|
|
openSkillPath: vi.fn(),
|
|
},
|
|
} as any;
|
|
|
|
describe('skill install routes', () => {
|
|
beforeEach(() => {
|
|
mocks.install.mockReset();
|
|
mocks.getAllSkillConfigs.mockReset();
|
|
mocks.updateSkillConfig.mockReset();
|
|
mocks.openPath.mockReset();
|
|
});
|
|
|
|
it('keeps /api/clawhub/install compatible with the marketplace payload', async () => {
|
|
mocks.install.mockResolvedValue({
|
|
success: true,
|
|
slug: 'demo-skill',
|
|
baseDir: 'C:/Users/Administrator/.openclaw/skills/demo-skill',
|
|
source: 'marketplace',
|
|
enabled: true,
|
|
});
|
|
|
|
const response = await handleSkillRoutes(normalizeRequest({
|
|
path: '/api/clawhub/install',
|
|
method: 'POST',
|
|
body: JSON.stringify({
|
|
slug: 'demo-skill',
|
|
version: '1.2.3',
|
|
force: true,
|
|
}),
|
|
}), ctx);
|
|
|
|
expect(mocks.install).toHaveBeenCalledWith({
|
|
kind: 'marketplace',
|
|
slug: 'demo-skill',
|
|
version: '1.2.3',
|
|
force: true,
|
|
});
|
|
expect(response?.ok).toBe(true);
|
|
expect(response?.json).toMatchObject({
|
|
success: true,
|
|
slug: 'demo-skill',
|
|
source: 'marketplace',
|
|
});
|
|
});
|
|
|
|
it('handles /api/skills/install for github-url payloads', async () => {
|
|
mocks.install.mockResolvedValue({
|
|
success: true,
|
|
slug: 'minimax-xlsx',
|
|
baseDir: 'C:/Users/Administrator/.openclaw/skills/minimax-xlsx',
|
|
source: 'github-url',
|
|
enabled: true,
|
|
});
|
|
|
|
const response = await handleSkillRoutes(normalizeRequest({
|
|
path: '/api/skills/install',
|
|
method: 'POST',
|
|
body: JSON.stringify({
|
|
kind: 'github-url',
|
|
url: 'https://github.com/MiniMax-AI/skills/blob/main/skills/minimax-xlsx/SKILL.md',
|
|
}),
|
|
}), ctx);
|
|
|
|
expect(mocks.install).toHaveBeenCalledWith({
|
|
kind: 'github-url',
|
|
url: 'https://github.com/MiniMax-AI/skills/blob/main/skills/minimax-xlsx/SKILL.md',
|
|
});
|
|
expect(response?.ok).toBe(true);
|
|
expect(response?.json).toMatchObject({
|
|
success: true,
|
|
slug: 'minimax-xlsx',
|
|
source: 'github-url',
|
|
});
|
|
});
|
|
|
|
it('returns the service status code when install validation fails', async () => {
|
|
mocks.install.mockRejectedValue(new mocks.MockSkillInstallServiceError(
|
|
'GitHub skill URL is invalid.',
|
|
400,
|
|
'invalid_github_url',
|
|
));
|
|
|
|
const response = await handleSkillRoutes(normalizeRequest({
|
|
path: '/api/skills/install',
|
|
method: 'POST',
|
|
body: JSON.stringify({
|
|
kind: 'github-url',
|
|
url: 'https://example.com/not-supported',
|
|
}),
|
|
}), ctx);
|
|
|
|
expect(response?.ok).toBe(false);
|
|
expect(response?.status).toBe(400);
|
|
expect(response?.error).toBe('GitHub skill URL is invalid.');
|
|
});
|
|
});
|