skillhub/web/src/features/skill/install-command.test.ts
eldenring_saver e93adaaaaa feat(web): replace clawhub with skillhub CLI in frontend
- Replace all `npx clawhub` references with `npx motovis-skillhub` / `skillhub`
- Update namespace separator from `--` to `/` in install commands
- Change env var CLAWHUB_REGISTRY to SKILLHUB_REGISTRY
- Add vite dev server plugin to serve /registry/skill.md
- Update i18n strings (zh/en): ClawHub CLI → SkillHub CLI
- Update skill.md/skill.md.template with new CLI commands
- Update quick-start.tsx syntax highlighting and env config
- Update e2e test assertions

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-16 15:49:06 +00:00

95 lines
3 KiB
TypeScript

import { createElement } from 'react'
import { renderToStaticMarkup } from 'react-dom/server'
import { afterEach, describe, expect, it, vi } from 'vitest'
import { InstallCommand, buildInstallCommand, buildInstallTarget, getBaseUrl } from './install-command'
vi.mock('react-i18next', () => ({
useTranslation: () => ({
t: (key: string) => key,
}),
}))
describe('install-command', () => {
const originalWindow = globalThis.window
function setMockWindow(appBaseUrl?: string) {
const location = {
protocol: 'https:',
host: 'fallback.example.com',
} satisfies Pick<Location, 'protocol' | 'host'>
Object.defineProperty(globalThis, 'window', {
configurable: true,
writable: true,
value: {
__SKILLHUB_RUNTIME_CONFIG__: {
appBaseUrl,
},
location,
} satisfies {
location: Pick<Location, 'protocol' | 'host'>
} & {
__SKILLHUB_RUNTIME_CONFIG__: {
appBaseUrl?: string
}
},
})
}
afterEach(() => {
if (originalWindow) {
Object.defineProperty(globalThis, 'window', {
configurable: true,
writable: true,
value: originalWindow,
})
return
}
Reflect.deleteProperty(globalThis, 'window')
})
it('uses the plain slug for the global namespace', () => {
expect(buildInstallTarget('global', 'my-skill')).toBe('my-skill')
expect(buildInstallCommand('global', 'my-skill', 'https://skill.xfyun.cn')).toBe(
'npx motovis-skillhub install my-skill --registry https://skill.xfyun.cn',
)
})
it('prefixes non-global namespaces in the install target', () => {
expect(buildInstallTarget('team-alpha', 'my-skill')).toBe('team-alpha/my-skill')
expect(buildInstallCommand('team-alpha', 'my-skill', 'https://skill.xfyun.cn')).toBe(
'npx motovis-skillhub install team-alpha/my-skill --registry https://skill.xfyun.cn',
)
})
it('uses the runtime app base url when available', () => {
setMockWindow('https://app.example.com')
expect(getBaseUrl()).toBe('https://app.example.com')
})
it('falls back to the browser origin when the app base url is missing', () => {
setMockWindow()
expect(getBaseUrl()).toBe('https://fallback.example.com')
})
it('falls back to browser origin when app base url is localhost', () => {
setMockWindow('http://localhost')
expect(getBaseUrl()).toBe('https://fallback.example.com')
})
it('falls back to browser origin when app base url contains localhost', () => {
setMockWindow('http://localhost:8080')
expect(getBaseUrl()).toBe('https://fallback.example.com')
})
it('renders the install command in a more compact code block', () => {
setMockWindow('http://localhost:3000')
const html = renderToStaticMarkup(createElement(InstallCommand, { namespace: 'global', slug: 'meeting-minutes-generator' }))
expect(html).toContain('px-4 py-3')
expect(html).toContain('leading-relaxed')
expect(html).toContain('break-all')
})
})