diff --git a/src/provider.test.tsx b/src/provider.test.tsx index 4bc7e49..7a62ee2 100644 --- a/src/provider.test.tsx +++ b/src/provider.test.tsx @@ -4,6 +4,7 @@ jest.mock('./context', () => ({ Provider: 'Provider' })); import * as React from 'react'; import { create } from 'react-test-renderer'; import { shallow } from 'enzyme'; +import type { LDClient } from 'launchdarkly-js-client-sdk'; import { LDFlagChangeset, LDFlagSet, LDOptions, LDUser } from 'launchdarkly-js-client-sdk'; import initLDClient from './initLDClient'; import { LDReactOptions, EnhancedComponent, defaultReactOptions, ProviderConfig } from './types'; @@ -18,6 +19,7 @@ const mockLDClient = { on: jest.fn((e: string, cb: () => void) => { cb(); }), + allFlags: jest.fn().mockReturnValue({}), }; describe('LDProvider', () => { @@ -58,6 +60,61 @@ describe('LDProvider', () => { expect(mockInitLDClient).toHaveBeenCalledWith(clientSideID, user, defaultReactOptions, options, undefined); }); + test('ld client is used if passed in', async () => { + const user: LDUser = { key: 'yus', name: 'yus ng' }; + const options: LDOptions = { bootstrap: {} }; + const ldClient = (await initLDClient(clientSideID, user, defaultReactOptions, options, undefined)).ldClient; + mockInitLDClient.mockClear(); + const props: ProviderConfig = { clientSideID, ldClient }; + const LaunchDarklyApp = ( + + + + ); + const instance = create(LaunchDarklyApp).root.findByType(LDProvider).instance as EnhancedComponent; + + await instance.componentDidMount(); + expect(mockInitLDClient).not.toHaveBeenCalled(); + }); + + test('ld client is used if passed in as promise', async () => { + const user1: LDUser = { key: 'yus', name: 'yus ng' }; + const user2: LDUser = { key: 'launch', name: 'darkly' }; + const options: LDOptions = { bootstrap: {} }; + const ldClient: Promise = new Promise(async resolve => + resolve((await initLDClient(clientSideID, user1, defaultReactOptions, options, undefined)).ldClient), + ); + const props: ProviderConfig = { clientSideID, ldClient, user: user2 }; + const LaunchDarklyApp = ( + + + + ); + const instance = create(LaunchDarklyApp).root.findByType(LDProvider).instance as EnhancedComponent; + + await instance.componentDidMount(); + expect(mockInitLDClient).toBeCalledTimes(1); + expect(mockInitLDClient).toHaveBeenCalledWith(clientSideID, user1, defaultReactOptions, options, undefined); + }); + + test('ld client is created if passed in promise resolves as undefined', async () => { + const user: LDUser = { key: 'yus', name: 'yus ng' }; + const options: LDOptions = { bootstrap: {} }; + const ldClient: Promise = new Promise(async resolve => + resolve(undefined), + ); + const props: ProviderConfig = { clientSideID, ldClient, user, options }; + const LaunchDarklyApp = ( + + + + ); + const instance = create(LaunchDarklyApp).root.findByType(LDProvider).instance as EnhancedComponent; + + await instance.componentDidMount(); + expect(mockInitLDClient).toHaveBeenCalledWith(clientSideID, user, defaultReactOptions, options, undefined); + }); + test('ldClient bootstraps with empty flags', () => { const user: LDUser = { key: 'yus', name: 'yus ng' }; const options: LDOptions = { diff --git a/src/provider.tsx b/src/provider.tsx index cceddb9..1b1b10e 100644 --- a/src/provider.tsx +++ b/src/provider.tsx @@ -3,7 +3,7 @@ import { LDClient, LDFlagSet, LDFlagChangeset } from 'launchdarkly-js-client-sdk import { EnhancedComponent, ProviderConfig, defaultReactOptions } from './types'; import { Provider, LDContext as HocState } from './context'; import initLDClient from './initLDClient'; -import { camelCaseKeys, getFlattenedFlagsFromChangeset } from './utils'; +import { camelCaseKeys, fetchFlags, getFlattenedFlagsFromChangeset } from './utils'; /** * The `LDProvider` is a component which accepts a config object which is used to @@ -62,8 +62,16 @@ class LDProvider extends React.Component implements En initLDClient = async () => { const { clientSideID, flags, options, user } = this.props; + let ldClient = await this.props.ldClient; const reactOptions = this.getReactOptions(); - const { flags: fetchedFlags, ldClient } = await initLDClient(clientSideID, user, reactOptions, options, flags); + let fetchedFlags; + if (ldClient) { + fetchedFlags = await fetchFlags(ldClient, reactOptions, flags); + } else { + const initialisedOutput = await initLDClient(clientSideID, user, reactOptions, options, flags); + fetchedFlags = initialisedOutput.flags; + ldClient = initialisedOutput.ldClient; + } this.setState({ flags: fetchedFlags, ldClient }); this.subscribeToChanges(ldClient); }; diff --git a/src/types.ts b/src/types.ts index fc5de48..f7b1b75 100644 --- a/src/types.ts +++ b/src/types.ts @@ -71,6 +71,14 @@ export interface ProviderConfig { * Otherwise, all flags will be requested and listened to. */ flags?: LDFlagSet; + + /** + * Optionally, the ldClient can be initialised outside of the provider + * and passed in, instead of being initialised by the provider. + * Note: it should only be passed in when it has emitted the 'ready' + * event, to ensure that the flags are properly set. + */ + ldClient?: LDClient | Promise; } /**