mirror of
https://github.com/Comfy-Org/ComfyUI_frontend.git
synced 2026-04-20 06:20:11 +00:00
test: add E2E and expand unit tests for missing media pipeline
E2E tests (10 scenarios): - Detection: overlay, errors tab group, row count, dropzone/library visibility - Upload flow: file picker → status card → confirm → row removed - Library select: dropdown → status card → confirm → row removed - Cancel: pending selection → returns to upload/library UI - All resolved: Missing Inputs group disappears - Locate: canvas pans to missing media node Unit test improvements: - Add makeCandidate helper to scan tests (DRY) - Add 5 new verifyCloudMediaCandidates tests (spy on updateInputs, mid-flight abort, isMissing:false skip, no-pending skip, multi-name grouping within same media type) - Add data-testid attributes to MissingMediaRow components - Add TestIds for missing media selectors
This commit is contained in:
68
browser_tests/assets/missing/missing_media_multiple.json
Normal file
68
browser_tests/assets/missing/missing_media_multiple.json
Normal file
@@ -0,0 +1,68 @@
|
||||
{
|
||||
"last_node_id": 12,
|
||||
"last_link_id": 0,
|
||||
"nodes": [
|
||||
{
|
||||
"id": 10,
|
||||
"type": "LoadImage",
|
||||
"pos": [50, 50],
|
||||
"size": [315, 314],
|
||||
"flags": {},
|
||||
"order": 0,
|
||||
"mode": 0,
|
||||
"inputs": [],
|
||||
"outputs": [
|
||||
{
|
||||
"name": "IMAGE",
|
||||
"type": "IMAGE",
|
||||
"links": null
|
||||
},
|
||||
{
|
||||
"name": "MASK",
|
||||
"type": "MASK",
|
||||
"links": null
|
||||
}
|
||||
],
|
||||
"properties": {
|
||||
"Node name for S&R": "LoadImage"
|
||||
},
|
||||
"widgets_values": ["nonexistent_test_image_aaa.png", "image"]
|
||||
},
|
||||
{
|
||||
"id": 11,
|
||||
"type": "LoadImage",
|
||||
"pos": [450, 50],
|
||||
"size": [315, 314],
|
||||
"flags": {},
|
||||
"order": 1,
|
||||
"mode": 0,
|
||||
"inputs": [],
|
||||
"outputs": [
|
||||
{
|
||||
"name": "IMAGE",
|
||||
"type": "IMAGE",
|
||||
"links": null
|
||||
},
|
||||
{
|
||||
"name": "MASK",
|
||||
"type": "MASK",
|
||||
"links": null
|
||||
}
|
||||
],
|
||||
"properties": {
|
||||
"Node name for S&R": "LoadImage"
|
||||
},
|
||||
"widgets_values": ["nonexistent_test_image_bbb.png", "image"]
|
||||
}
|
||||
],
|
||||
"links": [],
|
||||
"groups": [],
|
||||
"config": {},
|
||||
"extra": {
|
||||
"ds": {
|
||||
"offset": [0, 0],
|
||||
"scale": 1
|
||||
}
|
||||
},
|
||||
"version": 0.4
|
||||
}
|
||||
42
browser_tests/assets/missing/missing_media_single.json
Normal file
42
browser_tests/assets/missing/missing_media_single.json
Normal file
@@ -0,0 +1,42 @@
|
||||
{
|
||||
"last_node_id": 10,
|
||||
"last_link_id": 0,
|
||||
"nodes": [
|
||||
{
|
||||
"id": 10,
|
||||
"type": "LoadImage",
|
||||
"pos": [50, 50],
|
||||
"size": [315, 314],
|
||||
"flags": {},
|
||||
"order": 0,
|
||||
"mode": 0,
|
||||
"inputs": [],
|
||||
"outputs": [
|
||||
{
|
||||
"name": "IMAGE",
|
||||
"type": "IMAGE",
|
||||
"links": null
|
||||
},
|
||||
{
|
||||
"name": "MASK",
|
||||
"type": "MASK",
|
||||
"links": null
|
||||
}
|
||||
],
|
||||
"properties": {
|
||||
"Node name for S&R": "LoadImage"
|
||||
},
|
||||
"widgets_values": ["nonexistent_test_image_12345.png", "image"]
|
||||
}
|
||||
],
|
||||
"links": [],
|
||||
"groups": [],
|
||||
"config": {},
|
||||
"extra": {
|
||||
"ds": {
|
||||
"offset": [0, 0],
|
||||
"scale": 1
|
||||
}
|
||||
},
|
||||
"version": 0.4
|
||||
}
|
||||
BIN
browser_tests/assets/test_upload_image.png
Normal file
BIN
browser_tests/assets/test_upload_image.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 70 B |
@@ -36,7 +36,15 @@ export const TestIds = {
|
||||
about: 'about-panel',
|
||||
whatsNewSection: 'whats-new-section',
|
||||
missingNodePacksGroup: 'error-group-missing-node',
|
||||
missingModelsGroup: 'error-group-missing-model'
|
||||
missingModelsGroup: 'error-group-missing-model',
|
||||
missingMediaGroup: 'error-group-missing-media',
|
||||
missingMediaRow: 'missing-media-row',
|
||||
missingMediaUploadDropzone: 'missing-media-upload-dropzone',
|
||||
missingMediaLibrarySelect: 'missing-media-library-select',
|
||||
missingMediaStatusCard: 'missing-media-status-card',
|
||||
missingMediaConfirmButton: 'missing-media-confirm-button',
|
||||
missingMediaCancelButton: 'missing-media-cancel-button',
|
||||
missingMediaLocateButton: 'missing-media-locate-button'
|
||||
},
|
||||
keybindings: {
|
||||
presetMenu: 'keybinding-preset-menu'
|
||||
|
||||
222
browser_tests/tests/missingMedia.spec.ts
Normal file
222
browser_tests/tests/missingMedia.spec.ts
Normal file
@@ -0,0 +1,222 @@
|
||||
import { expect } from '@playwright/test'
|
||||
|
||||
import type { ComfyPage } from '../fixtures/ComfyPage'
|
||||
import { comfyPageFixture as test } from '../fixtures/ComfyPage'
|
||||
import { TestIds } from '../fixtures/selectors'
|
||||
|
||||
async function loadMissingMediaAndOpenErrorsTab(
|
||||
comfyPage: ComfyPage,
|
||||
workflow = 'missing/missing_media_single'
|
||||
) {
|
||||
await comfyPage.workflow.loadWorkflow(workflow)
|
||||
|
||||
const errorOverlay = comfyPage.page.getByTestId(TestIds.dialogs.errorOverlay)
|
||||
await expect(errorOverlay).toBeVisible()
|
||||
|
||||
await errorOverlay.getByTestId(TestIds.dialogs.errorOverlaySeeErrors).click()
|
||||
await expect(errorOverlay).not.toBeVisible()
|
||||
}
|
||||
|
||||
async function uploadFileViaDropzone(comfyPage: ComfyPage) {
|
||||
const dropzone = comfyPage.page.getByTestId(
|
||||
TestIds.dialogs.missingMediaUploadDropzone
|
||||
)
|
||||
const [fileChooser] = await Promise.all([
|
||||
comfyPage.page.waitForEvent('filechooser'),
|
||||
dropzone.click()
|
||||
])
|
||||
await fileChooser.setFiles(comfyPage.assetPath('test_upload_image.png'))
|
||||
}
|
||||
|
||||
async function confirmPendingSelection(comfyPage: ComfyPage) {
|
||||
const confirmButton = comfyPage.page.getByTestId(
|
||||
TestIds.dialogs.missingMediaConfirmButton
|
||||
)
|
||||
await expect(confirmButton).toBeEnabled()
|
||||
await confirmButton.click()
|
||||
}
|
||||
|
||||
function getMediaRow(comfyPage: ComfyPage) {
|
||||
return comfyPage.page.getByTestId(TestIds.dialogs.missingMediaRow)
|
||||
}
|
||||
|
||||
function getStatusCard(comfyPage: ComfyPage) {
|
||||
return comfyPage.page.getByTestId(TestIds.dialogs.missingMediaStatusCard)
|
||||
}
|
||||
|
||||
function getDropzone(comfyPage: ComfyPage) {
|
||||
return comfyPage.page.getByTestId(TestIds.dialogs.missingMediaUploadDropzone)
|
||||
}
|
||||
|
||||
test.describe('Missing media inputs in Error Tab', () => {
|
||||
test.beforeEach(async ({ comfyPage }) => {
|
||||
await comfyPage.settings.setSetting('Comfy.UseNewMenu', 'Top')
|
||||
await comfyPage.settings.setSetting(
|
||||
'Comfy.RightSidePanel.ShowErrorsTab',
|
||||
true
|
||||
)
|
||||
})
|
||||
|
||||
test.describe('Detection', () => {
|
||||
test('Shows error overlay when workflow has missing media inputs', async ({
|
||||
comfyPage
|
||||
}) => {
|
||||
await comfyPage.workflow.loadWorkflow('missing/missing_media_single')
|
||||
|
||||
const errorOverlay = comfyPage.page.getByTestId(
|
||||
TestIds.dialogs.errorOverlay
|
||||
)
|
||||
await expect(errorOverlay).toBeVisible()
|
||||
await expect(errorOverlay.getByText(/Missing Inputs/)).toBeVisible()
|
||||
})
|
||||
|
||||
test('Shows missing media group in errors tab after clicking See Errors', async ({
|
||||
comfyPage
|
||||
}) => {
|
||||
await loadMissingMediaAndOpenErrorsTab(comfyPage)
|
||||
|
||||
await expect(
|
||||
comfyPage.page.getByTestId(TestIds.dialogs.missingMediaGroup)
|
||||
).toBeVisible()
|
||||
})
|
||||
|
||||
test('Shows correct number of missing media rows', async ({
|
||||
comfyPage
|
||||
}) => {
|
||||
await loadMissingMediaAndOpenErrorsTab(
|
||||
comfyPage,
|
||||
'missing/missing_media_multiple'
|
||||
)
|
||||
|
||||
await expect(getMediaRow(comfyPage)).toHaveCount(2)
|
||||
})
|
||||
|
||||
test('Shows upload dropzone and library select for each missing item', async ({
|
||||
comfyPage
|
||||
}) => {
|
||||
await loadMissingMediaAndOpenErrorsTab(comfyPage)
|
||||
|
||||
await expect(getDropzone(comfyPage)).toBeVisible()
|
||||
await expect(
|
||||
comfyPage.page.getByTestId(TestIds.dialogs.missingMediaLibrarySelect)
|
||||
).toBeVisible()
|
||||
})
|
||||
|
||||
test('Does not show error overlay when all media inputs exist', async ({
|
||||
comfyPage
|
||||
}) => {
|
||||
await comfyPage.workflow.loadWorkflow('widgets/load_image_widget')
|
||||
|
||||
await expect(
|
||||
comfyPage.page.getByTestId(TestIds.dialogs.errorOverlay)
|
||||
).not.toBeVisible()
|
||||
})
|
||||
})
|
||||
|
||||
test.describe('Upload flow (2-step confirm)', () => {
|
||||
test('Upload via file picker shows status card then allows confirm', async ({
|
||||
comfyPage
|
||||
}) => {
|
||||
await loadMissingMediaAndOpenErrorsTab(comfyPage)
|
||||
await uploadFileViaDropzone(comfyPage)
|
||||
|
||||
await expect(getStatusCard(comfyPage)).toBeVisible()
|
||||
|
||||
await confirmPendingSelection(comfyPage)
|
||||
await expect(getMediaRow(comfyPage)).toHaveCount(0)
|
||||
})
|
||||
})
|
||||
|
||||
test.describe('Library select flow (2-step confirm)', () => {
|
||||
test('Selecting from library shows status card then allows confirm', async ({
|
||||
comfyPage
|
||||
}) => {
|
||||
await loadMissingMediaAndOpenErrorsTab(comfyPage)
|
||||
|
||||
const librarySelect = comfyPage.page.getByTestId(
|
||||
TestIds.dialogs.missingMediaLibrarySelect
|
||||
)
|
||||
await librarySelect.getByRole('combobox').click()
|
||||
|
||||
const optionCount = await comfyPage.page.getByRole('option').count()
|
||||
if (optionCount === 0) {
|
||||
test.skip()
|
||||
return
|
||||
}
|
||||
|
||||
await comfyPage.page.getByRole('option').first().click()
|
||||
|
||||
await expect(getStatusCard(comfyPage)).toBeVisible()
|
||||
|
||||
await confirmPendingSelection(comfyPage)
|
||||
await expect(getMediaRow(comfyPage)).toHaveCount(0)
|
||||
})
|
||||
})
|
||||
|
||||
test.describe('Cancel selection', () => {
|
||||
test('Cancelling pending selection returns to upload/library UI', async ({
|
||||
comfyPage
|
||||
}) => {
|
||||
await loadMissingMediaAndOpenErrorsTab(comfyPage)
|
||||
await uploadFileViaDropzone(comfyPage)
|
||||
|
||||
await expect(getStatusCard(comfyPage)).toBeVisible()
|
||||
await expect(getDropzone(comfyPage)).not.toBeVisible()
|
||||
|
||||
await comfyPage.page
|
||||
.getByTestId(TestIds.dialogs.missingMediaCancelButton)
|
||||
.click()
|
||||
|
||||
await expect(getStatusCard(comfyPage)).not.toBeVisible()
|
||||
await expect(getDropzone(comfyPage)).toBeVisible()
|
||||
})
|
||||
})
|
||||
|
||||
test.describe('All resolved', () => {
|
||||
test('Missing Inputs group disappears when all items are resolved', async ({
|
||||
comfyPage
|
||||
}) => {
|
||||
await loadMissingMediaAndOpenErrorsTab(comfyPage)
|
||||
await uploadFileViaDropzone(comfyPage)
|
||||
await confirmPendingSelection(comfyPage)
|
||||
|
||||
await expect(
|
||||
comfyPage.page.getByTestId(TestIds.dialogs.missingMediaGroup)
|
||||
).not.toBeVisible()
|
||||
})
|
||||
})
|
||||
|
||||
test.describe('Locate node', () => {
|
||||
test('Locate button navigates canvas to the missing media node', async ({
|
||||
comfyPage
|
||||
}) => {
|
||||
await loadMissingMediaAndOpenErrorsTab(comfyPage)
|
||||
|
||||
const offsetBefore = await comfyPage.page.evaluate(() => {
|
||||
const canvas = window['app']?.canvas
|
||||
return canvas?.ds?.offset
|
||||
? [canvas.ds.offset[0], canvas.ds.offset[1]]
|
||||
: null
|
||||
})
|
||||
|
||||
const locateButton = comfyPage.page.getByTestId(
|
||||
TestIds.dialogs.missingMediaLocateButton
|
||||
)
|
||||
await expect(locateButton).toBeVisible()
|
||||
await locateButton.click()
|
||||
|
||||
await comfyPage.page.waitForTimeout(500)
|
||||
|
||||
const offsetAfter = await comfyPage.page.evaluate(() => {
|
||||
const canvas = window['app']?.canvas
|
||||
return canvas?.ds?.offset
|
||||
? [canvas.ds.offset[0], canvas.ds.offset[1]]
|
||||
: null
|
||||
})
|
||||
|
||||
expect(offsetBefore).not.toBeNull()
|
||||
expect(offsetAfter).not.toBeNull()
|
||||
expect(offsetAfter).not.toEqual(offsetBefore)
|
||||
})
|
||||
})
|
||||
})
|
||||
@@ -1,5 +1,5 @@
|
||||
<template>
|
||||
<div class="flex w-full flex-col pb-3">
|
||||
<div data-testid="missing-media-row" class="flex w-full flex-col pb-3">
|
||||
<!-- File header -->
|
||||
<div class="flex h-8 w-full items-center gap-2">
|
||||
<i
|
||||
@@ -35,6 +35,7 @@
|
||||
|
||||
<!-- Confirm button (visible when pending selection exists) -->
|
||||
<Button
|
||||
data-testid="missing-media-confirm-button"
|
||||
variant="textonly"
|
||||
size="icon-sm"
|
||||
:aria-label="t('rightSidePanel.missingMedia.confirmSelection')"
|
||||
@@ -57,6 +58,7 @@
|
||||
<!-- Locate button (single node only) -->
|
||||
<Button
|
||||
v-if="isSingleNode"
|
||||
data-testid="missing-media-locate-button"
|
||||
variant="textonly"
|
||||
size="icon-sm"
|
||||
:aria-label="t('rightSidePanel.missingMedia.locateNode')"
|
||||
@@ -129,6 +131,7 @@
|
||||
<TransitionCollapse>
|
||||
<div
|
||||
v-if="isPending || isUploading"
|
||||
data-testid="missing-media-status-card"
|
||||
role="status"
|
||||
aria-live="polite"
|
||||
class="bg-foreground/5 relative mt-1 overflow-hidden rounded-lg border border-interface-stroke p-2"
|
||||
@@ -165,6 +168,7 @@
|
||||
</div>
|
||||
|
||||
<Button
|
||||
data-testid="missing-media-cancel-button"
|
||||
variant="textonly"
|
||||
size="icon-sm"
|
||||
:aria-label="t('rightSidePanel.missingMedia.cancelSelection')"
|
||||
@@ -183,6 +187,7 @@
|
||||
<!-- Upload dropzone -->
|
||||
<div class="flex w-full flex-col py-1">
|
||||
<button
|
||||
data-testid="missing-media-upload-dropzone"
|
||||
type="button"
|
||||
:class="
|
||||
cn(
|
||||
@@ -205,6 +210,7 @@
|
||||
|
||||
<!-- OR separator + Use from Library -->
|
||||
<MissingMediaLibrarySelect
|
||||
data-testid="missing-media-library-select"
|
||||
:model-value="undefined"
|
||||
:options="libraryOptions"
|
||||
:show-divider="true"
|
||||
|
||||
@@ -6,33 +6,28 @@ import {
|
||||
} from './missingMediaScan'
|
||||
import type { MissingMediaCandidate } from './types'
|
||||
|
||||
function makeCandidate(
|
||||
nodeId: string,
|
||||
name: string,
|
||||
overrides: Partial<MissingMediaCandidate> = {}
|
||||
): MissingMediaCandidate {
|
||||
return {
|
||||
nodeId,
|
||||
nodeType: 'LoadImage',
|
||||
widgetName: 'image',
|
||||
mediaType: 'image',
|
||||
name,
|
||||
isMissing: true,
|
||||
...overrides
|
||||
}
|
||||
}
|
||||
|
||||
describe('groupCandidatesByName', () => {
|
||||
it('groups candidates with the same name', () => {
|
||||
const candidates: MissingMediaCandidate[] = [
|
||||
{
|
||||
nodeId: '1',
|
||||
nodeType: 'LoadImage',
|
||||
widgetName: 'image',
|
||||
mediaType: 'image',
|
||||
name: 'photo.png',
|
||||
isMissing: true
|
||||
},
|
||||
{
|
||||
nodeId: '2',
|
||||
nodeType: 'LoadImage',
|
||||
widgetName: 'image',
|
||||
mediaType: 'image',
|
||||
name: 'photo.png',
|
||||
isMissing: true
|
||||
},
|
||||
{
|
||||
nodeId: '3',
|
||||
nodeType: 'LoadImage',
|
||||
widgetName: 'image',
|
||||
mediaType: 'image',
|
||||
name: 'other.png',
|
||||
isMissing: true
|
||||
}
|
||||
const candidates = [
|
||||
makeCandidate('1', 'photo.png'),
|
||||
makeCandidate('2', 'photo.png'),
|
||||
makeCandidate('3', 'other.png')
|
||||
]
|
||||
|
||||
const result = groupCandidatesByName(candidates)
|
||||
@@ -53,31 +48,18 @@ describe('groupCandidatesByName', () => {
|
||||
|
||||
describe('groupCandidatesByMediaType', () => {
|
||||
it('groups by media type in order: image, video, audio', () => {
|
||||
const candidates: MissingMediaCandidate[] = [
|
||||
{
|
||||
nodeId: '1',
|
||||
const candidates = [
|
||||
makeCandidate('1', 'sound.mp3', {
|
||||
nodeType: 'LoadAudio',
|
||||
widgetName: 'audio',
|
||||
mediaType: 'audio',
|
||||
name: 'sound.mp3',
|
||||
isMissing: true
|
||||
},
|
||||
{
|
||||
nodeId: '2',
|
||||
nodeType: 'LoadImage',
|
||||
widgetName: 'image',
|
||||
mediaType: 'image',
|
||||
name: 'photo.png',
|
||||
isMissing: true
|
||||
},
|
||||
{
|
||||
nodeId: '3',
|
||||
mediaType: 'audio'
|
||||
}),
|
||||
makeCandidate('2', 'photo.png'),
|
||||
makeCandidate('3', 'clip.mp4', {
|
||||
nodeType: 'LoadVideo',
|
||||
widgetName: 'file',
|
||||
mediaType: 'video',
|
||||
name: 'clip.mp4',
|
||||
isMissing: true
|
||||
}
|
||||
mediaType: 'video'
|
||||
})
|
||||
]
|
||||
|
||||
const result = groupCandidatesByMediaType(candidates)
|
||||
@@ -88,42 +70,41 @@ describe('groupCandidatesByMediaType', () => {
|
||||
})
|
||||
|
||||
it('omits media types with no candidates', () => {
|
||||
const candidates: MissingMediaCandidate[] = [
|
||||
{
|
||||
nodeId: '1',
|
||||
const candidates = [
|
||||
makeCandidate('1', 'clip.mp4', {
|
||||
nodeType: 'LoadVideo',
|
||||
widgetName: 'file',
|
||||
mediaType: 'video',
|
||||
name: 'clip.mp4',
|
||||
isMissing: true
|
||||
}
|
||||
mediaType: 'video'
|
||||
})
|
||||
]
|
||||
|
||||
const result = groupCandidatesByMediaType(candidates)
|
||||
expect(result).toHaveLength(1)
|
||||
expect(result[0].mediaType).toBe('video')
|
||||
})
|
||||
|
||||
it('groups multiple names within the same media type', () => {
|
||||
const candidates = [
|
||||
makeCandidate('1', 'a.png'),
|
||||
makeCandidate('2', 'b.png'),
|
||||
makeCandidate('3', 'a.png')
|
||||
]
|
||||
|
||||
const result = groupCandidatesByMediaType(candidates)
|
||||
expect(result).toHaveLength(1)
|
||||
expect(result[0].mediaType).toBe('image')
|
||||
expect(result[0].items).toHaveLength(2)
|
||||
expect(
|
||||
result[0].items.find((i) => i.name === 'a.png')?.referencingNodes
|
||||
).toHaveLength(2)
|
||||
})
|
||||
})
|
||||
|
||||
describe('verifyCloudMediaCandidates', () => {
|
||||
it('marks candidates missing when not in input assets', async () => {
|
||||
const candidates: MissingMediaCandidate[] = [
|
||||
{
|
||||
nodeId: '1',
|
||||
nodeType: 'LoadImage',
|
||||
widgetName: 'image',
|
||||
mediaType: 'image',
|
||||
name: 'abc123.png',
|
||||
isMissing: undefined
|
||||
},
|
||||
{
|
||||
nodeId: '2',
|
||||
nodeType: 'LoadImage',
|
||||
widgetName: 'image',
|
||||
mediaType: 'image',
|
||||
name: 'def456.png',
|
||||
isMissing: undefined
|
||||
}
|
||||
const candidates = [
|
||||
makeCandidate('1', 'abc123.png', { isMissing: undefined }),
|
||||
makeCandidate('2', 'def456.png', { isMissing: undefined })
|
||||
]
|
||||
|
||||
const mockStore = {
|
||||
@@ -137,38 +118,53 @@ describe('verifyCloudMediaCandidates', () => {
|
||||
expect(candidates[1].isMissing).toBe(false)
|
||||
})
|
||||
|
||||
it('respects abort signal', async () => {
|
||||
it('calls updateInputs before checking assets', async () => {
|
||||
let updateCalled = false
|
||||
const candidates = [makeCandidate('1', 'abc.png', { isMissing: undefined })]
|
||||
|
||||
const mockStore = {
|
||||
updateInputs: async () => {
|
||||
updateCalled = true
|
||||
},
|
||||
inputAssets: []
|
||||
}
|
||||
|
||||
await verifyCloudMediaCandidates(candidates, undefined, mockStore)
|
||||
|
||||
expect(updateCalled).toBe(true)
|
||||
})
|
||||
|
||||
it('respects abort signal before execution', async () => {
|
||||
const controller = new AbortController()
|
||||
controller.abort()
|
||||
|
||||
const candidates: MissingMediaCandidate[] = [
|
||||
{
|
||||
nodeId: '1',
|
||||
nodeType: 'LoadImage',
|
||||
widgetName: 'image',
|
||||
mediaType: 'image',
|
||||
name: 'abc123.png',
|
||||
isMissing: undefined
|
||||
}
|
||||
const candidates = [
|
||||
makeCandidate('1', 'abc123.png', { isMissing: undefined })
|
||||
]
|
||||
|
||||
await verifyCloudMediaCandidates(candidates, controller.signal)
|
||||
|
||||
// Should not have been modified
|
||||
expect(candidates[0].isMissing).toBeUndefined()
|
||||
})
|
||||
|
||||
it('skips candidates already resolved', async () => {
|
||||
const candidates: MissingMediaCandidate[] = [
|
||||
{
|
||||
nodeId: '1',
|
||||
nodeType: 'LoadImage',
|
||||
widgetName: 'image',
|
||||
mediaType: 'image',
|
||||
name: 'abc123.png',
|
||||
isMissing: true
|
||||
}
|
||||
]
|
||||
it('respects abort signal after updateInputs', async () => {
|
||||
const controller = new AbortController()
|
||||
const candidates = [makeCandidate('1', 'abc.png', { isMissing: undefined })]
|
||||
|
||||
const mockStore = {
|
||||
updateInputs: async () => {
|
||||
controller.abort()
|
||||
},
|
||||
inputAssets: [{ asset_hash: 'abc.png', name: 'photo.png' }]
|
||||
}
|
||||
|
||||
await verifyCloudMediaCandidates(candidates, controller.signal, mockStore)
|
||||
|
||||
expect(candidates[0].isMissing).toBeUndefined()
|
||||
})
|
||||
|
||||
it('skips candidates already resolved as true', async () => {
|
||||
const candidates = [makeCandidate('1', 'abc.png', { isMissing: true })]
|
||||
|
||||
const mockStore = {
|
||||
updateInputs: async () => {},
|
||||
@@ -177,7 +173,35 @@ describe('verifyCloudMediaCandidates', () => {
|
||||
|
||||
await verifyCloudMediaCandidates(candidates, undefined, mockStore)
|
||||
|
||||
// Already resolved, should remain unchanged
|
||||
expect(candidates[0].isMissing).toBe(true)
|
||||
})
|
||||
|
||||
it('skips candidates already resolved as false', async () => {
|
||||
const candidates = [makeCandidate('1', 'abc.png', { isMissing: false })]
|
||||
|
||||
const mockStore = {
|
||||
updateInputs: async () => {},
|
||||
inputAssets: []
|
||||
}
|
||||
|
||||
await verifyCloudMediaCandidates(candidates, undefined, mockStore)
|
||||
|
||||
expect(candidates[0].isMissing).toBe(false)
|
||||
})
|
||||
|
||||
it('skips entirely when no pending candidates', async () => {
|
||||
let updateCalled = false
|
||||
const candidates = [makeCandidate('1', 'abc.png', { isMissing: true })]
|
||||
|
||||
const mockStore = {
|
||||
updateInputs: async () => {
|
||||
updateCalled = true
|
||||
},
|
||||
inputAssets: []
|
||||
}
|
||||
|
||||
await verifyCloudMediaCandidates(candidates, undefined, mockStore)
|
||||
|
||||
expect(updateCalled).toBe(false)
|
||||
})
|
||||
})
|
||||
|
||||
@@ -17,7 +17,7 @@ import {
|
||||
import { resolveComboValues } from '@/utils/litegraphUtil'
|
||||
|
||||
/** Map of node types to their media widget name and media type. */
|
||||
export const MEDIA_NODE_WIDGETS: Record<
|
||||
const MEDIA_NODE_WIDGETS: Record<
|
||||
string,
|
||||
{ widgetName: string; mediaType: MediaType }
|
||||
> = {
|
||||
|
||||
Reference in New Issue
Block a user