diff --git a/package.json b/package.json index a54e7d3..24832bd 100644 --- a/package.json +++ b/package.json @@ -29,7 +29,8 @@ "check-typescript": "tsc", "prepublishOnly": "npm run build", "prettier": "prettier --write 'src/*.@(js|ts|tsx|json|css)'", - "link-dev": "./link-dev.sh" + "link-dev": "./link-dev.sh", + "check": "npm i && npm run prettier && npm run lint && tsc && npm run test" }, "repository": { "type": "git", diff --git a/src/getFlagsProxy.test.ts b/src/getFlagsProxy.test.ts index a12bfab..04aa638 100644 --- a/src/getFlagsProxy.test.ts +++ b/src/getFlagsProxy.test.ts @@ -2,11 +2,6 @@ import { LDClient, LDFlagSet } from 'launchdarkly-js-client-sdk'; import getFlagsProxy from './getFlagsProxy'; import { defaultReactOptions } from './types'; -// tslint:disable-next-line: no-unsafe-any -const variation = jest.fn((k: string): string | undefined => rawFlags[k]); - -const ldClient = ({ variation } as unknown) as LDClient; - const rawFlags: LDFlagSet = { 'foo-bar': 'foobar', 'baz-qux': 'bazqux', @@ -17,8 +12,18 @@ const camelizedFlags: LDFlagSet = { bazQux: 'bazqux', }; +// cast as unknown first to be able to partially mock ldClient +const ldClient = ({ variation: jest.fn((flagKey) => rawFlags[flagKey] as string) } as unknown) as LDClient; + beforeEach(jest.clearAllMocks); +test('native Object functions should be ignored', () => { + const { flags } = getFlagsProxy(ldClient, rawFlags); + flags.hasOwnProperty('fooBar'); + flags.propertyIsEnumerable('bazQux'); + expect(ldClient.variation).not.toHaveBeenCalled(); +}); + test('camel cases keys', () => { const { flags } = getFlagsProxy(ldClient, rawFlags); @@ -31,12 +36,12 @@ test('does not camel cases keys', () => { expect(flags).toEqual(rawFlags); }); -test('proxy calls variation on flag read', () => { +test('proxy calls ldClient.variation on flag read', () => { const { flags } = getFlagsProxy(ldClient, rawFlags); expect(flags.fooBar).toBe('foobar'); - expect(variation).toHaveBeenCalledWith('foo-bar', 'foobar'); + expect(ldClient.variation).toHaveBeenCalledWith('foo-bar', 'foobar'); }); test('returns flag key map', () => { @@ -56,5 +61,5 @@ test('does not use proxy if option is false', () => { expect(flags['foo-bar']).toBe('foobar'); - expect(variation).not.toHaveBeenCalled(); + expect(ldClient.variation).not.toHaveBeenCalled(); }); diff --git a/src/getFlagsProxy.ts b/src/getFlagsProxy.ts index 22c6ac4..409676a 100644 --- a/src/getFlagsProxy.ts +++ b/src/getFlagsProxy.ts @@ -58,17 +58,19 @@ function toFlagsProxy(ldClient: LDClient, flags: LDFlagSet, flagKeyMap: LDFlagKe // trap for reading a flag value using `LDClient#variation` to trigger an evaluation event get(target, prop, receiver) { const currentValue = Reflect.get(target, prop, receiver); - if (typeof prop === 'symbol') { + + // only process flag keys and ignore symbols and native Object functions + if (typeof prop === 'symbol' || !hasFlag(flagKeyMap, prop)) { return currentValue; } + if (currentValue === undefined) { return; } - const originalFlagKey = hasFlag(flagKeyMap, prop) ? flagKeyMap[prop] : prop; - const nextValue = ldClient.variation(originalFlagKey, currentValue); - return nextValue; + return ldClient.variation(flagKeyMap[prop], currentValue); }, + // disable all mutation functions to make proxy readonly setPrototypeOf: () => false, set: () => false,