mirror of
https://github.com/supabase/supabase.git
synced 2026-10-05 01:15:03 +03:00
fix(studio): hide stale flags when switching projects
This commit is contained in:
1 parent
4ea568c0ea
commit
2a31fb4ed8
2 files changed
+80
-8
No files matched your search
@@ -2,7 +2,7 @@ import type { Session } from '@supabase/supabase-js'
|
||||
import { QueryClient } from '@tanstack/react-query'
|
||||
import { act, screen, waitFor } from '@testing-library/react'
|
||||
import { platformComponents as components } from 'api-types'
|
||||
import { AuthContext, useFlag } from 'common'
|
||||
import { AuthContext, useFeatureFlags, useFlag } from 'common'
|
||||
import { HttpResponse } from 'msw'
|
||||
import { beforeEach, describe, expect, it, vi } from 'vitest'
|
||||
|
||||
@@ -44,7 +44,13 @@ const PROFILE_CONTEXT = createMockProfileContext()
|
||||
|
||||
function FlagValue() {
|
||||
const isEnabled = useFlag('projectTargetedFeature')
|
||||
return <div>{isEnabled ? 'Enabled' : 'Disabled'}</div>
|
||||
const { hasLoaded } = useFeatureFlags()
|
||||
return (
|
||||
<>
|
||||
<div>{isEnabled ? 'Enabled' : 'Disabled'}</div>
|
||||
<div>{hasLoaded ? 'Loaded' : 'Loading'}</div>
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
function TestProvider() {
|
||||
@@ -145,4 +151,59 @@ describe('StudioFeatureFlagProvider', () => {
|
||||
})
|
||||
expect(screen.getByText('Disabled')).toBeInTheDocument()
|
||||
})
|
||||
|
||||
it.each([
|
||||
{ destination: '/projects/project-b', ref: 'project-b', scenario: 'switching projects' },
|
||||
{ destination: '/organizations?slug=test-org', ref: undefined, scenario: 'leaving a project' },
|
||||
])('hides the previous project flags while $scenario', async ({ destination, ref }) => {
|
||||
const pendingFlags =
|
||||
Promise.withResolvers<Array<{ settingKey: string; settingValue: boolean }>>()
|
||||
getFlags.mockImplementation((_email, attributes) =>
|
||||
attributes.project_ref === ref
|
||||
? pendingFlags.promise
|
||||
: Promise.resolve([{ settingKey: 'projectTargetedFeature', settingValue: true }])
|
||||
)
|
||||
const queryClient = new QueryClient({ defaultOptions: { queries: { retry: false } } })
|
||||
customRender(<TestProvider />, { profileContext: PROFILE_CONTEXT, queryClient })
|
||||
expect(await screen.findByText('Enabled')).toBeInTheDocument()
|
||||
|
||||
await act(async () => routerMock.push(destination))
|
||||
|
||||
expect(screen.getByText('Disabled')).toBeInTheDocument()
|
||||
expect(screen.getByText('Loading')).toBeInTheDocument()
|
||||
|
||||
await act(async () => {
|
||||
pendingFlags.resolve([{ settingKey: 'projectTargetedFeature', settingValue: false }])
|
||||
})
|
||||
expect(await screen.findByText('Loaded')).toBeInTheDocument()
|
||||
expect(screen.getByText('Disabled')).toBeInTheDocument()
|
||||
})
|
||||
|
||||
it('ignores a late evaluation after navigating to a different project', async () => {
|
||||
const pendingFlags =
|
||||
Promise.withResolvers<Array<{ settingKey: string; settingValue: boolean }>>()
|
||||
getFlags.mockImplementation((_email, attributes) =>
|
||||
attributes.project_ref === 'project-b'
|
||||
? pendingFlags.promise
|
||||
: Promise.resolve([
|
||||
{
|
||||
settingKey: 'projectTargetedFeature',
|
||||
settingValue: attributes.project_ref === 'project-a',
|
||||
},
|
||||
])
|
||||
)
|
||||
const queryClient = new QueryClient({ defaultOptions: { queries: { retry: false } } })
|
||||
customRender(<TestProvider />, { profileContext: PROFILE_CONTEXT, queryClient })
|
||||
expect(await screen.findByText('Enabled')).toBeInTheDocument()
|
||||
|
||||
await act(async () => routerMock.push('/projects/project-b'))
|
||||
await act(async () => routerMock.push('/projects/project-c'))
|
||||
expect(await screen.findByText('Loaded')).toBeInTheDocument()
|
||||
expect(screen.getByText('Disabled')).toBeInTheDocument()
|
||||
|
||||
await act(async () => {
|
||||
pendingFlags.resolve([{ settingKey: 'projectTargetedFeature', settingValue: true }])
|
||||
})
|
||||
expect(screen.getByText('Disabled')).toBeInTheDocument()
|
||||
})
|
||||
})
|
||||
@@ -99,13 +99,24 @@ export const FeatureFlagProvider = ({
|
||||
const resolvedProjectRef = projectRef ?? params.ref
|
||||
const lastSentGroupContextRef = useRef<string | null>(null)
|
||||
|
||||
const [store, setStore] = useState<FeatureFlagContextType>({
|
||||
API_URL,
|
||||
configcat: {},
|
||||
posthog: {},
|
||||
hasLoaded: false,
|
||||
const [evaluation, setEvaluation] = useState<{
|
||||
projectRef?: string
|
||||
flags: FeatureFlagContextType
|
||||
}>({
|
||||
projectRef: resolvedProjectRef,
|
||||
flags: {
|
||||
API_URL,
|
||||
configcat: {},
|
||||
posthog: {},
|
||||
hasLoaded: false,
|
||||
},
|
||||
})
|
||||
|
||||
const store: FeatureFlagContextType =
|
||||
evaluation.projectRef === resolvedProjectRef
|
||||
? evaluation.flags
|
||||
: { API_URL, configcat: {}, posthog: {}, hasLoaded: false }
|
||||
|
||||
useEffect(() => {
|
||||
let mounted = true
|
||||
|
||||
@@ -229,7 +240,7 @@ export const FeatureFlagProvider = ({
|
||||
flagStore.hasLoaded = true
|
||||
|
||||
if (mounted) {
|
||||
setStore(flagStore)
|
||||
setEvaluation({ projectRef: resolvedProjectRef, flags: flagStore })
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in new issue
Block a user