react-client-sdk
react-client-sdk copied to clipboard
Support a MockedProvider for Storybook/Testing
Is your feature request related to a problem? Please describe.
I am working on a Typescript React App and currently use StorybookJS to be able to test my components over different use cases. These components are using the useFlags
React Hook and therefore I need to provide a LaunchDarkly Provider to this. I don't want to actually communicate with a launch darkly server and instead just want to provide mock values for these flags in these storybooks.
Therefore I would like a way to provide mock Feature Flag values to my rendered components just like you use the MockedProvider in the Apollo GraphQL Client instead of using the withLDProvider
.
As storybooks are not tests I cannot use Jest Mocks or Sinon.
Describe the solution you'd like The ability to do the following:
import { MockedProvider } from 'launchdarkly-react-client-sdk';
...
storiesOf('MyComponent', module)
.add('will not render button when feature flag off', () => (
<MockedProvider flags={{'test-flag': false}}>
<MyComponent />
</MockedProvider>
))
.add('will render button when feature flag on', () => (
<MockedProvider flags={{'test-flag': true}}>
<MyComponent />
</MockedProvider>
));
where my component is like:
const MyComponent = () => {
const { testFlag } = useFlags();
return (
<>
{testFlag && <div>Flag On!</div>}
</>
);
}
Describe alternatives you've considered
Maybe you are fine with me manually grabbing the Provider
from inside the library? In which case I can pretty much do the equivalent in my components. E.g. currently I have something written like the following.
import React, { ReactNode } from 'react';
import { LDFlagSet } from 'launchdarkly-js-sdk-common';
import { Provider } from 'launchdarkly-react-client-sdk/lib/context';
type Props = {
flags: LDFlagSet,
children: ReactNode,
}
export const MockProvider = (props: Props) => (
<Provider value={{ flags: props.flags }}>
{props.children}
</Provider>
);
If the above is a recommended way to do this mocking for our consumers and you don't want to provide a MockProvider
as part of the library is there a place we can update documentation to recommend this approach for future developers? I struggled to find anything about this while googling.
Thank you for filing this @JaidenAshmore! We are investigating ways to better support storybook use cases. One approach we are looking at is to officially support LaunchDarkly through storybook addons. We feel that this approach is best given storybook's rich ecosystem of addons and will be consistent with other products as well like apollo, figma, etc. Unfortunately there's no eta yet right now and it is still WIP. In the meantime you can proceed with the workaround you described above.
Awesome, a Storybook addon is a much better approach. looking forward to seeing that!
Hi! We use Storybook but also regular end-to-end tests in which we'll need to mock LaunchDarkly provider.
The only issue with the workaround is having to import from launchdarkly-react-client-sdk/lib/context
. I think if we could export the context as part of the package we could do this very easily and it will work without any problems (e.g. linting rules or things like snowpack that AFAIK has issues with this kind of imports).
I could open a PR with that and a MockProvider if you are ok with it.
@JaidenAshmore thank you for your patience. We have just published jest-launchdarkly-mock which you can use with jest to test launchdarkly react components. This is intended for jest only, not storybook however.
We are currently still working on the storybook addon so we can't guarantee an ETA just yet.
hi guys, any update regarding the storybook component?
+1
In case you wanted to use a hacked version of it. Here is something that worked for me:
import { Provider, LDContext } from 'launchdarkly-react-client-sdk/lib/context';
const LDContextMock: LDContext = {
flags: {
yourFlagName: true,
},
ldClient: undefined,
};
const LDFlagContextProviderMock = ({ children }) => (
<Provider value={LDContextMock}>
{children}
</Provider>
);
Include this in your story and wrap your LD dependent component with LDFlagContextProviderMock
.
It would be easier to create a MockLDProvider
in TypeScript if LDFlagSet
was exported from launchdarkly-react-client-sdk/lib/context
Now that LDContext
requires a LDFlagKeyMap
, we've also had to write my own camel-casing function to turn LDFlagSet
into a LDFlagKeyMap
to add to example code above. (We also wrote a fake version of LDClient
to take over the undefined
in the example code.)
It'd be nice for there to be a defined way of getting a working React setup that didn't contact LaunchDarkly's servers so that local development we could try things out without stomping on each other. That'd mean something like a function that creates a working LDContext
and a fake, no-requests LDClient
from a Map<string, boolean>
or LDFlagSet
or whatever.
I ended up using https://github.com/tdeekens/flopflip to wrap LaunchDarkly's React SDK. With it, I was able to use a memory/offline flags provider on tests and storybooks.
This is blocking us from upgrading to [email protected]
, because version 3 no longer ships /lib/context
and neither does it export the context or raw Provider
in the main module. (We need to set the mock flags in our Cypress tests.) Reading the LD guides, launchdarkly-react-client-sdk
doesn't seem to be supported for any alternative way of flag specification either, be it a fake source or loading flags from file.
It seems exceedingly simple to at least export the Context
used by LD, even if just secretly (requiring us to add a TS module declaration augment before using it), so that we could mock the flags as needed. Alternatively, please let me know if there's any work-around that works with v3.
This is blocking us from upgrading to
[email protected]
, because version 3 no longer ships/lib/context
and neither does it export the context or rawProvider
in the main module. (We need to set the mock flags in our Cypress tests.) Reading the LD guides,launchdarkly-react-client-sdk
doesn't seem to be supported for any alternative way of flag specification either, be it a fake source or loading flags from file.It seems exceedingly simple to at least export the
Context
used by LD, even if just secretly (requiring us to add a TS module declaration augment before using it), so that we could mock the flags as needed. Alternatively, please let me know if there's any work-around that works with v3.
@hon2a My workaround is to use https://github.com/tdeekens/flopflip and toggle between a memory adapter (tests and development) and the LD adapter.
@taschetto Thanks for the suggestion. I'd rather avoid adding another layer that I have no need for though. Still, it might come in handy eventually, if things get nowhere on this front.
Looks like some folks using my basic storybook plugin. I would love to hand over the maintenance as I can't prioritize keep up to date if it make sense
+1 That this is blocking us from upgrading to [email protected]
because we are using the unofficial add-on right now.
+1 for either exporting the Provider
or some official way to mock in storybook.
I've been able to work around this with bootstrap
import { LDProvider } from 'launchdarkly-react-client-sdk'
<LDProvider flags={options.flags ?? {}} options={{ bootstrap: options.flags }} clientSideID={''}>
{children}
</LDProvider>
but it causes my console to be flooded with
[LaunchDarkly] No environment/client-side ID was specified. Please see https://docs.launchdarkly.com/sdk/client-side/javascript#initializing-the-client for instructions on SDK initialization.
It would be great if the Provider
was exported like it was in the past!
@yusinto mentioned in #234 that the recommended approach for now is to follow this example using storybooks mocks
Are there any updates on this front?
It's kinda confusing to me that being able to provided mocked values from a provider is not supported in any way.
They say they will create the package for almost four years but no single update shared to community. (and keep saying they will) I asked if I can trust the word in the PR comment but not even a reply, so I don't think we can rely on it.
I've been able to work around this with
bootstrap
import { LDProvider } from 'launchdarkly-react-client-sdk' <LDProvider flags={options.flags ?? {}} options={{ bootstrap: options.flags }} clientSideID={''}> {children} </LDProvider>
but it causes my console to be flooded with
[LaunchDarkly] No environment/client-side ID was specified. Please see https://docs.launchdarkly.com/sdk/client-side/javascript#initializing-the-client for instructions on SDK initialization.
It would be great if the
Provider
was exported like it was in the past!
fixed the console span with new Logger:
export const noop = () => undefined;
export const withLaunchDarkly: Decorator = (Story, context) => {
const params = context.parameters["launchdarkly"]?.flags ?? {};
return (
<LDProvider
clientSideID=""
flags={params}
options={{
bootstrap: params,
logger: { debug: noop, error: noop, warn: noop, info: noop },
}}
>
<Story />
</LDProvider>
);
};