Phase 2 Task 33. Components can now ask "is this feature on?", "how many
sessions left?", and "what stage is the trial in?" without re-implementing
the read against useBillingStore.
- useFeature(flagKey): boolean — reads enabledFeatures from store
- useFeatureLimit(field): { used, limit, percentage, isAtLimit, isLoading }
with non-blocking 60s module-level cache and graceful 404 degradation
- useTrialBanner(): derives stage from subscription status + trial countdown,
returns null on initial load to prevent flicker
- usageApi.getCount(field) — calls /api/v1/usage/{field}; backend endpoint
is not yet implemented (planned), so the hook degrades to used=0
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
45 lines
1.4 KiB
TypeScript
45 lines
1.4 KiB
TypeScript
import { describe, it, expect, beforeEach } from 'vitest'
|
|
import { renderHook, act } from '@testing-library/react'
|
|
import { useFeature } from './useFeature'
|
|
import { useBillingStore } from '@/store/billingStore'
|
|
|
|
describe('useFeature', () => {
|
|
beforeEach(() => {
|
|
useBillingStore.setState({
|
|
subscription: null,
|
|
planBilling: null,
|
|
planLimits: {},
|
|
enabledFeatures: {},
|
|
isLoading: false,
|
|
error: null,
|
|
})
|
|
})
|
|
|
|
it('returns false when flag absent', () => {
|
|
const { result } = renderHook(() => useFeature('does_not_exist'))
|
|
expect(result.current).toBe(false)
|
|
})
|
|
|
|
it('returns true when flag is enabled', () => {
|
|
useBillingStore.setState({ enabledFeatures: { ai_builder: true } })
|
|
const { result } = renderHook(() => useFeature('ai_builder'))
|
|
expect(result.current).toBe(true)
|
|
})
|
|
|
|
it('returns false when flag is explicitly disabled', () => {
|
|
useBillingStore.setState({ enabledFeatures: { ai_builder: false } })
|
|
const { result } = renderHook(() => useFeature('ai_builder'))
|
|
expect(result.current).toBe(false)
|
|
})
|
|
|
|
it('updates when store changes (subscribes to store)', () => {
|
|
const { result } = renderHook(() => useFeature('foo'))
|
|
expect(result.current).toBe(false)
|
|
|
|
act(() => {
|
|
useBillingStore.setState({ enabledFeatures: { foo: true } })
|
|
})
|
|
expect(result.current).toBe(true)
|
|
})
|
|
})
|