Skip to content

Commit 6e30ca5

Browse files
committed
chore: refactoring react server component client
1 parent bfdc2bf commit 6e30ca5

8 files changed

Lines changed: 365 additions & 408 deletions

File tree

packages/sdk/react/.eslintrc.cjs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
module.exports = {
2-
ignorePatterns: ['contract-tests/next-env.d.ts'],
2+
ignorePatterns: ['contract-tests/next-env.d.ts', 'examples/server-only/next-env.d.ts'],
33
overrides: [
44
{
55
files: ['contract-tests/**/*.ts', 'contract-tests/**/*.tsx'],
Lines changed: 140 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,140 @@
1+
import { LDContext, LDFlagsStateOptions } from '@launchdarkly/js-server-sdk-common';
2+
3+
import { createLDServerSession, isServer } from '../../src/server/index';
4+
5+
const context: LDContext = { kind: 'user', key: 'test-user' };
6+
7+
function makeMockBaseClient() {
8+
return {
9+
initialized: jest.fn(() => true),
10+
boolVariation: jest.fn((_key: string, _ctx: LDContext, def: boolean) => Promise.resolve(def)),
11+
numberVariation: jest.fn((_key: string, _ctx: LDContext, def: number) => Promise.resolve(def)),
12+
stringVariation: jest.fn((_key: string, _ctx: LDContext, def: string) => Promise.resolve(def)),
13+
jsonVariation: jest.fn((_key: string, _ctx: LDContext, def: unknown) => Promise.resolve(def)),
14+
boolVariationDetail: jest.fn((_key: string, _ctx: LDContext, def: boolean) =>
15+
Promise.resolve({ value: def, variationIndex: null, reason: { kind: 'OFF' as const } }),
16+
),
17+
numberVariationDetail: jest.fn((_key: string, _ctx: LDContext, def: number) =>
18+
Promise.resolve({ value: def, variationIndex: null, reason: { kind: 'OFF' as const } }),
19+
),
20+
stringVariationDetail: jest.fn((_key: string, _ctx: LDContext, def: string) =>
21+
Promise.resolve({ value: def, variationIndex: null, reason: { kind: 'OFF' as const } }),
22+
),
23+
jsonVariationDetail: jest.fn((_key: string, _ctx: LDContext, def: unknown) =>
24+
Promise.resolve({ value: def, variationIndex: null, reason: { kind: 'OFF' as const } }),
25+
),
26+
// @ts-ignore — mock return shape matches LDFlagsState structurally
27+
allFlagsState: jest.fn((_context: LDContext, _options?: LDFlagsStateOptions) =>
28+
Promise.resolve({
29+
valid: true,
30+
getFlagValue: jest.fn(),
31+
getFlagReason: jest.fn(),
32+
allValues: jest.fn(() => ({})),
33+
toJSON: jest.fn(() => ({ $flagsState: {}, $valid: true })),
34+
}),
35+
),
36+
};
37+
}
38+
39+
it('isServer() returns true in a Node test environment', () => {
40+
expect(isServer()).toBe(true);
41+
});
42+
43+
it('getContext() returns the context passed at creation', () => {
44+
const client = makeMockBaseClient();
45+
const session = createLDServerSession(client, context);
46+
expect(session.getContext()).toEqual(context);
47+
});
48+
49+
it('initialized() delegates to the base client', () => {
50+
const client = makeMockBaseClient();
51+
client.initialized.mockReturnValue(false);
52+
const session = createLDServerSession(client, context);
53+
expect(session.initialized()).toBe(false);
54+
expect(client.initialized).toHaveBeenCalledTimes(1);
55+
});
56+
57+
it('boolVariation() calls base client with bound context', async () => {
58+
const client = makeMockBaseClient();
59+
client.boolVariation.mockResolvedValue(true);
60+
const session = createLDServerSession(client, context);
61+
const result = await session.boolVariation('my-flag', false);
62+
expect(result).toBe(true);
63+
expect(client.boolVariation).toHaveBeenCalledWith('my-flag', context, false);
64+
});
65+
66+
it('numberVariation() calls base client with bound context', async () => {
67+
const client = makeMockBaseClient();
68+
client.numberVariation.mockResolvedValue(42);
69+
const session = createLDServerSession(client, context);
70+
const result = await session.numberVariation('my-flag', 0);
71+
expect(result).toBe(42);
72+
expect(client.numberVariation).toHaveBeenCalledWith('my-flag', context, 0);
73+
});
74+
75+
it('stringVariation() calls base client with bound context', async () => {
76+
const client = makeMockBaseClient();
77+
client.stringVariation.mockResolvedValue('hello');
78+
const session = createLDServerSession(client, context);
79+
const result = await session.stringVariation('my-flag', 'default');
80+
expect(result).toBe('hello');
81+
expect(client.stringVariation).toHaveBeenCalledWith('my-flag', context, 'default');
82+
});
83+
84+
it('jsonVariation() calls base client with bound context', async () => {
85+
const client = makeMockBaseClient();
86+
const json = { key: 'value' };
87+
client.jsonVariation.mockResolvedValue(json);
88+
const session = createLDServerSession(client, context);
89+
const result = await session.jsonVariation('my-flag', {});
90+
expect(result).toEqual(json);
91+
expect(client.jsonVariation).toHaveBeenCalledWith('my-flag', context, {});
92+
});
93+
94+
it('boolVariationDetail() calls base client with bound context', async () => {
95+
const client = makeMockBaseClient();
96+
const detail = { value: true, variationIndex: 1, reason: { kind: 'RULE_MATCH' as const } };
97+
// @ts-ignore — valid LDEvaluationDetailTyped<boolean> shape; mock type is too narrow
98+
client.boolVariationDetail.mockResolvedValue(detail);
99+
const session = createLDServerSession(client, context);
100+
const result = await session.boolVariationDetail('my-flag', false);
101+
expect(result).toEqual(detail);
102+
expect(client.boolVariationDetail).toHaveBeenCalledWith('my-flag', context, false);
103+
});
104+
105+
it('allFlagsState() calls base client with bound context', async () => {
106+
const client = makeMockBaseClient();
107+
const session = createLDServerSession(client, context);
108+
await session.allFlagsState();
109+
expect(client.allFlagsState).toHaveBeenCalledWith(context, undefined);
110+
});
111+
112+
it('allFlagsState() forwards options to base client', async () => {
113+
const client = makeMockBaseClient();
114+
const session = createLDServerSession(client, context);
115+
const options = { clientSideOnly: true };
116+
await session.allFlagsState(options);
117+
expect(client.allFlagsState).toHaveBeenCalledWith(context, options);
118+
});
119+
120+
describe('given a browser environment (window defined)', () => {
121+
let originalWindow: typeof globalThis.window;
122+
123+
beforeEach(() => {
124+
originalWindow = globalThis.window;
125+
// @ts-ignore
126+
globalThis.window = {};
127+
});
128+
129+
afterEach(() => {
130+
// @ts-ignore
131+
globalThis.window = originalWindow;
132+
});
133+
134+
it('throws an error instead of returning a no-op session', () => {
135+
const client = makeMockBaseClient();
136+
expect(() => createLDServerSession(client, context)).toThrow(
137+
'createLDServerSession must only be called on the server.',
138+
);
139+
});
140+
});

0 commit comments

Comments
 (0)