Merge main (as of 10-06-2025) into rh-test (#5965)

## Summary

Merges latest changes from `main` as of 10-06-2025.

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-5965-Merge-main-as-of-10-06-2025-into-rh-test-2856d73d3650812cb95fd8917278a770)
by [Unito](https://www.unito.io)

---------

Signed-off-by: Marcel Petrick <mail@marcelpetrick.it>
Co-authored-by: filtered <176114999+webfiltered@users.noreply.github.com>
Co-authored-by: Christian Byrne <cbyrne@comfy.org>
Co-authored-by: github-actions <github-actions@github.com>
Co-authored-by: Claude <noreply@anthropic.com>
Co-authored-by: Alexander Brown <drjkl@comfy.org>
Co-authored-by: Benjamin Lu <benceruleanlu@proton.me>
Co-authored-by: Terry Jia <terryjia88@gmail.com>
Co-authored-by: snomiao <snomiao@gmail.com>
Co-authored-by: Simula_r <18093452+simula-r@users.noreply.github.com>
Co-authored-by: Jake Schroeder <jake.schroeder@isophex.com>
Co-authored-by: Comfy Org PR Bot <snomiao+comfy-pr@gmail.com>
Co-authored-by: AustinMroz <4284322+AustinMroz@users.noreply.github.com>
Co-authored-by: GitHub Action <action@github.com>
Co-authored-by: Johnpaul Chiwetelu <49923152+Myestery@users.noreply.github.com>
Co-authored-by: Marcel Petrick <mail@marcelpetrick.it>
Co-authored-by: Alexander Brown <DrJKL0424@gmail.com>
Co-authored-by: Benjamin Lu <benjaminlu1107@gmail.com>
Co-authored-by: Alexander Piskun <13381981+bigcat88@users.noreply.github.com>
Co-authored-by: Rizumu Ayaka <rizumu@ayaka.moe>
Co-authored-by: JakeSchroeder <jake@axiom.co>
Co-authored-by: AustinMroz <austin@comfy.org>
Co-authored-by: DrJKL <DrJKL@users.noreply.github.com>
Co-authored-by: ComfyUI Wiki <contact@comfyui-wiki.com>
This commit is contained in:
Arjan Singh
2025-10-08 19:06:40 -07:00
committed by GitHub
parent 529a4de583
commit 5869b04e57
454 changed files with 32333 additions and 37002 deletions

View File

@@ -4,6 +4,14 @@ import type { AssetItem } from '@/platform/assets/schemas/assetSchema'
import { assetService } from '@/platform/assets/services/assetService'
import { api } from '@/scripts/api'
vi.mock('@/scripts/api', () => ({
api: {
fetchApi: vi.fn(),
addEventListener: vi.fn(),
removeEventListener: vi.fn()
}
}))
const mockGetCategoryForNodeType = vi.fn()
vi.mock('@/stores/modelToNodeStore', () => ({
@@ -108,7 +116,9 @@ describe('assetService', () => {
const result = await assetService.getAssetModelFolders()
expect(api.fetchApi).toHaveBeenCalledWith('/assets?include_tags=models')
expect(api.fetchApi).toHaveBeenCalledWith(
'/assets?include_tags=models&limit=300'
)
expect(result).toHaveLength(2)
const folderNames = result.map((f) => f.name)
@@ -153,7 +163,7 @@ describe('assetService', () => {
const result = await assetService.getAssetModels('checkpoints')
expect(api.fetchApi).toHaveBeenCalledWith(
'/assets?include_tags=models,checkpoints'
'/assets?include_tags=models,checkpoints&limit=300'
)
expect(result).toEqual([
expect.objectContaining({ name: 'valid.safetensors', pathIndex: 0 })
@@ -181,41 +191,18 @@ describe('assetService', () => {
})
describe('isAssetBrowserEligible', () => {
it('should return true for eligible widget names with registered node types', () => {
it('should return true for registered node types', () => {
expect(
assetService.isAssetBrowserEligible(
'ckpt_name',
'CheckpointLoaderSimple'
)
assetService.isAssetBrowserEligible('CheckpointLoaderSimple')
).toBe(true)
expect(
assetService.isAssetBrowserEligible('lora_name', 'LoraLoader')
).toBe(true)
expect(assetService.isAssetBrowserEligible('vae_name', 'VAELoader')).toBe(
true
)
expect(assetService.isAssetBrowserEligible('LoraLoader')).toBe(true)
expect(assetService.isAssetBrowserEligible('VAELoader')).toBe(true)
})
it('should return false for non-eligible widget names', () => {
expect(assetService.isAssetBrowserEligible('seed', 'TestNode')).toBe(
false
)
expect(assetService.isAssetBrowserEligible('steps', 'TestNode')).toBe(
false
)
expect(
assetService.isAssetBrowserEligible('sampler_name', 'TestNode')
).toBe(false)
expect(assetService.isAssetBrowserEligible('', 'TestNode')).toBe(false)
})
it('should return false for eligible widget names with unregistered node types', () => {
expect(
assetService.isAssetBrowserEligible('ckpt_name', 'UnknownNode')
).toBe(false)
expect(
assetService.isAssetBrowserEligible('lora_name', 'UnknownNode')
).toBe(false)
it('should return false for unregistered node types', () => {
expect(assetService.isAssetBrowserEligible('UnknownNode')).toBe(false)
expect(assetService.isAssetBrowserEligible('NotRegistered')).toBe(false)
expect(assetService.isAssetBrowserEligible('')).toBe(false)
})
})
@@ -249,7 +236,7 @@ describe('assetService', () => {
// Verify API call includes correct category
expect(api.fetchApi).toHaveBeenCalledWith(
'/assets?include_tags=models,checkpoints'
'/assets?include_tags=models,checkpoints&limit=300'
)
})
@@ -301,4 +288,72 @@ describe('assetService', () => {
expect(result).toEqual(vaeAssets)
})
})
describe('getAssetsByTag', () => {
it('should fetch assets with correct tag query parameter', async () => {
const testAssets = [MOCK_ASSETS.checkpoints, MOCK_ASSETS.loras]
mockApiResponse(testAssets)
const result = await assetService.getAssetsByTag('models')
expect(api.fetchApi).toHaveBeenCalledWith(
'/assets?include_tags=models&limit=300'
)
expect(result).toEqual(testAssets)
})
it('should filter out assets with missing tag', async () => {
const testAssets = [
MOCK_ASSETS.checkpoints,
createTestAsset({
id: 'uuid-missing',
name: 'missing.safetensors',
tags: ['models', 'checkpoints', 'missing']
}),
MOCK_ASSETS.loras
]
mockApiResponse(testAssets)
const result = await assetService.getAssetsByTag('models')
expect(result).toHaveLength(2)
expect(result).toEqual([MOCK_ASSETS.checkpoints, MOCK_ASSETS.loras])
expect(result.some((a) => a.id === 'uuid-missing')).toBe(false)
})
it('should return empty array on API error', async () => {
mockApiError(500)
await expect(assetService.getAssetsByTag('models')).rejects.toThrow(
'Unable to load assets for tag models: Server returned 500. Please try again.'
)
})
it('should return empty array for empty response', async () => {
mockApiResponse([])
const result = await assetService.getAssetsByTag('nonexistent')
expect(result).toEqual([])
})
it('should return AssetItem[] with full metadata', async () => {
const fullAsset = createTestAsset({
id: 'test-full',
name: 'full-model.safetensors',
asset_hash: 'blake3:full123',
size: 999999,
tags: ['models', 'checkpoints'],
user_metadata: { filename: 'models/checkpoints/full-model.safetensors' }
})
mockApiResponse([fullAsset])
const result = await assetService.getAssetsByTag('models')
expect(result).toHaveLength(1)
expect(result[0]).toEqual(fullAsset)
expect(result[0]).toHaveProperty('asset_hash', 'blake3:full123')
expect(result[0]).toHaveProperty('user_metadata')
})
})
})