dify/web/app/components/plugins/install-plugin/__tests__/hooks.spec.ts

167 lines
5.3 KiB
TypeScript

import { renderHook } from '@testing-library/react'
import { beforeEach, describe, expect, it, vi } from 'vitest'
import { useGitHubReleases, useGitHubUpload } from '../hooks'
const mockNotify = vi.fn()
vi.mock('@/app/components/base/toast', () => ({
default: { notify: (...args: unknown[]) => mockNotify(...args) },
}))
vi.mock('@/config', () => ({
GITHUB_ACCESS_TOKEN: '',
}))
const mockUploadGitHub = vi.fn()
vi.mock('@/service/plugins', () => ({
uploadGitHub: (...args: unknown[]) => mockUploadGitHub(...args),
}))
vi.mock('@/utils/semver', () => ({
compareVersion: (a: string, b: string) => {
const parseVersion = (v: string) => v.replace(/^v/, '').split('.').map(Number)
const va = parseVersion(a)
const vb = parseVersion(b)
for (let i = 0; i < Math.max(va.length, vb.length); i++) {
const diff = (va[i] || 0) - (vb[i] || 0)
if (diff > 0)
return 1
if (diff < 0)
return -1
}
return 0
},
getLatestVersion: (versions: string[]) => {
return versions.sort((a, b) => {
const pa = a.replace(/^v/, '').split('.').map(Number)
const pb = b.replace(/^v/, '').split('.').map(Number)
for (let i = 0; i < Math.max(pa.length, pb.length); i++) {
const diff = (pa[i] || 0) - (pb[i] || 0)
if (diff !== 0)
return diff
}
return 0
}).pop()!
},
}))
const mockFetch = vi.fn()
globalThis.fetch = mockFetch
describe('install-plugin/hooks', () => {
beforeEach(() => {
vi.clearAllMocks()
})
describe('useGitHubReleases', () => {
describe('fetchReleases', () => {
it('fetches releases from GitHub API and formats them', async () => {
mockFetch.mockResolvedValue({
ok: true,
json: () => Promise.resolve([
{
tag_name: 'v1.0.0',
assets: [{ browser_download_url: 'https://example.com/v1.zip', name: 'plugin.zip' }],
body: 'Release notes',
},
]),
})
const { result } = renderHook(() => useGitHubReleases())
const releases = await result.current.fetchReleases('owner', 'repo')
expect(releases).toHaveLength(1)
expect(releases[0].tag_name).toBe('v1.0.0')
expect(releases[0].assets[0].name).toBe('plugin.zip')
expect(releases[0]).not.toHaveProperty('body')
})
it('returns empty array and shows toast on fetch error', async () => {
mockFetch.mockResolvedValue({
ok: false,
})
const { result } = renderHook(() => useGitHubReleases())
const releases = await result.current.fetchReleases('owner', 'repo')
expect(releases).toEqual([])
expect(mockNotify).toHaveBeenCalledWith(
expect.objectContaining({ type: 'error' }),
)
})
})
describe('checkForUpdates', () => {
it('detects newer version available', () => {
const { result } = renderHook(() => useGitHubReleases())
const releases = [
{ tag_name: 'v1.0.0', assets: [] },
{ tag_name: 'v2.0.0', assets: [] },
]
const { needUpdate, toastProps } = result.current.checkForUpdates(releases, 'v1.0.0')
expect(needUpdate).toBe(true)
expect(toastProps.message).toContain('v2.0.0')
})
it('returns no update when current is latest', () => {
const { result } = renderHook(() => useGitHubReleases())
const releases = [
{ tag_name: 'v1.0.0', assets: [] },
]
const { needUpdate, toastProps } = result.current.checkForUpdates(releases, 'v1.0.0')
expect(needUpdate).toBe(false)
expect(toastProps.type).toBe('info')
})
it('returns error for empty releases', () => {
const { result } = renderHook(() => useGitHubReleases())
const { needUpdate, toastProps } = result.current.checkForUpdates([], 'v1.0.0')
expect(needUpdate).toBe(false)
expect(toastProps.type).toBe('error')
expect(toastProps.message).toContain('empty')
})
})
})
describe('useGitHubUpload', () => {
it('uploads successfully and calls onSuccess', async () => {
const mockManifest = { name: 'test-plugin' }
mockUploadGitHub.mockResolvedValue({
manifest: mockManifest,
unique_identifier: 'uid-123',
})
const onSuccess = vi.fn()
const { result } = renderHook(() => useGitHubUpload())
const pkg = await result.current.handleUpload(
'https://github.com/owner/repo',
'v1.0.0',
'plugin.difypkg',
onSuccess,
)
expect(mockUploadGitHub).toHaveBeenCalledWith(
'https://github.com/owner/repo',
'v1.0.0',
'plugin.difypkg',
)
expect(onSuccess).toHaveBeenCalledWith({
manifest: mockManifest,
unique_identifier: 'uid-123',
})
expect(pkg.unique_identifier).toBe('uid-123')
})
it('shows toast on upload error', async () => {
mockUploadGitHub.mockRejectedValue(new Error('Upload failed'))
const { result } = renderHook(() => useGitHubUpload())
await expect(
result.current.handleUpload('url', 'v1', 'pkg'),
).rejects.toThrow('Upload failed')
expect(mockNotify).toHaveBeenCalledWith(
expect.objectContaining({ type: 'error', message: 'Error uploading package' }),
)
})
})
})