UNPKG

@splitsoftware/splitio-react

Version:

A React library to easily integrate and use Split JS SDK

170 lines (150 loc) 5.84 kB
import { EventEmitter } from 'events'; import jsSdkPackageJson from '@splitsoftware/splitio/package.json'; import reactSdkPackageJson from '../../../package.json'; export const jsSdkVersion = `javascript-${jsSdkPackageJson.version}`; export const reactSdkVersion = `react-${reactSdkPackageJson.version}`; export const Event = { SDK_READY_TIMED_OUT: 'init::timeout', SDK_READY: 'init::ready', SDK_READY_FROM_CACHE: 'init::cache-ready', SDK_UPDATE: 'state::update', }; function parseKey(key: SplitIO.SplitKey): SplitIO.SplitKey { if (key && typeof key === 'object' && key.constructor === Object) { return { matchingKey: (key as SplitIO.SplitKeyObject).matchingKey, bucketingKey: (key as SplitIO.SplitKeyObject).bucketingKey, }; } else { return { matchingKey: (key as string), bucketingKey: (key as string), }; } } function buildInstanceId(key: any, trafficType?: string) { return `${key.matchingKey ? key.matchingKey : key}-${key.bucketingKey ? key.bucketingKey : key}-${trafficType !== undefined ? trafficType : ''}`; } export function mockSdk() { return jest.fn((config: SplitIO.IBrowserSettings, __updateModules) => { function mockClient(_key: SplitIO.SplitKey) { // Readiness let isReady = false; let isReadyFromCache = false; let hasTimedout = false; let isDestroyed = false; let lastUpdate = 0; function syncLastUpdate() { const dateNow = Date.now(); lastUpdate = dateNow > lastUpdate ? dateNow : lastUpdate + 1; } const __emitter__ = new EventEmitter(); __emitter__.on(Event.SDK_READY, () => { isReady = true; syncLastUpdate(); }); __emitter__.on(Event.SDK_READY_FROM_CACHE, () => { isReadyFromCache = true; syncLastUpdate(); }); __emitter__.on(Event.SDK_READY_TIMED_OUT, () => { hasTimedout = true; syncLastUpdate(); }); __emitter__.on(Event.SDK_UPDATE, () => { syncLastUpdate(); }); let attributesCache = {}; // Client methods const track: jest.Mock = jest.fn(() => { return true; }); const getTreatmentsWithConfig: jest.Mock = jest.fn((featureFlagNames: string[]) => { return featureFlagNames.reduce((result: SplitIO.TreatmentsWithConfig, featureName: string) => { result[featureName] = { treatment: 'on', config: null }; return result; }, {}); }); const getTreatmentsWithConfigByFlagSets: jest.Mock = jest.fn((flagSets: string[]) => { return flagSets.reduce((result: SplitIO.TreatmentsWithConfig, flagSet: string) => { result[flagSet + '_feature_flag'] = { treatment: 'on', config: null }; return result; }, {}); }); const setAttributes: jest.Mock = jest.fn((attributes) => { attributesCache = Object.assign(attributesCache, attributes); return true; }); const clearAttributes: jest.Mock = jest.fn(() => { attributesCache = {}; return true; }); const getAttributes: jest.Mock = jest.fn(() => { return attributesCache; }); const ready: jest.Mock = jest.fn(() => { return new Promise<void>((res, rej) => { if (isReady) res(); else { __emitter__.on(Event.SDK_READY, res); } if (hasTimedout) rej(); else { __emitter__.on(Event.SDK_READY_TIMED_OUT, rej); } }); }); const __getStatus = () => ({ isReady, isReadyFromCache, isTimedout: hasTimedout && !isReady, hasTimedout, isDestroyed, isOperational: (isReady || isReadyFromCache) && !isDestroyed, lastUpdate, }); const destroy: jest.Mock = jest.fn(() => { isDestroyed = true; syncLastUpdate(); // __emitter__.removeAllListeners(); return Promise.resolve(); }); return Object.assign(Object.create(__emitter__), { getTreatmentsWithConfig, getTreatmentsWithConfigByFlagSets, track, ready, destroy, Event, setAttributes, clearAttributes, getAttributes, // EventEmitter exposed to trigger events manually __emitter__, // Clients expose a `__getStatus` method, that is not considered part of the public API, to get client readiness status (isReady, isReadyFromCache, isOperational, hasTimedout, isDestroyed) __getStatus, // Restore the mock client to its initial NO-READY status. // Useful when you want to reuse the same mock between tests after emitting events or destroying the instance. __restore() { isReady = isReadyFromCache = hasTimedout = isDestroyed = false; lastUpdate = 0; } }); } // Manager const names: jest.Mock = jest.fn().mockReturnValue([]); const manager: jest.Mock = jest.fn().mockReturnValue({ names }); // Cache of clients const __clients__: { [instanceId: string]: any } = {}; const client = jest.fn((key?: string) => { const clientKey = key || parseKey(config.core.key); const instanceId = buildInstanceId(clientKey); return __clients__[instanceId] || (__clients__[instanceId] = mockClient(clientKey)); }); // Factory destroy const destroy = jest.fn(() => { return Promise.all(Object.keys(__clients__).map(instanceId => __clients__[instanceId].destroy())); }); // SDK factory const factory = { client, manager, destroy, __names__: names, __clients__, settings: Object.assign({ version: jsSdkVersion, }, config), }; if (__updateModules) __updateModules(factory); return factory; }); } export function getLastInstance(SplitFactoryMock: any) { return SplitFactoryMock.mock.results.slice(-1)[0].value; }