mirror of
https://github.com/Comfy-Org/ComfyUI_frontend.git
synced 2026-03-14 09:27:41 +00:00
feat: add dev-time feature flag overrides via localStorage (#9075)
## Summary
- Adds `localStorage`-based dev-time override for feature flags, with
`ff:` key prefix (e.g.
`localStorage.setItem('ff:team_workspaces_enabled', 'true')`)
- Override priority: dev localStorage > remoteConfig >
serverFeatureFlags
- Guarded by `import.meta.env.DEV` — tree-shaken to empty function in
production builds
- Extracts `resolveFlag` helper in `useFeatureFlags` to eliminate
repeated fallback pattern
Fixes #9054
## Test plan
- [x] `getDevOverride` unit tests: boolean/number/string/object parsing,
prefix isolation, invalid JSON warning
- [x] `api.getServerFeature` / `serverSupportsFeature` override tests
- [x] `useFeatureFlags` override priority tests, including
`teamWorkspacesEnabled` bypassing guards
- [x] Production build verified: `getDevOverride` compiles to empty
function body, localStorage never accessed
- [x] `pnpm typecheck`, `pnpm lint` clean
┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-9075-feat-add-dev-time-feature-flag-overrides-via-localStorage-30f6d73d365081b394d3ccc461987b1a)
by [Unito](https://www.unito.io)
This commit is contained in:
60
src/utils/devFeatureFlagOverride.test.ts
Normal file
60
src/utils/devFeatureFlagOverride.test.ts
Normal file
@@ -0,0 +1,60 @@
|
||||
import { afterEach, describe, expect, it, vi } from 'vitest'
|
||||
|
||||
import { getDevOverride } from '@/utils/devFeatureFlagOverride'
|
||||
|
||||
describe('getDevOverride', () => {
|
||||
afterEach(() => {
|
||||
localStorage.clear()
|
||||
vi.restoreAllMocks()
|
||||
})
|
||||
|
||||
it('returns undefined when no override is set', () => {
|
||||
expect(getDevOverride('some_flag')).toBeUndefined()
|
||||
})
|
||||
|
||||
it('returns parsed boolean true', () => {
|
||||
localStorage.setItem('ff:some_flag', 'true')
|
||||
expect(getDevOverride<boolean>('some_flag')).toBe(true)
|
||||
})
|
||||
|
||||
it('returns parsed boolean false', () => {
|
||||
localStorage.setItem('ff:some_flag', 'false')
|
||||
expect(getDevOverride<boolean>('some_flag')).toBe(false)
|
||||
})
|
||||
|
||||
it('returns parsed number', () => {
|
||||
localStorage.setItem('ff:max_upload_size', '209715200')
|
||||
expect(getDevOverride<number>('max_upload_size')).toBe(209715200)
|
||||
})
|
||||
|
||||
it('returns parsed string', () => {
|
||||
localStorage.setItem('ff:some_flag', '"hello"')
|
||||
expect(getDevOverride<string>('some_flag')).toBe('hello')
|
||||
})
|
||||
|
||||
it('returns parsed object', () => {
|
||||
localStorage.setItem('ff:complex', '{"nested": true}')
|
||||
expect(getDevOverride<Record<string, boolean>>('complex')).toEqual({
|
||||
nested: true
|
||||
})
|
||||
})
|
||||
|
||||
it('uses ff: prefix for localStorage keys', () => {
|
||||
localStorage.setItem('some_flag', 'true')
|
||||
expect(getDevOverride('some_flag')).toBeUndefined()
|
||||
|
||||
localStorage.setItem('ff:some_flag', 'true')
|
||||
expect(getDevOverride('some_flag')).toBe(true)
|
||||
})
|
||||
|
||||
it('returns undefined and warns on invalid JSON', () => {
|
||||
const warnSpy = vi.spyOn(console, 'warn').mockImplementation(() => {})
|
||||
localStorage.setItem('ff:bad', 'True')
|
||||
|
||||
expect(getDevOverride('bad')).toBeUndefined()
|
||||
expect(warnSpy).toHaveBeenCalledWith(
|
||||
'[ff] Invalid JSON for override "bad":',
|
||||
'True'
|
||||
)
|
||||
})
|
||||
})
|
||||
Reference in New Issue
Block a user