mirror of
https://github.com/Comfy-Org/ComfyUI_frontend.git
synced 2026-04-29 02:32:18 +00:00
Initial 1.28 backports (#5986)
Includes - Subgraph widget promotion fixes (#5911) - fix "what's changed" release toast attention level logic (#5959) - Fix: Missing Node Title Editor bug (#5963) - OpenAIVideoSora2 Frontend pricing (#5958) - hotfix: quick test updates for sora2 pricing badge. (#5966) ┆Issue is synchronized with this [Notion page](https://www.notion.so/PR-5986-Initial-1-28-backports-2866d73d365081448ff2c4827bdbb9e5) by [Unito](https://www.unito.io) --------- Co-authored-by: Christian Byrne <cbyrne@comfy.org> Co-authored-by: Alexander Brown <drjkl@comfy.org> Co-authored-by: Marwan Ahmed <155799754+marawan206@users.noreply.github.com>
This commit is contained in:
@@ -301,7 +301,115 @@ describe('useNodePricing', () => {
|
||||
expect(price).toBe('$0.04-0.12/Run (varies with size & quality)')
|
||||
})
|
||||
})
|
||||
// ============================== OpenAIVideoSora2 ==============================
|
||||
describe('dynamic pricing - OpenAIVideoSora2', () => {
|
||||
it('should require model, duration & size when widgets are missing', () => {
|
||||
const { getNodeDisplayPrice } = useNodePricing()
|
||||
const node = createMockNode('OpenAIVideoSora2', [])
|
||||
expect(getNodeDisplayPrice(node)).toBe('Set model, duration & size')
|
||||
})
|
||||
|
||||
it('should require duration when duration is invalid or zero', () => {
|
||||
const { getNodeDisplayPrice } = useNodePricing()
|
||||
const nodeNaN = createMockNode('OpenAIVideoSora2', [
|
||||
{ name: 'model', value: 'sora-2-pro' },
|
||||
{ name: 'duration', value: 'oops' },
|
||||
{ name: 'size', value: '720x1280' }
|
||||
])
|
||||
expect(getNodeDisplayPrice(nodeNaN)).toBe('Set model, duration & size')
|
||||
|
||||
const nodeZero = createMockNode('OpenAIVideoSora2', [
|
||||
{ name: 'model', value: 'sora-2-pro' },
|
||||
{ name: 'duration', value: 0 },
|
||||
{ name: 'size', value: '720x1280' }
|
||||
])
|
||||
expect(getNodeDisplayPrice(nodeZero)).toBe('Set model, duration & size')
|
||||
})
|
||||
|
||||
it('should require size when size is missing', () => {
|
||||
const { getNodeDisplayPrice } = useNodePricing()
|
||||
const node = createMockNode('OpenAIVideoSora2', [
|
||||
{ name: 'model', value: 'sora-2-pro' },
|
||||
{ name: 'duration', value: 8 }
|
||||
])
|
||||
expect(getNodeDisplayPrice(node)).toBe('Set model, duration & size')
|
||||
})
|
||||
|
||||
it('should compute pricing for sora-2-pro with 1024x1792', () => {
|
||||
const { getNodeDisplayPrice } = useNodePricing()
|
||||
const node = createMockNode('OpenAIVideoSora2', [
|
||||
{ name: 'model', value: 'sora-2-pro' },
|
||||
{ name: 'duration', value: 8 },
|
||||
{ name: 'size', value: '1024x1792' }
|
||||
])
|
||||
expect(getNodeDisplayPrice(node)).toBe('$4.00/Run') // 0.5 * 8
|
||||
})
|
||||
|
||||
it('should compute pricing for sora-2-pro with 720x1280', () => {
|
||||
const { getNodeDisplayPrice } = useNodePricing()
|
||||
const node = createMockNode('OpenAIVideoSora2', [
|
||||
{ name: 'model', value: 'sora-2-pro' },
|
||||
{ name: 'duration', value: 12 },
|
||||
{ name: 'size', value: '720x1280' }
|
||||
])
|
||||
expect(getNodeDisplayPrice(node)).toBe('$3.60/Run') // 0.3 * 12
|
||||
})
|
||||
|
||||
it('should reject unsupported size for sora-2-pro', () => {
|
||||
const { getNodeDisplayPrice } = useNodePricing()
|
||||
const node = createMockNode('OpenAIVideoSora2', [
|
||||
{ name: 'model', value: 'sora-2-pro' },
|
||||
{ name: 'duration', value: 8 },
|
||||
{ name: 'size', value: '640x640' }
|
||||
])
|
||||
expect(getNodeDisplayPrice(node)).toBe(
|
||||
'Invalid size. Must be 720x1280, 1280x720, 1024x1792, or 1792x1024.'
|
||||
)
|
||||
})
|
||||
|
||||
it('should compute pricing for sora-2 (720x1280 only)', () => {
|
||||
const { getNodeDisplayPrice } = useNodePricing()
|
||||
const node = createMockNode('OpenAIVideoSora2', [
|
||||
{ name: 'model', value: 'sora-2' },
|
||||
{ name: 'duration', value: 10 },
|
||||
{ name: 'size', value: '720x1280' }
|
||||
])
|
||||
expect(getNodeDisplayPrice(node)).toBe('$1.00/Run') // 0.1 * 10
|
||||
})
|
||||
|
||||
it('should reject non-720 sizes for sora-2', () => {
|
||||
const { getNodeDisplayPrice } = useNodePricing()
|
||||
const node = createMockNode('OpenAIVideoSora2', [
|
||||
{ name: 'model', value: 'sora-2' },
|
||||
{ name: 'duration', value: 8 },
|
||||
{ name: 'size', value: '1024x1792' }
|
||||
])
|
||||
expect(getNodeDisplayPrice(node)).toBe(
|
||||
'sora-2 supports only 720x1280 or 1280x720'
|
||||
)
|
||||
})
|
||||
it('should accept duration_s alias for duration', () => {
|
||||
const { getNodeDisplayPrice } = useNodePricing()
|
||||
const node = createMockNode('OpenAIVideoSora2', [
|
||||
{ name: 'model', value: 'sora-2-pro' },
|
||||
{ name: 'duration_s', value: 4 },
|
||||
{ name: 'size', value: '1792x1024' }
|
||||
])
|
||||
expect(getNodeDisplayPrice(node)).toBe('$2.00/Run') // 0.5 * 4
|
||||
})
|
||||
|
||||
it('should be case-insensitive for model and size', () => {
|
||||
const { getNodeDisplayPrice } = useNodePricing()
|
||||
const node = createMockNode('OpenAIVideoSora2', [
|
||||
{ name: 'model', value: 'SoRa-2-PrO' },
|
||||
{ name: 'duration', value: 12 },
|
||||
{ name: 'size', value: '1280x720' }
|
||||
])
|
||||
expect(getNodeDisplayPrice(node)).toBe('$3.60/Run') // 0.3 * 12
|
||||
})
|
||||
})
|
||||
|
||||
// ============================== MinimaxHailuoVideoNode ==============================
|
||||
describe('dynamic pricing - MinimaxHailuoVideoNode', () => {
|
||||
it('should return $0.28 for 6s duration and 768P resolution', () => {
|
||||
const { getNodeDisplayPrice } = useNodePricing()
|
||||
|
||||
@@ -145,18 +145,24 @@ describe('useReleaseStore', () => {
|
||||
|
||||
it('should show toast for medium/high attention releases', () => {
|
||||
vi.mocked(semverCompare).mockReturnValue(1)
|
||||
|
||||
// Need multiple releases for hasMediumOrHighAttention to work
|
||||
const mediumRelease = {
|
||||
...mockRelease,
|
||||
id: 2,
|
||||
attention: 'medium' as const
|
||||
}
|
||||
store.releases = [mockRelease, mediumRelease]
|
||||
store.releases = [mockRelease]
|
||||
|
||||
expect(store.shouldShowToast).toBe(true)
|
||||
})
|
||||
|
||||
it('should not show toast for low attention releases', () => {
|
||||
vi.mocked(semverCompare).mockReturnValue(1)
|
||||
|
||||
const lowAttentionRelease = {
|
||||
...mockRelease,
|
||||
attention: 'low' as const
|
||||
}
|
||||
|
||||
store.releases = [lowAttentionRelease]
|
||||
|
||||
expect(store.shouldShowToast).toBe(false)
|
||||
})
|
||||
|
||||
it('should show red dot for new versions', () => {
|
||||
vi.mocked(semverCompare).mockReturnValue(1)
|
||||
|
||||
@@ -490,12 +496,7 @@ describe('useReleaseStore', () => {
|
||||
|
||||
vi.mocked(semverCompare).mockReturnValue(1)
|
||||
|
||||
const mediumRelease = { ...mockRelease, attention: 'medium' as const }
|
||||
store.releases = [
|
||||
mockRelease,
|
||||
mediumRelease,
|
||||
{ ...mockRelease, attention: 'low' as const }
|
||||
]
|
||||
store.releases = [mockRelease]
|
||||
|
||||
expect(store.shouldShowToast).toBe(true)
|
||||
})
|
||||
@@ -578,14 +579,7 @@ describe('useReleaseStore', () => {
|
||||
|
||||
it('should show toast when conditions are met', () => {
|
||||
vi.mocked(semverCompare).mockReturnValue(1)
|
||||
|
||||
// Need multiple releases for hasMediumOrHighAttention
|
||||
const mediumRelease = {
|
||||
...mockRelease,
|
||||
id: 2,
|
||||
attention: 'medium' as const
|
||||
}
|
||||
store.releases = [mockRelease, mediumRelease]
|
||||
store.releases = [mockRelease]
|
||||
|
||||
expect(store.shouldShowToast).toBe(true)
|
||||
})
|
||||
@@ -615,12 +609,7 @@ describe('useReleaseStore', () => {
|
||||
vi.mocked(semverCompare).mockReturnValue(1)
|
||||
|
||||
// Set up all conditions that would normally show toast
|
||||
const mediumRelease = {
|
||||
...mockRelease,
|
||||
id: 2,
|
||||
attention: 'medium' as const
|
||||
}
|
||||
store.releases = [mockRelease, mediumRelease]
|
||||
store.releases = [mockRelease]
|
||||
|
||||
expect(store.shouldShowToast).toBe(false)
|
||||
})
|
||||
|
||||
Reference in New Issue
Block a user