Skip to content

Commit c796b6f

Browse files
zashraf1985yavoronaozayr-zaviar
authored
feat(decide): Added wrapper methods for decide APIs with cleaner return type (#98)
Summary: Added Wrapper methods for decide APIS. Test Plan: Manually Tested thouroughly. Added unit tests. Co-authored-by: Polina Nguen <polina.nguen@optimizely.com> Co-authored-by: ozayr-zaviar <uzairzaviar@gmail.com>
1 parent c167f2a commit c796b6f

File tree

7 files changed

+349
-39
lines changed

7 files changed

+349
-39
lines changed

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@
2929
},
3030
"dependencies": {
3131
"@optimizely/js-sdk-logging": "^0.1.0",
32-
"@optimizely/optimizely-sdk": "^4.4.3",
32+
"@optimizely/optimizely-sdk": "^4.5.0-beta",
3333
"hoist-non-react-statics": "^3.3.0",
3434
"prop-types": "^15.6.2",
3535
"utility-types": "^2.1.0 || ^3.0.0"

src/Provider.tsx

Lines changed: 3 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -19,15 +19,10 @@ import { getLogger } from '@optimizely/js-sdk-logging';
1919

2020
import { OptimizelyContextProvider } from './Context';
2121
import { ReactSDKClient } from './client';
22-
import { areUsersEqual } from './utils';
22+
import { areUsersEqual, UserInfo } from './utils';
2323

2424
const logger = getLogger('<OptimizelyProvider>');
2525

26-
type UserInfo = {
27-
id: string;
28-
attributes?: UserAttributes;
29-
};
30-
3126
interface OptimizelyProviderProps {
3227
optimizely: ReactSDKClient;
3328
timeout?: number;
@@ -48,10 +43,7 @@ export class OptimizelyProvider extends React.Component<OptimizelyProviderProps,
4843
const { optimizely, userId, userAttributes, user } = props;
4944

5045
// check if user id/attributes are provided as props and set them ReactSDKClient
51-
let finalUser: {
52-
id: string;
53-
attributes: UserAttributes;
54-
} | null = null;
46+
let finalUser: UserInfo | null = null;
5547

5648
if (user) {
5749
if ('then' in user) {
@@ -93,7 +85,7 @@ export class OptimizelyProvider extends React.Component<OptimizelyProviderProps,
9385
!areUsersEqual(
9486
{
9587
id: optimizely.user.id,
96-
attributes: optimizely.user.attributes,
88+
attributes: optimizely.user.attributes || {},
9789
},
9890
{
9991
id: this.props.user.id,

src/client.spec.ts

Lines changed: 193 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,10 +25,18 @@ describe('ReactSDKClient', () => {
2525
};
2626

2727
let mockInnerClient: optimizely.Client;
28+
let mockOptimizelyUserContext: optimizely.OptimizelyUserContext;
2829
let createInstanceSpy: jest.Mock<optimizely.Client, [optimizely.Config]>;
2930

3031
beforeEach(() => {
32+
mockOptimizelyUserContext = {
33+
decide: jest.fn(),
34+
decideAll: jest.fn(),
35+
decideForKeys: jest.fn(),
36+
} as any;
37+
3138
mockInnerClient = {
39+
createUserContext: jest.fn(() => mockOptimizelyUserContext),
3240
activate: jest.fn(() => null),
3341
track: jest.fn(),
3442
isFeatureEnabled: jest.fn(() => false),
@@ -55,6 +63,7 @@ describe('ReactSDKClient', () => {
5563
clearAllNotificationListeners: jest.fn(),
5664
},
5765
};
66+
5867
const anyOptly = optimizely as any;
5968
anyOptly.createInstance.mockReturnValue(mockInnerClient);
6069
createInstanceSpy = optimizely.createInstance as jest.Mock<optimizely.Client, [optimizely.Config]>;
@@ -456,6 +465,190 @@ describe('ReactSDKClient', () => {
456465
expect(mockFn).toBeCalledTimes(1);
457466
expect(mockFn).toBeCalledWith('exp1', 'user2');
458467
});
468+
469+
it('can use pre-set and override user for decide', () => {
470+
const mockFn = mockOptimizelyUserContext.decide as jest.Mock;
471+
const mockCreateUserContext = mockInnerClient.createUserContext as jest.Mock;
472+
mockFn.mockReturnValue({
473+
enabled: true,
474+
flagKey: 'theFlag1',
475+
reasons: [],
476+
ruleKey: '',
477+
userContext: mockOptimizelyUserContext,
478+
variables: {},
479+
variationKey: 'varition1',
480+
});
481+
let result = instance.decide('exp1');
482+
expect(result).toEqual({
483+
enabled: true,
484+
flagKey: 'theFlag1',
485+
reasons: [],
486+
ruleKey: '',
487+
userContext: {
488+
id: 'user1',
489+
attributes: { foo: 'bar' },
490+
},
491+
variables: {},
492+
variationKey: 'varition1',
493+
});
494+
expect(mockFn).toBeCalledTimes(1);
495+
expect(mockFn).toBeCalledWith('exp1', []);
496+
expect(mockCreateUserContext).toBeCalledWith('user1', { foo: 'bar' });
497+
mockFn.mockReset();
498+
mockFn.mockReturnValue({
499+
enabled: true,
500+
flagKey: 'theFlag2',
501+
reasons: [],
502+
ruleKey: '',
503+
userContext: mockOptimizelyUserContext,
504+
variables: {},
505+
variationKey: 'varition2',
506+
});
507+
result = instance.decide('exp1', [optimizely.OptimizelyDecideOption.INCLUDE_REASONS], 'user2', { bar: 'baz' });
508+
expect(result).toEqual({
509+
enabled: true,
510+
flagKey: 'theFlag2',
511+
reasons: [],
512+
ruleKey: '',
513+
userContext: {
514+
id: 'user2',
515+
attributes: { bar: 'baz' },
516+
},
517+
variables: {},
518+
variationKey: 'varition2',
519+
});
520+
expect(mockFn).toBeCalledTimes(1);
521+
expect(mockFn).toBeCalledWith('exp1', [optimizely.OptimizelyDecideOption.INCLUDE_REASONS]);
522+
expect(mockCreateUserContext).toBeCalledWith('user2', { bar: 'baz' });
523+
});
524+
525+
it('can use pre-set and override user for decideAll', () => {
526+
const mockFn = mockOptimizelyUserContext.decideAll as jest.Mock;
527+
const mockCreateUserContext = mockInnerClient.createUserContext as jest.Mock;
528+
mockFn.mockReturnValue({
529+
'theFlag1': {
530+
enabled: true,
531+
flagKey: 'theFlag1',
532+
reasons: [],
533+
ruleKey: '',
534+
userContext: mockOptimizelyUserContext,
535+
variables: {},
536+
variationKey: 'varition1',
537+
}
538+
});
539+
let result = instance.decideAll();
540+
expect(result).toEqual({
541+
'theFlag1': {
542+
enabled: true,
543+
flagKey: 'theFlag1',
544+
reasons: [],
545+
ruleKey: '',
546+
userContext: {
547+
id: 'user1',
548+
attributes: { foo: 'bar' },
549+
},
550+
variables: {},
551+
variationKey: 'varition1',
552+
}
553+
});
554+
expect(mockFn).toBeCalledTimes(1);
555+
expect(mockFn).toBeCalledWith([]);
556+
expect(mockCreateUserContext).toBeCalledWith('user1', { foo: 'bar' });
557+
mockFn.mockReset();
558+
mockFn.mockReturnValue({
559+
'theFlag2': {
560+
enabled: true,
561+
flagKey: 'theFlag2',
562+
reasons: [],
563+
ruleKey: '',
564+
userContext: mockOptimizelyUserContext,
565+
variables: {},
566+
variationKey: 'varition2',
567+
}
568+
});
569+
result = instance.decideAll([optimizely.OptimizelyDecideOption.INCLUDE_REASONS], 'user2', { bar: 'baz' });
570+
expect(result).toEqual({
571+
'theFlag2': {
572+
enabled: true,
573+
flagKey: 'theFlag2',
574+
reasons: [],
575+
ruleKey: '',
576+
userContext: {
577+
id: 'user2',
578+
attributes: { bar: 'baz' },
579+
},
580+
variables: {},
581+
variationKey: 'varition2',
582+
}
583+
});
584+
expect(mockFn).toBeCalledTimes(1);
585+
expect(mockFn).toBeCalledWith([optimizely.OptimizelyDecideOption.INCLUDE_REASONS]);
586+
expect(mockCreateUserContext).toBeCalledWith('user2', { bar: 'baz' });
587+
});
588+
589+
it('can use pre-set and override user for decideForKeys', () => {
590+
const mockFn = mockOptimizelyUserContext.decideForKeys as jest.Mock;
591+
const mockCreateUserContext = mockInnerClient.createUserContext as jest.Mock;
592+
mockFn.mockReturnValue({
593+
'theFlag1': {
594+
enabled: true,
595+
flagKey: 'theFlag1',
596+
reasons: [],
597+
ruleKey: '',
598+
userContext: mockOptimizelyUserContext,
599+
variables: {},
600+
variationKey: 'varition1',
601+
}
602+
});
603+
let result = instance.decideForKeys(['theFlag1']);
604+
expect(result).toEqual({
605+
'theFlag1': {
606+
enabled: true,
607+
flagKey: 'theFlag1',
608+
reasons: [],
609+
ruleKey: '',
610+
userContext: {
611+
id: 'user1',
612+
attributes: { foo: 'bar' },
613+
},
614+
variables: {},
615+
variationKey: 'varition1',
616+
}
617+
});
618+
expect(mockFn).toBeCalledTimes(1);
619+
expect(mockFn).toBeCalledWith(['theFlag1'], []);
620+
expect(mockCreateUserContext).toBeCalledWith('user1', { foo: 'bar' });
621+
mockFn.mockReset();
622+
mockFn.mockReturnValue({
623+
'theFlag2': {
624+
enabled: true,
625+
flagKey: 'theFlag2',
626+
reasons: [],
627+
ruleKey: '',
628+
userContext: mockOptimizelyUserContext,
629+
variables: {},
630+
variationKey: 'varition2',
631+
}
632+
});
633+
result = instance.decideForKeys(['theFlag1'], [optimizely.OptimizelyDecideOption.INCLUDE_REASONS], 'user2', { bar: 'baz' });
634+
expect(result).toEqual({
635+
'theFlag2': {
636+
enabled: true,
637+
flagKey: 'theFlag2',
638+
reasons: [],
639+
ruleKey: '',
640+
userContext: {
641+
id: 'user2',
642+
attributes: { bar: 'baz' },
643+
},
644+
variables: {},
645+
variationKey: 'varition2',
646+
}
647+
});
648+
expect(mockFn).toBeCalledTimes(1);
649+
expect(mockFn).toBeCalledWith(['theFlag1'], [optimizely.OptimizelyDecideOption.INCLUDE_REASONS]);
650+
expect(mockCreateUserContext).toBeCalledWith('user2', { bar: 'baz' });
651+
});
459652
});
460653

461654
describe('getFeatureVariables', () => {

0 commit comments

Comments
 (0)