Download frontend/src/tests/SamplingPreviewWidget.test.tsx from turtle89431/Moxie-Multimedia: direct link, hf CLI and curl.
- Browser
- Download file 11.9 kB
-
https://huggingface.co/turtle89431/Moxie-Multimedia/resolve/main/frontend/src/tests/SamplingPreviewWidget.test.tsx
- Command line
-
hf download hf://turtle89431/Moxie-Multimedia/frontend/src/tests/SamplingPreviewWidget.test.tsx
-
curl -L -o SamplingPreviewWidget.test.tsx https://huggingface.co/turtle89431/Moxie-Multimedia/resolve/main/frontend/src/tests/SamplingPreviewWidget.test.tsx
11.9 kB
| import { act, fireEvent, render, screen } from '@testing-library/react' | |
| import { beforeEach, describe, expect, it, vi } from 'vitest' | |
| import { | |
| SAMPLING_PREVIEW_EVENT, | |
| SamplingPreviewWidget, | |
| type SamplingPreviewPayload, | |
| } from '@/components/widgets/SamplingPreviewWidget' | |
| describe('SamplingPreviewWidget', () => { | |
| const listeners = new Map<string, (event: CustomEvent<unknown>) => void>() | |
| const api = { | |
| addCustomEventListener: vi.fn((name: string, listener: (event: CustomEvent<unknown>) => void) => { | |
| listeners.set(name, listener) | |
| }), | |
| removeCustomEventListener: vi.fn((name: string) => listeners.delete(name)), | |
| } | |
| const rawApp = { | |
| api, | |
| ui: { settings: { settingsValues: { 'Comfy.Locale': 'en' } } }, | |
| } | |
| const app = rawApp as never | |
| beforeEach(() => { | |
| listeners.clear() | |
| vi.clearAllMocks() | |
| rawApp.ui.settings.settingsValues['Comfy.Locale'] = 'en' | |
| vi.stubGlobal('requestAnimationFrame', vi.fn(() => 1)) | |
| vi.stubGlobal('cancelAnimationFrame', vi.fn()) | |
| Object.defineProperty(URL, 'createObjectURL', { | |
| configurable: true, | |
| value: vi.fn(() => 'blob:preview'), | |
| }) | |
| Object.defineProperty(URL, 'revokeObjectURL', { | |
| configurable: true, | |
| value: vi.fn(), | |
| }) | |
| }) | |
| function publish(overrides: Partial<SamplingPreviewPayload> = {}) { | |
| const payload: SamplingPreviewPayload = { | |
| node_id: '42', | |
| prompt_id: 'prompt-1', | |
| image: btoa('frame-1'), | |
| mime: 'image/jpeg', | |
| step: 3, | |
| total: 8, | |
| fps: 24, | |
| frame_count: 60, | |
| segment_index: 1, | |
| sampling_pass: 'second', | |
| ...overrides, | |
| } | |
| act(() => listeners.get(SAMPLING_PREVIEW_EVENT)?.(new CustomEvent( | |
| SAMPLING_PREVIEW_EVENT, | |
| { detail: payload }, | |
| ))) | |
| } | |
| it('shows metadata in the playback bar without playback text or percentage', () => { | |
| const onVisibilityChange = vi.fn() | |
| render( | |
| <SamplingPreviewWidget | |
| app={app} | |
| nodeId="42" | |
| onVisibilityChange={onVisibilityChange} | |
| />, | |
| ) | |
| publish() | |
| expect(screen.getByAltText('Sampling preview').getAttribute('src')).toBe('blob:preview') | |
| expect(screen.getByText('Segment 2 路 Second pass 路 3/8')).not.toBeNull() | |
| const progress = screen.getByRole('progressbar', { | |
| name: 'Sampling preview playback progress', | |
| }) | |
| expect(progress.getAttribute('aria-valuemax')).toBe('100') | |
| expect(screen.queryByText('Preview playback')).toBeNull() | |
| expect(screen.queryByText(/%$/)).toBeNull() | |
| expect(onVisibilityChange).toHaveBeenLastCalledWith(true) | |
| }) | |
| it('pauses and resumes from either the image or progress bar', () => { | |
| render(<SamplingPreviewWidget app={app} nodeId="42" />) | |
| publish() | |
| const pauseControls = screen.getAllByRole('button', { name: 'Pause sampling preview' }) | |
| fireEvent.click(pauseControls[0]) | |
| expect(screen.getAllByRole('button', { name: 'Resume sampling preview' })).toHaveLength(2) | |
| fireEvent.click(screen.getAllByRole('button', { name: 'Resume sampling preview' })[1]) | |
| expect(screen.getAllByRole('button', { name: 'Pause sampling preview' })).toHaveLength(2) | |
| }) | |
| it('freezes an animated WebP fallback when browser frame decoding is unavailable', () => { | |
| const drawImage = vi.fn() | |
| vi.spyOn(HTMLCanvasElement.prototype, 'getContext').mockReturnValue({ drawImage } as never) | |
| render(<SamplingPreviewWidget app={app} nodeId="42" />) | |
| publish({ mime: 'image/webp' }) | |
| const image = screen.getByAltText('Sampling preview') as HTMLImageElement | |
| Object.defineProperties(image, { | |
| complete: { configurable: true, value: true }, | |
| naturalWidth: { configurable: true, value: 320 }, | |
| naturalHeight: { configurable: true, value: 180 }, | |
| }) | |
| fireEvent.click(screen.getAllByRole('button', { name: 'Pause sampling preview' })[0]) | |
| const frozenFrame = screen.getByTestId('sampling-preview-frozen-frame') as HTMLCanvasElement | |
| expect(drawImage).toHaveBeenCalledWith(image, 0, 0) | |
| expect(frozenFrame.hidden).toBe(false) | |
| expect(frozenFrame.width).toBe(320) | |
| expect(frozenFrame.height).toBe(180) | |
| publish({ image: btoa('new-preview'), mime: 'image/webp', step: 4 }) | |
| expect(drawImage).toHaveBeenCalledTimes(1) | |
| expect(frozenFrame.hidden).toBe(false) | |
| expect(screen.getByText('Segment 2 路 Second pass 路 3/8')).not.toBeNull() | |
| fireEvent.click(screen.getAllByRole('button', { name: 'Resume sampling preview' })[1]) | |
| expect(frozenFrame.hidden).toBe(true) | |
| expect(screen.getByText('Segment 2 路 Second pass 路 4/8')).not.toBeNull() | |
| }) | |
| it('hides the image while keeping its placeholder and show control', () => { | |
| render(<SamplingPreviewWidget app={app} nodeId="42" />) | |
| publish() | |
| fireEvent.click(screen.getByRole('button', { name: 'Hide sampling preview' })) | |
| expect(screen.queryByAltText('Sampling preview')).toBeNull() | |
| expect(screen.getByTestId('sampling-preview-placeholder')).not.toBeNull() | |
| expect(screen.getByRole('button', { name: 'Show sampling preview' })).not.toBeNull() | |
| expect(screen.getByText('Segment 2 路 Second pass 路 3/8')).not.toBeNull() | |
| }) | |
| it('ignores preview events targeting another node', () => { | |
| render(<SamplingPreviewWidget app={app} nodeId="42" />) | |
| publish({ node_id: '99' }) | |
| expect(screen.queryByAltText('Sampling preview')).toBeNull() | |
| }) | |
| it('renders a display-only preview without controls for pointer-transparent overlays', () => { | |
| render(<SamplingPreviewWidget app={app} nodeId="42" interactive={false} />) | |
| publish() | |
| expect(screen.getByAltText('Sampling preview')).not.toBeNull() | |
| expect(screen.queryByRole('button')).toBeNull() | |
| expect(screen.getByRole('progressbar')).not.toBeNull() | |
| }) | |
| it('keeps only the visibility icon interactive in a canvas-transparent overlay', () => { | |
| render( | |
| <SamplingPreviewWidget | |
| app={app} | |
| nodeId="42" | |
| interactive={false} | |
| visibilityControlOnly | |
| />, | |
| ) | |
| publish() | |
| const hideButton = screen.getByRole('button', { name: 'Hide sampling preview' }) | |
| expect(hideButton.className).toContain('pointer-events-auto') | |
| expect(screen.queryByRole('button', { name: 'Pause sampling preview' })).toBeNull() | |
| fireEvent.click(hideButton) | |
| expect(screen.getByRole('button', { name: 'Show sampling preview' })).not.toBeNull() | |
| }) | |
| it('reads the LiteGraph node id when the event arrives, after node creation', () => { | |
| let assignedNodeId = -1 | |
| render(<SamplingPreviewWidget app={app} nodeId={() => assignedNodeId} />) | |
| assignedNodeId = 42 | |
| publish() | |
| expect(screen.getByAltText('Sampling preview')).not.toBeNull() | |
| }) | |
| it('accepts the canvas display node id used by expanded graph execution', () => { | |
| render(<SamplingPreviewWidget app={app} nodeId="42" />) | |
| publish({ node_id: '42.3.1.87', display_node_id: '42' }) | |
| expect(screen.getByAltText('Sampling preview')).not.toBeNull() | |
| }) | |
| it('uses the current ComfyUI locale', () => { | |
| rawApp.ui.settings.settingsValues['Comfy.Locale'] = 'zh' | |
| render(<SamplingPreviewWidget app={app} nodeId="42" />) | |
| publish() | |
| expect(screen.getByText('鐗囨 2 路 浜岄噰 路 3/8')).not.toBeNull() | |
| expect(screen.getByRole('button', { name: '闅愯棌閲囨牱棰勮' })).not.toBeNull() | |
| }) | |
| it('clears stale preview when the project starts a new execution', () => { | |
| const onVisibilityChange = vi.fn() | |
| const view = render( | |
| <SamplingPreviewWidget | |
| app={app} | |
| nodeId="42" | |
| executionRevision={0} | |
| onVisibilityChange={onVisibilityChange} | |
| />, | |
| ) | |
| publish() | |
| expect(screen.getByAltText('Sampling preview')).not.toBeNull() | |
| view.rerender( | |
| <SamplingPreviewWidget | |
| app={app} | |
| nodeId="42" | |
| executionRevision={1} | |
| onVisibilityChange={onVisibilityChange} | |
| />, | |
| ) | |
| expect(screen.queryByAltText('Sampling preview')).toBeNull() | |
| expect(onVisibilityChange).toHaveBeenLastCalledWith(false) | |
| }) | |
| it('ignores queued preview frames from a previous prompt', () => { | |
| render(<SamplingPreviewWidget app={app} nodeId="42" />) | |
| act(() => listeners.get('execution_start')?.(new CustomEvent( | |
| 'execution_start', | |
| { detail: { prompt_id: 'prompt-2' } }, | |
| ))) | |
| publish({ prompt_id: 'prompt-1' }) | |
| expect(screen.queryByAltText('Sampling preview')).toBeNull() | |
| publish({ prompt_id: 'prompt-2' }) | |
| expect(screen.getByAltText('Sampling preview')).not.toBeNull() | |
| }) | |
| it('keeps the completed preview when an unrelated prompt starts', () => { | |
| render(<SamplingPreviewWidget app={app} nodeId="42" />) | |
| publish({ prompt_id: 'prompt-1' }) | |
| act(() => listeners.get('execution_start')?.(new CustomEvent( | |
| 'execution_start', | |
| { detail: { prompt_id: 'unrelated-prompt' } }, | |
| ))) | |
| expect(screen.getByAltText('Sampling preview')).not.toBeNull() | |
| }) | |
| it('uses the requested half-frame count and does not restart on every sampling step', () => { | |
| let nextFrame: FrameRequestCallback | undefined | |
| vi.mocked(requestAnimationFrame).mockImplementation((callback) => { | |
| nextFrame = callback | |
| return 1 | |
| }) | |
| vi.spyOn(performance, 'now').mockReturnValue(0) | |
| render(<SamplingPreviewWidget app={app} nodeId="42" />) | |
| publish({ frame_count: 60, fps: 24 }) | |
| act(() => nextFrame?.(1000)) | |
| const progress = screen.getByRole('progressbar', { | |
| name: 'Sampling preview playback progress', | |
| }) | |
| expect(progress.getAttribute('aria-valuenow')).toBe('40') | |
| publish({ image: btoa('new-preview'), step: 4, frame_count: 60, fps: 24 }) | |
| expect(progress.getAttribute('aria-valuenow')).toBe('40') | |
| }) | |
| it('plays every frame from the legacy backend payload until ComfyUI restarts', () => { | |
| let nextFrame: FrameRequestCallback | undefined | |
| let urlIndex = 0 | |
| vi.mocked(requestAnimationFrame).mockImplementation((callback) => { | |
| nextFrame = callback | |
| return 1 | |
| }) | |
| vi.mocked(URL.createObjectURL).mockImplementation(() => `blob:preview-${++urlIndex}`) | |
| vi.spyOn(performance, 'now').mockReturnValue(0) | |
| render(<SamplingPreviewWidget app={app} nodeId="42" />) | |
| publish({ | |
| images: [btoa('frame-1'), btoa('frame-2')], | |
| frame_count: 2, | |
| fps: 2, | |
| }) | |
| expect(screen.getByAltText('Sampling preview').getAttribute('src')).toBe('blob:preview-2') | |
| act(() => nextFrame?.(600)) | |
| expect(screen.getByAltText('Sampling preview').getAttribute('src')).toBe('blob:preview-3') | |
| }) | |
| it('holds the currently displayed addressable frame while paused', () => { | |
| let nextFrame: FrameRequestCallback | undefined | |
| let urlIndex = 0 | |
| vi.mocked(requestAnimationFrame).mockImplementation((callback) => { | |
| nextFrame = callback | |
| return 1 | |
| }) | |
| vi.mocked(URL.createObjectURL).mockImplementation(() => `blob:preview-${++urlIndex}`) | |
| vi.spyOn(performance, 'now').mockReturnValue(0) | |
| render(<SamplingPreviewWidget app={app} nodeId="42" />) | |
| publish({ | |
| images: [btoa('frame-1'), btoa('frame-2')], | |
| frame_count: 2, | |
| fps: 2, | |
| }) | |
| act(() => nextFrame?.(600)) | |
| const pausedSource = screen.getByAltText('Sampling preview').getAttribute('src') | |
| expect(pausedSource).toBe('blob:preview-3') | |
| fireEvent.click(screen.getAllByRole('button', { name: 'Pause sampling preview' })[0]) | |
| publish({ | |
| image: btoa('new-preview'), | |
| images: [btoa('new-frame-1'), btoa('new-frame-2')], | |
| frame_count: 2, | |
| fps: 2, | |
| step: 4, | |
| }) | |
| expect(screen.getByAltText('Sampling preview').getAttribute('src')).toBe(pausedSource) | |
| expect(screen.getByText('Segment 2 路 Second pass 路 3/8')).not.toBeNull() | |
| fireEvent.click(screen.getAllByRole('button', { name: 'Resume sampling preview' })[1]) | |
| expect(screen.getByAltText('Sampling preview').getAttribute('src')).not.toBe(pausedSource) | |
| expect(screen.getByText('Segment 2 路 Second pass 路 4/8')).not.toBeNull() | |
| }) | |
| }) | |