Skip to content

Commit

Permalink
added optimization tests
Browse files Browse the repository at this point in the history
  • Loading branch information
EmilianoSanchez committed Jul 22, 2023
1 parent 8345e49 commit 2fdf2f0
Show file tree
Hide file tree
Showing 3 changed files with 36 additions and 39 deletions.
48 changes: 27 additions & 21 deletions src/__tests__/SplitTreatments.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ jest.mock('../constants', () => {
import { getControlTreatmentsWithConfig, WARN_ST_NO_CLIENT } from '../constants';
import { getStatus } from '../utils';
import { newSplitFactoryLocalhostInstance } from './testUtils/utils';
import { useSplitTreatments } from '../useSplitTreatments';

describe('SplitTreatments', () => {

Expand Down Expand Up @@ -143,13 +144,26 @@ describe('SplitTreatments', () => {

});

let renderTimes = 0;

/**
* Tests for asserting that client.getTreatmentsWithConfig is not called unnecessarely
* Tests for asserting that client.getTreatmentsWithConfig is not called unnecessarily when using SplitTreatments and useSplitTreatments.
*/
describe('SplitTreatments optimization', () => {

let renderTimes = 0;

describe.each([
({ names, attributes }) => (
<SplitTreatments names={names} attributes={attributes} >
{() => {
renderTimes++;
return null;
}}
</SplitTreatments>
),
({ names, attributes }) => {
useSplitTreatments(names, attributes);
renderTimes++;
return null;
}
])('SplitTreatments & useSplitTreatments optimization', (InnerComponent) => {
let outerFactory = SplitSdk(sdkBrowser);
(outerFactory as any).client().__emitter__.emit(Event.SDK_READY);

Expand All @@ -162,12 +176,7 @@ describe('SplitTreatments optimization', () => {
return (
<SplitFactory factory={outerFactory} >
<SplitClient splitKey={splitKey} updateOnSdkUpdate={true} attributes={clientAttributes} >
<SplitTreatments names={names} attributes={attributes} >
{() => {
renderTimes++;
return null;
}}
</SplitTreatments>
<InnerComponent names={names} attributes={attributes} />
</SplitClient>
</SplitFactory>
);
Expand Down Expand Up @@ -224,7 +233,7 @@ describe('SplitTreatments optimization', () => {
expect(outerFactory.client().getTreatmentsWithConfig).toBeCalledTimes(2);
});

it('rerenders and re-evaluates feature flags if lastUpdate timestamp changes (e.g., SDK_UPDATE event).', (done) => {
it('rerenders and re-evaluates feature flags if lastUpdate timestamp changes (e.g., SDK_UPDATE event).', () => {
expect(renderTimes).toBe(1);

// State update and split evaluation
Expand All @@ -234,16 +243,13 @@ describe('SplitTreatments optimization', () => {
(outerFactory as any).client().destroy();
wrapper.rerender(<Component names={names} attributes={attributes} splitKey={splitKey} />);

setTimeout(() => {
// Updates were batched as a single render, due to automatic batching https://reactjs.org/blog/2022/03/29/react-v18.html#new-feature-automatic-batching
expect(renderTimes).toBe(3);
expect(outerFactory.client().getTreatmentsWithConfig).toBeCalledTimes(2);
// Updates were batched as a single render, due to automatic batching https://reactjs.org/blog/2022/03/29/react-v18.html#new-feature-automatic-batching
expect(renderTimes).toBe(3);
expect(outerFactory.client().getTreatmentsWithConfig).toBeCalledTimes(2);

// Restore the client to be READY
(outerFactory as any).client().__restore();
(outerFactory as any).client().__emitter__.emit(Event.SDK_READY);
done();
})
// Restore the client to be READY
(outerFactory as any).client().__restore();
(outerFactory as any).client().__emitter__.emit(Event.SDK_READY);
});

it('rerenders and re-evaluates feature flags if client changes.', () => {
Expand Down
4 changes: 2 additions & 2 deletions src/__tests__/useSplitClient.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -97,10 +97,10 @@ test('useSplitClient must update on SDK events', () => {
);

act(() => mainClient.__emitter__.emit(Event.SDK_READY_FROM_CACHE));
act(() => user2Client.__emitter__.emit(Event.SDK_READY_FROM_CACHE));
act(() => mainClient.__emitter__.emit(Event.SDK_READY));
act(() => user2Client.__emitter__.emit(Event.SDK_READY));
act(() => mainClient.__emitter__.emit(Event.SDK_UPDATE));
act(() => user2Client.__emitter__.emit(Event.SDK_READY_FROM_CACHE));
act(() => user2Client.__emitter__.emit(Event.SDK_READY));
act(() => user2Client.__emitter__.emit(Event.SDK_UPDATE));

// SplitContext renders 3 times: initially, when ready from cache, and when ready.
Expand Down
23 changes: 7 additions & 16 deletions src/__tests__/useSplitTreatments.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -34,14 +34,12 @@ function validateTreatments({ treatments, isReady, isReadyFromCache }: ISplitTre
}
}

test('useSplitTreatments', async () => {
test('useSplitTreatments must update on SDK events', async () => {
const outerFactory = SplitSdk(sdkBrowser);
const mainClient = outerFactory.client() as any;
const user2Client = outerFactory.client('user_2') as any;

let countSplitContext = 0, countSplitTreatments = 0, countUseSplitTreatments = 0, countUseSplitTreatmentsUser2 = 0, countUseSplitTreatmentsUser2WithUpdate = 0;
const lastUpdateSetUser2 = new Set<number>();
const lastUpdateSetUser2WithUpdate = new Set<number>();

render(
<SplitFactory factory={outerFactory} >
Expand All @@ -63,31 +61,26 @@ test('useSplitTreatments', async () => {
const context = useSplitTreatments(['split_test'], undefined, 'user_2');
expect(context.client).toBe(user2Client);
validateTreatments(context);
lastUpdateSetUser2.add(context.lastUpdate);
countUseSplitTreatmentsUser2++;
return null;
})}
{React.createElement(() => {
const context = useSplitTreatments(['split_test'], undefined, 'user_2', { updateOnSdkUpdate: true });
expect(context.client).toBe(user2Client);
validateTreatments(context);
lastUpdateSetUser2WithUpdate.add(context.lastUpdate);
countUseSplitTreatmentsUser2WithUpdate++;
return null;
})}
</>
</SplitFactory>
);

// Adding a delay between events to make sure they are processed with a different lastUpdate timestamp.
act(() => mainClient.__emitter__.emit(Event.SDK_READY_FROM_CACHE));
act(() => user2Client.__emitter__.emit(Event.SDK_READY_FROM_CACHE));
await new Promise(resolve => setTimeout(resolve, 10));
act(() => mainClient.__emitter__.emit(Event.SDK_READY));
act(() => user2Client.__emitter__.emit(Event.SDK_READY));
await new Promise(resolve => setTimeout(resolve, 10));
act(() => mainClient.__emitter__.emit(Event.SDK_UPDATE));
act(() => user2Client.__emitter__.emit(Event.SDK_UPDATE));
await act(() => mainClient.__emitter__.emit(Event.SDK_READY_FROM_CACHE));
await act(() => mainClient.__emitter__.emit(Event.SDK_READY));
await act(() => mainClient.__emitter__.emit(Event.SDK_UPDATE));
await act(() => user2Client.__emitter__.emit(Event.SDK_READY_FROM_CACHE));
await act(() => user2Client.__emitter__.emit(Event.SDK_READY));
await act(() => user2Client.__emitter__.emit(Event.SDK_UPDATE));

// SplitContext renders 3 times: initially, when ready from cache, and when ready.
expect(countSplitContext).toEqual(3);
Expand All @@ -100,10 +93,8 @@ test('useSplitTreatments', async () => {

// If useSplitTreatments uses a different client than the context one, it renders when the context renders and when the new client is ready and ready from cache.
expect(countUseSplitTreatmentsUser2).toEqual(countSplitContext + 2);
expect(lastUpdateSetUser2.size).toEqual(3);
// If it is used with `updateOnSdkUpdate: true`, it also renders when the client emits an SDK_UPDATE event.
expect(countUseSplitTreatmentsUser2WithUpdate).toEqual(countSplitContext + 3);
expect(lastUpdateSetUser2WithUpdate.size).toEqual(4);
expect(user2Client.getTreatmentsWithConfig).toHaveBeenCalledTimes(5);
expect(user2Client.getTreatmentsWithConfig).toHaveBeenLastCalledWith(['split_test'], undefined);
});

0 comments on commit 2fdf2f0

Please sign in to comment.