@envive-ai/react-hooks 0.3.63 → 0.3.65
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/application/models/featureGates.cjs +2 -2
- package/dist/application/models/featureGates.d.cts +2 -2
- package/dist/application/models/featureGates.d.ts +2 -2
- package/dist/application/models/featureGates.js +2 -2
- package/dist/application/utils/elementObserver.d.cts +2 -2
- package/dist/atoms/app/index.d.cts +7 -7
- package/dist/atoms/app/index.d.ts +1 -1
- package/dist/atoms/app/variant.d.ts +6 -6
- package/dist/atoms/chat/chatState.d.cts +19 -19
- package/dist/atoms/chat/chatState.d.ts +19 -19
- package/dist/atoms/chat/form.d.cts +2 -2
- package/dist/atoms/chat/form.d.ts +3 -3
- package/dist/atoms/chat/index.d.ts +3 -3
- package/dist/atoms/chat/lastMessage.d.cts +2 -2
- package/dist/atoms/chat/lastMessage.d.ts +2 -2
- package/dist/atoms/chat/messageQueue.d.ts +7 -7
- package/dist/atoms/chat/performanceMetrics.d.cts +6 -6
- package/dist/atoms/chat/performanceMetrics.d.ts +6 -6
- package/dist/atoms/chat/renderedWidgetRefs.d.cts +2 -2
- package/dist/atoms/chat/renderedWidgetRefs.d.ts +3 -3
- package/dist/atoms/chat/replies.d.cts +3 -3
- package/dist/atoms/chat/suggestions.d.ts +3 -3
- package/dist/atoms/envive/enviveConfig.d.cts +14 -14
- package/dist/atoms/globalSearch/globalSearch.d.cts +5 -5
- package/dist/atoms/org/customerService.d.cts +6 -6
- package/dist/atoms/org/customerService.d.ts +6 -6
- package/dist/atoms/org/graphqlConfig.d.cts +4 -4
- package/dist/atoms/org/graphqlConfig.d.ts +4 -4
- package/dist/atoms/org/newOrgConfigAtom.d.cts +2 -2
- package/dist/atoms/org/newOrgConfigAtom.d.ts +2 -2
- package/dist/atoms/org/orgAnalyticsConfig.d.cts +4 -4
- package/dist/atoms/org/orgAnalyticsConfig.d.ts +4 -4
- package/dist/atoms/search/types.d.cts +1 -1
- package/dist/atoms/search/utils.d.cts +1 -1
- package/dist/atoms/widget/chatPreviewLoading.d.cts +2 -2
- package/dist/atoms/widget/chatPreviewLoading.d.ts +2 -2
- package/dist/contexts/pageContext/pageContext.cjs +12 -24
- package/dist/contexts/pageContext/pageContext.js +12 -24
- package/dist/contexts/systemSettingsContext/systemSettingsContext.d.cts +2 -2
- package/dist/contexts/types.d.cts +1 -1
- package/dist/contexts/types.d.ts +1 -1
- package/dist/contexts/typesV3.cjs +8 -1
- package/dist/contexts/typesV3.d.cts +23 -6
- package/dist/contexts/typesV3.d.ts +23 -6
- package/dist/contexts/typesV3.js +8 -2
- package/dist/hooks/Intersection/useIntersection.cjs +9 -2
- package/dist/hooks/Intersection/useIntersection.d.cts +2 -2
- package/dist/hooks/Intersection/useIntersection.d.ts +2 -2
- package/dist/hooks/Intersection/useIntersection.js +9 -2
- package/dist/hooks/SystemSettingsContext/useSystemSettingsContext.d.cts +2 -2
- package/dist/hooks/TrackComponentVisibleEvent/useTrackComponentVisibleEvent.cjs +12 -3
- package/dist/hooks/TrackComponentVisibleEvent/useTrackComponentVisibleEvent.d.cts +2 -1
- package/dist/hooks/TrackComponentVisibleEvent/useTrackComponentVisibleEvent.d.ts +2 -1
- package/dist/hooks/TrackComponentVisibleEvent/useTrackComponentVisibleEvent.js +13 -4
- package/dist/hooks/WidgetLoadDiagnostics/index.cjs +4 -0
- package/dist/hooks/WidgetLoadDiagnostics/index.d.cts +2 -0
- package/dist/hooks/WidgetLoadDiagnostics/index.d.ts +2 -0
- package/dist/hooks/WidgetLoadDiagnostics/index.js +3 -0
- package/dist/hooks/WidgetLoadDiagnostics/useWidgetLoadDiagnostics.cjs +122 -0
- package/dist/hooks/WidgetLoadDiagnostics/useWidgetLoadDiagnostics.d.cts +48 -0
- package/dist/hooks/WidgetLoadDiagnostics/useWidgetLoadDiagnostics.d.ts +48 -0
- package/dist/hooks/WidgetLoadDiagnostics/useWidgetLoadDiagnostics.js +120 -0
- package/dist/hooks/utils.d.cts +1 -1
- package/dist/hooks/utils.d.ts +1 -1
- package/dist/services/amplitudeService/eventNames.cjs +2 -1
- package/dist/services/amplitudeService/eventNames.d.cts +2 -1
- package/dist/services/amplitudeService/eventNames.d.ts +2 -1
- package/dist/services/amplitudeService/eventNames.js +2 -1
- package/dist/services/enviveConfigService/enviveConfigService.cjs +21 -35
- package/dist/services/enviveConfigService/enviveConfigService.d.cts +1 -2
- package/dist/services/enviveConfigService/enviveConfigService.d.ts +1 -2
- package/dist/services/enviveConfigService/enviveConfigService.js +21 -35
- package/dist/services/enviveConfigService/fetchBootstrapConfig.cjs +15 -16
- package/dist/services/enviveConfigService/fetchBootstrapConfig.js +15 -16
- package/dist/services/enviveConfigService/fetchGraphQLConfig.cjs +1 -69
- package/dist/services/enviveConfigService/fetchGraphQLConfig.js +2 -69
- package/dist/services/featureFlagService/index.cjs +7 -5
- package/dist/services/featureFlagService/index.d.cts +2 -1
- package/dist/services/featureFlagService/index.d.ts +2 -1
- package/dist/services/featureFlagService/index.js +7 -5
- package/dist/services/ga4ProjectionService/ga4EventSchema.cjs +3 -2
- package/dist/services/ga4ProjectionService/ga4EventSchema.js +3 -2
- package/dist/services/hardcopyService/hardcopyService.cjs +12 -3
- package/dist/services/hardcopyService/hardcopyService.d.cts +3 -1
- package/dist/services/hardcopyService/hardcopyService.d.ts +3 -1
- package/dist/services/hardcopyService/hardcopyService.js +12 -3
- package/package.json +5 -1
- package/src/application/models/featureGates.ts +11 -8
- package/src/contexts/pageContext/__tests__/pageContext.test.tsx +4 -59
- package/src/contexts/pageContext/pageContext.tsx +20 -41
- package/src/contexts/typesV3.ts +22 -4
- package/src/hooks/Intersection/useIntersection.ts +11 -0
- package/src/hooks/TrackComponentVisibleEvent/__tests__/useTrackComponentVisibleEvent.test.tsx +64 -0
- package/src/hooks/TrackComponentVisibleEvent/useTrackComponentVisibleEvent.ts +23 -4
- package/src/hooks/WidgetLoadDiagnostics/__tests__/useWidgetLoadDiagnostics.test.ts +161 -0
- package/src/hooks/WidgetLoadDiagnostics/index.ts +5 -0
- package/src/hooks/WidgetLoadDiagnostics/useWidgetLoadDiagnostics.ts +178 -0
- package/src/services/amplitudeService/eventNames.ts +4 -0
- package/src/services/enviveConfigService/__tests__/enviveConfigService.test.ts +72 -158
- package/src/services/enviveConfigService/__tests__/fetchBootstrapConfig.test.ts +111 -13
- package/src/services/enviveConfigService/__tests__/fetchGraphQLConfig.test.ts +161 -490
- package/src/services/enviveConfigService/enviveConfigService.ts +51 -87
- package/src/services/enviveConfigService/fetchBootstrapConfig.ts +74 -66
- package/src/services/enviveConfigService/fetchGraphQLConfig.ts +0 -120
- package/src/services/featureFlagService/__tests__/getFeatureFlagOverrides.test.ts +54 -0
- package/src/services/featureFlagService/index.ts +19 -6
- package/src/services/ga4ProjectionService/ga4EventSchema.ts +5 -0
- package/src/services/hardcopyService/__tests__/hardcopyService.test.ts +16 -5
- package/src/services/hardcopyService/hardcopyService.ts +10 -2
- package/dist/application/models/graphql/queries/getWidgetConfigQuery.cjs +0 -42
- package/dist/application/models/graphql/queries/getWidgetConfigQuery.js +0 -41
- package/src/application/models/graphql/queries/getWidgetConfigQuery.ts +0 -55
|
@@ -1,36 +1,25 @@
|
|
|
1
1
|
import { mockV3ColorsConfig, mockV3FrontendConfig } from 'src/contexts/graphqlContext/mockV3Config';
|
|
2
2
|
import { FloatingButtonLocation } from '@envive-ai/react-toolkit-v3/FloatingButton';
|
|
3
|
-
import { DEFAULT_PAGE_VARIANTS,
|
|
3
|
+
import { DEFAULT_PAGE_VARIANTS, transformV3ProductsConfig } from '../fetchGraphQLConfig';
|
|
4
4
|
|
|
5
5
|
const { MockLogger } = vi.hoisted(() => {
|
|
6
6
|
const MockLogger = vi.fn(function () {
|
|
7
|
-
return { logDebug: vi.fn(), logError: vi.fn() };
|
|
7
|
+
return { logDebug: vi.fn(), logError: vi.fn(), logWarn: vi.fn() };
|
|
8
8
|
});
|
|
9
9
|
return { MockLogger };
|
|
10
10
|
});
|
|
11
11
|
|
|
12
12
|
vi.mock('src/application/logging/logger', () => ({ default: MockLogger }));
|
|
13
13
|
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
me: {
|
|
24
|
-
getWidgetConfig: {
|
|
25
|
-
productsConfig: {
|
|
26
|
-
v_three_config: { values: v3RootConfigValues },
|
|
27
|
-
frontend: frontendValues == null ? undefined : { values: frontendValues },
|
|
28
|
-
},
|
|
29
|
-
},
|
|
30
|
-
},
|
|
31
|
-
},
|
|
32
|
-
}),
|
|
33
|
-
});
|
|
14
|
+
// transformV3ProductsConfig takes the `productsConfig` object (the shape both
|
|
15
|
+
// getWidgetConfig and /v1/session/bootstrap's widget_config carry). It is the
|
|
16
|
+
// single products-config transform on both config paths; bootstrap reuses it, so
|
|
17
|
+
// its behaviour is covered here directly rather than through a fetch wrapper.
|
|
18
|
+
function makeProductsConfig(v3RootConfigValues: any, frontendValues?: any) {
|
|
19
|
+
return {
|
|
20
|
+
v_three_config: { values: v3RootConfigValues },
|
|
21
|
+
frontend: frontendValues == null ? undefined : { values: frontendValues },
|
|
22
|
+
};
|
|
34
23
|
}
|
|
35
24
|
|
|
36
25
|
// Minimal valid raw v3 config with all fields present
|
|
@@ -57,49 +46,12 @@ beforeEach(() => {
|
|
|
57
46
|
vi.clearAllMocks();
|
|
58
47
|
});
|
|
59
48
|
|
|
60
|
-
describe('
|
|
61
|
-
describe('error handling', () => {
|
|
62
|
-
it('throws when the HTTP response is not ok', async () => {
|
|
63
|
-
mockFetch.mockResolvedValueOnce({ ok: false, statusText: 'Unauthorized' });
|
|
64
|
-
|
|
65
|
-
await expect(
|
|
66
|
-
fetchUnifiedGraphQLConfig('https://api.example.com', 'key', 'user_1', [], 'app'),
|
|
67
|
-
).rejects.toThrow('Unified config request failed');
|
|
68
|
-
});
|
|
69
|
-
|
|
70
|
-
it('throws when the response contains GraphQL errors', async () => {
|
|
71
|
-
mockFetch.mockResolvedValueOnce({
|
|
72
|
-
ok: true,
|
|
73
|
-
json: () => Promise.resolve({ errors: [{ message: 'Not authorized' }], data: null }),
|
|
74
|
-
});
|
|
75
|
-
|
|
76
|
-
await expect(
|
|
77
|
-
fetchUnifiedGraphQLConfig('https://api.example.com', 'key', 'user_1', [], 'app'),
|
|
78
|
-
).rejects.toThrow('Unified config GraphQL errors');
|
|
79
|
-
});
|
|
80
|
-
|
|
81
|
-
it('propagates the error when fetch throws', async () => {
|
|
82
|
-
mockFetch.mockRejectedValueOnce(new Error('Network failure'));
|
|
83
|
-
|
|
84
|
-
await expect(
|
|
85
|
-
fetchUnifiedGraphQLConfig('https://api.example.com', 'key', 'user_1', [], 'app'),
|
|
86
|
-
).rejects.toThrow('Network failure');
|
|
87
|
-
});
|
|
88
|
-
});
|
|
89
|
-
|
|
49
|
+
describe('transformV3ProductsConfig', () => {
|
|
90
50
|
describe('no colors fallback', () => {
|
|
91
|
-
it('returns mock config when v3RootConfig has no colors field',
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
const result = await fetchUnifiedGraphQLConfig(
|
|
95
|
-
'https://api.example.com',
|
|
96
|
-
'key',
|
|
97
|
-
'user_1',
|
|
98
|
-
[],
|
|
99
|
-
'app',
|
|
100
|
-
);
|
|
51
|
+
it('returns mock config when v3RootConfig has no colors field', () => {
|
|
52
|
+
const result = transformV3ProductsConfig(makeProductsConfig({ ui_configs: {} }));
|
|
101
53
|
|
|
102
|
-
expect(result
|
|
54
|
+
expect(result).toEqual({
|
|
103
55
|
colorsConfig: mockV3ColorsConfig,
|
|
104
56
|
frontendConfig: mockV3FrontendConfig,
|
|
105
57
|
orgPageConfig: {
|
|
@@ -110,24 +62,10 @@ describe('productsConfig transform (via getWidgetConfig)', () => {
|
|
|
110
62
|
});
|
|
111
63
|
});
|
|
112
64
|
|
|
113
|
-
it('returns mock config when
|
|
114
|
-
|
|
115
|
-
ok: true,
|
|
116
|
-
json: () =>
|
|
117
|
-
Promise.resolve({
|
|
118
|
-
data: { me: { getWidgetConfig: { productsConfig: {} } } },
|
|
119
|
-
}),
|
|
120
|
-
});
|
|
121
|
-
|
|
122
|
-
const result = await fetchUnifiedGraphQLConfig(
|
|
123
|
-
'https://api.example.com',
|
|
124
|
-
'key',
|
|
125
|
-
'user_1',
|
|
126
|
-
[],
|
|
127
|
-
'app',
|
|
128
|
-
);
|
|
65
|
+
it('returns mock config when productsConfig is empty', () => {
|
|
66
|
+
const result = transformV3ProductsConfig({});
|
|
129
67
|
|
|
130
|
-
expect(result
|
|
68
|
+
expect(result).toEqual({
|
|
131
69
|
colorsConfig: mockV3ColorsConfig,
|
|
132
70
|
frontendConfig: mockV3FrontendConfig,
|
|
133
71
|
orgPageConfig: {
|
|
@@ -145,97 +83,70 @@ describe('productsConfig transform (via getWidgetConfig)', () => {
|
|
|
145
83
|
['middle-left', FloatingButtonLocation.MIDDLE_LEFT],
|
|
146
84
|
['middle-right', FloatingButtonLocation.MIDDLE_RIGHT],
|
|
147
85
|
['bottom-right', FloatingButtonLocation.BOTTOM_RIGHT],
|
|
148
|
-
])('maps position "%s" to the correct enum value',
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
const result = await fetchUnifiedGraphQLConfig(
|
|
155
|
-
'https://api.example.com',
|
|
156
|
-
'key',
|
|
157
|
-
'user_1',
|
|
158
|
-
[],
|
|
159
|
-
'app',
|
|
86
|
+
])('maps position "%s" to the correct enum value', (position, expected) => {
|
|
87
|
+
const result = transformV3ProductsConfig(
|
|
88
|
+
makeProductsConfig({
|
|
89
|
+
...baseV3Config,
|
|
90
|
+
ui_configs: { ...baseV3Config.ui_configs, floating_button: { position } },
|
|
91
|
+
}),
|
|
160
92
|
);
|
|
161
93
|
|
|
162
|
-
expect((result.
|
|
163
|
-
expected,
|
|
164
|
-
);
|
|
94
|
+
expect((result.frontendConfig as any)?.uiConfigs?.floatingButton?.position).toBe(expected);
|
|
165
95
|
});
|
|
166
96
|
|
|
167
|
-
it('
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
'user_1',
|
|
177
|
-
[],
|
|
178
|
-
'app',
|
|
179
|
-
);
|
|
180
|
-
|
|
181
|
-
expect(result.orgConfig).toEqual({ colorsConfig: undefined, frontendConfig: undefined });
|
|
97
|
+
it('throws on an unrecognised position string (caller degrades to mock)', () => {
|
|
98
|
+
expect(() =>
|
|
99
|
+
transformV3ProductsConfig(
|
|
100
|
+
makeProductsConfig({
|
|
101
|
+
...baseV3Config,
|
|
102
|
+
ui_configs: { ...baseV3Config.ui_configs, floating_button: { position: 'top-center' } },
|
|
103
|
+
}),
|
|
104
|
+
),
|
|
105
|
+
).toThrow('Invalid position');
|
|
182
106
|
});
|
|
183
107
|
});
|
|
184
108
|
|
|
185
109
|
describe('typography normalization', () => {
|
|
186
|
-
it('uses underscore-separated font size keys (b_1) when camelCase keys are absent',
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
110
|
+
it('uses underscore-separated font size keys (b_1) when camelCase keys are absent', () => {
|
|
111
|
+
const result = transformV3ProductsConfig(
|
|
112
|
+
makeProductsConfig({
|
|
113
|
+
...baseV3Config,
|
|
114
|
+
ui_configs: {
|
|
115
|
+
...baseV3Config.ui_configs,
|
|
116
|
+
look_and_feel: {
|
|
117
|
+
...baseV3Config.ui_configs.look_and_feel,
|
|
118
|
+
typography: {
|
|
119
|
+
...baseV3Config.ui_configs.look_and_feel.typography,
|
|
120
|
+
font_size: { b_1: '14px', h_1: '20px' },
|
|
121
|
+
},
|
|
196
122
|
},
|
|
197
123
|
},
|
|
198
|
-
},
|
|
199
|
-
});
|
|
200
|
-
|
|
201
|
-
const result = await fetchUnifiedGraphQLConfig(
|
|
202
|
-
'https://api.example.com',
|
|
203
|
-
'key',
|
|
204
|
-
'user_1',
|
|
205
|
-
[],
|
|
206
|
-
'app',
|
|
124
|
+
}),
|
|
207
125
|
);
|
|
208
|
-
const fontSize = (result.
|
|
209
|
-
?.fontSize;
|
|
126
|
+
const fontSize = (result.frontendConfig as any)?.uiConfigs?.lookAndFeel?.typography?.fontSize;
|
|
210
127
|
|
|
211
128
|
expect(fontSize?.b1).toBe('14px');
|
|
212
129
|
expect(fontSize?.h1).toBe('20px');
|
|
213
130
|
});
|
|
214
131
|
|
|
215
|
-
it('normalises lh_N line height keys to numeric string keys',
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
132
|
+
it('normalises lh_N line height keys to numeric string keys', () => {
|
|
133
|
+
const result = transformV3ProductsConfig(
|
|
134
|
+
makeProductsConfig({
|
|
135
|
+
...baseV3Config,
|
|
136
|
+
ui_configs: {
|
|
137
|
+
...baseV3Config.ui_configs,
|
|
138
|
+
look_and_feel: {
|
|
139
|
+
...baseV3Config.ui_configs.look_and_feel,
|
|
140
|
+
typography: {
|
|
141
|
+
...baseV3Config.ui_configs.look_and_feel.typography,
|
|
142
|
+
line_height: { lh_114: '1.14', lh_128: '1.28' },
|
|
143
|
+
},
|
|
225
144
|
},
|
|
226
145
|
},
|
|
227
|
-
},
|
|
228
|
-
});
|
|
229
|
-
|
|
230
|
-
const result = await fetchUnifiedGraphQLConfig(
|
|
231
|
-
'https://api.example.com',
|
|
232
|
-
'key',
|
|
233
|
-
'user_1',
|
|
234
|
-
[],
|
|
235
|
-
'app',
|
|
146
|
+
}),
|
|
236
147
|
);
|
|
237
|
-
const lineHeight = (result.
|
|
238
|
-
?.
|
|
148
|
+
const lineHeight = (result.frontendConfig as any)?.uiConfigs?.lookAndFeel?.typography
|
|
149
|
+
?.lineHeight;
|
|
239
150
|
|
|
240
151
|
expect(lineHeight?.['114']).toBe('1.14');
|
|
241
152
|
expect(lineHeight?.['128']).toBe('1.28');
|
|
@@ -243,69 +154,49 @@ describe('productsConfig transform (via getWidgetConfig)', () => {
|
|
|
243
154
|
});
|
|
244
155
|
|
|
245
156
|
describe('chat header logo fallback', () => {
|
|
246
|
-
it('falls back chatHeaderLogoDarkSrc to chatHeaderLogoSrc when not set',
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
157
|
+
it('falls back chatHeaderLogoDarkSrc to chatHeaderLogoSrc when not set', () => {
|
|
158
|
+
const result = transformV3ProductsConfig(
|
|
159
|
+
makeProductsConfig({
|
|
160
|
+
...baseV3Config,
|
|
161
|
+
ui_configs: {
|
|
162
|
+
...baseV3Config.ui_configs,
|
|
163
|
+
look_and_feel: {
|
|
164
|
+
...baseV3Config.ui_configs.look_and_feel,
|
|
165
|
+
chat_header_logo_src: 'logo.png',
|
|
166
|
+
chat_header_logo_dark_src: null,
|
|
167
|
+
chat_header_logo_light_src: 'logo-light.png',
|
|
168
|
+
},
|
|
256
169
|
},
|
|
257
|
-
},
|
|
258
|
-
});
|
|
259
|
-
|
|
260
|
-
const result = await fetchUnifiedGraphQLConfig(
|
|
261
|
-
'https://api.example.com',
|
|
262
|
-
'key',
|
|
263
|
-
'user_1',
|
|
264
|
-
[],
|
|
265
|
-
'app',
|
|
170
|
+
}),
|
|
266
171
|
);
|
|
267
|
-
const lookAndFeel = (result.
|
|
172
|
+
const lookAndFeel = (result.frontendConfig as any)?.uiConfigs?.lookAndFeel;
|
|
268
173
|
|
|
269
174
|
expect(lookAndFeel?.chatHeaderLogoDarkSrc).toBe('logo.png');
|
|
270
175
|
});
|
|
271
176
|
|
|
272
|
-
it('falls back chatHeaderLogoLightSrc to chatHeaderLogoSrc when not set',
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
177
|
+
it('falls back chatHeaderLogoLightSrc to chatHeaderLogoSrc when not set', () => {
|
|
178
|
+
const result = transformV3ProductsConfig(
|
|
179
|
+
makeProductsConfig({
|
|
180
|
+
...baseV3Config,
|
|
181
|
+
ui_configs: {
|
|
182
|
+
...baseV3Config.ui_configs,
|
|
183
|
+
look_and_feel: {
|
|
184
|
+
...baseV3Config.ui_configs.look_and_feel,
|
|
185
|
+
chat_header_logo_src: 'logo.png',
|
|
186
|
+
chat_header_logo_dark_src: 'logo-dark.png',
|
|
187
|
+
chat_header_logo_light_src: null,
|
|
188
|
+
},
|
|
282
189
|
},
|
|
283
|
-
},
|
|
284
|
-
});
|
|
285
|
-
|
|
286
|
-
const result = await fetchUnifiedGraphQLConfig(
|
|
287
|
-
'https://api.example.com',
|
|
288
|
-
'key',
|
|
289
|
-
'user_1',
|
|
290
|
-
[],
|
|
291
|
-
'app',
|
|
190
|
+
}),
|
|
292
191
|
);
|
|
293
|
-
const lookAndFeel = (result.
|
|
192
|
+
const lookAndFeel = (result.frontendConfig as any)?.uiConfigs?.lookAndFeel;
|
|
294
193
|
|
|
295
194
|
expect(lookAndFeel?.chatHeaderLogoLightSrc).toBe('logo.png');
|
|
296
195
|
});
|
|
297
196
|
|
|
298
|
-
it('uses explicit dark/light src when both are set',
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
const result = await fetchUnifiedGraphQLConfig(
|
|
302
|
-
'https://api.example.com',
|
|
303
|
-
'key',
|
|
304
|
-
'user_1',
|
|
305
|
-
[],
|
|
306
|
-
'app',
|
|
307
|
-
);
|
|
308
|
-
const lookAndFeel = (result.orgConfig?.frontendConfig as any)?.uiConfigs?.lookAndFeel;
|
|
197
|
+
it('uses explicit dark/light src when both are set', () => {
|
|
198
|
+
const result = transformV3ProductsConfig(makeProductsConfig(baseV3Config));
|
|
199
|
+
const lookAndFeel = (result.frontendConfig as any)?.uiConfigs?.lookAndFeel;
|
|
309
200
|
|
|
310
201
|
expect(lookAndFeel?.chatHeaderLogoDarkSrc).toBe('logo-dark.png');
|
|
311
202
|
expect(lookAndFeel?.chatHeaderLogoLightSrc).toBe('logo-light.png');
|
|
@@ -313,61 +204,39 @@ describe('productsConfig transform (via getWidgetConfig)', () => {
|
|
|
313
204
|
});
|
|
314
205
|
|
|
315
206
|
describe('merchantOverrideCss', () => {
|
|
316
|
-
it('uses merchantOverrideCss from the v3 frontend config when present',
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
const result = await fetchUnifiedGraphQLConfig(
|
|
326
|
-
'https://api.example.com',
|
|
327
|
-
'key',
|
|
328
|
-
'user_1',
|
|
329
|
-
[],
|
|
330
|
-
'app',
|
|
207
|
+
it('uses merchantOverrideCss from the v3 frontend config when present', () => {
|
|
208
|
+
const result = transformV3ProductsConfig(
|
|
209
|
+
makeProductsConfig({
|
|
210
|
+
...baseV3Config,
|
|
211
|
+
ui_configs: {
|
|
212
|
+
...baseV3Config.ui_configs,
|
|
213
|
+
merchant_override_css: 'body { color: red; }',
|
|
214
|
+
},
|
|
215
|
+
}),
|
|
331
216
|
);
|
|
332
217
|
|
|
333
|
-
expect((result.
|
|
334
|
-
'body { color: red; }',
|
|
335
|
-
);
|
|
218
|
+
expect((result.frontendConfig as any)?.merchantOverrideCss).toBe('body { color: red; }');
|
|
336
219
|
});
|
|
337
220
|
|
|
338
|
-
it('falls back to frontendValues.merchant_override_css when absent from v3 config',
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
const result = await fetchUnifiedGraphQLConfig(
|
|
342
|
-
'https://api.example.com',
|
|
343
|
-
'key',
|
|
344
|
-
'user_1',
|
|
345
|
-
[],
|
|
346
|
-
'app',
|
|
221
|
+
it('falls back to frontendValues.merchant_override_css when absent from v3 config', () => {
|
|
222
|
+
const result = transformV3ProductsConfig(
|
|
223
|
+
makeProductsConfig(baseV3Config, { merchant_override_css: 'body { color: blue; }' }),
|
|
347
224
|
);
|
|
348
225
|
|
|
349
|
-
expect((result.
|
|
350
|
-
'body { color: blue; }',
|
|
351
|
-
);
|
|
226
|
+
expect((result.frontendConfig as any)?.merchantOverrideCss).toBe('body { color: blue; }');
|
|
352
227
|
});
|
|
353
228
|
});
|
|
354
229
|
|
|
355
230
|
describe('page variants', () => {
|
|
356
|
-
it('uses DEFAULT_PAGE_VARIANTS when page_variants is not an array',
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
const result = await fetchUnifiedGraphQLConfig(
|
|
360
|
-
'https://api.example.com',
|
|
361
|
-
'key',
|
|
362
|
-
'user_1',
|
|
363
|
-
[],
|
|
364
|
-
'app',
|
|
231
|
+
it('uses DEFAULT_PAGE_VARIANTS when page_variants is not an array', () => {
|
|
232
|
+
const result = transformV3ProductsConfig(
|
|
233
|
+
makeProductsConfig({ ...baseV3Config, page_variants: null }),
|
|
365
234
|
);
|
|
366
235
|
|
|
367
|
-
expect(result.
|
|
236
|
+
expect(result.orgPageConfig?.pageVariants).toEqual(DEFAULT_PAGE_VARIANTS);
|
|
368
237
|
});
|
|
369
238
|
|
|
370
|
-
it('transforms each page variant entry from the API response',
|
|
239
|
+
it('transforms each page variant entry from the API response', () => {
|
|
371
240
|
const rawVariant = {
|
|
372
241
|
variant_id: 'plp',
|
|
373
242
|
variant_type: 'plp',
|
|
@@ -375,67 +244,47 @@ describe('productsConfig transform (via getWidgetConfig)', () => {
|
|
|
375
244
|
variant_checks: [{ check_type: 'UrlResolver' }],
|
|
376
245
|
widget_mounting: [],
|
|
377
246
|
};
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
const result = await fetchUnifiedGraphQLConfig(
|
|
381
|
-
'https://api.example.com',
|
|
382
|
-
'key',
|
|
383
|
-
'user_1',
|
|
384
|
-
[],
|
|
385
|
-
'app',
|
|
247
|
+
const result = transformV3ProductsConfig(
|
|
248
|
+
makeProductsConfig({ ...baseV3Config, page_variants: [rawVariant] }),
|
|
386
249
|
);
|
|
387
250
|
|
|
388
|
-
expect(result.
|
|
389
|
-
expect(result.
|
|
390
|
-
expect(result.
|
|
391
|
-
'UrlResolver',
|
|
392
|
-
);
|
|
251
|
+
expect(result.orgPageConfig?.pageVariants).toHaveLength(1);
|
|
252
|
+
expect(result.orgPageConfig?.pageVariants[0].variantId).toBe('plp');
|
|
253
|
+
expect(result.orgPageConfig?.pageVariants[0].variantTests?.[0].testType).toBe('UrlResolver');
|
|
393
254
|
});
|
|
394
255
|
});
|
|
395
256
|
|
|
396
257
|
describe('widget configs', () => {
|
|
397
|
-
it('keys widget configs by widgetConfigId',
|
|
258
|
+
it('keys widget configs by widgetConfigId', () => {
|
|
398
259
|
const rawWidgets = [
|
|
399
260
|
{ widget_config_id: 'widget-1', widget_type: 'floating_button' },
|
|
400
261
|
{ widget_config_id: 'widget-2', widget_type: 'search_bar' },
|
|
401
262
|
];
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
const result = await fetchUnifiedGraphQLConfig(
|
|
405
|
-
'https://api.example.com',
|
|
406
|
-
'key',
|
|
407
|
-
'user_1',
|
|
408
|
-
[],
|
|
409
|
-
'app',
|
|
263
|
+
const result = transformV3ProductsConfig(
|
|
264
|
+
makeProductsConfig({ ...baseV3Config, widget_configs: rawWidgets }),
|
|
410
265
|
);
|
|
411
266
|
|
|
412
|
-
expect(Object.keys(result.
|
|
267
|
+
expect(Object.keys(result.orgPageConfig?.widgetConfigs ?? {})).toEqual([
|
|
413
268
|
'widget-1',
|
|
414
269
|
'widget-2',
|
|
415
270
|
]);
|
|
416
|
-
expect(
|
|
417
|
-
|
|
418
|
-
)
|
|
271
|
+
expect((result.orgPageConfig?.widgetConfigs as any)?.['widget-1']?.widgetConfigId).toBe(
|
|
272
|
+
'widget-1',
|
|
273
|
+
);
|
|
419
274
|
});
|
|
420
275
|
|
|
421
|
-
it('also exposes widget configs on frontendConfig',
|
|
276
|
+
it('also exposes widget configs on frontendConfig', () => {
|
|
422
277
|
const rawWidgets = [{ widget_config_id: 'widget-1', widget_type: 'floating_button' }];
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
const result = await fetchUnifiedGraphQLConfig(
|
|
426
|
-
'https://api.example.com',
|
|
427
|
-
'key',
|
|
428
|
-
'user_1',
|
|
429
|
-
[],
|
|
430
|
-
'app',
|
|
278
|
+
const result = transformV3ProductsConfig(
|
|
279
|
+
makeProductsConfig({ ...baseV3Config, widget_configs: rawWidgets }),
|
|
431
280
|
);
|
|
432
281
|
|
|
433
|
-
expect((result.
|
|
282
|
+
expect((result.frontendConfig as any)?.widgetConfigs?.['widget-1']).toBeDefined();
|
|
434
283
|
});
|
|
435
284
|
});
|
|
436
285
|
|
|
437
286
|
describe('mounting configs', () => {
|
|
438
|
-
it('keys mounting configs by their key field',
|
|
287
|
+
it('keys mounting configs by their key field', () => {
|
|
439
288
|
const rawMountingConfigs = [
|
|
440
289
|
{
|
|
441
290
|
key: 'plp-mount',
|
|
@@ -447,237 +296,59 @@ describe('productsConfig transform (via getWidgetConfig)', () => {
|
|
|
447
296
|
},
|
|
448
297
|
},
|
|
449
298
|
];
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
const result = await fetchUnifiedGraphQLConfig(
|
|
453
|
-
'https://api.example.com',
|
|
454
|
-
'key',
|
|
455
|
-
'user_1',
|
|
456
|
-
[],
|
|
457
|
-
'app',
|
|
299
|
+
const result = transformV3ProductsConfig(
|
|
300
|
+
makeProductsConfig({ ...baseV3Config, mounting_configs: rawMountingConfigs }),
|
|
458
301
|
);
|
|
459
302
|
|
|
460
|
-
expect(result.
|
|
461
|
-
expect(
|
|
462
|
-
result.orgConfig?.orgPageConfig?.mountingConfigs?.['plp-mount']?.mountingConfigId,
|
|
463
|
-
).toBe('mc-1');
|
|
303
|
+
expect(result.orgPageConfig?.mountingConfigs?.['plp-mount']).toBeDefined();
|
|
304
|
+
expect(result.orgPageConfig?.mountingConfigs?.['plp-mount']?.mountingConfigId).toBe('mc-1');
|
|
464
305
|
});
|
|
465
306
|
});
|
|
466
307
|
|
|
467
308
|
describe('colors config', () => {
|
|
468
|
-
it('camelCases the colors values from the API response',
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
const result = await fetchUnifiedGraphQLConfig(
|
|
475
|
-
'https://api.example.com',
|
|
476
|
-
'key',
|
|
477
|
-
'user_1',
|
|
478
|
-
[],
|
|
479
|
-
'app',
|
|
309
|
+
it('camelCases the colors values from the API response', () => {
|
|
310
|
+
const result = transformV3ProductsConfig(
|
|
311
|
+
makeProductsConfig({
|
|
312
|
+
...baseV3Config,
|
|
313
|
+
colors: { values: { text_primary: '#111', background_primary: '#fff' } },
|
|
314
|
+
}),
|
|
480
315
|
);
|
|
481
316
|
|
|
482
|
-
expect((result.
|
|
483
|
-
expect((result.
|
|
317
|
+
expect((result.colorsConfig as any)?.textPrimary).toBe('#111');
|
|
318
|
+
expect((result.colorsConfig as any)?.backgroundPrimary).toBe('#fff');
|
|
484
319
|
});
|
|
485
320
|
});
|
|
486
321
|
|
|
487
322
|
describe('v3FrontendConfigCleanup edge cases', () => {
|
|
488
|
-
it('returns config as-is when uiConfigs is absent',
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
const result = await fetchUnifiedGraphQLConfig(
|
|
492
|
-
'https://api.example.com',
|
|
493
|
-
'key',
|
|
494
|
-
'user_1',
|
|
495
|
-
[],
|
|
496
|
-
'app',
|
|
323
|
+
it('returns config as-is when uiConfigs is absent', () => {
|
|
324
|
+
const result = transformV3ProductsConfig(
|
|
325
|
+
makeProductsConfig({ ...baseV3Config, ui_configs: undefined }),
|
|
497
326
|
);
|
|
498
327
|
|
|
499
|
-
expect(result.
|
|
500
|
-
expect(result.
|
|
328
|
+
expect(result.frontendConfig).toBeDefined();
|
|
329
|
+
expect(result.colorsConfig).toBeDefined();
|
|
501
330
|
});
|
|
502
331
|
|
|
503
|
-
it('returns config as-is when floatingButton is absent',
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
look_and_feel: baseV3Config.ui_configs.look_and_feel,
|
|
508
|
-
},
|
|
509
|
-
});
|
|
510
|
-
|
|
511
|
-
const result = await fetchUnifiedGraphQLConfig(
|
|
512
|
-
'https://api.example.com',
|
|
513
|
-
'key',
|
|
514
|
-
'user_1',
|
|
515
|
-
[],
|
|
516
|
-
'app',
|
|
517
|
-
);
|
|
518
|
-
|
|
519
|
-
expect(result.orgConfig?.frontendConfig).toBeDefined();
|
|
520
|
-
});
|
|
521
|
-
|
|
522
|
-
it('returns config as-is when lookAndFeel is absent', async () => {
|
|
523
|
-
makeOkFetch({
|
|
524
|
-
...baseV3Config,
|
|
525
|
-
ui_configs: {
|
|
526
|
-
floating_button: baseV3Config.ui_configs.floating_button,
|
|
527
|
-
},
|
|
528
|
-
});
|
|
529
|
-
|
|
530
|
-
const result = await fetchUnifiedGraphQLConfig(
|
|
531
|
-
'https://api.example.com',
|
|
532
|
-
'key',
|
|
533
|
-
'user_1',
|
|
534
|
-
[],
|
|
535
|
-
'app',
|
|
332
|
+
it('returns config as-is when floatingButton is absent', () => {
|
|
333
|
+
const result = transformV3ProductsConfig(
|
|
334
|
+
makeProductsConfig({
|
|
335
|
+
...baseV3Config,
|
|
336
|
+
ui_configs: { look_and_feel: baseV3Config.ui_configs.look_and_feel },
|
|
337
|
+
}),
|
|
536
338
|
);
|
|
537
339
|
|
|
538
|
-
expect(result.
|
|
539
|
-
});
|
|
540
|
-
});
|
|
541
|
-
|
|
542
|
-
describe('fetch call', () => {
|
|
543
|
-
it('sends a POST to the correct GraphQL endpoint with the API key', async () => {
|
|
544
|
-
makeOkFetch(null);
|
|
545
|
-
|
|
546
|
-
await fetchUnifiedGraphQLConfig('https://api.example.com', 'test-key', 'user_1', [], 'app');
|
|
547
|
-
|
|
548
|
-
expect(mockFetch).toHaveBeenCalledWith('https://api.example.com/v1/graphql', {
|
|
549
|
-
method: 'POST',
|
|
550
|
-
headers: {
|
|
551
|
-
'Content-Type': 'application/json',
|
|
552
|
-
Authorization: 'Bearer test-key',
|
|
553
|
-
},
|
|
554
|
-
body: expect.any(String),
|
|
555
|
-
});
|
|
340
|
+
expect(result.frontendConfig).toBeDefined();
|
|
556
341
|
});
|
|
557
|
-
});
|
|
558
|
-
});
|
|
559
342
|
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
Promise.resolve({
|
|
566
|
-
data: {
|
|
567
|
-
me: {
|
|
568
|
-
getWidgetConfig: {
|
|
569
|
-
productsConfig: {
|
|
570
|
-
v_three_config: { values: v3RootConfigValues },
|
|
571
|
-
},
|
|
572
|
-
resolution,
|
|
573
|
-
},
|
|
574
|
-
},
|
|
575
|
-
},
|
|
343
|
+
it('returns config as-is when lookAndFeel is absent', () => {
|
|
344
|
+
const result = transformV3ProductsConfig(
|
|
345
|
+
makeProductsConfig({
|
|
346
|
+
...baseV3Config,
|
|
347
|
+
ui_configs: { floating_button: baseV3Config.ui_configs.floating_button },
|
|
576
348
|
}),
|
|
577
|
-
|
|
578
|
-
}
|
|
579
|
-
|
|
580
|
-
it('issues a getWidgetConfig query (not getProductsConfigByVersion) and forwards userId', async () => {
|
|
581
|
-
makeUnifiedOkFetch(null);
|
|
582
|
-
|
|
583
|
-
await fetchUnifiedGraphQLConfig('https://api.example.com', 'key', 'user_1', [], 'app');
|
|
584
|
-
|
|
585
|
-
const body = JSON.parse(mockFetch.mock.calls[0][1].body);
|
|
586
|
-
expect(body.query).toContain('getWidgetConfig');
|
|
587
|
-
expect(body.query).not.toContain('getProductsConfigByVersion');
|
|
588
|
-
// userId is required by the schema; omitting it 400s. Regression guard.
|
|
589
|
-
// version is absent without a URL-param override so the resolver uses
|
|
590
|
-
// Statsig bucketing (passing 'deployed' here would bypass Statsig).
|
|
591
|
-
expect(body.variables).toEqual({ userId: 'user_1', gateNames: [], source: 'app' });
|
|
592
|
-
});
|
|
593
|
-
|
|
594
|
-
it('threads a spiffy_config_version URL param through as the version variable', async () => {
|
|
595
|
-
window.history.replaceState({}, '', '/?spiffy_config_version=1.2.3');
|
|
596
|
-
try {
|
|
597
|
-
makeUnifiedOkFetch(null);
|
|
598
|
-
|
|
599
|
-
await fetchUnifiedGraphQLConfig('https://api.example.com', 'key', 'user_1', [], 'app');
|
|
600
|
-
|
|
601
|
-
const body = JSON.parse(mockFetch.mock.calls[0][1].body);
|
|
602
|
-
expect(body.variables).toEqual({
|
|
603
|
-
userId: 'user_1',
|
|
604
|
-
version: '1.2.3',
|
|
605
|
-
gateNames: [],
|
|
606
|
-
source: 'app',
|
|
607
|
-
});
|
|
608
|
-
} finally {
|
|
609
|
-
window.history.replaceState({}, '', '/');
|
|
610
|
-
}
|
|
611
|
-
});
|
|
612
|
-
|
|
613
|
-
it('parses productsConfig into the same shape as fetchGraphQLConfig', async () => {
|
|
614
|
-
makeUnifiedOkFetch(baseV3Config);
|
|
615
|
-
|
|
616
|
-
const result = await fetchUnifiedGraphQLConfig(
|
|
617
|
-
'https://api.example.com',
|
|
618
|
-
'key',
|
|
619
|
-
'user_1',
|
|
620
|
-
[],
|
|
621
|
-
'app',
|
|
622
|
-
);
|
|
623
|
-
|
|
624
|
-
expect(result.orgConfig?.colorsConfig).toBeDefined();
|
|
625
|
-
expect(result.orgConfig?.frontendConfig).toBeDefined();
|
|
626
|
-
expect(result.orgConfig?.orgPageConfig).toBeDefined();
|
|
627
|
-
});
|
|
628
|
-
|
|
629
|
-
it('returns resolution metadata alongside the config so callers can thread baseVersion into inference', async () => {
|
|
630
|
-
makeUnifiedOkFetch(baseV3Config, {
|
|
631
|
-
baseVersion: '1.0.670',
|
|
632
|
-
baseVersionSource: 'merchant_layer',
|
|
633
|
-
});
|
|
349
|
+
);
|
|
634
350
|
|
|
635
|
-
|
|
636
|
-
'https://api.example.com',
|
|
637
|
-
'key',
|
|
638
|
-
'user_1',
|
|
639
|
-
[],
|
|
640
|
-
'app',
|
|
641
|
-
);
|
|
642
|
-
|
|
643
|
-
expect(result.orgConfig?.resolution).toEqual({
|
|
644
|
-
baseVersion: '1.0.670',
|
|
645
|
-
baseVersionSource: 'merchant_layer',
|
|
351
|
+
expect(result.frontendConfig).toBeDefined();
|
|
646
352
|
});
|
|
647
353
|
});
|
|
648
|
-
|
|
649
|
-
it('selects resolution fields in the query so the server can fill them', async () => {
|
|
650
|
-
makeUnifiedOkFetch(null);
|
|
651
|
-
|
|
652
|
-
await fetchUnifiedGraphQLConfig('https://api.example.com', 'key', 'user_1', [], 'app');
|
|
653
|
-
|
|
654
|
-
const body = JSON.parse(mockFetch.mock.calls[0][1].body);
|
|
655
|
-
expect(body.query).toContain('resolution');
|
|
656
|
-
expect(body.query).toContain('baseVersion');
|
|
657
|
-
expect(body.query).toContain('baseVersionSource');
|
|
658
|
-
});
|
|
659
|
-
|
|
660
|
-
it('falls back to DEFAULT_PAGE_VARIANTS when productsConfig has no page_variants array', async () => {
|
|
661
|
-
// Reuses the no-colors fallback path: when productsConfig is missing
|
|
662
|
-
// colors, DEFAULT_PAGE_VARIANTS is returned as the orgPageConfig.
|
|
663
|
-
makeUnifiedOkFetch(null);
|
|
664
|
-
|
|
665
|
-
const result = await fetchUnifiedGraphQLConfig(
|
|
666
|
-
'https://api.example.com',
|
|
667
|
-
'key',
|
|
668
|
-
'user_1',
|
|
669
|
-
[],
|
|
670
|
-
'app',
|
|
671
|
-
);
|
|
672
|
-
|
|
673
|
-
expect(result.orgConfig?.orgPageConfig?.pageVariants).toEqual(DEFAULT_PAGE_VARIANTS);
|
|
674
|
-
});
|
|
675
|
-
|
|
676
|
-
it('propagates the error on network failure', async () => {
|
|
677
|
-
mockFetch.mockRejectedValueOnce(new Error('boom'));
|
|
678
|
-
|
|
679
|
-
await expect(
|
|
680
|
-
fetchUnifiedGraphQLConfig('https://api.example.com', 'key', 'user_1', [], 'app'),
|
|
681
|
-
).rejects.toThrow('boom');
|
|
682
|
-
});
|
|
683
354
|
});
|