Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
50 commits
Select commit Hold shift + click to select a range
c77f0cb
[backport cloud/1.37] fix: version mismatch warning appearing in Play…
comfy-pr-bot Jan 14, 2026
5ec29f6
[backport cloud/1.37] linear v2: Simple Mode (#8047)
AustinMroz Jan 14, 2026
3c99e75
[backport cloud/1.37] [API Nodes] add price badges for Meshy 3D nodes…
comfy-pr-bot Jan 14, 2026
cd6047f
[backport cloud/1.37] Fix: Update for Image Widget test (#8051)
comfy-pr-bot Jan 14, 2026
d85c469
[backport cloud/1.37] feat(price-badges): add ByteDance SeeDance 1.5 …
comfy-pr-bot Jan 15, 2026
9132f87
[backport cloud/1.37] Linear mode bug fixes (#8072)
comfy-pr-bot Jan 15, 2026
a3cd630
[backport cloud/1.37] fix: prevent Record Audio waveform from overflo…
comfy-pr-bot Jan 15, 2026
40b0954
[backport cloud/1.37] Further linear fixes (#8084)
AustinMroz Jan 15, 2026
5a276f2
[backport cloud/1.37] Make sure toggle visibility checks remote confi…
AustinMroz Jan 16, 2026
ac6adb0
[backport cloud/1.37] Fix copypasted primitives inside subgraphs (#8096)
comfy-pr-bot Jan 16, 2026
3eb8c6a
[backport cloud/1.37] Improve linear compatibility with Safari, run b…
AustinMroz Jan 16, 2026
e036d76
[backport cloud/1.37] Fix asset selection in litegraph (#8119)
comfy-pr-bot Jan 17, 2026
a55cae5
[backport cloud/1.37] Update beta message in linear mode (#8109)
comfy-pr-bot Jan 17, 2026
05cbcce
[backport cloud/1.37] feat: make subgraphs blueprints appear higher i…
comfy-pr-bot Jan 18, 2026
995906a
[backport cloud/1.37] control widget fixes (#8163)
AustinMroz Jan 19, 2026
def9b55
Remove hamburger menu from tabs (#8067)
pythongosssss Jan 15, 2026
b7ddd50
[cloud/1.37] Regenerate expectations (#8198)
DrJKL Jan 21, 2026
f074243
[backport cloud/1.37] feat: When a list of strings is received, show …
comfy-pr-bot Jan 21, 2026
abb2b15
[backport cloud/1.37] feat: add per-tab workspace authentication infr…
comfy-pr-bot Jan 22, 2026
751253f
[backport cloud/1.37] feat: add maxColumns prop to VirtualGrid for re…
comfy-pr-bot Jan 22, 2026
14a2208
[backport cloud/1.37] feat(StatusBadge): add dot mode with CVA varian…
comfy-pr-bot Jan 22, 2026
06bc103
[backport cloud/1.37] feat(ui): add shadcn-vue Select components (#8234)
comfy-pr-bot Jan 22, 2026
a6da367
[backport cloud/1.37] feat(ui): add TagsInput component with click-to…
comfy-pr-bot Jan 22, 2026
32ce523
[backport cloud/1.37] feat: add isCloud guard to team workspaces feat…
simula-r Jan 22, 2026
b8a103b
[backport cloud/1.37] feat: add workspace session, auth, and store in…
simula-r Jan 22, 2026
7faf8e0
[backport cloud/1.37] fix: Consistent keydown handling for EditableTe…
DrJKL Jan 22, 2026
0c3d569
[backport cloud/1.37] feat: add badge support to NavItem component (#…
DrJKL Jan 22, 2026
5d94c11
[backport cloud/1.37] feat: add session download tracking to assetDow…
comfy-pr-bot Jan 22, 2026
2d0f3d6
[backport cloud/1.37] refactor: restructure BaseModalLayout from flex…
DrJKL Jan 22, 2026
eb7d0c7
[backport cloud/1.37] feat: implement progressive pagination for Asse…
DrJKL Jan 22, 2026
b98d53e
[backport cloud/1.37] feat(assets): add ModelInfoPanel for asset brow…
DrJKL Jan 22, 2026
88f7886
[backport cloud/1.37] Updates: Model Management (#8255)
comfy-pr-bot Jan 23, 2026
9db2fd8
[backport cloud/1.37] Workspaces 3 create a workspace (#8221) (#8252)
simula-r Jan 23, 2026
9b7f20c
[backport cloud/1.37] Add telemetry for entering linear mode (#8265)
comfy-pr-bot Jan 23, 2026
7a5fb57
[backport cloud/1.37] fix: use authenticated API for remote config po…
comfy-pr-bot Jan 24, 2026
12761f8
[backport cloud/1.37] Linear: progressbar, tooltips, and output fixes…
comfy-pr-bot Jan 24, 2026
4c3c61f
[backport cloud/1.37] [bugfix] Fix inconsistent menu icon sizes in Co…
comfy-pr-bot Jan 24, 2026
113a6a7
[backport cloud/1.37] fix: fallback to asset metadata/name when filen…
comfy-pr-bot Jan 25, 2026
745ea0a
[backport cloud/1.37] [refactor] Manager dialog simplification (#8306)
DrJKL Jan 25, 2026
ec91aa8
[backport cloud/1.37] Updates: More Modal Modification (#8308)
DrJKL Jan 25, 2026
3eb15bb
[backport cloud/1.37] feat: add getAssetFilename util with fallback c…
comfy-pr-bot Jan 25, 2026
3b7e102
[backport cloud/1.37] Add 3d control buttons to linear mode (#8289)
comfy-pr-bot Jan 25, 2026
1963f28
[backport cloud/1.37] Workspaces 4 members invites (#8301)
simula-r Jan 25, 2026
e82c692
[backport cloud/1.37] fix: workspace icon flash and credits showing 0…
comfy-pr-bot Jan 27, 2026
e751cf4
[backport cloud/1.37] perf: remove autoplay from assets cards (#8327)
comfy-pr-bot Jan 27, 2026
a5f50ac
[backport cloud/1.37] feat: add Hugging Face model source support (#8…
comfy-pr-bot Jan 27, 2026
4c2edae
[backport cloud/1.37] Fix dragging Vue nodes into canvas from library…
comfy-pr-bot Jan 28, 2026
8e5a037
[backport cloud/1.37] Feat/workspaces 5 auth gate check (#8357)
simula-r Jan 28, 2026
72add79
[backport cloud/1.37] fix: move WorkspaceAuthGate to LayoutDefault fo…
comfy-pr-bot Jan 29, 2026
2f9ede1
[backport cloud/1.37] Templates: Search speed (#8397)
comfy-pr-bot Jan 29, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ test.describe('Properties panel', () => {
const { propertiesPanel } = comfyPage.menu

await expect(propertiesPanel.panelTitle).toContainText(
'No node(s) selected'
'No item(s) selected'
)

await comfyPage.selectNodes(['KSampler', 'CLIP Text Encode (Prompt)'])
Expand Down
11 changes: 4 additions & 7 deletions browser_tests/tests/templates.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -82,9 +82,7 @@ test.describe('Templates', () => {
await expect(comfyPage.templates.content).toBeVisible()

await comfyPage.page
.locator(
'nav > div:nth-child(3) > div > span:has-text("Getting Started")'
)
.getByRole('button', { name: 'Getting Started' })
.click()
await comfyPage.templates.loadTemplate('default')
await expect(comfyPage.templates.content).toBeHidden()
Expand Down Expand Up @@ -189,9 +187,7 @@ test.describe('Templates', () => {
const templateGrid = comfyPage.page.locator(
'[data-testid="template-workflows-content"]'
)
const nav = comfyPage.page
.locator('header')
.filter({ hasText: 'Templates' })
const nav = comfyPage.page.locator('header', { hasText: 'Templates' })

await comfyPage.templates.waitForMinimumCardCount(1)
await expect(templateGrid).toBeVisible()
Expand All @@ -201,7 +197,8 @@ test.describe('Templates', () => {
await comfyPage.page.setViewportSize(mobileSize)
await comfyPage.templates.waitForMinimumCardCount(1)
await expect(templateGrid).toBeVisible()
await expect(nav).not.toBeVisible() // Nav should collapse at mobile size
// Nav header is clipped by overflow-hidden parent at mobile size
await expect(nav).not.toBeInViewport()

const tabletSize = { width: 1024, height: 800 }
await comfyPage.page.setViewportSize(tabletSize)
Expand Down
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
5 changes: 4 additions & 1 deletion browser_tests/tests/widget.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -194,7 +194,10 @@ test.describe('Image widget', () => {
const comboEntry = comfyPage.page.getByRole('menuitem', {
name: 'image32x32.webp'
})
await comboEntry.click({ noWaitAfter: true })
await comboEntry.click()

// Stabilization for the image swap
await comfyPage.nextFrame()

// Expect the image preview to change automatically
await expect(comfyPage.canvas).toHaveScreenshot(
Expand Down
4 changes: 4 additions & 0 deletions docs/testing/vitest-patterns.md
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,10 @@ describe('MyStore', () => {

**Why `stubActions: false`?** By default, testing pinia stubs all actions. Set to `false` when testing actual store behavior.

## i18n in Component Tests

Use real `createI18n` with empty messages instead of mocking `vue-i18n`. See `SearchBox.test.ts` for example.

## Mock Patterns

### Reset all mocks at once
Expand Down
5 changes: 4 additions & 1 deletion packages/design-system/src/css/style.css
Original file line number Diff line number Diff line change
Expand Up @@ -247,6 +247,7 @@
--inverted-background-hover: var(--color-charcoal-600);
--warning-background: var(--color-gold-400);
--warning-background-hover: var(--color-gold-500);
--success-background: var(--color-jade-600);
--border-default: var(--color-smoke-600);
--border-subtle: var(--color-smoke-400);
--muted-background: var(--color-smoke-700);
Expand Down Expand Up @@ -281,7 +282,7 @@
--modal-card-border-highlighted: var(--secondary-background-selected);
--modal-card-button-surface: var(--color-smoke-300);
--modal-card-placeholder-background: var(--color-smoke-600);
--modal-card-tag-background: var(--color-smoke-400);
--modal-card-tag-background: var(--color-smoke-200);
--modal-card-tag-foreground: var(--base-foreground);
--modal-panel-background: var(--color-white);
}
Expand Down Expand Up @@ -372,6 +373,7 @@
--inverted-background-hover: var(--color-smoke-200);
--warning-background: var(--color-gold-600);
--warning-background-hover: var(--color-gold-500);
--success-background: var(--color-jade-600);
--border-default: var(--color-charcoal-200);
--border-subtle: var(--color-charcoal-300);
--muted-background: var(--color-charcoal-100);
Expand Down Expand Up @@ -516,6 +518,7 @@
--color-inverted-background-hover: var(--inverted-background-hover);
--color-warning-background: var(--warning-background);
--color-warning-background-hover: var(--warning-background-hover);
--color-success-background: var(--success-background);
--color-border-default: var(--border-default);
--color-border-subtle: var(--border-subtle);
--color-muted-background: var(--muted-background);
Expand Down
206 changes: 206 additions & 0 deletions src/components/auth/WorkspaceAuthGate.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,206 @@
import { flushPromises, mount } from '@vue/test-utils'
import { beforeEach, describe, expect, it, vi } from 'vitest'
import { ref } from 'vue'

import WorkspaceAuthGate from './WorkspaceAuthGate.vue'

const mockIsInitialized = ref(false)
const mockCurrentUser = ref<object | null>(null)

vi.mock('@/stores/firebaseAuthStore', () => ({
useFirebaseAuthStore: () => ({
isInitialized: mockIsInitialized,
currentUser: mockCurrentUser
})
}))

const mockRefreshRemoteConfig = vi.fn()
vi.mock('@/platform/remoteConfig/refreshRemoteConfig', () => ({
refreshRemoteConfig: (options: unknown) => mockRefreshRemoteConfig(options)
}))

const mockTeamWorkspacesEnabled = vi.hoisted(() => ({ value: false }))
vi.mock('@/composables/useFeatureFlags', () => ({
useFeatureFlags: () => ({
flags: {
get teamWorkspacesEnabled() {
return mockTeamWorkspacesEnabled.value
}
}
})
}))

const mockWorkspaceStoreInitialize = vi.fn()
const mockWorkspaceStoreInitState = vi.hoisted(() => ({
value: 'uninitialized' as string
}))
vi.mock('@/platform/workspace/stores/teamWorkspaceStore', () => ({
useTeamWorkspaceStore: () => ({
get initState() {
return mockWorkspaceStoreInitState.value
},
initialize: mockWorkspaceStoreInitialize
})
}))

const mockIsCloud = vi.hoisted(() => ({ value: true }))
vi.mock('@/platform/distribution/types', () => ({
get isCloud() {
return mockIsCloud.value
}
}))

vi.mock('primevue/progressspinner', () => ({
default: { template: '<div class="progress-spinner" />' }
}))

describe('WorkspaceAuthGate', () => {
beforeEach(() => {
vi.clearAllMocks()
mockIsCloud.value = true
mockIsInitialized.value = false
mockCurrentUser.value = null
mockTeamWorkspacesEnabled.value = false
mockWorkspaceStoreInitState.value = 'uninitialized'
mockRefreshRemoteConfig.mockResolvedValue(undefined)
mockWorkspaceStoreInitialize.mockResolvedValue(undefined)
})

const mountComponent = () =>
mount(WorkspaceAuthGate, {
slots: {
default: '<div data-testid="slot-content">App Content</div>'
}
})
Comment on lines +69 to +74
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick | 🔵 Trivial

Consider using function declaration for test helper.

Per codebase conventions, prefer function declarations over function expressions for pure functions. This is a minor style nit.

♻️ Suggested change
-  const mountComponent = () =>
-    mount(WorkspaceAuthGate, {
+  function mountComponent() {
+    return mount(WorkspaceAuthGate, {
       slots: {
         default: '<div data-testid="slot-content">App Content</div>'
       }
     })
+  }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
const mountComponent = () =>
mount(WorkspaceAuthGate, {
slots: {
default: '<div data-testid="slot-content">App Content</div>'
}
})
function mountComponent() {
return mount(WorkspaceAuthGate, {
slots: {
default: '<div data-testid="slot-content">App Content</div>'
}
})
}
🤖 Prompt for AI Agents
In `@src/components/auth/WorkspaceAuthGate.test.ts` around lines 69 - 74, Change
the test helper from a function expression to a function declaration: replace
the const mountComponent = () => ... with a declared function named
mountComponent() that returns the mounted WorkspaceAuthGate with the same slots;
update any local references to mountComponent unchanged. This keeps behavior
identical but follows the codebase convention for pure test helpers (refer to
the mountComponent helper and the WorkspaceAuthGate test).


describe('non-cloud builds', () => {
it('renders slot immediately when isCloud is false', async () => {
mockIsCloud.value = false

const wrapper = mountComponent()
await flushPromises()

expect(wrapper.find('[data-testid="slot-content"]').exists()).toBe(true)
expect(wrapper.find('.progress-spinner').exists()).toBe(false)
expect(mockRefreshRemoteConfig).not.toHaveBeenCalled()
})
})

describe('cloud builds - unauthenticated user', () => {
it('shows spinner while waiting for Firebase auth', () => {
mockIsInitialized.value = false

const wrapper = mountComponent()

expect(wrapper.find('.progress-spinner').exists()).toBe(true)
expect(wrapper.find('[data-testid="slot-content"]').exists()).toBe(false)
})

it('renders slot when Firebase initializes with no user', async () => {
mockIsInitialized.value = false

const wrapper = mountComponent()
expect(wrapper.find('.progress-spinner').exists()).toBe(true)

mockIsInitialized.value = true
mockCurrentUser.value = null
await flushPromises()

expect(wrapper.find('[data-testid="slot-content"]').exists()).toBe(true)
expect(mockRefreshRemoteConfig).not.toHaveBeenCalled()
})
})

describe('cloud builds - authenticated user', () => {
beforeEach(() => {
mockIsInitialized.value = true
mockCurrentUser.value = { uid: 'user-123' }
})

it('refreshes remote config with auth after Firebase init', async () => {
mountComponent()
await flushPromises()

expect(mockRefreshRemoteConfig).toHaveBeenCalledWith({ useAuth: true })
})

it('renders slot when teamWorkspacesEnabled is false', async () => {
mockTeamWorkspacesEnabled.value = false

const wrapper = mountComponent()
await flushPromises()

expect(wrapper.find('[data-testid="slot-content"]').exists()).toBe(true)
expect(mockWorkspaceStoreInitialize).not.toHaveBeenCalled()
})

it('initializes workspace store when teamWorkspacesEnabled is true', async () => {
mockTeamWorkspacesEnabled.value = true

const wrapper = mountComponent()
await flushPromises()

expect(mockWorkspaceStoreInitialize).toHaveBeenCalled()
expect(wrapper.find('[data-testid="slot-content"]').exists()).toBe(true)
})

it('skips workspace init when store is already initialized', async () => {
mockTeamWorkspacesEnabled.value = true
mockWorkspaceStoreInitState.value = 'ready'

const wrapper = mountComponent()
await flushPromises()

expect(mockWorkspaceStoreInitialize).not.toHaveBeenCalled()
expect(wrapper.find('[data-testid="slot-content"]').exists()).toBe(true)
})
})

describe('error handling - graceful degradation', () => {
beforeEach(() => {
mockIsInitialized.value = true
mockCurrentUser.value = { uid: 'user-123' }
})

it('renders slot when remote config refresh fails', async () => {
mockRefreshRemoteConfig.mockRejectedValue(new Error('Network error'))

const wrapper = mountComponent()
await flushPromises()

expect(wrapper.find('[data-testid="slot-content"]').exists()).toBe(true)
})

it('renders slot when remote config refresh times out', async () => {
vi.useFakeTimers()
// Never-resolving promise simulates a hanging request
mockRefreshRemoteConfig.mockReturnValue(new Promise(() => {}))

const wrapper = mountComponent()
await flushPromises()

// Still showing spinner before timeout
expect(wrapper.find('.progress-spinner').exists()).toBe(true)

// Advance past the 10 second timeout
await vi.advanceTimersByTimeAsync(10_001)
await flushPromises()

// Should render slot after timeout (graceful degradation)
expect(wrapper.find('[data-testid="slot-content"]').exists()).toBe(true)
vi.useRealTimers()
})

it('renders slot when workspace store initialization fails', async () => {
mockTeamWorkspacesEnabled.value = true
mockWorkspaceStoreInitialize.mockRejectedValue(
new Error('Workspace init failed')
)

const wrapper = mountComponent()
await flushPromises()

expect(wrapper.find('[data-testid="slot-content"]').exists()).toBe(true)
})
})
})
Loading