mirror of
https://github.com/langgenius/dify.git
synced 2026-11-13 23:06:40 +08:00
113 lines
5.1 KiB
TypeScript
113 lines
5.1 KiB
TypeScript
import type { ComponentProps } from 'react'
|
|
import type { ConsoleQueryTestOptions } from '@/test/console/query-data'
|
|
import { screen, waitFor } from '@testing-library/react'
|
|
import userEvent from '@testing-library/user-event'
|
|
import { renderWithConsoleQuery } from '@/test/console/query-data'
|
|
import InstallPluginMenu from '../install-plugin-menu'
|
|
|
|
const { upload } = vi.hoisted(() => ({ upload: vi.fn() }))
|
|
vi.mock('@/service/plugins', async (importOriginal) => ({
|
|
...(await importOriginal<typeof import('@/service/plugins')>()),
|
|
uploadFile: upload,
|
|
}))
|
|
// Catalog invalidation is independent of choosing and closing an installer.
|
|
vi.mock('../../install-plugin/hooks/use-refresh-plugin-list', () => ({
|
|
default: () => ({ refreshPluginList: vi.fn() }),
|
|
}))
|
|
const renderMenu = (
|
|
props: Partial<ComponentProps<typeof InstallPluginMenu>> = {},
|
|
systemFeatures: ConsoleQueryTestOptions['systemFeatures'] = { enable_marketplace: true },
|
|
) => {
|
|
const onSwitchToMarketplaceTab = vi.fn()
|
|
return {
|
|
...renderWithConsoleQuery(
|
|
<InstallPluginMenu onSwitchToMarketplaceTab={onSwitchToMarketplaceTab} {...props} />,
|
|
{ systemFeatures },
|
|
),
|
|
onSwitchToMarketplaceTab,
|
|
}
|
|
}
|
|
const openMenu = async (
|
|
user: ReturnType<typeof userEvent.setup>,
|
|
name = 'plugin.installPlugin',
|
|
) => {
|
|
await user.click(screen.getByRole('button', { name }))
|
|
}
|
|
|
|
it('offers all enabled installation sources and switches to the marketplace', async () => {
|
|
const user = userEvent.setup()
|
|
const { onSwitchToMarketplaceTab } = renderMenu({ triggerLabel: 'Install' })
|
|
await openMenu(user, 'Install')
|
|
expect(screen.getByRole('menuitem', { name: 'plugin.source.github' })).toBeInTheDocument()
|
|
expect(screen.getByRole('menuitem', { name: 'plugin.source.local' })).toBeInTheDocument()
|
|
await user.click(screen.getByRole('menuitem', { name: 'plugin.source.marketplace' }))
|
|
expect(onSwitchToMarketplaceTab).toHaveBeenCalledOnce()
|
|
})
|
|
|
|
it('prevents opening the menu or selecting a file while installation is disabled', async () => {
|
|
const user = userEvent.setup()
|
|
const { container, onSwitchToMarketplaceTab } = renderMenu({ disabled: true })
|
|
const trigger = screen.getByRole('button', { name: 'plugin.installPlugin' })
|
|
expect(trigger).toBeDisabled()
|
|
await user.click(trigger)
|
|
// A native file input has no implicit ARIA role and is activated by the menu action.
|
|
const input = container.querySelector<HTMLInputElement>('input[type="file"]')!
|
|
expect(input).toBeDisabled()
|
|
await user.upload(input, new File(['content'], 'plugin.difypkg'))
|
|
expect(screen.queryByRole('menu')).not.toBeInTheDocument()
|
|
expect(screen.queryByRole('dialog')).not.toBeInTheDocument()
|
|
expect(onSwitchToMarketplaceTab).not.toHaveBeenCalled()
|
|
expect(upload).not.toHaveBeenCalled()
|
|
})
|
|
|
|
it('offers only the marketplace when custom installation is restricted', async () => {
|
|
const user = userEvent.setup()
|
|
renderMenu(
|
|
{},
|
|
{
|
|
enable_marketplace: true,
|
|
plugin_installation_permission: { restrict_to_marketplace_only: true },
|
|
},
|
|
)
|
|
await openMenu(user)
|
|
expect(screen.getByRole('menuitem', { name: 'plugin.source.marketplace' })).toBeInTheDocument()
|
|
expect(screen.queryByRole('menuitem', { name: 'plugin.source.github' })).not.toBeInTheDocument()
|
|
expect(screen.queryByRole('menuitem', { name: 'plugin.source.local' })).not.toBeInTheDocument()
|
|
})
|
|
|
|
it('opens the GitHub installer and returns to the dropdown when it is cancelled', async () => {
|
|
const user = userEvent.setup()
|
|
renderMenu()
|
|
await openMenu(user)
|
|
await user.click(screen.getByRole('menuitem', { name: 'plugin.source.github' }))
|
|
await screen.findByRole('dialog', { name: 'plugin.installFromGitHub.installPlugin' })
|
|
await user.click(screen.getByRole('button', { name: 'common.operation.close' }))
|
|
await waitFor(() => expect(screen.queryByRole('dialog')).not.toBeInTheDocument())
|
|
await openMenu(user)
|
|
expect(screen.getByRole('menuitem', { name: 'plugin.source.github' })).toBeInTheDocument()
|
|
})
|
|
|
|
it('passes the selected file to the real installer and permits selecting the same file after closing', async () => {
|
|
const user = userEvent.setup()
|
|
upload.mockRejectedValue(new Error('Upload unavailable'))
|
|
const { container } = renderMenu()
|
|
const input = container.querySelector<HTMLInputElement>('input[type="file"]')!
|
|
const file = new File(['content'], 'plugin.difypkg')
|
|
const filePicker = vi.spyOn(input, 'click')
|
|
await openMenu(user)
|
|
await user.click(screen.getByRole('menuitem', { name: 'plugin.source.local' }))
|
|
expect(filePicker).toHaveBeenCalledOnce()
|
|
filePicker.mockRestore()
|
|
await user.upload(input, file)
|
|
await screen.findByRole('dialog', { name: 'plugin.installModal.uploadFailed' })
|
|
expect(upload).toHaveBeenCalledExactlyOnceWith(file, false)
|
|
await user.click(screen.getByText('common.operation.close'))
|
|
await waitFor(() => expect(screen.queryByRole('dialog')).not.toBeInTheDocument())
|
|
|
|
await openMenu(user)
|
|
await user.click(screen.getByRole('menuitem', { name: 'plugin.source.local' }))
|
|
await user.upload(input, file)
|
|
await screen.findByRole('dialog', { name: 'plugin.installModal.uploadFailed' })
|
|
expect(upload).toHaveBeenCalledTimes(2)
|
|
})
|