+
{parameterRule?.tagPlaceholder?.[language]}
)
diff --git a/web/app/components/header/account-setting/model-provider-page/model-parameter-modal/presets-parameter.spec.tsx b/web/app/components/header/account-setting/model-provider-page/model-parameter-modal/presets-parameter.spec.tsx
new file mode 100644
index 0000000000..04789d163e
--- /dev/null
+++ b/web/app/components/header/account-setting/model-provider-page/model-parameter-modal/presets-parameter.spec.tsx
@@ -0,0 +1,32 @@
+import { fireEvent, render, screen } from '@testing-library/react'
+import { vi } from 'vitest'
+import PresetsParameter from './presets-parameter'
+
+vi.mock('@/app/components/base/dropdown', () => ({
+ default: ({ renderTrigger, items, onSelect }: { renderTrigger: (open: boolean) => React.ReactNode, items: { value: number, text: string }[], onSelect: (item: { value: number }) => void }) => (
+
+ {renderTrigger(false)}
+ {items.map(item => (
+
+ ))}
+
+ ),
+}))
+
+describe('PresetsParameter', () => {
+ beforeEach(() => {
+ vi.clearAllMocks()
+ })
+
+ it('should render presets and handle selection', () => {
+ const onSelect = vi.fn()
+ render(
)
+
+ expect(screen.getByText('common.modelProvider.loadPresets')).toBeInTheDocument()
+
+ fireEvent.click(screen.getByText('common.model.tone.Creative'))
+ expect(onSelect).toHaveBeenCalledWith(1)
+ })
+})
diff --git a/web/app/components/header/account-setting/model-provider-page/model-parameter-modal/status-indicators.spec.tsx b/web/app/components/header/account-setting/model-provider-page/model-parameter-modal/status-indicators.spec.tsx
new file mode 100644
index 0000000000..a5b6e490af
--- /dev/null
+++ b/web/app/components/header/account-setting/model-provider-page/model-parameter-modal/status-indicators.spec.tsx
@@ -0,0 +1,103 @@
+import { fireEvent, render, screen } from '@testing-library/react'
+import { vi } from 'vitest'
+import StatusIndicators from './status-indicators'
+
+let installedPlugins = [{ name: 'demo-plugin', plugin_unique_identifier: 'demo@1.0.0' }]
+
+vi.mock('@/service/use-plugins', () => ({
+ useInstalledPluginList: () => ({ data: { plugins: installedPlugins } }),
+}))
+
+vi.mock('@/app/components/base/tooltip', () => ({
+ default: ({ popupContent }: { popupContent: React.ReactNode }) =>
{popupContent}
,
+}))
+
+vi.mock('@/app/components/workflow/nodes/_base/components/switch-plugin-version', () => ({
+ SwitchPluginVersion: ({ uniqueIdentifier }: { uniqueIdentifier: string }) =>
{`SwitchVersion:${uniqueIdentifier}`}
,
+}))
+
+const t = (key: string) => key
+
+describe('StatusIndicators', () => {
+ beforeEach(() => {
+ vi.clearAllMocks()
+ installedPlugins = [{ name: 'demo-plugin', plugin_unique_identifier: 'demo@1.0.0' }]
+ })
+
+ it('should render nothing when model is available and enabled', () => {
+ const { container } = render(
+
,
+ )
+ expect(container).toBeEmptyDOMElement()
+ })
+
+ it('should render warning states when provider model is disabled', () => {
+ const parentClick = vi.fn()
+ const { rerender } = render(
+
+
+
,
+ )
+ expect(screen.getByText('nodes.agent.modelSelectorTooltips.deprecated')).toBeInTheDocument()
+
+ rerender(
+
+
+
,
+ )
+ expect(screen.getByText('nodes.agent.modelNotSupport.title')).toBeInTheDocument()
+ expect(screen.getByText('nodes.agent.linkToPlugin').closest('a')).toHaveAttribute('href', '/plugins')
+ fireEvent.click(screen.getByText('nodes.agent.modelNotSupport.title'))
+ fireEvent.click(screen.getByText('nodes.agent.linkToPlugin'))
+ expect(parentClick).not.toHaveBeenCalled()
+
+ rerender(
+
+
+
,
+ )
+ expect(screen.getByText('SwitchVersion:demo@1.0.0')).toBeInTheDocument()
+ })
+
+ it('should render marketplace warning when provider is unavailable', () => {
+ render(
+
,
+ )
+ expect(screen.getByText('nodes.agent.modelNotInMarketplace.title')).toBeInTheDocument()
+ })
+})
diff --git a/web/app/components/header/account-setting/model-provider-page/model-parameter-modal/trigger.spec.tsx b/web/app/components/header/account-setting/model-provider-page/model-parameter-modal/trigger.spec.tsx
new file mode 100644
index 0000000000..5e22309a33
--- /dev/null
+++ b/web/app/components/header/account-setting/model-provider-page/model-parameter-modal/trigger.spec.tsx
@@ -0,0 +1,47 @@
+import type { ComponentProps } from 'react'
+import { render, screen } from '@testing-library/react'
+import Trigger from './trigger'
+
+vi.mock('../hooks', () => ({
+ useLanguage: () => 'en_US',
+}))
+
+vi.mock('@/context/provider-context', () => ({
+ useProviderContext: () => ({
+ modelProviders: [{ provider: 'openai', label: { en_US: 'OpenAI' } }],
+ }),
+}))
+
+vi.mock('../model-icon', () => ({
+ default: () =>
Icon
,
+}))
+
+vi.mock('../model-name', () => ({
+ default: ({ modelItem }: { modelItem: { model: string } }) =>
{modelItem.model}
,
+}))
+
+describe('Trigger', () => {
+ const currentProvider = { provider: 'openai', label: { en_US: 'OpenAI' } } as unknown as ComponentProps
['currentProvider']
+ const currentModel = { model: 'gpt-4' } as unknown as ComponentProps['currentModel']
+
+ it('should render initialized state', () => {
+ render(
+ ,
+ )
+ expect(screen.getByText('gpt-4')).toBeInTheDocument()
+ expect(screen.getByTestId('model-icon')).toBeInTheDocument()
+ })
+
+ it('should render fallback model id when current model is missing', () => {
+ render(
+ ,
+ )
+ expect(screen.getByText('gpt-4')).toBeInTheDocument()
+ })
+})
diff --git a/web/eslint-suppressions.json b/web/eslint-suppressions.json
index 535d9889dd..ff98c40fd5 100644
--- a/web/eslint-suppressions.json
+++ b/web/eslint-suppressions.json
@@ -4387,11 +4387,6 @@
"count": 1
}
},
- "app/components/header/account-setting/model-provider-page/model-parameter-modal/parameter-item.tsx": {
- "tailwindcss/enforce-consistent-class-order": {
- "count": 6
- }
- },
"app/components/header/account-setting/model-provider-page/model-parameter-modal/presets-parameter.tsx": {
"tailwindcss/enforce-consistent-class-order": {
"count": 1