Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 5 additions & 15 deletions packages/sdk/electron/__tests__/ElectronClient.ipcMain.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,14 +6,14 @@ import type {
LDEvaluationDetail,
LDEvaluationDetailTyped,
LDIdentifyOptions,
LDLogger,
} from '@launchdarkly/js-client-sdk-common';

import { ElectronClient } from '../src/ElectronClient';
import { getIPCChannelName } from '../src/ElectronIPC';
import { deriveNamespace, getIPCChannelName } from '../src/ElectronIPC';
import ElectronCrypto from '../src/platform/ElectronCrypto';
import ElectronEncoding from '../src/platform/ElectronEncoding';
import ElectronInfo from '../src/platform/ElectronInfo';
import { createMockLogger } from './testHelpers';

type MockIpcMain = IpcMain & {
getHandler: (eventName: string) => Function | undefined;
Expand Down Expand Up @@ -63,7 +63,7 @@ const mockPort: MockPort = {
};

const getEventName = (baseName: Parameters<typeof getIPCChannelName>[1]) =>
getIPCChannelName(clientSideId, baseName);
getIPCChannelName(deriveNamespace(clientSideId), baseName);

const DEFAULT_INITIAL_CONTEXT = { kind: 'user' as const, key: 'test-user' };

Expand All @@ -72,12 +72,7 @@ beforeEach(() => {
});

describe('given an initialized ElectronClient', () => {
const logger: LDLogger = {
debug: jest.fn(),
info: jest.fn(),
warn: jest.fn(),
error: jest.fn(),
};
const logger = createMockLogger();

const client = new ElectronClient(clientSideId, DEFAULT_INITIAL_CONTEXT, {
initialConnectionMode: 'offline',
Expand Down Expand Up @@ -514,12 +509,7 @@ describe('given an initialized ElectronClient', () => {
});

describe('close()', () => {
const logger: LDLogger = {
debug: jest.fn(),
info: jest.fn(),
warn: jest.fn(),
error: jest.fn(),
};
const logger = createMockLogger();

it('removes all ipcMain listeners and handlers for the client so channels are no longer registered', async () => {
const client = new ElectronClient(clientSideId, DEFAULT_INITIAL_CONTEXT, {
Expand Down
28 changes: 28 additions & 0 deletions packages/sdk/electron/__tests__/ElectronIPC.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
import { deriveNamespace, getIPCChannelName } from '../src/ElectronIPC';

it('derives namespace from credential alone', () => {
expect(deriveNamespace('mob-abc-123')).toBe('mob-abc-123');
});

it('derives namespace from credential with custom namespace', () => {
expect(deriveNamespace('mob-abc-123', 'my-namespace')).toBe('my-namespace_mob-abc-123');
});

it('produces different namespaces with and without custom namespace', () => {
const credential = 'mob-abc-123';
expect(deriveNamespace(credential)).not.toBe(deriveNamespace(credential, 'ns'));
});

it('produces different namespaces for different custom namespaces', () => {
const credential = 'mob-abc-123';
expect(deriveNamespace(credential, 'ns-a')).not.toBe(deriveNamespace(credential, 'ns-b'));
});

it('undefined namespace equals no namespace', () => {
const credential = 'mob-abc-123';
expect(deriveNamespace(credential, undefined)).toBe(deriveNamespace(credential));
});

it('builds IPC channel names', () => {
expect(getIPCChannelName('ns', 'allFlags')).toBe('ld:ns:allFlags');
});
190 changes: 20 additions & 170 deletions packages/sdk/electron/__tests__/bridge/LDClientBridge.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,11 @@ import { ipcRenderer } from 'electron';

import '../../src/bridge';
import type { LDClientBridge } from '../../src/bridge/LDClientBridge';
import type { LDContext, LDEvaluationDetail, LDEvaluationDetailTyped } from '../../src/index';
import { deriveNamespace } from '../../src/ElectronIPC';
import type { LDContext } from '../../src/index';

const clientSideId = 'client-side-id';
let ldClientBridge: (clientSideId: string) => LDClientBridge;
let ldClientBridge: (namespace: string) => LDClientBridge;

jest.mock('electron', () => ({
contextBridge: {
Expand Down Expand Up @@ -43,7 +44,7 @@ globalThis.MessageChannel = jest.fn().mockImplementation(() => ({
port2: port2Mock,
}));

const getEventName = (baseName: string) => `ld:${clientSideId}:${baseName}`;
const getEventName = (baseName: string) => `ld:${deriveNamespace(clientSideId)}:${baseName}`;

beforeEach(() => {
jest.clearAllMocks();
Expand All @@ -60,7 +61,7 @@ describe('given a registered LDClientBridge', () => {
let bridge: LDClientBridge;

beforeEach(() => {
bridge = ldClientBridge(clientSideId);
bridge = ldClientBridge(deriveNamespace(clientSideId));
});

it('passes allFlags() call through to ipcRenderer', () => {
Expand All @@ -73,37 +74,28 @@ describe('given a registered LDClientBridge', () => {
expect(result).toEqual({ flag1: true });
});

it('passes boolVariation() call through to ipcRenderer', () => {
(ipcRenderer.sendSync as jest.Mock).mockReturnValueOnce(true);

const result = bridge.boolVariation('flag1', false);

expect(ipcRenderer.sendSync).toHaveBeenCalledTimes(1);
expect(ipcRenderer.sendSync).toHaveBeenNthCalledWith(
1,
getEventName('boolVariation'),
'flag1',
false,
);
expect(result).toEqual(true);
});

it('passes boolVariationDetail() call through to ipcRenderer', () => {
const expected: LDEvaluationDetailTyped<boolean> = {
value: true,
reason: { kind: 'RULE_MATCH' },
};

it.each([
['boolVariation', true, false],
['boolVariationDetail', { value: true, reason: { kind: 'RULE_MATCH' } }, false],
['numberVariation', 1234.5, 0],
['numberVariationDetail', { value: 1234.5, reason: { kind: 'RULE_MATCH' } }, 0],
['stringVariation', 'value', ''],
['stringVariationDetail', { value: 'value', reason: { kind: 'RULE_MATCH' } }, ''],
['jsonVariation', { key1: 'value1' }, {}],
['jsonVariationDetail', { value: { key1: 'value1' }, reason: { kind: 'RULE_MATCH' } }, {}],
['variation', true, false],
['variationDetail', { value: true, reason: { kind: 'RULE_MATCH' } }, false],
])('passes %s() call through to ipcRenderer', (method, expected, defaultValue) => {
(ipcRenderer.sendSync as jest.Mock).mockReturnValueOnce(expected);

const result = bridge.boolVariationDetail('flag1', false);
const result = (bridge as any)[method]('flag1', defaultValue);

expect(ipcRenderer.sendSync).toHaveBeenCalledTimes(1);
expect(ipcRenderer.sendSync).toHaveBeenNthCalledWith(
1,
getEventName('boolVariationDetail'),
getEventName(method),
'flag1',
false,
defaultValue,
);
expect(result).toEqual(expected);
});
Expand Down Expand Up @@ -141,113 +133,6 @@ describe('given a registered LDClientBridge', () => {
});
});

it('passes jsonVariation() call through to ipcRenderer', () => {
const expected = { key1: 'value1', key2: true };

(ipcRenderer.sendSync as jest.Mock).mockReturnValueOnce(expected);

const result = bridge.jsonVariation('flag1', {});

expect(ipcRenderer.sendSync).toHaveBeenCalledTimes(1);
expect(ipcRenderer.sendSync).toHaveBeenNthCalledWith(
1,
getEventName('jsonVariation'),
'flag1',
{},
);
expect(result).toEqual(expected);
});

it('passes jsonVariationDetail() call through to ipcRenderer', () => {
const expected: LDEvaluationDetailTyped<unknown> = {
value: { key1: 'value1', key2: true },
reason: { kind: 'RULE_MATCH' },
};

(ipcRenderer.sendSync as jest.Mock).mockReturnValueOnce(expected);

const result = bridge.jsonVariationDetail('flag1', {});

expect(ipcRenderer.sendSync).toHaveBeenCalledTimes(1);
expect(ipcRenderer.sendSync).toHaveBeenNthCalledWith(
1,
getEventName('jsonVariationDetail'),
'flag1',
{},
);
expect(result).toEqual(expected);
});

it('passes numberVariation() call through to ipcRenderer', () => {
(ipcRenderer.sendSync as jest.Mock).mockReturnValueOnce(1234.5);

const result = bridge.numberVariation('flag1', 0);

expect(ipcRenderer.sendSync).toHaveBeenCalledTimes(1);
expect(ipcRenderer.sendSync).toHaveBeenNthCalledWith(
1,
getEventName('numberVariation'),
'flag1',
0,
);
expect(result).toEqual(1234.5);
});

it('passes numberVariationDetail() call through to ipcRenderer', () => {
const expected: LDEvaluationDetailTyped<number> = {
value: 1234.5,
reason: { kind: 'RULE_MATCH' },
};

(ipcRenderer.sendSync as jest.Mock).mockReturnValueOnce(expected);

const result = bridge.numberVariationDetail('flag1', 0);

expect(ipcRenderer.sendSync).toHaveBeenCalledTimes(1);
expect(ipcRenderer.sendSync).toHaveBeenNthCalledWith(
1,
getEventName('numberVariationDetail'),
'flag1',
0,
);
expect(result).toEqual(expected);
});

it('passes stringVariation() call through to ipcRenderer', () => {
(ipcRenderer.sendSync as jest.Mock).mockReturnValueOnce('value');

const result = bridge.stringVariation('flag1', '');

expect(ipcRenderer.sendSync).toHaveBeenCalledTimes(1);
expect(ipcRenderer.sendSync).toHaveBeenNthCalledWith(
1,
getEventName('stringVariation'),
'flag1',
'',
);
expect(result).toEqual('value');
});

it('passes stringVariationDetail() call through to ipcRenderer', () => {
const expected: LDEvaluationDetailTyped<string> = {
value: 'value',
reason: { kind: 'RULE_MATCH' },
};

(ipcRenderer.sendSync as jest.Mock).mockReturnValueOnce(expected);

const result = bridge.stringVariationDetail('flag1', '');

expect(ipcRenderer.sendSync).toHaveBeenCalledTimes(1);
expect(ipcRenderer.sendSync).toHaveBeenNthCalledWith(
1,
getEventName('stringVariationDetail'),
'flag1',
'',
);
expect(result).toEqual(expected);
});

it('passes track() call through to ipcRenderer', () => {
bridge.track('event1', { key1: 'value1' }, 1234.5);

Expand All @@ -261,41 +146,6 @@ describe('given a registered LDClientBridge', () => {
);
});

it('passes variation() call through to ipcRenderer', () => {
(ipcRenderer.sendSync as jest.Mock).mockReturnValueOnce(true);

const result = bridge.variation('flag1', false);

expect(ipcRenderer.sendSync).toHaveBeenCalledTimes(1);
expect(ipcRenderer.sendSync).toHaveBeenNthCalledWith(
1,
getEventName('variation'),
'flag1',
false,
);
expect(result).toEqual(true);
});

it('passes variationDetail() call through to ipcRenderer', () => {
const expected: LDEvaluationDetail = {
value: true,
reason: { kind: 'RULE_MATCH' },
};

(ipcRenderer.sendSync as jest.Mock).mockReturnValueOnce(expected);

const result = bridge.variationDetail('flag1', false);

expect(ipcRenderer.sendSync).toHaveBeenCalledTimes(1);
expect(ipcRenderer.sendSync).toHaveBeenNthCalledWith(
1,
getEventName('variationDetail'),
'flag1',
false,
);
expect(result).toEqual(expected);
});

it('passes setConnectionMode() call through to ipcRenderer', async () => {
await bridge.setConnectionMode('streaming');

Expand Down
Loading
Loading