|
1 | 1 | import type { EvaluationContext} from '@openfeature/web-sdk'; |
2 | | -import { OpenFeature } from '@openfeature/web-sdk'; |
| 2 | +import { InMemoryProvider, OpenFeature } from '@openfeature/web-sdk'; |
3 | 3 | import '@testing-library/jest-dom'; // see: https://testing-library.com/docs/react-testing-library/setup |
4 | | -import { render, renderHook, screen, waitFor } from '@testing-library/react'; |
| 4 | +import { render, renderHook, screen, waitFor, fireEvent, act } from '@testing-library/react'; |
5 | 5 | import * as React from 'react'; |
6 | | -import { OpenFeatureProvider, useOpenFeatureClient, useWhenProviderReady } from '../src'; |
| 6 | +import { |
| 7 | + OpenFeatureProvider, |
| 8 | + useOpenFeatureClient, |
| 9 | + useWhenProviderReady, |
| 10 | + useContextMutator, |
| 11 | + useStringFlagValue, |
| 12 | +} from '../src'; |
7 | 13 | import { TestingProvider } from './test.utils'; |
8 | 14 |
|
9 | 15 | describe('OpenFeatureProvider', () => { |
@@ -34,6 +40,9 @@ describe('OpenFeatureProvider', () => { |
34 | 40 | if (context.user == '[email protected]') { |
35 | 41 | return 'both'; |
36 | 42 | } |
| 43 | + if (context.done === true) { |
| 44 | + return 'parting'; |
| 45 | + } |
37 | 46 | return 'greeting'; |
38 | 47 | }, |
39 | 48 | }, |
@@ -138,4 +147,148 @@ describe('OpenFeatureProvider', () => { |
138 | 147 | }); |
139 | 148 | }); |
140 | 149 | }); |
| 150 | + describe('useMutateContext', () => { |
| 151 | + const MutateButton = () => { |
| 152 | + const { setContext } = useContextMutator(); |
| 153 | + |
| 154 | + return <button onClick={() => setContext({ user: '[email protected]' })}>Update Context </button>; |
| 155 | + }; |
| 156 | + const TestComponent = ({ name }: { name: string }) => { |
| 157 | + const flagValue = useStringFlagValue<'hi' | 'bye' | 'aloha'>(SUSPENSE_FLAG_KEY, 'hi'); |
| 158 | + |
| 159 | + return ( |
| 160 | + <div> |
| 161 | + <MutateButton /> |
| 162 | + <div>{`${name} says ${flagValue}`}</div> |
| 163 | + </div> |
| 164 | + ); |
| 165 | + }; |
| 166 | + |
| 167 | + it('should update context when a domain is set', async () => { |
| 168 | + const DOMAIN = 'mutate-context-tests'; |
| 169 | + OpenFeature.setProvider(DOMAIN, suspendingProvider()); |
| 170 | + render( |
| 171 | + <OpenFeatureProvider domain={DOMAIN}> |
| 172 | + <React.Suspense fallback={<div>{FALLBACK}</div>}> |
| 173 | + <TestComponent name="Will" /> |
| 174 | + </React.Suspense> |
| 175 | + </OpenFeatureProvider>, |
| 176 | + ); |
| 177 | + |
| 178 | + await waitFor(() => { |
| 179 | + expect(screen.getByText('Will says hi')).toBeInTheDocument(); |
| 180 | + }); |
| 181 | + |
| 182 | + act(() => { |
| 183 | + fireEvent.click(screen.getByText('Update Context')); |
| 184 | + }); |
| 185 | + await waitFor( |
| 186 | + () => { |
| 187 | + expect(screen.getByText('Will says aloha')).toBeInTheDocument(); |
| 188 | + }, |
| 189 | + { timeout: DELAY * 4 }, |
| 190 | + ); |
| 191 | + }); |
| 192 | + |
| 193 | + it('should update nested contexts', async () => { |
| 194 | + const DOMAIN1 = 'Wills Domain'; |
| 195 | + const DOMAIN2 = 'Todds Domain'; |
| 196 | + OpenFeature.setProvider(DOMAIN1, suspendingProvider()); |
| 197 | + OpenFeature.setProvider(DOMAIN2, suspendingProvider()); |
| 198 | + render( |
| 199 | + <OpenFeatureProvider domain={DOMAIN1}> |
| 200 | + <React.Suspense fallback={<div>{FALLBACK}</div>}> |
| 201 | + <TestComponent name="Will" /> |
| 202 | + <OpenFeatureProvider domain={DOMAIN2}> |
| 203 | + <React.Suspense fallback={<div>{FALLBACK}</div>}> |
| 204 | + <TestComponent name="Todd" /> |
| 205 | + </React.Suspense> |
| 206 | + </OpenFeatureProvider> |
| 207 | + </React.Suspense> |
| 208 | + </OpenFeatureProvider>, |
| 209 | + ); |
| 210 | + |
| 211 | + await waitFor(() => { |
| 212 | + expect(screen.getByText('Todd says hi')).toBeInTheDocument(); |
| 213 | + }); |
| 214 | + |
| 215 | + act(() => { |
| 216 | + // Click the Update context button in Todds domain |
| 217 | + fireEvent.click(screen.getAllByText('Update Context')[1]); |
| 218 | + }); |
| 219 | + await waitFor( |
| 220 | + () => { |
| 221 | + expect(screen.getByText('Todd says aloha')).toBeInTheDocument(); |
| 222 | + }, |
| 223 | + { timeout: DELAY * 4 }, |
| 224 | + ); |
| 225 | + await waitFor( |
| 226 | + () => { |
| 227 | + expect(screen.getByText('Will says hi')).toBeInTheDocument(); |
| 228 | + }, |
| 229 | + { timeout: DELAY * 4 }, |
| 230 | + ); |
| 231 | + }); |
| 232 | + |
| 233 | + it('should update nested global contexts', async () => { |
| 234 | + const DOMAIN1 = 'Wills Domain'; |
| 235 | + OpenFeature.setProvider(DOMAIN1, suspendingProvider()); |
| 236 | + OpenFeature.setProvider(new InMemoryProvider({ |
| 237 | + globalFlagsHere: { |
| 238 | + defaultVariant: 'a', |
| 239 | + variants: { |
| 240 | + a: 'Smile', |
| 241 | + b: 'Frown', |
| 242 | + }, |
| 243 | + disabled: false, |
| 244 | + contextEvaluator: (ctx: EvaluationContext) => { |
| 245 | + if (ctx.user === '[email protected]') { |
| 246 | + return 'b'; |
| 247 | + } |
| 248 | + |
| 249 | + return 'a'; |
| 250 | + }, |
| 251 | + } |
| 252 | + })); |
| 253 | + const GlobalComponent = ({ name }: { name: string }) => { |
| 254 | + const flagValue = useStringFlagValue<'b' | 'a'>('globalFlagsHere', 'a'); |
| 255 | + |
| 256 | + return ( |
| 257 | + <div> |
| 258 | + <MutateButton /> |
| 259 | + <div>{`${name} likes to ${flagValue}`}</div> |
| 260 | + </div> |
| 261 | + ); |
| 262 | + }; |
| 263 | + render( |
| 264 | + <OpenFeatureProvider domain={DOMAIN1}> |
| 265 | + <React.Suspense fallback={<div>{FALLBACK}</div>}> |
| 266 | + <TestComponent name="Will" /> |
| 267 | + <OpenFeatureProvider> |
| 268 | + <React.Suspense fallback={<div>{FALLBACK}</div>}> |
| 269 | + <GlobalComponent name="Todd" /> |
| 270 | + </React.Suspense> |
| 271 | + </OpenFeatureProvider> |
| 272 | + </React.Suspense> |
| 273 | + </OpenFeatureProvider>, |
| 274 | + ); |
| 275 | + |
| 276 | + await waitFor(() => { |
| 277 | + expect(screen.getByText('Todd likes to Smile')).toBeInTheDocument(); |
| 278 | + }); |
| 279 | + |
| 280 | + act(() => { |
| 281 | + // Click the Update context button in Todds domain |
| 282 | + fireEvent.click(screen.getAllByText('Update Context')[1]); |
| 283 | + }); |
| 284 | + await waitFor( |
| 285 | + () => { |
| 286 | + expect(screen.getByText('Todd likes to Frown')).toBeInTheDocument(); |
| 287 | + }, |
| 288 | + { timeout: DELAY * 4 }, |
| 289 | + ); |
| 290 | + |
| 291 | + expect(screen.getByText('Will says hi')).toBeInTheDocument(); |
| 292 | + }); |
| 293 | + }); |
141 | 294 | }); |
0 commit comments