Files
ComfyUI_frontend/src/composables/useFeatureFlags.test.ts
Christian Byrne cc10684c19 feat: enable linear mode toggle for nightly builds (#8569)
Enables the linear mode toggle for all nightly build users by
short-circuiting the `linearToggleEnabled` feature flag.

## Changes
- Adds `isNightly` check in `linearToggleEnabled` getter  
- Returns `true` for nightly builds, bypassing remote config/server
feature checks
- Adds unit tests for the new behavior

## Reviewers
- @AustinMroz (linear mode maintainer)
- @christian-byrne (isNightly author)

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-8569-feat-enable-linear-mode-toggle-for-nightly-builds-2fc6d73d3650819681f8dcdc23b6eefe)
by [Unito](https://www.unito.io)


<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->

## Summary by CodeRabbit

* **New Features**
  * Enabled linear toggle feature for nightly distribution builds.
* Feature flag system now respects nightly vs. standard build
configurations.

* **Tests**
* Added test coverage for nightly build feature flag behavior and remote
configuration handling.

<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2026-02-02 19:54:50 -08:00

179 lines
5.4 KiB
TypeScript

import { beforeEach, describe, expect, it, vi } from 'vitest'
import { isReactive, isReadonly } from 'vue'
import {
ServerFeatureFlag,
useFeatureFlags
} from '@/composables/useFeatureFlags'
import * as distributionTypes from '@/platform/distribution/types'
import { api } from '@/scripts/api'
// Mock the API module
vi.mock('@/scripts/api', () => ({
api: {
getServerFeature: vi.fn()
}
}))
// Mock the distribution types module
vi.mock('@/platform/distribution/types', () => ({
isCloud: false,
isNightly: false
}))
describe('useFeatureFlags', () => {
beforeEach(() => {
vi.clearAllMocks()
})
describe('flags object', () => {
it('should provide reactive readonly flags', () => {
const { flags } = useFeatureFlags()
expect(isReadonly(flags)).toBe(true)
expect(isReactive(flags)).toBe(true)
})
it('should access supportsPreviewMetadata', () => {
vi.mocked(api.getServerFeature).mockImplementation(
(path, defaultValue) => {
if (path === ServerFeatureFlag.SUPPORTS_PREVIEW_METADATA) return true
return defaultValue
}
)
const { flags } = useFeatureFlags()
expect(flags.supportsPreviewMetadata).toBe(true)
expect(api.getServerFeature).toHaveBeenCalledWith(
ServerFeatureFlag.SUPPORTS_PREVIEW_METADATA
)
})
it('should access maxUploadSize', () => {
vi.mocked(api.getServerFeature).mockImplementation(
(path, defaultValue) => {
if (path === ServerFeatureFlag.MAX_UPLOAD_SIZE) return 209715200 // 200MB
return defaultValue
}
)
const { flags } = useFeatureFlags()
expect(flags.maxUploadSize).toBe(209715200)
expect(api.getServerFeature).toHaveBeenCalledWith(
ServerFeatureFlag.MAX_UPLOAD_SIZE
)
})
it('should access supportsManagerV4', () => {
vi.mocked(api.getServerFeature).mockImplementation(
(path, defaultValue) => {
if (path === ServerFeatureFlag.MANAGER_SUPPORTS_V4) return true
return defaultValue
}
)
const { flags } = useFeatureFlags()
expect(flags.supportsManagerV4).toBe(true)
expect(api.getServerFeature).toHaveBeenCalledWith(
ServerFeatureFlag.MANAGER_SUPPORTS_V4
)
})
it('should return undefined when features are not available and no default provided', () => {
vi.mocked(api.getServerFeature).mockImplementation(
(_path, defaultValue) => defaultValue
)
const { flags } = useFeatureFlags()
expect(flags.supportsPreviewMetadata).toBeUndefined()
expect(flags.maxUploadSize).toBeUndefined()
expect(flags.supportsManagerV4).toBeUndefined()
})
})
describe('featureFlag', () => {
it('should create reactive computed for custom feature flags', () => {
vi.mocked(api.getServerFeature).mockImplementation(
(path, defaultValue) => {
if (path === 'custom.feature') return 'custom-value'
return defaultValue
}
)
const { featureFlag } = useFeatureFlags()
const customFlag = featureFlag('custom.feature', 'default')
expect(customFlag.value).toBe('custom-value')
expect(api.getServerFeature).toHaveBeenCalledWith(
'custom.feature',
'default'
)
})
it('should handle nested paths', () => {
vi.mocked(api.getServerFeature).mockImplementation(
(path, defaultValue) => {
if (path === 'extension.custom.nested.feature') return true
return defaultValue
}
)
const { featureFlag } = useFeatureFlags()
const nestedFlag = featureFlag('extension.custom.nested.feature', false)
expect(nestedFlag.value).toBe(true)
})
it('should work with ServerFeatureFlag enum', () => {
vi.mocked(api.getServerFeature).mockImplementation(
(path, defaultValue) => {
if (path === ServerFeatureFlag.MAX_UPLOAD_SIZE) return 104857600
return defaultValue
}
)
const { featureFlag } = useFeatureFlags()
const maxUploadSize = featureFlag(ServerFeatureFlag.MAX_UPLOAD_SIZE)
expect(maxUploadSize.value).toBe(104857600)
})
})
describe('linearToggleEnabled', () => {
it('should return true when isNightly is true', () => {
vi.mocked(distributionTypes).isNightly = true
const { flags } = useFeatureFlags()
expect(flags.linearToggleEnabled).toBe(true)
expect(api.getServerFeature).not.toHaveBeenCalled()
})
it('should check remote config and server feature when isNightly is false', () => {
vi.mocked(distributionTypes).isNightly = false
vi.mocked(api.getServerFeature).mockImplementation(
(path, defaultValue) => {
if (path === ServerFeatureFlag.LINEAR_TOGGLE_ENABLED) return true
return defaultValue
}
)
const { flags } = useFeatureFlags()
expect(flags.linearToggleEnabled).toBe(true)
expect(api.getServerFeature).toHaveBeenCalledWith(
ServerFeatureFlag.LINEAR_TOGGLE_ENABLED,
false
)
})
it('should return false when isNightly is false and flag is disabled', () => {
vi.mocked(distributionTypes).isNightly = false
vi.mocked(api.getServerFeature).mockImplementation(
(_path, defaultValue) => defaultValue
)
const { flags } = useFeatureFlags()
expect(flags.linearToggleEnabled).toBe(false)
})
})
})