@croct/plug-react 0.10.0 → 0.11.0

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.
Files changed (99) hide show
  1. package/CroctProvider.cjs +81 -0
  2. package/CroctProvider.d.cts +11 -0
  3. package/CroctProvider.d.ts +8 -4
  4. package/CroctProvider.js +57 -46
  5. package/api.cjs +22 -0
  6. package/{src/react-app-env.d.ts → api.d.cts} +1 -0
  7. package/api.d.ts +1 -0
  8. package/api.js +1 -18
  9. package/components/Personalization/index.cjs +36 -0
  10. package/components/Personalization/index.d.cts +13 -0
  11. package/components/Personalization/index.d.ts +7 -4
  12. package/components/Personalization/index.js +10 -11
  13. package/components/Slot/index.cjs +36 -0
  14. package/components/Slot/index.d.cts +22 -0
  15. package/components/Slot/index.d.ts +9 -6
  16. package/components/Slot/index.js +10 -12
  17. package/components/index.cjs +24 -0
  18. package/components/index.d.cts +9 -0
  19. package/components/index.d.ts +9 -2
  20. package/components/index.js +2 -19
  21. package/global.d.cjs +1 -0
  22. package/{src/global.d.ts → global.d.d.cts} +1 -1
  23. package/global.d.d.ts +7 -0
  24. package/global.d.js +0 -0
  25. package/hash.cjs +36 -0
  26. package/hash.d.cts +2 -0
  27. package/hash.d.ts +2 -1
  28. package/hash.js +10 -11
  29. package/hooks/Cache.cjs +88 -0
  30. package/hooks/Cache.d.cts +9 -0
  31. package/hooks/Cache.d.ts +4 -17
  32. package/hooks/Cache.js +58 -56
  33. package/hooks/index.cjs +26 -0
  34. package/hooks/index.d.cts +8 -0
  35. package/hooks/index.d.ts +8 -3
  36. package/hooks/index.js +3 -20
  37. package/hooks/useContent.cjs +92 -0
  38. package/hooks/useContent.d.cts +20 -0
  39. package/hooks/useContent.d.ts +6 -5
  40. package/hooks/useContent.js +65 -42
  41. package/hooks/useCroct.cjs +38 -0
  42. package/hooks/useCroct.d.cts +5 -0
  43. package/hooks/useCroct.d.ts +4 -1
  44. package/hooks/useCroct.js +12 -12
  45. package/hooks/useEvaluation.cjs +85 -0
  46. package/hooks/useEvaluation.d.cts +14 -0
  47. package/hooks/useEvaluation.d.ts +5 -3
  48. package/hooks/useEvaluation.js +54 -45
  49. package/hooks/useLoader.cjs +82 -0
  50. package/hooks/useLoader.d.cts +7 -0
  51. package/hooks/useLoader.d.ts +5 -3
  52. package/hooks/useLoader.js +54 -59
  53. package/index.cjs +32 -0
  54. package/index.d.cts +13 -0
  55. package/index.d.ts +10 -3
  56. package/index.js +6 -23
  57. package/package.json +42 -11
  58. package/react-app-env.d.cjs +1 -0
  59. package/react-app-env.d.d.cts +2 -0
  60. package/react-app-env.d.d.ts +2 -0
  61. package/react-app-env.d.js +0 -0
  62. package/ssr-polyfills.cjs +86 -0
  63. package/ssr-polyfills.d.cts +2 -0
  64. package/ssr-polyfills.d.ts +2 -3
  65. package/ssr-polyfills.js +49 -64
  66. package/CroctProvider.js.map +0 -1
  67. package/api.js.map +0 -1
  68. package/components/Personalization/index.js.map +0 -1
  69. package/components/Slot/index.js.map +0 -1
  70. package/components/index.js.map +0 -1
  71. package/hash.js.map +0 -1
  72. package/hooks/Cache.js.map +0 -1
  73. package/hooks/index.js.map +0 -1
  74. package/hooks/useContent.js.map +0 -1
  75. package/hooks/useCroct.js.map +0 -1
  76. package/hooks/useEvaluation.js.map +0 -1
  77. package/hooks/useLoader.js.map +0 -1
  78. package/index.js.map +0 -1
  79. package/src/api.ts +0 -1
  80. package/src/components/index.ts +0 -2
  81. package/src/hash.test.ts +0 -22
  82. package/src/hash.ts +0 -12
  83. package/src/hooks/Cache.test.ts +0 -280
  84. package/src/hooks/Cache.ts +0 -97
  85. package/src/hooks/index.ts +0 -3
  86. package/src/hooks/useContent.ssr.test.ts +0 -23
  87. package/src/hooks/useContent.test.ts +0 -183
  88. package/src/hooks/useContent.ts +0 -107
  89. package/src/hooks/useCroct.ts +0 -16
  90. package/src/hooks/useEvaluation.ssr.test.ts +0 -23
  91. package/src/hooks/useEvaluation.test.ts +0 -180
  92. package/src/hooks/useEvaluation.ts +0 -94
  93. package/src/hooks/useLoader.test.ts +0 -407
  94. package/src/hooks/useLoader.ts +0 -84
  95. package/src/index.ts +0 -6
  96. package/src/ssr-polyfills.ssr.test.ts +0 -46
  97. package/src/ssr-polyfills.test.ts +0 -65
  98. package/src/ssr-polyfills.ts +0 -70
  99. package/ssr-polyfills.js.map +0 -1
@@ -1,97 +0,0 @@
1
- export type EntryLoader<R> = (...args: any) => Promise<R>;
2
-
3
- export type EntryOptions<R> = {
4
- cacheKey: string,
5
- loader: EntryLoader<R>,
6
- fallback?: R,
7
- expiration?: number,
8
- };
9
-
10
- type Entry<R = any> = {
11
- promise: Promise<any>,
12
- result?: R,
13
- dispose: () => void,
14
- timeout?: number,
15
- error?: any,
16
- };
17
-
18
- export class Cache {
19
- private readonly cache: Record<string, Entry> = {};
20
-
21
- private readonly defaultExpiration: number;
22
-
23
- public constructor(defaultExpiration: number) {
24
- this.defaultExpiration = defaultExpiration;
25
- }
26
-
27
- public load<R>(configuration: EntryOptions<R>): R {
28
- const {cacheKey, loader, fallback, expiration = this.defaultExpiration} = configuration;
29
-
30
- const cachedEntry = this.get<R>(cacheKey);
31
-
32
- if (cachedEntry !== undefined) {
33
- if (cachedEntry.error !== undefined) {
34
- if (fallback !== undefined) {
35
- return fallback;
36
- }
37
-
38
- throw cachedEntry.error;
39
- }
40
-
41
- if (cachedEntry.result !== undefined) {
42
- return cachedEntry.result;
43
- }
44
-
45
- throw cachedEntry.promise;
46
- }
47
-
48
- const entry: Entry<R> = {
49
- dispose: () => {
50
- if (entry.timeout !== undefined || expiration < 0) {
51
- return;
52
- }
53
-
54
- entry.timeout = window.setTimeout(
55
- (): void => {
56
- delete this.cache[cacheKey];
57
- },
58
- expiration,
59
- );
60
- },
61
- promise: loader()
62
- .then((result): R => {
63
- entry.result = result;
64
-
65
- return result;
66
- })
67
- .catch(error => {
68
- entry.error = error;
69
- })
70
- .finally(() => {
71
- entry.dispose();
72
- }),
73
- };
74
-
75
- this.cache[cacheKey] = entry;
76
-
77
- throw entry.promise;
78
- }
79
-
80
- public get<R>(cacheKey: string): Entry<R>|undefined {
81
- const entry = this.cache[cacheKey];
82
-
83
- if (entry === undefined) {
84
- return undefined;
85
- }
86
-
87
- if (entry.timeout !== undefined) {
88
- clearTimeout(entry.timeout);
89
-
90
- delete entry.timeout;
91
-
92
- entry.dispose();
93
- }
94
-
95
- return entry;
96
- }
97
- }
@@ -1,3 +0,0 @@
1
- export * from './useEvaluation';
2
- export * from './useContent';
3
- export * from './useCroct';
@@ -1,23 +0,0 @@
1
- import {renderHook} from '@testing-library/react';
2
- import {useContent} from './useContent';
3
-
4
- jest.mock(
5
- '../ssr-polyfills',
6
- () => ({
7
- __esModule: true,
8
- isSsr: (): boolean => true,
9
- }),
10
- );
11
-
12
- describe('useContent (SSR)', () => {
13
- it('should render the initial value on the server-side', () => {
14
- const {result} = renderHook(() => useContent('slot-id', {initial: 'foo'}));
15
-
16
- expect(result.current).toBe('foo');
17
- });
18
-
19
- it('should require an initial value for server-side rending', () => {
20
- expect(() => useContent('slot-id'))
21
- .toThrow('The initial content is required for server-side rendering (SSR).');
22
- });
23
- });
@@ -1,183 +0,0 @@
1
- import {renderHook, waitFor} from '@testing-library/react';
2
- import {Plug} from '@croct/plug';
3
- import {useCroct} from './useCroct';
4
- import {useLoader} from './useLoader';
5
- import {useContent} from './useContent';
6
- import {hash} from '../hash';
7
-
8
- jest.mock(
9
- './useCroct',
10
- () => ({
11
- useCroct: jest.fn(),
12
- }),
13
- );
14
-
15
- jest.mock(
16
- './useLoader',
17
- () => ({
18
- useLoader: jest.fn(),
19
- }),
20
- );
21
-
22
- describe('useContent (CSR)', () => {
23
- beforeEach(() => {
24
- jest.resetAllMocks();
25
- });
26
-
27
- it('should fetch the content', () => {
28
- const fetch: Plug['fetch'] = jest.fn().mockResolvedValue({
29
- content: {},
30
- });
31
-
32
- jest.mocked(useCroct).mockReturnValue({fetch: fetch} as Plug);
33
- jest.mocked(useLoader).mockReturnValue({
34
- title: 'foo',
35
- });
36
-
37
- const slotId = 'home-banner@1';
38
- const preferredLocale = 'en';
39
- const attributes = {example: 'value'};
40
- const cacheKey = 'unique';
41
-
42
- const {result} = renderHook(
43
- () => useContent<{title: string}>(slotId, {
44
- preferredLocale: preferredLocale,
45
- attributes: attributes,
46
- cacheKey: cacheKey,
47
- fallback: {
48
- title: 'error',
49
- },
50
- expiration: 50,
51
- }),
52
- );
53
-
54
- expect(useCroct).toHaveBeenCalled();
55
- expect(useLoader).toHaveBeenCalledWith({
56
- cacheKey: hash(`useContent:${cacheKey}:${slotId}:${preferredLocale}:${JSON.stringify(attributes)}`),
57
- fallback: {
58
- title: 'error',
59
- },
60
- expiration: 50,
61
- loader: expect.any(Function),
62
- });
63
-
64
- jest.mocked(useLoader)
65
- .mock
66
- .calls[0][0]
67
- .loader();
68
-
69
- expect(fetch).toHaveBeenCalledWith(slotId, {
70
- preferredLocale: 'en',
71
- attributes: attributes,
72
- });
73
-
74
- expect(result.current).toEqual({title: 'foo'});
75
- });
76
-
77
- it('should use the initial value when the cache key changes if the stale-while-loading flag is false', async () => {
78
- const key = {
79
- current: 'initial',
80
- };
81
-
82
- const fetch: Plug['fetch'] = jest.fn().mockResolvedValue({content: {}});
83
-
84
- jest.mocked(useCroct).mockReturnValue({fetch: fetch} as Plug);
85
-
86
- jest.mocked(useLoader).mockImplementation(
87
- () => ({title: key.current === 'initial' ? 'first' : 'second'}),
88
- );
89
-
90
- const slotId = 'home-banner@1';
91
-
92
- const {result, rerender} = renderHook(
93
- () => useContent<{title: string}>(slotId, {
94
- cacheKey: key.current,
95
- initial: {
96
- title: 'initial',
97
- },
98
- }),
99
- );
100
-
101
- expect(useCroct).toHaveBeenCalled();
102
-
103
- expect(useLoader).toHaveBeenCalledWith(expect.objectContaining({
104
- cacheKey: hash(`useContent:${key.current}:${slotId}::${JSON.stringify({})}`),
105
- initial: {
106
- title: 'initial',
107
- },
108
- }));
109
-
110
- await waitFor(() => expect(result.current).toEqual({title: 'first'}));
111
-
112
- key.current = 'next';
113
-
114
- rerender();
115
-
116
- expect(useLoader).toHaveBeenCalledWith(expect.objectContaining({
117
- cacheKey: hash(`useContent:${key.current}:${slotId}::${JSON.stringify({})}`),
118
- initial: {
119
- title: 'initial',
120
- },
121
- }));
122
-
123
- await waitFor(() => expect(result.current).toEqual({title: 'second'}));
124
- });
125
-
126
- it('should use the last fetched content as initial value if the stale-while-loading flag is true', async () => {
127
- const key = {
128
- current: 'initial',
129
- };
130
-
131
- const fetch: Plug['fetch'] = jest.fn().mockResolvedValue({content: {}});
132
-
133
- jest.mocked(useCroct).mockReturnValue({fetch: fetch} as Plug);
134
-
135
- const firstResult = {
136
- title: 'first',
137
- };
138
-
139
- const secondResult = {
140
- title: 'second',
141
- };
142
-
143
- jest.mocked(useLoader).mockImplementation(
144
- () => (key.current === 'initial' ? firstResult : secondResult),
145
- );
146
-
147
- const slotId = 'home-banner@1';
148
-
149
- const {result, rerender} = renderHook(
150
- () => useContent<{title: string}>(slotId, {
151
- cacheKey: key.current,
152
- initial: {
153
- title: 'initial',
154
- },
155
- staleWhileLoading: true,
156
- }),
157
- );
158
-
159
- expect(useCroct).toHaveBeenCalled();
160
-
161
- expect(useLoader).toHaveBeenCalledWith(expect.objectContaining({
162
- cacheKey: hash(`useContent:${key.current}:${slotId}::${JSON.stringify({})}`),
163
- initial: {
164
- title: 'initial',
165
- },
166
- }));
167
-
168
- await waitFor(() => expect(result.current).toEqual({title: 'first'}));
169
-
170
- key.current = 'next';
171
-
172
- rerender();
173
-
174
- expect(useLoader).toHaveBeenCalledWith(expect.objectContaining({
175
- cacheKey: hash(`useContent:${key.current}:${slotId}::${JSON.stringify({})}`),
176
- initial: {
177
- title: 'first',
178
- },
179
- }));
180
-
181
- await waitFor(() => expect(result.current).toEqual({title: 'second'}));
182
- });
183
- });
@@ -1,107 +0,0 @@
1
- import {SlotContent, VersionedSlotId, VersionedSlotMap} from '@croct/plug/slot';
2
- import {JsonObject} from '@croct/plug/sdk/json';
3
- import {FetchOptions} from '@croct/plug/plug';
4
- import {useEffect, useState} from 'react';
5
- import {useLoader} from './useLoader';
6
- import {useCroct} from './useCroct';
7
- import {isSsr} from '../ssr-polyfills';
8
- import {hash} from '../hash';
9
-
10
- export type UseContentOptions<I, F> = FetchOptions & {
11
- fallback?: F,
12
- initial?: I,
13
- cacheKey?: string,
14
- expiration?: number,
15
- staleWhileLoading?: boolean,
16
- };
17
-
18
- function useCsrContent<I, F>(
19
- id: VersionedSlotId,
20
- options: UseContentOptions<I, F> = {},
21
- ): SlotContent | I | F {
22
- const {
23
- fallback,
24
- cacheKey,
25
- expiration,
26
- initial: initialContent,
27
- staleWhileLoading = false,
28
- ...fetchOptions
29
- } = options;
30
-
31
- const [initial, setInitial] = useState<SlotContent | I | F | undefined>(initialContent);
32
- const {preferredLocale} = fetchOptions;
33
- const croct = useCroct();
34
-
35
- const result: SlotContent | I | F = useLoader({
36
- cacheKey: hash(
37
- `useContent:${cacheKey ?? ''}`
38
- + `:${id}`
39
- + `:${preferredLocale ?? ''}`
40
- + `:${JSON.stringify(fetchOptions.attributes ?? {})}`,
41
- ),
42
- loader: () => croct.fetch(id, fetchOptions).then(({content}) => content),
43
- initial: initial,
44
- fallback: fallback,
45
- expiration: expiration,
46
- });
47
-
48
- useEffect(
49
- () => {
50
- if (staleWhileLoading) {
51
- setInitial(current => {
52
- if (current !== result) {
53
- return result;
54
- }
55
-
56
- return current;
57
- });
58
- }
59
- },
60
- [result, staleWhileLoading],
61
- );
62
-
63
- return result;
64
- }
65
-
66
- function useSsrContent<I, F>(
67
- _: VersionedSlotId,
68
- {initial}: UseContentOptions<I, F> = {},
69
- ): SlotContent | I | F {
70
- if (initial === undefined) {
71
- throw new Error(
72
- 'The initial content is required for server-side rendering (SSR). '
73
- + 'For help, see https://croct.help/sdk/react/missing-slot-content',
74
- );
75
- }
76
-
77
- return initial;
78
- }
79
-
80
- type UseContentHook = {
81
- <P extends JsonObject, I = P, F = P>(
82
- id: keyof VersionedSlotMap extends never ? string : never,
83
- options?: UseContentOptions<I, F>
84
- ): P | I | F,
85
-
86
- <S extends VersionedSlotId>(
87
- id: S,
88
- options?: UseContentOptions<never, never>
89
- ): SlotContent<S>,
90
-
91
- <I, S extends VersionedSlotId>(
92
- id: S,
93
- options?: UseContentOptions<I, never>
94
- ): SlotContent<S> | I,
95
-
96
- <F, S extends VersionedSlotId>(
97
- id: S,
98
- options?: UseContentOptions<never, F>
99
- ): SlotContent<S> | F,
100
-
101
- <I, F, S extends VersionedSlotId>(
102
- id: S,
103
- options?: UseContentOptions<I, F>
104
- ): SlotContent<S> | I | F,
105
- };
106
-
107
- export const useContent: UseContentHook = isSsr() ? useSsrContent : useCsrContent;
@@ -1,16 +0,0 @@
1
- import {Plug} from '@croct/plug';
2
- import {useContext} from 'react';
3
- import {CroctContext} from '../CroctProvider';
4
-
5
- export function useCroct(): Plug {
6
- const context = useContext(CroctContext);
7
-
8
- if (context === null) {
9
- throw new Error(
10
- 'useCroct() can only be used in the context of a <CroctProvider> component. '
11
- + 'For help, see https://croct.help/sdk/react/missing-provider',
12
- );
13
- }
14
-
15
- return context.plug;
16
- }
@@ -1,23 +0,0 @@
1
- import {renderHook} from '@testing-library/react';
2
- import {useEvaluation} from './useEvaluation';
3
-
4
- jest.mock(
5
- '../ssr-polyfills',
6
- () => ({
7
- __esModule: true,
8
- isSsr: (): boolean => true,
9
- }),
10
- );
11
-
12
- describe('useEvaluation (SSR)', () => {
13
- it('should render the initial value on the server-side', () => {
14
- const {result} = renderHook(() => useEvaluation('location', {initial: 'foo'}));
15
-
16
- expect(result.current).toBe('foo');
17
- });
18
-
19
- it('should require an initial value for server-side rending', () => {
20
- expect(() => useEvaluation('location'))
21
- .toThrow('The initial value is required for server-side rendering (SSR).');
22
- });
23
- });
@@ -1,180 +0,0 @@
1
- import {renderHook, waitFor} from '@testing-library/react';
2
- import {EvaluationOptions} from '@croct/sdk/facade/evaluatorFacade';
3
- import {Plug} from '@croct/plug';
4
- import {useEvaluation} from './useEvaluation';
5
- import {useCroct} from './useCroct';
6
- import {useLoader} from './useLoader';
7
- import {hash} from '../hash';
8
-
9
- jest.mock(
10
- './useCroct',
11
- () => ({
12
- useCroct: jest.fn(),
13
- }),
14
- );
15
-
16
- jest.mock(
17
- './useLoader',
18
- () => ({
19
- useLoader: jest.fn(),
20
- }),
21
- );
22
-
23
- describe('useEvaluation', () => {
24
- beforeEach(() => {
25
- jest.resetAllMocks();
26
- });
27
-
28
- it('should evaluate a query', () => {
29
- const evaluationOptions: EvaluationOptions = {
30
- timeout: 100,
31
- attributes: {
32
- foo: 'bar',
33
- },
34
- };
35
-
36
- const evaluate: Plug['evaluate'] = jest.fn();
37
-
38
- jest.mocked(useCroct).mockReturnValue({evaluate: evaluate} as Plug);
39
- jest.mocked(useLoader).mockReturnValue('foo');
40
-
41
- const query = 'location';
42
- const cacheKey = 'unique';
43
-
44
- const {result} = renderHook(
45
- () => useEvaluation(query, {
46
- ...evaluationOptions,
47
- cacheKey: cacheKey,
48
- fallback: 'error',
49
- expiration: 50,
50
- }),
51
- );
52
-
53
- expect(useCroct).toHaveBeenCalled();
54
- expect(useLoader).toHaveBeenCalledWith({
55
- cacheKey: hash(`useEvaluation:${cacheKey}:${query}:${JSON.stringify(evaluationOptions.attributes)}`),
56
- fallback: 'error',
57
- expiration: 50,
58
- loader: expect.any(Function),
59
- });
60
-
61
- jest.mocked(useLoader)
62
- .mock
63
- .calls[0][0]
64
- .loader();
65
-
66
- expect(evaluate).toHaveBeenCalledWith(query, evaluationOptions);
67
-
68
- expect(result.current).toBe('foo');
69
- });
70
-
71
- it('should remove undefined evaluation options', () => {
72
- const evaluationOptions: EvaluationOptions = {
73
- timeout: undefined,
74
- attributes: undefined,
75
- };
76
-
77
- const evaluate: Plug['evaluate'] = jest.fn();
78
-
79
- jest.mocked(useCroct).mockReturnValue({evaluate: evaluate} as Plug);
80
- jest.mocked(useLoader).mockReturnValue('foo');
81
-
82
- const query = 'location';
83
-
84
- renderHook(() => useEvaluation(query, evaluationOptions));
85
-
86
- jest.mocked(useLoader)
87
- .mock
88
- .calls[0][0]
89
- .loader();
90
-
91
- expect(evaluate).toHaveBeenCalledWith(query, {});
92
- });
93
-
94
- it('should use the initial value when the cache key changes if the stale-while-loading flag is false', async () => {
95
- const key = {
96
- current: 'initial',
97
- };
98
-
99
- const evaluate: Plug['evaluate'] = jest.fn();
100
-
101
- jest.mocked(useCroct).mockReturnValue({evaluate: evaluate} as Plug);
102
-
103
- jest.mocked(useLoader).mockImplementation(
104
- () => (key.current === 'initial' ? 'first' : 'second'),
105
- );
106
-
107
- const query = 'location';
108
-
109
- const {result, rerender} = renderHook(
110
- () => useEvaluation(query, {
111
- cacheKey: key.current,
112
- initial: 'initial',
113
- }),
114
- );
115
-
116
- expect(useCroct).toHaveBeenCalled();
117
-
118
- expect(useLoader).toHaveBeenCalledWith(expect.objectContaining({
119
- cacheKey: hash(`useEvaluation:${key.current}:${query}:${JSON.stringify({})}`),
120
- initial: 'initial',
121
- }));
122
-
123
- await waitFor(() => expect(result.current).toEqual('first'));
124
-
125
- key.current = 'next';
126
-
127
- rerender();
128
-
129
- expect(useLoader).toHaveBeenCalledWith(expect.objectContaining({
130
- cacheKey: hash(`useEvaluation:${key.current}:${query}:${JSON.stringify({})}`),
131
- initial: 'initial',
132
- }));
133
-
134
- await waitFor(() => expect(result.current).toEqual('second'));
135
- });
136
-
137
- it('should use the last evaluation result if the stale-while-loading flag is true', async () => {
138
- const key = {
139
- current: 'initial',
140
- };
141
-
142
- const evaluate: Plug['evaluate'] = jest.fn();
143
-
144
- jest.mocked(useCroct).mockReturnValue({evaluate: evaluate} as Plug);
145
-
146
- jest.mocked(useLoader).mockImplementation(
147
- () => (key.current === 'initial' ? 'first' : 'second'),
148
- );
149
-
150
- const query = 'location';
151
-
152
- const {result, rerender} = renderHook(
153
- () => useEvaluation(query, {
154
- cacheKey: key.current,
155
- initial: 'initial',
156
- staleWhileLoading: true,
157
- }),
158
- );
159
-
160
- expect(useCroct).toHaveBeenCalled();
161
-
162
- expect(useLoader).toHaveBeenCalledWith(expect.objectContaining({
163
- cacheKey: hash(`useEvaluation:${key.current}:${query}:${JSON.stringify({})}`),
164
- initial: 'initial',
165
- }));
166
-
167
- await waitFor(() => expect(result.current).toEqual('first'));
168
-
169
- key.current = 'next';
170
-
171
- rerender();
172
-
173
- expect(useLoader).toHaveBeenCalledWith(expect.objectContaining({
174
- cacheKey: hash(`useEvaluation:${key.current}:${query}:${JSON.stringify({})}`),
175
- initial: 'first',
176
- }));
177
-
178
- await waitFor(() => expect(result.current).toEqual('second'));
179
- });
180
- });