import type { ReactElement } from 'react' import type { AutoUpdateConfig } from '../auto-update-setting/types' import type { Permissions, ReferenceSetting } from '@/app/components/plugins/types' import { fireEvent, screen, waitFor } from '@testing-library/react' import * as React from 'react' import { beforeEach, describe, expect, it, vi } from 'vitest' import { PermissionType } from '@/app/components/plugins/types' import { renderWithConsoleQuery } from '@/test/console/query-data' import { AUTO_UPDATE_MODE, AUTO_UPDATE_STRATEGY } from '../auto-update-setting/types' import ReferenceSettingModal from '../index' const mockSystemFeatures = { enable_marketplace: true, rbac_enabled: false, } const render = (ui: ReactElement) => renderWithConsoleQuery(ui, { systemFeatures: mockSystemFeatures }) let mockDialogOnOpenChange: ((open: boolean) => void) | undefined vi.mock('@langgenius/dify-ui/dialog', () => ({ Dialog: ({ children, open, onOpenChange, }: { children: React.ReactNode open?: boolean onOpenChange?: (open: boolean) => void }) => { mockDialogOnOpenChange = onOpenChange return open === false ? null : <>{children} }, DialogContent: ({ children, className }: { children: React.ReactNode; className?: string }) => (
{children}
), DialogCloseButton: () => ( ), })) // Mock OptionCard component vi.mock('@/app/components/workflow/nodes/_base/components/option-card', () => ({ default: ({ title, onSelect, selected, className, disabled, tooltip, }: { title: string onSelect: () => void selected: boolean className?: string disabled?: boolean tooltip?: string }) => ( ), })) // Mock AutoUpdateSetting component const mockAutoUpdateSettingOnChange = vi.fn() vi.mock('../auto-update-setting', () => ({ default: ({ payload, onChange, }: { payload: AutoUpdateConfig onChange: (payload: AutoUpdateConfig) => void }) => { mockAutoUpdateSettingOnChange.mockImplementation(onChange) return (
{payload.strategy_setting} {payload.upgrade_mode}
) }, })) // ================================ // Test Data Factories // ================================ const createMockPermissions = (overrides: Partial = {}): Permissions => ({ install_permission: PermissionType.everyone, debug_permission: PermissionType.admin, ...overrides, }) const createMockAutoUpdateConfig = ( overrides: Partial = {}, ): AutoUpdateConfig => ({ strategy_setting: AUTO_UPDATE_STRATEGY.fixOnly, upgrade_time_of_day: 36000, upgrade_mode: AUTO_UPDATE_MODE.update_all, exclude_plugins: [], include_plugins: [], ...overrides, }) const createMockReferenceSetting = ( overrides: Partial = {}, ): ReferenceSetting => ({ permission: createMockPermissions(), auto_upgrade: createMockAutoUpdateConfig(), ...overrides, }) // ================================ // Test Suites // ================================ describe('reference-setting-modal', () => { beforeEach(() => { vi.clearAllMocks() mockSystemFeatures.enable_marketplace = true mockSystemFeatures.rbac_enabled = false }) // Label component tests moved to label.spec.tsx // ============================================================ // ReferenceSettingModal (PluginSettingModal) Component Tests // ============================================================ describe('ReferenceSettingModal (index.tsx)', () => { const defaultProps = { payload: createMockReferenceSetting(), canSetPermissions: true, canSetAutoUpdate: true, onHide: vi.fn(), onSave: vi.fn(), } describe('Rendering', () => { it('should render modal with correct title', () => { // Arrange & Act render() // Assert // Assert expect(screen.getByText('plugin.privilege.title'))!.toBeInTheDocument() }) it('should render install permission section', () => { // Arrange & Act render() // Assert // Assert expect(screen.getByText('plugin.privilege.whoCanInstall'))!.toBeInTheDocument() }) it('should render debug permission section', () => { // Arrange & Act render() // Assert // Assert expect(screen.getByText('plugin.privilege.whoCanDebug'))!.toBeInTheDocument() }) it('should render all permission options for install', () => { // Arrange & Act render() // Assert - should have 6 option cards total (3 for install, 3 for debug) expect(screen.getAllByTestId(/option-card/)).toHaveLength(6) }) it('should render cancel and save buttons', () => { // Arrange & Act render() // Assert // Assert expect(screen.getByText('common.operation.cancel'))!.toBeInTheDocument() expect(screen.getByText('common.operation.save'))!.toBeInTheDocument() }) it('should render AutoUpdateSetting when marketplace is enabled', () => { // Arrange mockSystemFeatures.enable_marketplace = true // Act render() // Assert // Assert expect(screen.getByTestId('auto-update-setting'))!.toBeInTheDocument() }) it('should not render AutoUpdateSetting when marketplace is disabled', () => { // Arrange mockSystemFeatures.enable_marketplace = false // Act render() // Assert // Assert // Assert // Assert // Assert // Assert // Assert // Assert // Assert // Assert // Assert // Assert // Assert // Assert // Assert // Assert // Assert // Assert // Assert // Assert // Assert // Assert // Assert // Assert // Assert // Assert // Assert // Assert // Assert // Assert // Assert // Assert expect(screen.queryByTestId('auto-update-setting')).not.toBeInTheDocument() }) it('should hide permission controls when permission editing is disabled', () => { render() expect(screen.queryByText('plugin.privilege.whoCanInstall')).not.toBeInTheDocument() expect(screen.queryByText('plugin.privilege.whoCanDebug')).not.toBeInTheDocument() }) it('should hide AutoUpdateSetting when auto update editing is disabled', () => { render() expect(screen.queryByTestId('auto-update-setting')).not.toBeInTheDocument() }) it('should render modal with closable attribute', () => { // Arrange & Act render() // Assert // Assert expect(screen.getByTestId('modal-close'))!.toBeInTheDocument() }) it('should disable permission controls with settings permissions tooltip beside titles when RBAC is enabled', () => { mockSystemFeatures.rbac_enabled = true render() expect( screen.getAllByLabelText('plugin.privilege.configurePermissionsInSettings'), ).toHaveLength(2) expect( screen.queryByText('plugin.privilege.configurePermissionsInSettings'), ).not.toBeInTheDocument() expect( screen.getAllByTestId(/option-card/).every((option) => option.hasAttribute('disabled')), ).toBe(true) }) }) describe('State Management', () => { it('should initialize with payload permission values', () => { // Arrange const payload = createMockReferenceSetting({ permission: { install_permission: PermissionType.admin, debug_permission: PermissionType.noOne, }, }) // Act render() // Assert - admin option should be selected for install (first one) const adminOptions = screen.getAllByTestId('option-card-plugin.privilege.admins') expect(adminOptions[0])!.toHaveAttribute('aria-pressed', 'true') // Install permission // Assert - noOne option should be selected for debug (second one) const noOneOptions = screen.getAllByTestId('option-card-plugin.privilege.noone') expect(noOneOptions[1])!.toHaveAttribute('aria-pressed', 'true') // Debug permission }) it('should update tempPrivilege when permission option is clicked', () => { // Arrange render() // Act - click on "No One" for install permission const noOneOptions = screen.getAllByTestId('option-card-plugin.privilege.noone') fireEvent.click(noOneOptions[0]!) // First one is for install permission // Assert - the option should now be selected // Assert - the option should now be selected expect(noOneOptions[0])!.toHaveAttribute('aria-pressed', 'true') }) it('should not update permission when RBAC disables permission controls', () => { mockSystemFeatures.rbac_enabled = true render() const noOneOptions = screen.getAllByTestId('option-card-plugin.privilege.noone') fireEvent.click(noOneOptions[0]!) expect(noOneOptions[0])!.toHaveAttribute('aria-pressed', 'false') }) it('should initialize with payload auto_upgrade values', () => { // Arrange const payload = createMockReferenceSetting({ auto_upgrade: createMockAutoUpdateConfig({ strategy_setting: AUTO_UPDATE_STRATEGY.latest, }), }) // Act render() // Assert // Assert expect(screen.getByTestId('auto-update-strategy'))!.toHaveTextContent('latest') }) }) describe('User Interactions', () => { it('should call onHide when cancel button is clicked', () => { // Arrange const onHide = vi.fn() // Act render() fireEvent.click(screen.getByText('common.operation.cancel')) // Assert expect(onHide).toHaveBeenCalledTimes(1) }) it('should call onHide when modal close button is clicked', () => { // Arrange const onHide = vi.fn() // Act render() fireEvent.click(screen.getByTestId('modal-close')) // Assert expect(onHide).toHaveBeenCalledTimes(1) }) it('should call onSave with correct payload when save button is clicked', async () => { // Arrange const onSave = vi.fn().mockResolvedValue(undefined) const onHide = vi.fn() // Act render() fireEvent.click(screen.getByText('common.operation.save')) // Assert await waitFor(() => { expect(onSave).toHaveBeenCalledWith( expect.objectContaining({ permission: expect.any(Object), auto_upgrade: expect.any(Object), }), ) }) }) it('should call onHide after successful save', async () => { // Arrange const onSave = vi.fn().mockResolvedValue(undefined) const onHide = vi.fn() // Act render() fireEvent.click(screen.getByText('common.operation.save')) // Assert await waitFor(() => { expect(onHide).toHaveBeenCalledTimes(1) }) }) it('should update install permission when Everyone option is clicked', () => { // Arrange const payload = createMockReferenceSetting({ permission: { install_permission: PermissionType.noOne, debug_permission: PermissionType.noOne, }, }) // Act render() // Click Everyone for install permission const everyoneOptions = screen.getAllByTestId('option-card-plugin.privilege.everyone') fireEvent.click(everyoneOptions[0]!) // Assert // Assert expect(everyoneOptions[0])!.toHaveAttribute('aria-pressed', 'true') }) it('should update debug permission when Admins Only option is clicked', () => { // Arrange const payload = createMockReferenceSetting({ permission: { install_permission: PermissionType.everyone, debug_permission: PermissionType.everyone, }, }) // Act render() // Click Admins Only for debug permission (second set of options) const adminOptions = screen.getAllByTestId('option-card-plugin.privilege.admins') fireEvent.click(adminOptions[1]!) // Second one is for debug permission // Assert // Assert expect(adminOptions[1])!.toHaveAttribute('aria-pressed', 'true') }) it('should update auto_upgrade config when changed in AutoUpdateSetting', async () => { // Arrange const onSave = vi.fn().mockResolvedValue(undefined) // Act render() // Change auto update strategy fireEvent.click(screen.getByTestId('auto-update-change')) // Save to verify the change fireEvent.click(screen.getByText('common.operation.save')) // Assert await waitFor(() => { expect(onSave).toHaveBeenCalledWith( expect.objectContaining({ auto_upgrade: expect.objectContaining({ strategy_setting: AUTO_UPDATE_STRATEGY.latest, }), }), ) }) }) it('should preserve permission values when permission editing is disabled', async () => { const onSave = vi.fn().mockResolvedValue(undefined) const payload = createMockReferenceSetting({ permission: { install_permission: PermissionType.admin, debug_permission: PermissionType.noOne, }, }) render( , ) fireEvent.click(screen.getByTestId('auto-update-change')) fireEvent.click(screen.getByText('common.operation.save')) await waitFor(() => { expect(onSave).toHaveBeenCalledWith( expect.objectContaining({ permission: payload.permission, auto_upgrade: expect.objectContaining({ strategy_setting: AUTO_UPDATE_STRATEGY.latest, }), }), ) }) }) it('should preserve auto update values when auto update editing is disabled', async () => { const onSave = vi.fn().mockResolvedValue(undefined) const payload = createMockReferenceSetting({ auto_upgrade: createMockAutoUpdateConfig({ strategy_setting: AUTO_UPDATE_STRATEGY.fixOnly, }), }) render( , ) const noOneOptions = screen.getAllByTestId('option-card-plugin.privilege.noone') fireEvent.click(noOneOptions[0]!) fireEvent.click(screen.getByText('common.operation.save')) await waitFor(() => { expect(onSave).toHaveBeenCalledWith( expect.objectContaining({ permission: expect.objectContaining({ install_permission: PermissionType.noOne, }), auto_upgrade: payload.auto_upgrade, }), ) }) }) }) describe('Callback Stability and Memoization', () => { it('handlePrivilegeChange should create new handler with correct key', () => { // Arrange render() // Act - click install permission option const everyoneOptions = screen.getAllByTestId('option-card-plugin.privilege.everyone') fireEvent.click(everyoneOptions[0]!) // Assert - install permission should be updated // Assert - install permission should be updated expect(everyoneOptions[0])!.toHaveAttribute('aria-pressed', 'true') }) }) describe('Edge Cases and Error Handling', () => { it('should handle undefined permission values', () => { // Arrange const payload = { permission: undefined as unknown as Permissions, auto_upgrade: createMockAutoUpdateConfig(), } // Act render() // Assert - should use default PermissionType.noOne const noOneOptions = screen.getAllByTestId('option-card-plugin.privilege.noone') expect(noOneOptions[0])!.toHaveAttribute('aria-pressed', 'true') }) it('should handle missing install_permission', () => { // Arrange const payload = createMockReferenceSetting({ permission: { install_permission: undefined as unknown as PermissionType, debug_permission: PermissionType.everyone, }, }) // Act render() // Assert - should fall back to PermissionType.noOne // Assert - should fall back to PermissionType.noOne expect(screen.getByText('plugin.privilege.title'))!.toBeInTheDocument() }) it('should handle missing debug_permission', () => { // Arrange const payload = createMockReferenceSetting({ permission: { install_permission: PermissionType.everyone, debug_permission: undefined as unknown as PermissionType, }, }) // Act render() // Assert - should fall back to PermissionType.noOne // Assert - should fall back to PermissionType.noOne expect(screen.getByText('plugin.privilege.title'))!.toBeInTheDocument() }) it('should handle slow async onSave gracefully', async () => { // Arrange - test that the component handles async save correctly let resolvePromise: () => void const onSave = vi.fn().mockImplementation(() => { return new Promise((resolve) => { resolvePromise = resolve }) }) const onHide = vi.fn() // Act render() fireEvent.click(screen.getByText('common.operation.save')) // Assert - onSave should be called immediately expect(onSave).toHaveBeenCalledTimes(1) // onHide should not be called until save resolves expect(onHide).not.toHaveBeenCalled() // Resolve the promise resolvePromise!() // Now onHide should be called await waitFor(() => { expect(onHide).toHaveBeenCalledTimes(1) }) }) }) describe('Props Variations', () => { it('should render with all PermissionType combinations', () => { // Test each permission type const permissionTypes = [ PermissionType.everyone, PermissionType.admin, PermissionType.noOne, ] permissionTypes.forEach((installPerm) => { permissionTypes.forEach((debugPerm) => { // Arrange const payload = createMockReferenceSetting({ permission: { install_permission: installPerm, debug_permission: debugPerm, }, }) // Act const { unmount } = render( , ) expect(screen.getByText('plugin.privilege.title'))!.toBeInTheDocument() unmount() }) }) }) it('should render with all AUTO_UPDATE_STRATEGY values', () => { // Test each strategy const strategies = [ AUTO_UPDATE_STRATEGY.disabled, AUTO_UPDATE_STRATEGY.fixOnly, AUTO_UPDATE_STRATEGY.latest, ] strategies.forEach((strategy) => { // Arrange const payload = createMockReferenceSetting({ auto_upgrade: createMockAutoUpdateConfig({ strategy_setting: strategy, }), }) // Act const { unmount } = render() // Assert // Assert expect(screen.getByTestId('auto-update-strategy'))!.toHaveTextContent(strategy) unmount() }) }) it('should render with all AUTO_UPDATE_MODE values', () => { // Test each mode const modes = [ AUTO_UPDATE_MODE.update_all, AUTO_UPDATE_MODE.partial, AUTO_UPDATE_MODE.exclude, ] modes.forEach((mode) => { // Arrange const payload = createMockReferenceSetting({ auto_upgrade: createMockAutoUpdateConfig({ upgrade_mode: mode, }), }) // Act const { unmount } = render() // Assert // Assert expect(screen.getByTestId('auto-update-mode'))!.toHaveTextContent(mode) unmount() }) }) }) describe('State Updates', () => { it('should preserve tempPrivilege when changing install_permission', async () => { // Arrange const onSave = vi.fn().mockResolvedValue(undefined) const payload = createMockReferenceSetting({ permission: { install_permission: PermissionType.everyone, debug_permission: PermissionType.admin, }, }) // Act render() // Change install permission to noOne const noOneOptions = screen.getAllByTestId('option-card-plugin.privilege.noone') fireEvent.click(noOneOptions[0]!) // Save fireEvent.click(screen.getByText('common.operation.save')) // Assert - debug_permission should still be admin await waitFor(() => { expect(onSave).toHaveBeenCalledWith( expect.objectContaining({ permission: expect.objectContaining({ install_permission: PermissionType.noOne, debug_permission: PermissionType.admin, }), }), ) }) }) it('should preserve tempPrivilege when changing debug_permission', async () => { // Arrange const onSave = vi.fn().mockResolvedValue(undefined) const payload = createMockReferenceSetting({ permission: { install_permission: PermissionType.admin, debug_permission: PermissionType.everyone, }, }) // Act render() // Change debug permission to noOne const noOneOptions = screen.getAllByTestId('option-card-plugin.privilege.noone') fireEvent.click(noOneOptions[1]!) // Second one is for debug // Save fireEvent.click(screen.getByText('common.operation.save')) // Assert - install_permission should still be admin await waitFor(() => { expect(onSave).toHaveBeenCalledWith( expect.objectContaining({ permission: expect.objectContaining({ install_permission: PermissionType.admin, debug_permission: PermissionType.noOne, }), }), ) }) }) it('should update tempAutoUpdateConfig independently of permissions', async () => { // Arrange const onSave = vi.fn().mockResolvedValue(undefined) const initialPayload = createMockReferenceSetting() // Act render() // Change auto update fireEvent.click(screen.getByTestId('auto-update-change')) // Change install permission const everyoneOptions = screen.getAllByTestId('option-card-plugin.privilege.everyone') fireEvent.click(everyoneOptions[0]!) // Save fireEvent.click(screen.getByText('common.operation.save')) // Assert - both changes should be saved await waitFor(() => { expect(onSave).toHaveBeenCalledWith( expect.objectContaining({ permission: expect.objectContaining({ install_permission: PermissionType.everyone, }), auto_upgrade: expect.objectContaining({ strategy_setting: AUTO_UPDATE_STRATEGY.latest, }), }), ) }) }) }) describe('Modal Integration', () => { it('should pass isShow=true to Modal', () => { // Arrange & Act render() // Assert - modal should be visible // Assert - modal should be visible expect(screen.getByTestId('modal'))!.toBeInTheDocument() }) }) describe('Layout and Structure', () => { it('should render permission sections in correct order', () => { // Arrange & Act render() // Assert - check order by getting all section labels const labels = screen.getAllByText(/plugin\.privilege\.whoCan/) expect(labels[0])!.toHaveTextContent('plugin.privilege.whoCanInstall') expect(labels[1])!.toHaveTextContent('plugin.privilege.whoCanDebug') }) it('should render three options per permission section', () => { // Arrange & Act render() // Assert const everyoneOptions = screen.getAllByTestId('option-card-plugin.privilege.everyone') const adminOptions = screen.getAllByTestId('option-card-plugin.privilege.admins') const noOneOptions = screen.getAllByTestId('option-card-plugin.privilege.noone') expect(everyoneOptions).toHaveLength(2) // One for install, one for debug expect(adminOptions).toHaveLength(2) expect(noOneOptions).toHaveLength(2) }) it('should render footer with action buttons', () => { // Arrange & Act render() // Assert const cancelButton = screen.getByText('common.operation.cancel') const saveButton = screen.getByText('common.operation.save') expect(cancelButton)!.toBeInTheDocument() expect(saveButton)!.toBeInTheDocument() }) }) }) // ============================================================ // Integration Tests // ============================================================ describe('Integration', () => { it('should handle complete workflow: change permissions, update auto-update, save', async () => { // Arrange const onSave = vi.fn().mockResolvedValue(undefined) const onHide = vi.fn() const initialPayload = createMockReferenceSetting({ permission: { install_permission: PermissionType.noOne, debug_permission: PermissionType.noOne, }, auto_upgrade: createMockAutoUpdateConfig({ strategy_setting: AUTO_UPDATE_STRATEGY.disabled, }), }) // Act render( , ) // Change install permission to Everyone const everyoneOptions = screen.getAllByTestId('option-card-plugin.privilege.everyone') fireEvent.click(everyoneOptions[0]!) // Change debug permission to Admins Only const adminOptions = screen.getAllByTestId('option-card-plugin.privilege.admins') fireEvent.click(adminOptions[1]!) // Change auto-update strategy fireEvent.click(screen.getByTestId('auto-update-change')) // Save fireEvent.click(screen.getByText('common.operation.save')) // Assert await waitFor(() => { expect(onSave).toHaveBeenCalledWith({ permission: { install_permission: PermissionType.everyone, debug_permission: PermissionType.admin, }, auto_upgrade: expect.objectContaining({ strategy_setting: AUTO_UPDATE_STRATEGY.latest, }), }) expect(onHide).toHaveBeenCalled() }) }) it('should cancel without saving changes', () => { // Arrange const onSave = vi.fn() const onHide = vi.fn() const initialPayload = createMockReferenceSetting() // Act render( , ) // Make some changes const noOneOptions = screen.getAllByTestId('option-card-plugin.privilege.noone') fireEvent.click(noOneOptions[0]!) // Cancel fireEvent.click(screen.getByText('common.operation.cancel')) // Assert expect(onSave).not.toHaveBeenCalled() expect(onHide).toHaveBeenCalledTimes(1) }) it('Label component should work correctly within modal context', () => { // Arrange const props = { payload: createMockReferenceSetting(), canSetPermissions: true, canSetAutoUpdate: true, onHide: vi.fn(), onSave: vi.fn(), } // Act render() // Assert - Labels are rendered correctly // Assert - Labels are rendered correctly expect(screen.getByText('plugin.privilege.whoCanInstall'))!.toBeInTheDocument() expect(screen.getByText('plugin.privilege.whoCanDebug'))!.toBeInTheDocument() }) }) })