From 561d83d5b3480f4cd4bf797404617b6b3c032caa Mon Sep 17 00:00:00 2001 From: Emiliano Sanchez Date: Mon, 15 Jan 2024 17:16:46 -0300 Subject: [PATCH] Component prop update test --- src/SplitFactoryProvider.tsx | 2 +- src/__tests__/SplitFactoryProvider.test.tsx | 72 +++++++++++++++------ 2 files changed, 54 insertions(+), 20 deletions(-) diff --git a/src/SplitFactoryProvider.tsx b/src/SplitFactoryProvider.tsx index d3ac78b..9253ce8 100644 --- a/src/SplitFactoryProvider.tsx +++ b/src/SplitFactoryProvider.tsx @@ -28,7 +28,7 @@ export function SplitFactoryProvider(props: ISplitFactoryProps) { } const [stateFactory, setStateFactory] = React.useState(propFactory || null); - const factory = propFactory || stateFactory; + const factory = propFactory || (stateFactory && config === (stateFactory as IFactoryWithClients).config ? stateFactory : null); const client = factory ? getSplitClient(factory) : null; React.useEffect(() => { diff --git a/src/__tests__/SplitFactoryProvider.test.tsx b/src/__tests__/SplitFactoryProvider.test.tsx index 2964a60..a77d4af 100644 --- a/src/__tests__/SplitFactoryProvider.test.tsx +++ b/src/__tests__/SplitFactoryProvider.test.tsx @@ -336,40 +336,74 @@ describe('SplitFactoryProvider', () => { logSpy.mockRestore(); }); - test('cleans up on unmount.', () => { - let destroyMainClientSpy; - let destroySharedClientSpy; - const wrapper = render( - - {({ factory }) => { - if (!factory) return null; // 1st render + test('cleans up on update and unmount.', () => { + let renderTimes = 0; + const createdFactories = new Set(); + const clientDestroySpies: jest.SpyInstance[] = []; + + const Component = ({ factory, isReady, hasTimedout }: ISplitFactoryChildProps) => { + renderTimes++; + if (factory) createdFactories.add(factory); + + switch (renderTimes) { + case 1: + case 3: + expect(isReady).toBe(false); + expect(hasTimedout).toBe(false); + expect(factory).toBe(null); + return null; - // 2nd render (SDK ready) + case 2: + case 4: + expect(isReady).toBe(true); + expect(hasTimedout).toBe(true); expect(__factories.size).toBe(1); - destroyMainClientSpy = jest.spyOn((factory as SplitIO.ISDK).client(), 'destroy'); + clientDestroySpies.push(jest.spyOn((factory as SplitIO.ISDK).client(), 'destroy')); return ( {({ client }) => { - destroySharedClientSpy = jest.spyOn(client as SplitIO.IClient, 'destroy'); + clientDestroySpies.push(jest.spyOn(client as SplitIO.IClient, 'destroy')); return null; }} ); - }} - - ); + case 5: + throw new Error('Child must not be rerendered'); + } + }; - // SDK ready to re-render - act(() => { + const emitSdkEvents = () => { const factory = (SplitSdk as jest.Mock).mock.results.slice(-1)[0].value; + factory.client().__emitter__.emit(Event.SDK_READY_TIMED_OUT) factory.client().__emitter__.emit(Event.SDK_READY) - }); + }; + + // 1st render + const wrapper = render( + + {Component} + + ); + + // 2nd render: SDK ready (timeout is ignored due to updateOnSdkTimedout=false) + act(emitSdkEvents); + + // 3rd render: Update config prop -> factory is recreated + wrapper.rerender( + + {Component} + + ); + + // 4th render: SDK timeout (ready is ignored due to updateOnSdkReady=false) + act(emitSdkEvents); wrapper.unmount(); - // the factory created by the component is removed from `factories` cache and its clients are destroyed + + // Created factories are removed from `factories` cache and their clients are destroyed + expect(createdFactories.size).toBe(2); expect(__factories.size).toBe(0); - expect(destroyMainClientSpy).toBeCalledTimes(1); - expect(destroySharedClientSpy).toBeCalledTimes(1); + clientDestroySpies.forEach(spy => expect(spy).toBeCalledTimes(1)); }); test('doesn\'t clean up on unmount if the factory is provided as a prop.', () => {