@envive-ai/react-hooks 0.3.57 → 0.3.59
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/commerce-api.cjs +1 -1
- package/dist/application/commerce-api.js +1 -1
- package/dist/application/models/graphql/index.cjs +0 -4
- package/dist/application/models/graphql/index.d.cts +2 -3
- package/dist/application/models/graphql/index.d.ts +2 -3
- package/dist/application/models/graphql/index.js +1 -3
- package/dist/application/models/graphql/queries/getMerchantColorsQuery.d.cts +2 -3
- package/dist/application/models/graphql/queries/getMerchantColorsQuery.d.ts +2 -3
- package/dist/application/models/graphql/queries/getWidgetConfigQuery.cjs +22 -3
- package/dist/application/models/graphql/queries/getWidgetConfigQuery.js +22 -3
- package/dist/application/models/index.cjs +0 -4
- package/dist/application/models/index.d.cts +2 -3
- package/dist/application/models/index.d.ts +2 -3
- package/dist/application/models/index.js +1 -3
- package/dist/application/utils/elementObserver.d.cts +2 -2
- package/dist/application/utils/index.cjs +1 -1
- package/dist/application/utils/index.js +1 -1
- package/dist/atoms/app/index.d.cts +7 -7
- package/dist/atoms/app/index.d.ts +7 -7
- package/dist/atoms/app/variant.d.cts +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 +2 -2
- package/dist/atoms/chat/index.d.cts +2 -2
- package/dist/atoms/chat/index.d.ts +2 -2
- package/dist/atoms/chat/lastMessage.d.cts +2 -2
- package/dist/atoms/chat/lastMessage.d.ts +2 -2
- package/dist/atoms/chat/messageQueue.d.cts +6 -6
- package/dist/atoms/chat/messageQueue.d.ts +6 -6
- 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 +2 -2
- package/dist/atoms/chat/replies.d.cts +3 -3
- package/dist/atoms/chat/replies.d.ts +3 -3
- package/dist/atoms/chat/suggestions.d.cts +2 -2
- package/dist/atoms/chat/suggestions.d.ts +2 -2
- package/dist/atoms/envive/enviveConfig.d.cts +14 -14
- package/dist/atoms/envive/enviveConfig.d.ts +13 -13
- package/dist/atoms/globalSearch/globalSearch.d.ts +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/chatSearch.d.cts +17 -17
- package/dist/atoms/search/chatSearch.d.ts +17 -17
- package/dist/atoms/search/searchAPI.d.cts +13 -13
- package/dist/atoms/search/searchAPI.d.ts +13 -13
- package/dist/atoms/widget/chatPreviewLoading.d.cts +2 -2
- package/dist/atoms/widget/chatPreviewLoading.d.ts +2 -2
- package/dist/contexts/pageContext/pageContext.cjs +1 -1
- package/dist/contexts/pageContext/pageContext.js +1 -1
- package/dist/contexts/systemSettingsContext/systemSettingsContext.d.cts +2 -2
- package/dist/contexts/systemSettingsContext/systemSettingsContext.d.ts +2 -2
- package/dist/contexts/types.d.cts +1 -1
- package/dist/contexts/types.d.ts +1 -1
- package/dist/contexts/typesV3.d.cts +1 -1
- package/dist/contexts/typesV3.d.ts +1 -1
- package/dist/contexts/widgetConfigContext/widgetConfigContext.cjs +22 -31
- package/dist/contexts/widgetConfigContext/widgetConfigContext.js +22 -31
- package/dist/hooks/GrabAndScroll/useGrabAndScroll.d.cts +2 -2
- package/dist/hooks/GrabAndScroll/useGrabAndScroll.d.ts +2 -2
- package/dist/hooks/Intersection/useIntersection.cjs +14 -3
- package/dist/hooks/Intersection/useIntersection.js +14 -3
- package/dist/hooks/PageViewedEvent/usePageViewedEvent.cjs +1 -1
- package/dist/hooks/PageViewedEvent/usePageViewedEvent.js +1 -1
- package/dist/hooks/ShopifyUrlOperations/useShopifyUrlOperations.d.cts +2 -2
- package/dist/hooks/ShopifyUrlOperations/useShopifyUrlOperations.d.ts +2 -2
- package/dist/hooks/SystemSettingsContext/useSystemSettingsContext.d.cts +2 -2
- package/dist/hooks/utils.d.ts +1 -1
- package/dist/services/enviveConfigService/enviveConfigService.cjs +3 -24
- package/dist/services/enviveConfigService/enviveConfigService.js +4 -25
- package/dist/services/enviveConfigService/fetchGraphQLConfig.cjs +59 -54
- package/dist/services/enviveConfigService/fetchGraphQLConfig.js +60 -54
- package/dist/services/hardcopyService/hardcopyService.cjs +1 -1
- package/dist/services/hardcopyService/hardcopyService.js +1 -1
- package/dist/types/config-versions.cjs +1 -3
- package/dist/types/config-versions.js +2 -3
- package/package.json +1 -1
- package/src/application/models/graphql/index.ts +0 -1
- package/src/application/models/graphql/queries/getMerchantColorsQuery.ts +4 -15
- package/src/application/models/graphql/queries/getWidgetConfigQuery.ts +35 -14
- package/src/application/models/index.ts +0 -1
- package/src/contexts/pageContext/__tests__/pageContext.test.tsx +1 -2
- package/src/contexts/pageContext/pageContext.tsx +1 -2
- package/src/contexts/widgetConfigContext/__tests__/widgetConfigContext.test.tsx +52 -0
- package/src/contexts/widgetConfigContext/widgetConfigContext.tsx +39 -36
- package/src/hooks/Intersection/__tests__/useIntersection.test.ts +134 -0
- package/src/hooks/Intersection/useIntersection.ts +20 -4
- package/src/services/enviveConfigService/__tests__/enviveConfigService.test.ts +51 -75
- package/src/services/enviveConfigService/__tests__/fetchGraphQLConfig.test.ts +242 -90
- package/src/services/enviveConfigService/enviveConfigService.ts +15 -41
- package/src/services/enviveConfigService/fetchGraphQLConfig.ts +106 -68
- package/dist/application/models/graphql/queries/getColorsAndFrontendQuery.cjs +0 -18
- package/dist/application/models/graphql/queries/getColorsAndFrontendQuery.js +0 -18
- package/dist/application/models/graphql/queries/getMerchantColorsQuery.cjs +0 -18
- package/dist/application/models/graphql/queries/getMerchantColorsQuery.js +0 -18
- package/dist/application/models/graphql/queries/getMerchantFrontendConfigQuery.cjs +0 -18
- package/dist/application/models/graphql/queries/getMerchantFrontendConfigQuery.d.cts +0 -75
- package/dist/application/models/graphql/queries/getMerchantFrontendConfigQuery.d.ts +0 -75
- package/dist/application/models/graphql/queries/getMerchantFrontendConfigQuery.js +0 -18
- package/src/application/models/graphql/queries/getColorsAndFrontendQuery.ts +0 -13
- package/src/application/models/graphql/queries/getMerchantFrontendConfigQuery.ts +0 -103
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
import { act, renderHook } from '@testing-library/react';
|
|
2
|
+
import { RefObject } from 'react';
|
|
3
|
+
import { useIntersection } from '../useIntersection';
|
|
4
|
+
|
|
5
|
+
class MockIntersectionObserver {
|
|
6
|
+
static instances: MockIntersectionObserver[] = [];
|
|
7
|
+
|
|
8
|
+
observedTargets: Element[] = [];
|
|
9
|
+
|
|
10
|
+
disconnected = false;
|
|
11
|
+
|
|
12
|
+
private callback: IntersectionObserverCallback;
|
|
13
|
+
|
|
14
|
+
constructor(callback: IntersectionObserverCallback) {
|
|
15
|
+
this.callback = callback;
|
|
16
|
+
MockIntersectionObserver.instances.push(this);
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
observe(target: Element) {
|
|
20
|
+
this.observedTargets.push(target);
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
disconnect() {
|
|
24
|
+
this.disconnected = true;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
unobserve() {}
|
|
28
|
+
|
|
29
|
+
fire(isIntersecting: boolean, target: Element) {
|
|
30
|
+
this.callback(
|
|
31
|
+
[
|
|
32
|
+
{
|
|
33
|
+
isIntersecting,
|
|
34
|
+
intersectionRatio: isIntersecting ? 1 : 0,
|
|
35
|
+
boundingClientRect: {} as DOMRectReadOnly,
|
|
36
|
+
target,
|
|
37
|
+
} as IntersectionObserverEntry,
|
|
38
|
+
],
|
|
39
|
+
this as unknown as IntersectionObserver,
|
|
40
|
+
);
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
const lastObserver = () =>
|
|
45
|
+
MockIntersectionObserver.instances[MockIntersectionObserver.instances.length - 1];
|
|
46
|
+
|
|
47
|
+
describe('useIntersection', () => {
|
|
48
|
+
beforeEach(() => {
|
|
49
|
+
MockIntersectionObserver.instances = [];
|
|
50
|
+
vi.stubGlobal('IntersectionObserver', MockIntersectionObserver);
|
|
51
|
+
});
|
|
52
|
+
|
|
53
|
+
afterEach(() => {
|
|
54
|
+
vi.unstubAllGlobals();
|
|
55
|
+
});
|
|
56
|
+
|
|
57
|
+
it('observes the node and reports visibility when the observer fires', () => {
|
|
58
|
+
const node = document.createElement('span');
|
|
59
|
+
const ref: RefObject<HTMLElement> = { current: node };
|
|
60
|
+
|
|
61
|
+
const { result } = renderHook(() => useIntersection(ref, '0px'));
|
|
62
|
+
|
|
63
|
+
expect(MockIntersectionObserver.instances).toHaveLength(1);
|
|
64
|
+
expect(lastObserver().observedTargets).toEqual([node]);
|
|
65
|
+
expect(result.current).toBe(false);
|
|
66
|
+
|
|
67
|
+
act(() => {
|
|
68
|
+
lastObserver().fire(true, node);
|
|
69
|
+
});
|
|
70
|
+
expect(result.current).toBe(true);
|
|
71
|
+
});
|
|
72
|
+
|
|
73
|
+
it('attaches late when the ref starts out null', () => {
|
|
74
|
+
const ref = { current: null as HTMLElement | null };
|
|
75
|
+
|
|
76
|
+
const { rerender } = renderHook(() => useIntersection(ref as RefObject<HTMLElement>, '0px'));
|
|
77
|
+
expect(MockIntersectionObserver.instances).toHaveLength(0);
|
|
78
|
+
|
|
79
|
+
const node = document.createElement('span');
|
|
80
|
+
ref.current = node;
|
|
81
|
+
rerender();
|
|
82
|
+
|
|
83
|
+
expect(MockIntersectionObserver.instances).toHaveLength(1);
|
|
84
|
+
expect(lastObserver().observedTargets).toEqual([node]);
|
|
85
|
+
});
|
|
86
|
+
|
|
87
|
+
it('does not re-attach across rerenders while the node is unchanged', () => {
|
|
88
|
+
const node = document.createElement('span');
|
|
89
|
+
const ref: RefObject<HTMLElement> = { current: node };
|
|
90
|
+
|
|
91
|
+
const { rerender } = renderHook(() => useIntersection(ref, '0px'));
|
|
92
|
+
rerender();
|
|
93
|
+
rerender();
|
|
94
|
+
|
|
95
|
+
expect(MockIntersectionObserver.instances).toHaveLength(1);
|
|
96
|
+
expect(lastObserver().observedTargets).toEqual([node]);
|
|
97
|
+
});
|
|
98
|
+
|
|
99
|
+
it('re-attaches to the new node when the node under the ref is replaced', () => {
|
|
100
|
+
// Mount churn (e.g. a portal re-creating the widget subtree) can swap the DOM node
|
|
101
|
+
// while the hook instance survives. The old implementation kept observing the detached
|
|
102
|
+
// node forever, so the widget read as permanently not-visible and never fetched.
|
|
103
|
+
const nodeA = document.createElement('span');
|
|
104
|
+
const ref: RefObject<HTMLElement> = { current: nodeA };
|
|
105
|
+
|
|
106
|
+
const { result, rerender } = renderHook(() => useIntersection(ref, '0px'));
|
|
107
|
+
const firstObserver = lastObserver();
|
|
108
|
+
expect(firstObserver.observedTargets).toEqual([nodeA]);
|
|
109
|
+
|
|
110
|
+
const nodeB = document.createElement('span');
|
|
111
|
+
(ref as { current: HTMLElement | null }).current = nodeB;
|
|
112
|
+
rerender();
|
|
113
|
+
|
|
114
|
+
expect(firstObserver.disconnected).toBe(true);
|
|
115
|
+
expect(MockIntersectionObserver.instances).toHaveLength(2);
|
|
116
|
+
expect(lastObserver().observedTargets).toEqual([nodeB]);
|
|
117
|
+
|
|
118
|
+
// Visibility of the replacement node is now detected
|
|
119
|
+
act(() => {
|
|
120
|
+
lastObserver().fire(true, nodeB);
|
|
121
|
+
});
|
|
122
|
+
expect(result.current).toBe(true);
|
|
123
|
+
});
|
|
124
|
+
|
|
125
|
+
it('disconnects the observer on unmount', () => {
|
|
126
|
+
const node = document.createElement('span');
|
|
127
|
+
const ref: RefObject<HTMLElement> = { current: node };
|
|
128
|
+
|
|
129
|
+
const { unmount } = renderHook(() => useIntersection(ref, '0px'));
|
|
130
|
+
unmount();
|
|
131
|
+
|
|
132
|
+
expect(lastObserver().disconnected).toBe(true);
|
|
133
|
+
});
|
|
134
|
+
});
|
|
@@ -7,23 +7,37 @@ const logger = new Logger('useIntersection');
|
|
|
7
7
|
export const useIntersection = (element: RefObject<HTMLElement>, rootMargin: string) => {
|
|
8
8
|
const [isVisible, setIsVisible] = useState(false);
|
|
9
9
|
const observerRef = useRef<IntersectionObserver | null>(null);
|
|
10
|
+
const observedNodeRef = useRef<HTMLElement | null>(null);
|
|
10
11
|
|
|
11
|
-
// Runs after every render so it catches when element.current transitions from null
|
|
12
|
-
//
|
|
13
|
-
//
|
|
12
|
+
// Runs after every render so it catches when element.current transitions from null to a
|
|
13
|
+
// mounted DOM node, AND when the node under the ref is replaced. The replacement case is
|
|
14
|
+
// load-bearing: mount churn (e.g. the shadow-root portal re-creating the widget subtree)
|
|
15
|
+
// can swap the DOM node while this hook instance survives, and an observer left on the
|
|
16
|
+
// detached old node never fires again — the widget then reads as permanently not-visible
|
|
17
|
+
// and never starts loading. Bails once the current node is the one being observed,
|
|
18
|
+
// keeping per-render cost O(1).
|
|
14
19
|
useEffect(() => {
|
|
15
20
|
const current = element?.current;
|
|
16
21
|
logger.logDebug('effect ran', {
|
|
17
22
|
elementCurrent: current,
|
|
18
23
|
rootMargin,
|
|
19
24
|
alreadyObserving: !!observerRef.current,
|
|
25
|
+
observingCurrentNode: observedNodeRef.current === current,
|
|
20
26
|
});
|
|
21
27
|
|
|
22
28
|
if (!current) {
|
|
23
29
|
logger.logDebug('element.current is null — observer NOT attached');
|
|
24
30
|
return;
|
|
25
31
|
}
|
|
26
|
-
if (
|
|
32
|
+
if (observedNodeRef.current === current) return; // already observing this exact node
|
|
33
|
+
|
|
34
|
+
if (observerRef.current) {
|
|
35
|
+
logger.logDebug('observed node was replaced — re-attaching observer', {
|
|
36
|
+
previousNode: observedNodeRef.current,
|
|
37
|
+
newNode: current,
|
|
38
|
+
});
|
|
39
|
+
observerRef.current.disconnect();
|
|
40
|
+
}
|
|
27
41
|
|
|
28
42
|
const observer = new IntersectionObserver(
|
|
29
43
|
([entry]) => {
|
|
@@ -40,6 +54,7 @@ export const useIntersection = (element: RefObject<HTMLElement>, rootMargin: str
|
|
|
40
54
|
|
|
41
55
|
observer.observe(current);
|
|
42
56
|
observerRef.current = observer;
|
|
57
|
+
observedNodeRef.current = current;
|
|
43
58
|
logger.logDebug('observer attached to element', current);
|
|
44
59
|
});
|
|
45
60
|
|
|
@@ -48,6 +63,7 @@ export const useIntersection = (element: RefObject<HTMLElement>, rootMargin: str
|
|
|
48
63
|
return () => {
|
|
49
64
|
observerRef.current?.disconnect();
|
|
50
65
|
observerRef.current = null;
|
|
66
|
+
observedNodeRef.current = null;
|
|
51
67
|
};
|
|
52
68
|
}, []);
|
|
53
69
|
|
|
@@ -3,35 +3,30 @@ import { EnviveConfigService } from '../enviveConfigService';
|
|
|
3
3
|
const mockFetch = vi.fn();
|
|
4
4
|
globalThis.fetch = mockFetch;
|
|
5
5
|
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
domain: 'test.com',
|
|
13
|
-
status: 'active',
|
|
14
|
-
created_at: '2024-01-01T00:00:00.000Z',
|
|
15
|
-
updated_at: '2024-01-01T00:00:00.000Z',
|
|
16
|
-
},
|
|
17
|
-
},
|
|
18
|
-
gates: [],
|
|
19
|
-
};
|
|
20
|
-
|
|
21
|
-
function makeOkOrgConfigFetch(overrides: Record<string, unknown> = {}) {
|
|
22
|
-
mockFetch.mockResolvedValueOnce({
|
|
23
|
-
ok: true,
|
|
24
|
-
json: () => Promise.resolve({ ...ORG_CONFIG_RESPONSE, ...overrides }),
|
|
25
|
-
});
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
// productsConfig has no colors, so fetchGraphQLConfig falls back to its mock
|
|
29
|
-
// config (see fetchGraphQLConfig.test.ts "no colors fallback") - fine here
|
|
30
|
-
// since these tests only care about caching/fetch-count behaviour.
|
|
31
|
-
function makeOkGraphqlFetch() {
|
|
6
|
+
// getWidgetConfig is the single config call now: it returns org identity +
|
|
7
|
+
// gates + experiment assignments alongside productsConfig (the separate
|
|
8
|
+
// GET /v1/org/config fetch was removed). productsConfig has no colors, so
|
|
9
|
+
// fetchUnifiedGraphQLConfig falls back to its mock config — fine here since
|
|
10
|
+
// these tests only care about caching / fetch-count behaviour.
|
|
11
|
+
function makeOkConfigFetch() {
|
|
32
12
|
mockFetch.mockResolvedValueOnce({
|
|
33
13
|
ok: true,
|
|
34
|
-
json: () =>
|
|
14
|
+
json: () =>
|
|
15
|
+
Promise.resolve({
|
|
16
|
+
data: {
|
|
17
|
+
me: {
|
|
18
|
+
org: {
|
|
19
|
+
id: 'org-1',
|
|
20
|
+
shortName: 'test-org',
|
|
21
|
+
displayName: 'Test Org',
|
|
22
|
+
domain: 'test.com',
|
|
23
|
+
createdAt: '2024-01-01T00:00:00.000Z',
|
|
24
|
+
updatedAt: '2024-01-01T00:00:00.000Z',
|
|
25
|
+
},
|
|
26
|
+
getWidgetConfig: { productsConfig: {}, gates: [], experimentAssignments: [] },
|
|
27
|
+
},
|
|
28
|
+
},
|
|
29
|
+
}),
|
|
35
30
|
});
|
|
36
31
|
}
|
|
37
32
|
|
|
@@ -60,27 +55,25 @@ afterEach(() => {
|
|
|
60
55
|
});
|
|
61
56
|
|
|
62
57
|
describe('EnviveConfigService caching', () => {
|
|
63
|
-
it('fetches
|
|
64
|
-
|
|
65
|
-
makeOkGraphqlFetch();
|
|
58
|
+
it('fetches the unified config once on a cold cache', async () => {
|
|
59
|
+
makeOkConfigFetch();
|
|
66
60
|
|
|
67
61
|
const result = await makeService().getEnviveConfig();
|
|
68
62
|
|
|
69
|
-
expect(mockFetch).toHaveBeenCalledTimes(
|
|
63
|
+
expect(mockFetch).toHaveBeenCalledTimes(1);
|
|
70
64
|
expect(result.org.org.id).toBe('org-1');
|
|
71
65
|
});
|
|
72
66
|
|
|
73
67
|
it('serves a warm cache to a new instance with identical params without hitting the network', async () => {
|
|
74
|
-
|
|
75
|
-
makeOkGraphqlFetch();
|
|
68
|
+
makeOkConfigFetch();
|
|
76
69
|
await makeService().getEnviveConfig();
|
|
77
|
-
expect(mockFetch).toHaveBeenCalledTimes(
|
|
70
|
+
expect(mockFetch).toHaveBeenCalledTimes(1);
|
|
78
71
|
|
|
79
72
|
// A fresh instance (as happens on the next page navigation) should read
|
|
80
73
|
// from the sessionStorage cache instead of refetching.
|
|
81
74
|
const result = await makeService().getEnviveConfig();
|
|
82
75
|
|
|
83
|
-
expect(mockFetch).toHaveBeenCalledTimes(
|
|
76
|
+
expect(mockFetch).toHaveBeenCalledTimes(1);
|
|
84
77
|
expect(result.org.org.id).toBe('org-1');
|
|
85
78
|
});
|
|
86
79
|
|
|
@@ -88,18 +81,16 @@ describe('EnviveConfigService caching', () => {
|
|
|
88
81
|
vi.useFakeTimers();
|
|
89
82
|
vi.setSystemTime(new Date('2026-01-01T00:00:00.000Z'));
|
|
90
83
|
|
|
91
|
-
|
|
92
|
-
makeOkGraphqlFetch();
|
|
84
|
+
makeOkConfigFetch();
|
|
93
85
|
await makeService().getEnviveConfig();
|
|
94
|
-
expect(mockFetch).toHaveBeenCalledTimes(
|
|
86
|
+
expect(mockFetch).toHaveBeenCalledTimes(1);
|
|
95
87
|
|
|
96
88
|
vi.setSystemTime(new Date('2026-01-01T00:05:01.000Z'));
|
|
97
89
|
|
|
98
|
-
|
|
99
|
-
makeOkGraphqlFetch();
|
|
90
|
+
makeOkConfigFetch();
|
|
100
91
|
await makeService().getEnviveConfig();
|
|
101
92
|
|
|
102
|
-
expect(mockFetch).toHaveBeenCalledTimes(
|
|
93
|
+
expect(mockFetch).toHaveBeenCalledTimes(2);
|
|
103
94
|
});
|
|
104
95
|
|
|
105
96
|
it.each(['envive_config_version', 'spiffy_config_version'])(
|
|
@@ -107,59 +98,51 @@ describe('EnviveConfigService caching', () => {
|
|
|
107
98
|
async param => {
|
|
108
99
|
window.history.replaceState({}, '', `/?${param}=1.2.3`);
|
|
109
100
|
|
|
110
|
-
|
|
111
|
-
makeOkGraphqlFetch();
|
|
101
|
+
makeOkConfigFetch();
|
|
112
102
|
await makeService().getEnviveConfig();
|
|
113
|
-
expect(mockFetch).toHaveBeenCalledTimes(
|
|
103
|
+
expect(mockFetch).toHaveBeenCalledTimes(1);
|
|
114
104
|
|
|
115
|
-
|
|
116
|
-
makeOkGraphqlFetch();
|
|
105
|
+
makeOkConfigFetch();
|
|
117
106
|
await makeService().getEnviveConfig();
|
|
118
107
|
|
|
119
108
|
// No cache read/write happened, so a second instance refetches instead
|
|
120
109
|
// of reusing anything the first instance stored.
|
|
121
|
-
expect(mockFetch).toHaveBeenCalledTimes(
|
|
110
|
+
expect(mockFetch).toHaveBeenCalledTimes(2);
|
|
122
111
|
},
|
|
123
112
|
);
|
|
124
113
|
|
|
125
114
|
it('does not read a version-pinned cache entry after the override is removed', async () => {
|
|
126
115
|
window.history.replaceState({}, '', '/?envive_config_version=1.2.3');
|
|
127
|
-
|
|
128
|
-
makeOkGraphqlFetch();
|
|
116
|
+
makeOkConfigFetch();
|
|
129
117
|
await makeService().getEnviveConfig();
|
|
130
118
|
|
|
131
119
|
window.history.replaceState({}, '', '/');
|
|
132
|
-
|
|
133
|
-
makeOkGraphqlFetch();
|
|
120
|
+
makeOkConfigFetch();
|
|
134
121
|
await makeService().getEnviveConfig();
|
|
135
122
|
|
|
136
|
-
expect(mockFetch).toHaveBeenCalledTimes(
|
|
123
|
+
expect(mockFetch).toHaveBeenCalledTimes(2);
|
|
137
124
|
});
|
|
138
125
|
|
|
139
126
|
it('keeps separate cache entries per userId', async () => {
|
|
140
|
-
|
|
141
|
-
makeOkGraphqlFetch();
|
|
127
|
+
makeOkConfigFetch();
|
|
142
128
|
await makeService({ userId: 'user-1' }).getEnviveConfig();
|
|
143
|
-
expect(mockFetch).toHaveBeenCalledTimes(
|
|
129
|
+
expect(mockFetch).toHaveBeenCalledTimes(1);
|
|
144
130
|
|
|
145
|
-
|
|
146
|
-
makeOkGraphqlFetch();
|
|
131
|
+
makeOkConfigFetch();
|
|
147
132
|
await makeService({ userId: 'user-2' }).getEnviveConfig();
|
|
148
133
|
|
|
149
|
-
expect(mockFetch).toHaveBeenCalledTimes(
|
|
134
|
+
expect(mockFetch).toHaveBeenCalledTimes(2);
|
|
150
135
|
});
|
|
151
136
|
|
|
152
137
|
it('keeps separate cache entries per namespace/source/baseUrl/apiKey', async () => {
|
|
153
|
-
|
|
154
|
-
makeOkGraphqlFetch();
|
|
138
|
+
makeOkConfigFetch();
|
|
155
139
|
await makeService({ namespace: 'ns-a' }).getEnviveConfig();
|
|
156
|
-
expect(mockFetch).toHaveBeenCalledTimes(
|
|
140
|
+
expect(mockFetch).toHaveBeenCalledTimes(1);
|
|
157
141
|
|
|
158
|
-
|
|
159
|
-
makeOkGraphqlFetch();
|
|
142
|
+
makeOkConfigFetch();
|
|
160
143
|
await makeService({ namespace: 'ns-b' }).getEnviveConfig();
|
|
161
144
|
|
|
162
|
-
expect(mockFetch).toHaveBeenCalledTimes(
|
|
145
|
+
expect(mockFetch).toHaveBeenCalledTimes(2);
|
|
163
146
|
});
|
|
164
147
|
|
|
165
148
|
it('fails open and still returns config when sessionStorage.setItem throws', async () => {
|
|
@@ -167,8 +150,7 @@ describe('EnviveConfigService caching', () => {
|
|
|
167
150
|
throw new Error('quota exceeded');
|
|
168
151
|
});
|
|
169
152
|
|
|
170
|
-
|
|
171
|
-
makeOkGraphqlFetch();
|
|
153
|
+
makeOkConfigFetch();
|
|
172
154
|
|
|
173
155
|
const result = await makeService().getEnviveConfig();
|
|
174
156
|
|
|
@@ -180,8 +162,7 @@ describe('EnviveConfigService caching', () => {
|
|
|
180
162
|
throw new Error('storage disabled');
|
|
181
163
|
});
|
|
182
164
|
|
|
183
|
-
|
|
184
|
-
makeOkGraphqlFetch();
|
|
165
|
+
makeOkConfigFetch();
|
|
185
166
|
|
|
186
167
|
const result = await makeService().getEnviveConfig();
|
|
187
168
|
|
|
@@ -190,17 +171,12 @@ describe('EnviveConfigService caching', () => {
|
|
|
190
171
|
|
|
191
172
|
it('fails open and refetches when a cached entry is corrupted JSON', async () => {
|
|
192
173
|
const service = makeService();
|
|
193
|
-
|
|
194
|
-
// populating every session storage key with garbage after a real write,
|
|
195
|
-
// then corrupting it directly.
|
|
196
|
-
makeOkOrgConfigFetch();
|
|
197
|
-
makeOkGraphqlFetch();
|
|
174
|
+
makeOkConfigFetch();
|
|
198
175
|
await service.getEnviveConfig();
|
|
199
176
|
const [key] = Object.keys(sessionStorage);
|
|
200
177
|
sessionStorage.setItem(key, 'not json');
|
|
201
178
|
|
|
202
|
-
|
|
203
|
-
makeOkGraphqlFetch();
|
|
179
|
+
makeOkConfigFetch();
|
|
204
180
|
const result = await makeService().getEnviveConfig();
|
|
205
181
|
|
|
206
182
|
expect(result.org.org.id).toBe('org-1');
|