Skip to content

Commit 2bb47e1

Browse files
committed
test: replay coverage PR for CodeRabbit canary
Apply the PR #13362 diff on top of the PR #13486 CodeRabbit path-instruction branch so CodeRabbit can review real changed test files before #13486 merges. Verification: replayed the origin/pr-13362 commit range onto origin/pr-13486 with git cherry-pick --no-commit and no conflicts; parsed .coderabbit.yaml with Ruby YAML.load_file successfully; ran git diff --cached --check successfully.
1 parent 676c3c5 commit 2bb47e1

80 files changed

Lines changed: 16434 additions & 111 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

src/base/common/async.test.ts

Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'
2+
3+
describe('runWhenGlobalIdle', () => {
4+
beforeEach(() => {
5+
vi.resetModules()
6+
})
7+
8+
afterEach(() => {
9+
vi.useRealTimers()
10+
vi.unstubAllGlobals()
11+
})
12+
13+
it('falls back to a timeout when idle callbacks are unavailable', async () => {
14+
vi.useFakeTimers()
15+
vi.stubGlobal('requestIdleCallback', undefined)
16+
vi.stubGlobal('cancelIdleCallback', undefined)
17+
const { runWhenGlobalIdle } = await import('./async')
18+
const runner = vi.fn()
19+
20+
const disposable = runWhenGlobalIdle(runner)
21+
await vi.runAllTimersAsync()
22+
23+
expect(runner).toHaveBeenCalledOnce()
24+
const deadline = runner.mock.calls[0][0]
25+
expect(deadline.didTimeout).toBe(true)
26+
expect(deadline.timeRemaining()).toBeGreaterThanOrEqual(0)
27+
28+
disposable.dispose()
29+
disposable.dispose()
30+
})
31+
32+
it('cancels fallback idle work before it runs', async () => {
33+
vi.useFakeTimers()
34+
vi.stubGlobal('requestIdleCallback', undefined)
35+
vi.stubGlobal('cancelIdleCallback', undefined)
36+
const { runWhenGlobalIdle } = await import('./async')
37+
const runner = vi.fn()
38+
39+
runWhenGlobalIdle(runner).dispose()
40+
await vi.runAllTimersAsync()
41+
42+
expect(runner).not.toHaveBeenCalled()
43+
})
44+
45+
it('uses native idle callbacks when available', async () => {
46+
const requestIdleCallback = vi.fn(() => 42)
47+
const cancelIdleCallback = vi.fn()
48+
vi.stubGlobal('requestIdleCallback', requestIdleCallback)
49+
vi.stubGlobal('cancelIdleCallback', cancelIdleCallback)
50+
const { runWhenGlobalIdle } = await import('./async')
51+
const runner = vi.fn()
52+
53+
const disposable = runWhenGlobalIdle(runner, 250)
54+
55+
expect(requestIdleCallback).toHaveBeenCalledWith(runner, { timeout: 250 })
56+
57+
disposable.dispose()
58+
disposable.dispose()
59+
60+
expect(cancelIdleCallback).toHaveBeenCalledOnce()
61+
expect(cancelIdleCallback).toHaveBeenCalledWith(42)
62+
})
63+
64+
it('omits native idle timeout options when no timeout is supplied', async () => {
65+
const requestIdleCallback = vi.fn(() => 7)
66+
vi.stubGlobal('requestIdleCallback', requestIdleCallback)
67+
vi.stubGlobal('cancelIdleCallback', vi.fn())
68+
const { runWhenGlobalIdle } = await import('./async')
69+
const runner = vi.fn()
70+
71+
runWhenGlobalIdle(runner)
72+
73+
expect(requestIdleCallback).toHaveBeenCalledWith(runner, undefined)
74+
})
75+
})

src/base/common/downloadUtil.test.ts

Lines changed: 21 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { fromAny, fromPartial } from '@total-typescript/shoehorn'
1+
import { fromPartial } from '@total-typescript/shoehorn'
22
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'
33

44
import {
@@ -122,6 +122,22 @@ describe('downloadUtil', () => {
122122
expect(createObjectURLSpy).not.toHaveBeenCalled()
123123
})
124124

125+
it('throws for an empty URL', () => {
126+
expect(() => downloadFile('')).toThrow(
127+
'Invalid URL provided for download'
128+
)
129+
expect(fetchMock).not.toHaveBeenCalled()
130+
expect(createObjectURLSpy).not.toHaveBeenCalled()
131+
})
132+
133+
it('throws for a whitespace URL', () => {
134+
expect(() => downloadFile(' ')).toThrow(
135+
'Invalid URL provided for download'
136+
)
137+
expect(fetchMock).not.toHaveBeenCalled()
138+
expect(createObjectURLSpy).not.toHaveBeenCalled()
139+
})
140+
125141
it('should prefer custom filename over extracted filename', () => {
126142
const testUrl =
127143
'https://example.com/api/file?filename=extracted-image.jpg'
@@ -339,7 +355,7 @@ describe('downloadUtil', () => {
339355
const testUrl = 'https://storage.googleapis.com/bucket/image.png'
340356
const blob = new Blob(['test'], { type: 'image/png' })
341357
const mockTab = { location: { href: '' }, closed: false, close: vi.fn() }
342-
windowOpenSpy.mockReturnValue(fromAny<Window, unknown>(mockTab))
358+
windowOpenSpy.mockReturnValue(fromPartial<Window>(mockTab))
343359
fetchMock.mockResolvedValue(
344360
fromPartial<Response>({
345361
ok: true,
@@ -359,7 +375,7 @@ describe('downloadUtil', () => {
359375
mockIsCloud.value = true
360376
const blob = new Blob(['test'], { type: 'image/png' })
361377
const mockTab = { location: { href: '' }, closed: false, close: vi.fn() }
362-
windowOpenSpy.mockReturnValue(fromAny<Window, unknown>(mockTab))
378+
windowOpenSpy.mockReturnValue(fromPartial<Window>(mockTab))
363379
fetchMock.mockResolvedValue(
364380
fromPartial<Response>({
365381
ok: true,
@@ -379,7 +395,7 @@ describe('downloadUtil', () => {
379395
const testUrl = 'https://storage.googleapis.com/bucket/missing.png'
380396
const consoleSpy = vi.spyOn(console, 'error').mockImplementation(() => {})
381397
const mockTab = { location: { href: '' }, closed: false, close: vi.fn() }
382-
windowOpenSpy.mockReturnValue(fromAny<Window, unknown>(mockTab))
398+
windowOpenSpy.mockReturnValue(fromPartial<Window>(mockTab))
383399
fetchMock.mockResolvedValue(
384400
fromPartial<Response>({ ok: false, status: 404 })
385401
)
@@ -395,7 +411,7 @@ describe('downloadUtil', () => {
395411
mockIsCloud.value = true
396412
const blob = new Blob(['test'], { type: 'image/png' })
397413
const mockTab = { location: { href: '' }, closed: true, close: vi.fn() }
398-
windowOpenSpy.mockReturnValue(fromAny<Window, unknown>(mockTab))
414+
windowOpenSpy.mockReturnValue(fromPartial<Window>(mockTab))
399415
fetchMock.mockResolvedValue(
400416
fromPartial<Response>({
401417
ok: true,

src/base/credits/comfyCredits.test.ts

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import {
44
CREDITS_PER_USD,
55
COMFY_CREDIT_RATE_CENTS,
66
centsToCredits,
7+
clampUsd,
78
creditsToCents,
89
creditsToUsd,
910
formatCredits,
@@ -43,4 +44,21 @@ describe('comfyCredits helpers', () => {
4344
expect(formatCreditsFromUsd({ usd: 1, locale })).toBe('211.00')
4445
expect(formatUsd({ value: 4.2, locale })).toBe('4.20')
4546
})
47+
48+
test('formats with compatible fraction digit bounds', () => {
49+
expect(
50+
formatCredits({
51+
value: 12.345,
52+
locale: 'en-US',
53+
numberOptions: { minimumFractionDigits: 4, maximumFractionDigits: 2 }
54+
})
55+
).toBe('12.35')
56+
})
57+
58+
test('clamps USD purchase values into the supported range', () => {
59+
expect(clampUsd(Number.NaN)).toBe(0)
60+
expect(clampUsd(-5)).toBe(1)
61+
expect(clampUsd(42)).toBe(42)
62+
expect(clampUsd(5000)).toBe(1000)
63+
})
4664
})

src/extensions/core/load3d.ts

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -260,7 +260,6 @@ useExtensionService().registerExtension({
260260
if (!isLoad3dNode(selectedNode)) return
261261

262262
ComfyApp.copyToClipspace(selectedNode)
263-
// @ts-expect-error clipspace_return_node is an extension property added at runtime
264263
ComfyApp.clipspace_return_node = selectedNode
265264

266265
const props = { node: selectedNode }

src/lib/litegraph/src/LGraph.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2494,6 +2494,7 @@ export class LGraph
24942494
// Deprecated - old schema version, links are arrays
24952495
if (Array.isArray(data.links)) {
24962496
for (const linkData of data.links) {
2497+
if (!linkData) continue
24972498
const link = LLink.createFromArray(linkData)
24982499
this._links.set(link.id, link)
24992500
}

src/lib/litegraph/src/types/serialisation.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -133,7 +133,7 @@ export interface ISerialisedGraph extends BaseExportedGraph {
133133
last_node_id: SerializedNodeId
134134
last_link_id: number
135135
nodes: ISerialisedNode[]
136-
links: SerialisedLLinkArray[]
136+
links: (SerialisedLLinkArray | null)[]
137137
floatingLinks?: SerialisableLLink[]
138138
groups: ISerialisedGroup[]
139139
version: typeof LiteGraph.VERSION

src/platform/workflow/persistence/composables/useWorkflowAutoSave.test.ts

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -148,13 +148,13 @@ describe('useWorkflowAutoSave', () => {
148148

149149
const serviceInstance = vi.mocked(useWorkflowService).mock.results[0].value
150150
const graphChangedCallback = vi.mocked(api.addEventListener).mock
151-
.calls[0][1]
151+
.calls[0][1] as EventListener | undefined
152152

153-
graphChangedCallback?.({} as Parameters<typeof graphChangedCallback>[0])
153+
graphChangedCallback?.({} as Event)
154154

155155
vi.advanceTimersByTime(500)
156156

157-
graphChangedCallback?.({} as Parameters<typeof graphChangedCallback>[0])
157+
graphChangedCallback?.({} as Event)
158158

159159
vi.advanceTimersByTime(1999)
160160
expect(serviceInstance.saveWorkflow).not.toHaveBeenCalled()
@@ -221,8 +221,8 @@ describe('useWorkflowAutoSave', () => {
221221
vi.advanceTimersByTime(1000)
222222

223223
const graphChangedCallback = vi.mocked(api.addEventListener).mock
224-
.calls[0][1]
225-
graphChangedCallback?.({} as Parameters<typeof graphChangedCallback>[0])
224+
.calls[0][1] as EventListener | undefined
225+
graphChangedCallback?.({} as Event)
226226

227227
resolveSave!()
228228
await Promise.resolve()
@@ -269,8 +269,8 @@ describe('useWorkflowAutoSave', () => {
269269
mockAutoSaveDelay = -500
270270

271271
const graphChangedCallback = vi.mocked(api.addEventListener).mock
272-
.calls[0][1]
273-
graphChangedCallback?.({} as Parameters<typeof graphChangedCallback>[0])
272+
.calls[0][1] as EventListener | undefined
273+
graphChangedCallback?.({} as Event)
274274

275275
await vi.runAllTimersAsync()
276276

src/renderer/extensions/linearMode/PartnerNodesList.vue

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ const { isCreditsBadge } = usePriceBadge()
2121
const { t } = useI18n()
2222
2323
const creditsBadges = computed(() =>
24-
mapAllNodes(app.graph, (node) => {
24+
mapAllNodes(app.rootGraph, (node) => {
2525
if (node.isSubgraphNode()) return
2626
2727
const priceBadge = node.badges.find(isCreditsBadge)

src/schemas/nodeDefSchema.test.ts

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
import { describe, expect, it } from 'vitest'
2+
3+
import {
4+
getComboSpecComboOptions,
5+
getInputSpecType,
6+
isComboInputSpec,
7+
isComboInputSpecV1,
8+
isComboInputSpecV2,
9+
isFloatInputSpec,
10+
isIntInputSpec,
11+
isMediaUploadComboInput
12+
} from './nodeDefSchema'
13+
import type {
14+
ComboInputSpec,
15+
ComboInputSpecV2,
16+
InputSpec
17+
} from './nodeDefSchema'
18+
19+
describe('node definition schema helpers', () => {
20+
it('identifies input spec variants', () => {
21+
const intSpec: InputSpec = ['INT', {}]
22+
const floatSpec: InputSpec = ['FLOAT', {}]
23+
const comboV1: ComboInputSpec = [['a', 'b'], {}]
24+
const comboV2: ComboInputSpecV2 = ['COMBO', { options: ['a', 'b'] }]
25+
26+
expect(isIntInputSpec(intSpec)).toBe(true)
27+
expect(isFloatInputSpec(floatSpec)).toBe(true)
28+
expect(isComboInputSpecV1(comboV1)).toBe(true)
29+
expect(isComboInputSpecV2(comboV2)).toBe(true)
30+
expect(isComboInputSpec(comboV1)).toBe(true)
31+
expect(isComboInputSpec(comboV2)).toBe(true)
32+
expect(getInputSpecType(comboV1)).toBe('COMBO')
33+
expect(getInputSpecType(intSpec)).toBe('INT')
34+
})
35+
36+
it('reads combo options from legacy and v2 combo specs', () => {
37+
expect(getComboSpecComboOptions([['a', 1], {}])).toEqual(['a', 1])
38+
expect(
39+
getComboSpecComboOptions(['COMBO', { options: ['x', 'y'] }])
40+
).toEqual(['x', 'y'])
41+
expect(getComboSpecComboOptions(['COMBO', {}])).toEqual([])
42+
})
43+
44+
it('detects media upload combo inputs', () => {
45+
expect(isMediaUploadComboInput([['a'], { image_upload: true }])).toBe(true)
46+
expect(
47+
isMediaUploadComboInput(['COMBO', { animated_image_upload: true }])
48+
).toBe(true)
49+
expect(isMediaUploadComboInput(['COMBO', { video_upload: true }])).toBe(
50+
true
51+
)
52+
expect(isMediaUploadComboInput(['STRING', { image_upload: true }])).toBe(
53+
false
54+
)
55+
expect(isMediaUploadComboInput(['COMBO', undefined])).toBe(false)
56+
})
57+
})

0 commit comments

Comments
 (0)