Skip to content
Merged
Show file tree
Hide file tree
Changes from 2 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
25 changes: 11 additions & 14 deletions packages/sdk/svelte/__tests__/lib/client/SvelteLDClient.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,11 @@ import { EventEmitter } from 'node:events';
import { get } from 'svelte/store';
import { afterEach, beforeEach, describe, expect, it, Mock, vi } from 'vitest';

import { initialize, LDClient } from '@launchdarkly/js-client-sdk/compat';
import { createClient, LDClient } from '@launchdarkly/js-client-sdk';

import { LD } from '../../../src/lib/client/SvelteLDClient';

vi.mock('@launchdarkly/js-client-sdk/compat', { spy: true });
vi.mock('@launchdarkly/js-client-sdk', { spy: true });

const clientSideID = 'test-client-side-id';
const rawFlags = { 'test-flag': true, 'another-test-flag': 'flag-value' };
Expand All @@ -21,6 +21,7 @@ const mockLDClient = {
allFlags: vi.fn().mockReturnValue(rawFlags),
variation: vi.fn((_, defaultValue) => defaultValue),
identify: vi.fn(),
start: vi.fn(),
Comment thread
cursor[bot] marked this conversation as resolved.
};

describe('launchDarkly', () => {
Expand All @@ -40,8 +41,7 @@ describe('launchDarkly', () => {
const ld = LD;

beforeEach(() => {
// mocks the initialize function to return the mockLDClient
(initialize as Mock<typeof initialize>).mockReturnValue(
(createClient as Mock<typeof createClient>).mockReturnValue(
mockLDClient as unknown as LDClient,
);
});
Expand All @@ -66,23 +66,23 @@ describe('launchDarkly', () => {
ld.initialize(clientSideID, mockContext);

expect(get(initializing)).toBe(true); // should be true before the ready event is emitted
mockLDEventEmitter.emit('ready');
mockLDEventEmitter.emit('initialized');
Comment thread
cursor[bot] marked this conversation as resolved.
Outdated

expect(get(initializing)).toBe(false);
});

it('should initialize the LaunchDarkly SDK instance', () => {
ld.initialize(clientSideID, mockContext);

expect(initialize).toHaveBeenCalledWith('test-client-side-id', mockContext);
expect(createClient).toHaveBeenCalledWith('test-client-side-id', mockContext, undefined);
});

it('should register function that gets flag values when client is ready', () => {
const newFlags = { ...rawFlags, 'new-flag': true };
const allFlagsSpy = vi.spyOn(mockLDClient, 'allFlags').mockReturnValue(newFlags);

ld.initialize(clientSideID, mockContext);
mockLDEventEmitter.emit('ready');
mockLDEventEmitter.emit('initialized');

expect(allFlagsSpy).toHaveBeenCalledOnce();
expect(allFlagsSpy).toHaveReturnedWith(newFlags);
Expand All @@ -104,8 +104,7 @@ describe('launchDarkly', () => {
const ld = LD;

beforeEach(() => {
// mocks the initialize function to return the mockLDClient
(initialize as Mock<typeof initialize>).mockReturnValue(
Comment thread
cursor[bot] marked this conversation as resolved.
(createClient as Mock<typeof createClient>).mockReturnValue(
mockLDClient as unknown as LDClient,
);
});
Expand All @@ -132,7 +131,7 @@ describe('launchDarkly', () => {
const flagStore2 = ld.watch(stringFlagKey);

// emit ready event to set initial flag values
mockLDEventEmitter.emit('ready');
mockLDEventEmitter.emit('initialized');

// 'test-flag' initial value is true according to `rawFlags`
expect(get(flagStore)).toBe(true);
Expand Down Expand Up @@ -166,8 +165,7 @@ describe('launchDarkly', () => {
const ld = LD;

beforeEach(() => {
// mocks the initialize function to return the mockLDClient
(initialize as Mock<typeof initialize>).mockReturnValue(
(createClient as Mock<typeof createClient>).mockReturnValue(
mockLDClient as unknown as LDClient,
);
});
Expand All @@ -191,8 +189,7 @@ describe('launchDarkly', () => {
const ld = LD;

beforeEach(() => {
// mocks the initialize function to return the mockLDClient
(initialize as Mock<typeof initialize>).mockReturnValue(
(createClient as Mock<typeof createClient>).mockReturnValue(
mockLDClient as unknown as LDClient,
);
});
Expand Down
44 changes: 24 additions & 20 deletions packages/sdk/svelte/src/lib/client/SvelteLDClient.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,13 @@
import { derived, type Readable, readonly, writable, type Writable } from 'svelte/store';

import type { LDFlagSet } from '@launchdarkly/js-client-sdk';
import {
initialize,
createClient as createClientSdk,
type LDClient,
type LDContext,
type LDFlagSet,
type LDFlagValue,
} from '@launchdarkly/js-client-sdk/compat';
type LDOptions,
} from '@launchdarkly/js-client-sdk';

export type { LDContext, LDFlagValue };

Expand Down Expand Up @@ -62,35 +63,38 @@
* Creates a LaunchDarkly instance.
* @returns {Object} The LaunchDarkly instance object.
*/
function createLD() {
function init() {
Comment thread
cursor[bot] marked this conversation as resolved.
Outdated
let coreLdClient: LDClient | undefined;
const loading = writable(true);
const flagsWritable = writable<LDFlags>({});
const initializeResult = writable<string>('pending');

/**
* Initializes the LaunchDarkly client.
* @param {LDClientID} clientId - The client ID.
* @param {LDContext} context - The user context.
* @returns {Object} An object with the initialization status store.
*/
function LDInitialize(clientId: LDClientID, context: LDContext) {
coreLdClient = initialize(clientId, context);
coreLdClient!.on('ready', () => {
loading.set(false);
const rawFlags = coreLdClient!.allFlags();
const allFlags = toFlagsProxy(coreLdClient!, rawFlags);
flagsWritable.set(allFlags);
});
function initialize(clientId: LDClientID, context: LDContext, options?: LDOptions) {
coreLdClient = createClientSdk(clientId, context, options);

coreLdClient!.on('change', () => {
coreLdClient.on('change', () => {
const rawFlags = coreLdClient!.allFlags();
const allFlags = toFlagsProxy(coreLdClient!, rawFlags);
flagsWritable.set(allFlags);
});

return {
initializing: loading,
};
// TODO: currently all options are defaulted which means that the client initailization will timeout in 5 seconds.
// we will need to address this before this SDK is marked as stable.
void coreLdClient.start();

Check failure on line 88 in packages/sdk/svelte/src/lib/client/SvelteLDClient.ts

View workflow job for this annotation

GitHub Actions / build-test-svelte

Expected 'undefined' and instead saw 'void'
void coreLdClient.waitForInitialization()

Check failure on line 89 in packages/sdk/svelte/src/lib/client/SvelteLDClient.ts

View workflow job for this annotation

GitHub Actions / build-test-svelte

Insert `⏎······`

Check failure on line 89 in packages/sdk/svelte/src/lib/client/SvelteLDClient.ts

View workflow job for this annotation

GitHub Actions / build-test-svelte

Expected 'undefined' and instead saw 'void'
.then((result) => {
initializeResult.set(result.status);
})
.catch(() => {
// NOTE: this should never happen as we don't throw errors from initialization.
options?.logger?.error('Failed to initialize LaunchDarkly client');
initializeResult.set('failed');
});
Comment thread
cursor[bot] marked this conversation as resolved.
Comment thread
joker23 marked this conversation as resolved.
Comment thread
cursor[bot] marked this conversation as resolved.
}

/**
Expand Down Expand Up @@ -125,12 +129,12 @@
return {
identify,
flags: readonly(flagsWritable),
initialize: LDInitialize,
initializing: readonly(loading),
initialize,
initalizationState: readonly(initializeResult),
watch,
useFlag,
};
}

/** The LaunchDarkly instance */
export const LD = createLD();
export const LD = init();
8 changes: 5 additions & 3 deletions packages/sdk/svelte/src/lib/provider/LDProvider.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -5,15 +5,17 @@

export let clientID: LDClientID;
export let context: LDContext;
const { initialize, initializing } = LD;
const { initialize, initalizationState } = LD;

onMount(() => {
initialize(clientID, context);
});
</script>

{#if $$slots.initializing && $initializing}
{#if $$slots.initializing && $initalizationState === 'pending'}
<slot name="initializing">Loading flags (default loading slot value)...</slot>
{:else}
{:else if $initalizationState === 'complete'}
<slot />
{:else}
<slot name="failed">Failed to initialize LaunchDarkly client ({$initalizationState})</slot>
Comment thread
joker23 marked this conversation as resolved.
Outdated
{/if}
Loading