mirror of
https://github.com/Comfy-Org/ComfyUI_frontend.git
synced 2026-04-27 17:52:16 +00:00
refactor: extract shouldUseAssetBrowser(), fix missing inputNameForBrowser, sanitize logs (#8867)
## Summary Extract duplicated asset-browser eligibility guard into `shouldUseAssetBrowser()`, fix a missing parameter bug, and sanitize a log statement. ## Changes - **What**: - DRY: Extract the repeated 3-condition guard (`isCloud && isUsingAssetAPI && isAssetBrowserEligible`) into `assetService.shouldUseAssetBrowser()`, used by `widgetInputs.ts` and `useComboWidget.ts` - Bug fix: `createAssetBrowserWidget()` in `useComboWidget.ts` was missing the `inputNameForBrowser` parameter, which could show wrong assets for nodes with multiple model inputs - Security: `createAssetWidget.ts` no longer logs the full raw asset object on validation failure - `WidgetSelect.vue` keeps an inline guard because it has a special `widget.type === "asset"` fallback that must stay gated behind `isUsingAssetAPI` ## Review Focus - `shouldUseAssetBrowser()` correctly combines the three conditions that were previously duplicated - `WidgetSelect.vue` preserves exact behavioral equivalence (a widget can have `type === "asset"` when the setting is off if a user toggles the setting after creating asset widgets) Fixes #8744 ┆Issue is synchronized with this [Notion page](https://www.notion.so/PR-8867-refactor-extract-shouldUseAssetBrowser-fix-missing-inputNameForBrowser-sanitize-logs-3076d73d3650818cabdcd76a351dac31) by [Unito](https://www.unito.io)
This commit is contained in:
@@ -15,27 +15,16 @@ const i18n = createI18n({
|
||||
})
|
||||
|
||||
// Mock modules
|
||||
vi.mock('@/platform/distribution/types', () => ({
|
||||
isCloud: true
|
||||
}))
|
||||
|
||||
vi.mock('@/platform/assets/services/assetService', () => ({
|
||||
assetService: {
|
||||
isAssetBrowserEligible: vi.fn(() => true)
|
||||
shouldUseAssetBrowser: vi.fn(() => true),
|
||||
isAssetAPIEnabled: vi.fn(() => true)
|
||||
}
|
||||
}))
|
||||
|
||||
const mockSettingStoreGet = vi.fn()
|
||||
|
||||
vi.mock('@/platform/settings/settingStore', () => ({
|
||||
useSettingStore: vi.fn(() => ({
|
||||
get: mockSettingStoreGet
|
||||
}))
|
||||
}))
|
||||
|
||||
// Import after mocks are defined
|
||||
import { assetService } from '@/platform/assets/services/assetService'
|
||||
const mockAssetServiceEligible = vi.mocked(assetService.isAssetBrowserEligible)
|
||||
const mockShouldUseAssetBrowser = vi.mocked(assetService.shouldUseAssetBrowser)
|
||||
|
||||
describe('WidgetSelect asset mode', () => {
|
||||
const createWidget = (): SimplifiedWidget<string | undefined> => ({
|
||||
@@ -49,8 +38,7 @@ describe('WidgetSelect asset mode', () => {
|
||||
|
||||
beforeEach(() => {
|
||||
vi.clearAllMocks()
|
||||
mockAssetServiceEligible.mockReturnValue(true)
|
||||
mockSettingStoreGet.mockReturnValue(true) // Default to true for UseAssetAPI
|
||||
mockShouldUseAssetBrowser.mockReturnValue(true)
|
||||
})
|
||||
|
||||
// Helper to mount with common setup
|
||||
@@ -76,17 +64,8 @@ describe('WidgetSelect asset mode', () => {
|
||||
).toBe(true)
|
||||
})
|
||||
|
||||
it('uses default widget when UseAssetAPI setting is false', () => {
|
||||
mockSettingStoreGet.mockReturnValue(false)
|
||||
const wrapper = mountWidget()
|
||||
|
||||
expect(
|
||||
wrapper.findComponent({ name: 'WidgetSelectDefault' }).exists()
|
||||
).toBe(true)
|
||||
})
|
||||
|
||||
it('uses default widget when node is not eligible', () => {
|
||||
mockAssetServiceEligible.mockReturnValue(false)
|
||||
it('uses default widget when shouldUseAssetBrowser returns false', () => {
|
||||
mockShouldUseAssetBrowser.mockReturnValue(false)
|
||||
const wrapper = mountWidget()
|
||||
|
||||
expect(
|
||||
|
||||
@@ -18,35 +18,21 @@ import WidgetSelect from '@/renderer/extensions/vueNodes/widgets/components/Widg
|
||||
import WidgetSelectDefault from '@/renderer/extensions/vueNodes/widgets/components/WidgetSelectDefault.vue'
|
||||
import WidgetSelectDropdown from '@/renderer/extensions/vueNodes/widgets/components/WidgetSelectDropdown.vue'
|
||||
|
||||
// Mock state for distribution and settings
|
||||
const mockDistributionState = vi.hoisted(() => ({ isCloud: false }))
|
||||
const mockSettingStoreGet = vi.hoisted(() => vi.fn(() => false))
|
||||
const mockIsAssetBrowserEligible = vi.hoisted(() => vi.fn(() => false))
|
||||
|
||||
vi.mock('@/platform/distribution/types', () => ({
|
||||
get isCloud() {
|
||||
return mockDistributionState.isCloud
|
||||
}
|
||||
}))
|
||||
|
||||
vi.mock('@/platform/settings/settingStore', () => ({
|
||||
useSettingStore: vi.fn(() => ({
|
||||
get: mockSettingStoreGet
|
||||
}))
|
||||
}))
|
||||
// Mock state for asset service
|
||||
const mockShouldUseAssetBrowser = vi.hoisted(() => vi.fn(() => false))
|
||||
const mockIsAssetAPIEnabled = vi.hoisted(() => vi.fn(() => false))
|
||||
|
||||
vi.mock('@/platform/assets/services/assetService', () => ({
|
||||
assetService: {
|
||||
isAssetBrowserEligible: mockIsAssetBrowserEligible
|
||||
shouldUseAssetBrowser: mockShouldUseAssetBrowser,
|
||||
isAssetAPIEnabled: mockIsAssetAPIEnabled
|
||||
}
|
||||
}))
|
||||
|
||||
describe('WidgetSelect Value Binding', () => {
|
||||
beforeEach(() => {
|
||||
// Reset all mocks before each test
|
||||
mockDistributionState.isCloud = false
|
||||
mockSettingStoreGet.mockReturnValue(false)
|
||||
mockIsAssetBrowserEligible.mockReturnValue(false)
|
||||
mockShouldUseAssetBrowser.mockReturnValue(false)
|
||||
mockIsAssetAPIEnabled.mockReturnValue(false)
|
||||
vi.clearAllMocks()
|
||||
})
|
||||
|
||||
@@ -263,10 +249,8 @@ describe('WidgetSelect Value Binding', () => {
|
||||
})
|
||||
|
||||
describe('Asset mode detection', () => {
|
||||
it('enables asset mode when all conditions are met', () => {
|
||||
mockDistributionState.isCloud = true
|
||||
mockSettingStoreGet.mockReturnValue(true)
|
||||
mockIsAssetBrowserEligible.mockReturnValue(true)
|
||||
it('enables asset mode when shouldUseAssetBrowser returns true', () => {
|
||||
mockShouldUseAssetBrowser.mockReturnValue(true)
|
||||
|
||||
const widget = createMockWidget('test.safetensors')
|
||||
const wrapper = mount(WidgetSelect, {
|
||||
@@ -284,8 +268,8 @@ describe('WidgetSelect Value Binding', () => {
|
||||
expect(wrapper.findComponent(WidgetSelectDropdown).exists()).toBe(true)
|
||||
})
|
||||
|
||||
it('disables asset mode when conditions are not met', () => {
|
||||
mockDistributionState.isCloud = false
|
||||
it('disables asset mode when shouldUseAssetBrowser returns false', () => {
|
||||
mockShouldUseAssetBrowser.mockReturnValue(false)
|
||||
|
||||
const widget = createMockWidget('test.safetensors')
|
||||
const wrapper = mount(WidgetSelect, {
|
||||
|
||||
@@ -24,8 +24,6 @@
|
||||
import { computed } from 'vue'
|
||||
|
||||
import { assetService } from '@/platform/assets/services/assetService'
|
||||
import { isCloud } from '@/platform/distribution/types'
|
||||
import { useSettingStore } from '@/platform/settings/settingStore'
|
||||
import WidgetSelectDefault from '@/renderer/extensions/vueNodes/widgets/components/WidgetSelectDefault.vue'
|
||||
import WidgetSelectDropdown from '@/renderer/extensions/vueNodes/widgets/components/WidgetSelectDropdown.vue'
|
||||
import WidgetWithControl from '@/renderer/extensions/vueNodes/widgets/components/WidgetWithControl.vue'
|
||||
@@ -111,19 +109,11 @@ const specDescriptor = computed<{
|
||||
}
|
||||
})
|
||||
|
||||
const isAssetMode = computed(() => {
|
||||
if (isCloud) {
|
||||
const settingStore = useSettingStore()
|
||||
const isUsingAssetAPI = settingStore.get('Comfy.Assets.UseAssetAPI')
|
||||
const isEligible =
|
||||
assetService.isAssetBrowserEligible(props.nodeType, props.widget.name) ||
|
||||
props.widget.type === 'asset'
|
||||
|
||||
return isUsingAssetAPI && isEligible
|
||||
}
|
||||
|
||||
return false
|
||||
})
|
||||
const isAssetMode = computed(
|
||||
() =>
|
||||
assetService.shouldUseAssetBrowser(props.nodeType, props.widget.name) ||
|
||||
(assetService.isAssetAPIEnabled() && props.widget.type === 'asset')
|
||||
)
|
||||
|
||||
const assetKind = computed(() => specDescriptor.value.kind)
|
||||
const isDropdownUIWidget = computed(
|
||||
|
||||
@@ -74,7 +74,8 @@ vi.mock('@/i18n', () => ({
|
||||
|
||||
vi.mock('@/platform/assets/services/assetService', () => ({
|
||||
assetService: {
|
||||
isAssetBrowserEligible: vi.fn(() => false)
|
||||
isAssetBrowserEligible: vi.fn(() => false),
|
||||
shouldUseAssetBrowser: vi.fn(() => false)
|
||||
}
|
||||
}))
|
||||
|
||||
@@ -135,6 +136,7 @@ describe('useComboWidget', () => {
|
||||
vi.clearAllMocks()
|
||||
mockSettingStoreGet.mockReturnValue(false)
|
||||
vi.mocked(assetService.isAssetBrowserEligible).mockReturnValue(false)
|
||||
vi.mocked(assetService.shouldUseAssetBrowser).mockReturnValue(false)
|
||||
vi.mocked(useAssetBrowserDialog).mockClear()
|
||||
mockDistributionState.isCloud = false
|
||||
mockAssetsStoreState.inputAssets = []
|
||||
@@ -165,8 +167,8 @@ describe('useComboWidget', () => {
|
||||
|
||||
it('should create normal combo widget when asset API is disabled', () => {
|
||||
mockDistributionState.isCloud = true
|
||||
mockSettingStoreGet.mockReturnValue(false) // Asset API disabled
|
||||
vi.mocked(assetService.isAssetBrowserEligible).mockReturnValue(true) // Widget is eligible
|
||||
mockSettingStoreGet.mockReturnValue(false)
|
||||
vi.mocked(assetService.shouldUseAssetBrowser).mockReturnValue(false)
|
||||
|
||||
const constructor = useComboWidget()
|
||||
const mockWidget = createMockWidget()
|
||||
@@ -187,14 +189,12 @@ describe('useComboWidget', () => {
|
||||
expect.any(Function),
|
||||
{ values: ['model1.safetensors', 'model2.safetensors'] }
|
||||
)
|
||||
expect(mockSettingStoreGet).toHaveBeenCalledWith('Comfy.Assets.UseAssetAPI')
|
||||
expect(widget).toBe(mockWidget)
|
||||
})
|
||||
|
||||
it('should create asset browser widget when API enabled', () => {
|
||||
mockDistributionState.isCloud = true
|
||||
mockSettingStoreGet.mockReturnValue(true)
|
||||
vi.mocked(assetService.isAssetBrowserEligible).mockReturnValue(true)
|
||||
vi.mocked(assetService.shouldUseAssetBrowser).mockReturnValue(true)
|
||||
|
||||
const constructor = useComboWidget()
|
||||
const mockWidget = createMockWidget({
|
||||
@@ -218,8 +218,7 @@ describe('useComboWidget', () => {
|
||||
expect.any(Function),
|
||||
expect.any(Object)
|
||||
)
|
||||
expect(mockSettingStoreGet).toHaveBeenCalledWith('Comfy.Assets.UseAssetAPI')
|
||||
expect(vi.mocked(assetService.isAssetBrowserEligible)).toHaveBeenCalledWith(
|
||||
expect(vi.mocked(assetService.shouldUseAssetBrowser)).toHaveBeenCalledWith(
|
||||
'CheckpointLoaderSimple',
|
||||
'ckpt_name'
|
||||
)
|
||||
@@ -228,8 +227,7 @@ describe('useComboWidget', () => {
|
||||
|
||||
it('should create asset browser widget when default value provided without options', () => {
|
||||
mockDistributionState.isCloud = true
|
||||
mockSettingStoreGet.mockReturnValue(true)
|
||||
vi.mocked(assetService.isAssetBrowserEligible).mockReturnValue(true)
|
||||
vi.mocked(assetService.shouldUseAssetBrowser).mockReturnValue(true)
|
||||
|
||||
const constructor = useComboWidget()
|
||||
const mockWidget = createMockWidget({
|
||||
@@ -254,14 +252,12 @@ describe('useComboWidget', () => {
|
||||
expect.any(Function),
|
||||
expect.any(Object)
|
||||
)
|
||||
expect(mockSettingStoreGet).toHaveBeenCalledWith('Comfy.Assets.UseAssetAPI')
|
||||
expect(widget).toBe(mockWidget)
|
||||
})
|
||||
|
||||
it('should show Select model when asset widget has undefined current value', () => {
|
||||
mockDistributionState.isCloud = true
|
||||
mockSettingStoreGet.mockReturnValue(true)
|
||||
vi.mocked(assetService.isAssetBrowserEligible).mockReturnValue(true)
|
||||
vi.mocked(assetService.shouldUseAssetBrowser).mockReturnValue(true)
|
||||
|
||||
const constructor = useComboWidget()
|
||||
const mockWidget = createMockWidget({
|
||||
@@ -285,7 +281,6 @@ describe('useComboWidget', () => {
|
||||
expect.any(Function),
|
||||
expect.any(Object)
|
||||
)
|
||||
expect(mockSettingStoreGet).toHaveBeenCalledWith('Comfy.Assets.UseAssetAPI')
|
||||
expect(widget).toBe(mockWidget)
|
||||
})
|
||||
|
||||
|
||||
@@ -8,7 +8,6 @@ import type { IBaseWidget } from '@/lib/litegraph/src/types/widgets'
|
||||
import { assetService } from '@/platform/assets/services/assetService'
|
||||
import { createAssetWidget } from '@/platform/assets/utils/createAssetWidget'
|
||||
import { isCloud } from '@/platform/distribution/types'
|
||||
import { useSettingStore } from '@/platform/settings/settingStore'
|
||||
import type {
|
||||
ComboInputSpec,
|
||||
InputSpec
|
||||
@@ -91,6 +90,7 @@ function createAssetBrowserWidget(
|
||||
node,
|
||||
widgetName: inputSpec.name,
|
||||
nodeTypeForBrowser: node.comfyClass ?? '',
|
||||
inputNameForBrowser: inputSpec.name,
|
||||
defaultValue,
|
||||
onValueChange: (widget, newValue, oldValue) => {
|
||||
node.onWidgetChanged?.(widget.name, newValue, oldValue, widget)
|
||||
@@ -177,14 +177,7 @@ const addComboWidget = (
|
||||
const defaultValue = getDefaultValue(inputSpec)
|
||||
|
||||
if (isCloud) {
|
||||
const settingStore = useSettingStore()
|
||||
const isUsingAssetAPI = settingStore.get('Comfy.Assets.UseAssetAPI')
|
||||
const isEligible = assetService.isAssetBrowserEligible(
|
||||
node.comfyClass,
|
||||
inputSpec.name
|
||||
)
|
||||
|
||||
if (isUsingAssetAPI && isEligible) {
|
||||
if (assetService.shouldUseAssetBrowser(node.comfyClass, inputSpec.name)) {
|
||||
return createAssetBrowserWidget(node, inputSpec, defaultValue)
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user