Files
dify/web/app/components/plugins/install-plugin/__tests__/hooks.spec.ts
2026-03-18 22:00:25 +08:00

139 lines
4.4 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),
}))
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' }),
)
})
})
})