@hubspot/ui-extensions 0.9.3 → 0.9.5
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/__tests__/experimental/crm/fetchAssociations.spec.d.ts +1 -0
- package/dist/__tests__/experimental/crm/fetchAssociations.spec.js +442 -0
- package/dist/__tests__/experimental/crm/fetchCrmProperties.spec.js +1 -0
- package/dist/__tests__/experimental/hooks/useAssociations.spec.d.ts +1 -0
- package/dist/__tests__/experimental/hooks/useAssociations.spec.js +419 -0
- package/dist/coreComponents.d.ts +12 -0
- package/dist/coreComponents.js +8 -0
- package/dist/experimental/crm/fetchAssociations.d.ts +45 -0
- package/dist/experimental/crm/fetchAssociations.js +64 -0
- package/dist/experimental/crm/fetchCrmProperties.d.ts +1 -0
- package/dist/experimental/crm/fetchCrmProperties.js +18 -13
- package/dist/experimental/hooks/useAssociations.d.ts +22 -0
- package/dist/experimental/hooks/useAssociations.js +176 -0
- package/dist/experimental/hooks/useCrmProperties.js +42 -20
- package/dist/experimental/index.d.ts +20 -9
- package/dist/experimental/index.js +12 -5
- package/dist/experimental/types.d.ts +21 -54
- package/dist/types.d.ts +68 -1
- package/dist/types.js +1 -0
- package/package.json +2 -2
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,442 @@
|
|
|
1
|
+
// Set up the mock before importing the module
|
|
2
|
+
const mockFetchAssociations = jest.fn();
|
|
3
|
+
const mockSelf = {
|
|
4
|
+
fetchAssociations: mockFetchAssociations,
|
|
5
|
+
};
|
|
6
|
+
// Mock the global self object
|
|
7
|
+
Object.defineProperty(global, 'self', {
|
|
8
|
+
value: mockSelf,
|
|
9
|
+
writable: true,
|
|
10
|
+
});
|
|
11
|
+
import { fetchAssociations, pageToOffset, offsetToPage, calculatePaginationFlags, } from '../../../experimental/crm/fetchAssociations';
|
|
12
|
+
describe('fetchAssociations', () => {
|
|
13
|
+
// Helper functions
|
|
14
|
+
const createMockResponse = (data, overrides = {}) => ({
|
|
15
|
+
ok: true,
|
|
16
|
+
json: jest.fn().mockResolvedValue({ data, cleanup: jest.fn() }),
|
|
17
|
+
...overrides,
|
|
18
|
+
});
|
|
19
|
+
const createErrorResponse = (statusText) => ({
|
|
20
|
+
ok: false,
|
|
21
|
+
statusText,
|
|
22
|
+
json: jest.fn().mockResolvedValue({}),
|
|
23
|
+
});
|
|
24
|
+
const expectError = async (request, expectedMessage) => {
|
|
25
|
+
await expect(fetchAssociations(request)).rejects.toThrow(expectedMessage);
|
|
26
|
+
};
|
|
27
|
+
const createBasicRequest = (overrides = {}) => ({
|
|
28
|
+
toObjectType: '0-1',
|
|
29
|
+
properties: ['firstname', 'lastname'],
|
|
30
|
+
...overrides,
|
|
31
|
+
});
|
|
32
|
+
beforeEach(() => {
|
|
33
|
+
jest.clearAllMocks();
|
|
34
|
+
// Set up dynamic mock that responds to request parameters
|
|
35
|
+
mockFetchAssociations.mockImplementation((request) => {
|
|
36
|
+
const { pageLength = 100, offset = 0, properties = [] } = request;
|
|
37
|
+
// Generate mock properties based on the request
|
|
38
|
+
const mockProperties = {};
|
|
39
|
+
properties.forEach((prop) => {
|
|
40
|
+
mockProperties[prop] = `Mock ${prop} (page ${Math.floor(offset / pageLength) + 1})`;
|
|
41
|
+
});
|
|
42
|
+
const mockResults = [
|
|
43
|
+
{
|
|
44
|
+
toObjectId: 1000 + offset,
|
|
45
|
+
associationTypes: [
|
|
46
|
+
{ category: 'HUBSPOT_DEFINED', typeId: 1, label: 'Primary' },
|
|
47
|
+
],
|
|
48
|
+
properties: mockProperties,
|
|
49
|
+
},
|
|
50
|
+
{
|
|
51
|
+
toObjectId: 1001 + offset,
|
|
52
|
+
associationTypes: [
|
|
53
|
+
{
|
|
54
|
+
category: 'USER_DEFINED',
|
|
55
|
+
typeId: 100,
|
|
56
|
+
label: 'Custom Association',
|
|
57
|
+
},
|
|
58
|
+
],
|
|
59
|
+
properties: mockProperties,
|
|
60
|
+
},
|
|
61
|
+
];
|
|
62
|
+
const mockData = {
|
|
63
|
+
results: mockResults,
|
|
64
|
+
hasMore: offset < 200,
|
|
65
|
+
nextOffset: offset + pageLength,
|
|
66
|
+
};
|
|
67
|
+
return Promise.resolve(createMockResponse(mockData));
|
|
68
|
+
});
|
|
69
|
+
});
|
|
70
|
+
describe('basic functionality', () => {
|
|
71
|
+
it('should return mock associations with default parameters', async () => {
|
|
72
|
+
const request = createBasicRequest();
|
|
73
|
+
const result = await fetchAssociations(request);
|
|
74
|
+
expect(result).toHaveProperty('data');
|
|
75
|
+
expect(result).toHaveProperty('cleanup');
|
|
76
|
+
expect(typeof result.cleanup).toBe('function');
|
|
77
|
+
const { data } = result;
|
|
78
|
+
expect(data.results).toHaveLength(2);
|
|
79
|
+
expect(data.hasMore).toBe(true);
|
|
80
|
+
expect(data.nextOffset).toBe(100);
|
|
81
|
+
// Check first association
|
|
82
|
+
expect(data.results[0]).toEqual({
|
|
83
|
+
toObjectId: 1000,
|
|
84
|
+
associationTypes: [
|
|
85
|
+
{ category: 'HUBSPOT_DEFINED', typeId: 1, label: 'Primary' },
|
|
86
|
+
],
|
|
87
|
+
properties: {
|
|
88
|
+
firstname: 'Mock firstname (page 1)',
|
|
89
|
+
lastname: 'Mock lastname (page 1)',
|
|
90
|
+
},
|
|
91
|
+
});
|
|
92
|
+
// Check second association
|
|
93
|
+
expect(data.results[1]).toEqual({
|
|
94
|
+
toObjectId: 1001,
|
|
95
|
+
associationTypes: [
|
|
96
|
+
{
|
|
97
|
+
category: 'USER_DEFINED',
|
|
98
|
+
typeId: 100,
|
|
99
|
+
label: 'Custom Association',
|
|
100
|
+
},
|
|
101
|
+
],
|
|
102
|
+
properties: {
|
|
103
|
+
firstname: 'Mock firstname (page 1)',
|
|
104
|
+
lastname: 'Mock lastname (page 1)',
|
|
105
|
+
},
|
|
106
|
+
});
|
|
107
|
+
});
|
|
108
|
+
it('should return a cleanup function', async () => {
|
|
109
|
+
const request = createBasicRequest({ properties: ['firstname'] });
|
|
110
|
+
const result = await fetchAssociations(request);
|
|
111
|
+
expect(typeof result.cleanup).toBe('function');
|
|
112
|
+
// Should not throw when called
|
|
113
|
+
expect(() => result.cleanup()).not.toThrow();
|
|
114
|
+
});
|
|
115
|
+
it('should return a Promise', () => {
|
|
116
|
+
const request = createBasicRequest({ properties: ['firstname'] });
|
|
117
|
+
const result = fetchAssociations(request);
|
|
118
|
+
expect(result).toBeInstanceOf(Promise);
|
|
119
|
+
});
|
|
120
|
+
});
|
|
121
|
+
describe('pagination handling', () => {
|
|
122
|
+
it('should handle custom page length and offset', async () => {
|
|
123
|
+
const request = createBasicRequest({
|
|
124
|
+
properties: ['email'],
|
|
125
|
+
pageLength: 25,
|
|
126
|
+
offset: 50,
|
|
127
|
+
});
|
|
128
|
+
const result = await fetchAssociations(request);
|
|
129
|
+
const { data } = result;
|
|
130
|
+
expect(data.results).toHaveLength(2);
|
|
131
|
+
expect(data.nextOffset).toBe(75); // 50 + 25
|
|
132
|
+
expect(data.hasMore).toBe(true);
|
|
133
|
+
// Check that offset is reflected in object IDs
|
|
134
|
+
expect(data.results[0].toObjectId).toBe(1050); // 1000 + 50
|
|
135
|
+
expect(data.results[1].toObjectId).toBe(1051); // 1001 + 50
|
|
136
|
+
// Check that page number is reflected in properties
|
|
137
|
+
expect(data.results[0].properties.email).toBe('Mock email (page 3)'); // Math.floor(50 / 25) + 1 = 3
|
|
138
|
+
});
|
|
139
|
+
it('should calculate hasMore correctly based on offset', async () => {
|
|
140
|
+
// Test when there are more results
|
|
141
|
+
const request1 = createBasicRequest({
|
|
142
|
+
properties: ['firstname'],
|
|
143
|
+
offset: 100,
|
|
144
|
+
});
|
|
145
|
+
const result1 = await fetchAssociations(request1);
|
|
146
|
+
expect(result1.data.hasMore).toBe(true);
|
|
147
|
+
// Test when there are no more results
|
|
148
|
+
const request2 = createBasicRequest({
|
|
149
|
+
properties: ['firstname'],
|
|
150
|
+
offset: 200,
|
|
151
|
+
});
|
|
152
|
+
const result2 = await fetchAssociations(request2);
|
|
153
|
+
expect(result2.data.hasMore).toBe(false);
|
|
154
|
+
// Test edge case at the boundary
|
|
155
|
+
const request3 = createBasicRequest({
|
|
156
|
+
properties: ['firstname'],
|
|
157
|
+
offset: 199,
|
|
158
|
+
});
|
|
159
|
+
const result3 = await fetchAssociations(request3);
|
|
160
|
+
expect(result3.data.hasMore).toBe(true);
|
|
161
|
+
});
|
|
162
|
+
it('should generate different page numbers for different offsets', async () => {
|
|
163
|
+
const request1 = createBasicRequest({
|
|
164
|
+
properties: ['firstname'],
|
|
165
|
+
pageLength: 10,
|
|
166
|
+
offset: 0,
|
|
167
|
+
});
|
|
168
|
+
const result1 = await fetchAssociations(request1);
|
|
169
|
+
expect(result1.data.results[0].properties.firstname).toBe('Mock firstname (page 1)');
|
|
170
|
+
const request2 = createBasicRequest({
|
|
171
|
+
properties: ['firstname'],
|
|
172
|
+
pageLength: 10,
|
|
173
|
+
offset: 20,
|
|
174
|
+
});
|
|
175
|
+
const result2 = await fetchAssociations(request2);
|
|
176
|
+
expect(result2.data.results[0].properties.firstname).toBe('Mock firstname (page 3)'); // Math.floor(20 / 10) + 1 = 3
|
|
177
|
+
});
|
|
178
|
+
});
|
|
179
|
+
describe('properties handling', () => {
|
|
180
|
+
it('should handle empty properties array', async () => {
|
|
181
|
+
const request = createBasicRequest({ properties: [] });
|
|
182
|
+
const result = await fetchAssociations(request);
|
|
183
|
+
const { data } = result;
|
|
184
|
+
expect(data.results).toHaveLength(2);
|
|
185
|
+
expect(data.results[0].properties).toEqual({});
|
|
186
|
+
expect(data.results[1].properties).toEqual({});
|
|
187
|
+
});
|
|
188
|
+
it('should handle missing properties in request', async () => {
|
|
189
|
+
const request = createBasicRequest();
|
|
190
|
+
// Remove properties to test missing case
|
|
191
|
+
delete request.properties;
|
|
192
|
+
const result = await fetchAssociations(request);
|
|
193
|
+
const { data } = result;
|
|
194
|
+
expect(data.results).toHaveLength(2);
|
|
195
|
+
expect(data.results[0].properties).toEqual({});
|
|
196
|
+
expect(data.results[1].properties).toEqual({});
|
|
197
|
+
});
|
|
198
|
+
it('should handle multiple properties correctly', async () => {
|
|
199
|
+
const request = createBasicRequest({
|
|
200
|
+
properties: ['firstname', 'lastname', 'email', 'phone'],
|
|
201
|
+
});
|
|
202
|
+
const result = await fetchAssociations(request);
|
|
203
|
+
const { data } = result;
|
|
204
|
+
expect(data.results[0].properties).toEqual({
|
|
205
|
+
firstname: 'Mock firstname (page 1)',
|
|
206
|
+
lastname: 'Mock lastname (page 1)',
|
|
207
|
+
email: 'Mock email (page 1)',
|
|
208
|
+
phone: 'Mock phone (page 1)',
|
|
209
|
+
});
|
|
210
|
+
});
|
|
211
|
+
it('should handle different object types', async () => {
|
|
212
|
+
const request = createBasicRequest({
|
|
213
|
+
toObjectType: '0-2',
|
|
214
|
+
properties: ['name'],
|
|
215
|
+
});
|
|
216
|
+
const result = await fetchAssociations(request);
|
|
217
|
+
const { data } = result;
|
|
218
|
+
expect(data.results).toHaveLength(2);
|
|
219
|
+
expect(data.results[0].properties.name).toBe('Mock name (page 1)');
|
|
220
|
+
});
|
|
221
|
+
});
|
|
222
|
+
describe('error handling', () => {
|
|
223
|
+
it('should throw an error when response is not OK', async () => {
|
|
224
|
+
mockFetchAssociations.mockResolvedValueOnce(createErrorResponse('Not Found'));
|
|
225
|
+
const request = createBasicRequest({ properties: ['firstname'] });
|
|
226
|
+
await expectError(request, 'Failed to fetch associations: Not Found');
|
|
227
|
+
});
|
|
228
|
+
it('should throw an error when fetch fails', async () => {
|
|
229
|
+
mockFetchAssociations.mockRejectedValueOnce(new Error('Network error'));
|
|
230
|
+
const request = createBasicRequest({ properties: ['firstname'] });
|
|
231
|
+
await expectError(request, 'Network error');
|
|
232
|
+
});
|
|
233
|
+
it('should handle unknown errors', async () => {
|
|
234
|
+
mockFetchAssociations.mockRejectedValueOnce('Unknown error');
|
|
235
|
+
const request = createBasicRequest({ properties: ['firstname'] });
|
|
236
|
+
await expectError(request, 'Failed to fetch associations: Unknown error');
|
|
237
|
+
});
|
|
238
|
+
});
|
|
239
|
+
describe('response validation', () => {
|
|
240
|
+
it('should throw error for invalid response format - missing results', async () => {
|
|
241
|
+
const invalidData = { hasMore: true, nextOffset: 0 }; // missing results
|
|
242
|
+
mockFetchAssociations.mockResolvedValueOnce(createMockResponse(invalidData));
|
|
243
|
+
const request = createBasicRequest({ properties: ['firstname'] });
|
|
244
|
+
await expectError(request, 'Invalid response format');
|
|
245
|
+
});
|
|
246
|
+
it('should throw error for invalid response format - null data', async () => {
|
|
247
|
+
mockFetchAssociations.mockResolvedValueOnce(createMockResponse(null));
|
|
248
|
+
const request = createBasicRequest({ properties: ['firstname'] });
|
|
249
|
+
await expectError(request, 'Invalid response format');
|
|
250
|
+
});
|
|
251
|
+
it('should throw error for invalid association result structure', async () => {
|
|
252
|
+
const invalidData = {
|
|
253
|
+
results: [{ invalidStructure: true }],
|
|
254
|
+
hasMore: false,
|
|
255
|
+
nextOffset: 0,
|
|
256
|
+
};
|
|
257
|
+
mockFetchAssociations.mockResolvedValueOnce(createMockResponse(invalidData));
|
|
258
|
+
const request = createBasicRequest({ properties: ['firstname'] });
|
|
259
|
+
await expectError(request, 'Invalid response format');
|
|
260
|
+
});
|
|
261
|
+
it('should throw error for invalid association types array', async () => {
|
|
262
|
+
const invalidData = {
|
|
263
|
+
results: [
|
|
264
|
+
{
|
|
265
|
+
toObjectId: 1000,
|
|
266
|
+
associationTypes: 'not an array',
|
|
267
|
+
properties: {},
|
|
268
|
+
},
|
|
269
|
+
],
|
|
270
|
+
hasMore: false,
|
|
271
|
+
nextOffset: 0,
|
|
272
|
+
};
|
|
273
|
+
mockFetchAssociations.mockResolvedValueOnce(createMockResponse(invalidData));
|
|
274
|
+
const request = createBasicRequest({ properties: ['firstname'] });
|
|
275
|
+
await expectError(request, 'Invalid response format');
|
|
276
|
+
});
|
|
277
|
+
it('should throw error for missing hasMore field', async () => {
|
|
278
|
+
const invalidData = {
|
|
279
|
+
results: [],
|
|
280
|
+
nextOffset: 0, // missing hasMore
|
|
281
|
+
};
|
|
282
|
+
mockFetchAssociations.mockResolvedValueOnce(createMockResponse(invalidData));
|
|
283
|
+
const request = createBasicRequest({ properties: ['firstname'] });
|
|
284
|
+
await expectError(request, 'Invalid response format');
|
|
285
|
+
});
|
|
286
|
+
});
|
|
287
|
+
describe('API integration', () => {
|
|
288
|
+
it('should call self.fetchAssociations with correct parameters', async () => {
|
|
289
|
+
const emptyData = { results: [], hasMore: false, nextOffset: 0 };
|
|
290
|
+
mockFetchAssociations.mockResolvedValueOnce(createMockResponse(emptyData));
|
|
291
|
+
const request = createBasicRequest({
|
|
292
|
+
properties: ['firstname'],
|
|
293
|
+
pageLength: 50,
|
|
294
|
+
});
|
|
295
|
+
await fetchAssociations(request);
|
|
296
|
+
expect(mockFetchAssociations).toHaveBeenCalledWith(request, undefined);
|
|
297
|
+
});
|
|
298
|
+
it('should return cleanup function from API response', async () => {
|
|
299
|
+
const mockCleanup = jest.fn();
|
|
300
|
+
const emptyData = { results: [], hasMore: false, nextOffset: 0 };
|
|
301
|
+
const mockApiResponse = { data: emptyData, cleanup: mockCleanup };
|
|
302
|
+
mockFetchAssociations.mockResolvedValueOnce({
|
|
303
|
+
ok: true,
|
|
304
|
+
json: jest.fn().mockResolvedValue(mockApiResponse),
|
|
305
|
+
});
|
|
306
|
+
const result = await fetchAssociations(createBasicRequest());
|
|
307
|
+
expect(result.cleanup).toBe(mockCleanup);
|
|
308
|
+
result.cleanup();
|
|
309
|
+
expect(mockCleanup).toHaveBeenCalledTimes(1);
|
|
310
|
+
});
|
|
311
|
+
it('should provide default cleanup function when none provided', async () => {
|
|
312
|
+
const emptyData = { results: [], hasMore: false, nextOffset: 0 };
|
|
313
|
+
const mockApiResponse = { data: emptyData }; // no cleanup function provided
|
|
314
|
+
mockFetchAssociations.mockResolvedValueOnce({
|
|
315
|
+
ok: true,
|
|
316
|
+
json: jest.fn().mockResolvedValue(mockApiResponse),
|
|
317
|
+
});
|
|
318
|
+
const result = await fetchAssociations(createBasicRequest());
|
|
319
|
+
expect(typeof result.cleanup).toBe('function');
|
|
320
|
+
expect(() => result.cleanup()).not.toThrow();
|
|
321
|
+
});
|
|
322
|
+
it('should successfully fetch associations with valid response', async () => {
|
|
323
|
+
const validData = {
|
|
324
|
+
results: [
|
|
325
|
+
{
|
|
326
|
+
toObjectId: 2000,
|
|
327
|
+
associationTypes: [
|
|
328
|
+
{ category: 'HUBSPOT_DEFINED', typeId: 1, label: 'Primary' },
|
|
329
|
+
],
|
|
330
|
+
properties: {
|
|
331
|
+
firstname: 'John',
|
|
332
|
+
lastname: 'Doe',
|
|
333
|
+
},
|
|
334
|
+
},
|
|
335
|
+
],
|
|
336
|
+
hasMore: true,
|
|
337
|
+
nextOffset: 100,
|
|
338
|
+
};
|
|
339
|
+
mockFetchAssociations.mockResolvedValueOnce(createMockResponse(validData));
|
|
340
|
+
const request = createBasicRequest();
|
|
341
|
+
const result = await fetchAssociations(request);
|
|
342
|
+
expect(mockFetchAssociations).toHaveBeenCalledWith(request, undefined);
|
|
343
|
+
expect(result.data).toEqual(validData);
|
|
344
|
+
expect(typeof result.cleanup).toBe('function');
|
|
345
|
+
});
|
|
346
|
+
});
|
|
347
|
+
describe('options handling', () => {
|
|
348
|
+
it('should pass formatting options to the underlying fetch function', async () => {
|
|
349
|
+
const emptyData = { results: [], hasMore: false, nextOffset: 0 };
|
|
350
|
+
mockFetchAssociations.mockResolvedValueOnce(createMockResponse(emptyData));
|
|
351
|
+
const options = {
|
|
352
|
+
propertiesToFormat: ['firstname'],
|
|
353
|
+
formattingOptions: {
|
|
354
|
+
date: { format: 'MM/dd/yyyy' },
|
|
355
|
+
currency: { addSymbol: true },
|
|
356
|
+
},
|
|
357
|
+
};
|
|
358
|
+
const request = createBasicRequest();
|
|
359
|
+
await fetchAssociations(request, options);
|
|
360
|
+
expect(mockFetchAssociations).toHaveBeenCalledWith(request, options);
|
|
361
|
+
});
|
|
362
|
+
it('should handle propertiesToFormat set to "all"', async () => {
|
|
363
|
+
const emptyData = { results: [], hasMore: false, nextOffset: 0 };
|
|
364
|
+
mockFetchAssociations.mockResolvedValueOnce(createMockResponse(emptyData));
|
|
365
|
+
const options = {
|
|
366
|
+
propertiesToFormat: 'all',
|
|
367
|
+
formattingOptions: {
|
|
368
|
+
dateTime: { relative: true },
|
|
369
|
+
},
|
|
370
|
+
};
|
|
371
|
+
const request = createBasicRequest();
|
|
372
|
+
await fetchAssociations(request, options);
|
|
373
|
+
expect(mockFetchAssociations).toHaveBeenCalledWith(request, options);
|
|
374
|
+
});
|
|
375
|
+
it('should preserve error handling with formatting options', async () => {
|
|
376
|
+
mockFetchAssociations.mockRejectedValueOnce(new Error('Network error'));
|
|
377
|
+
const options = {
|
|
378
|
+
propertiesToFormat: ['firstname'],
|
|
379
|
+
formattingOptions: {
|
|
380
|
+
date: { format: 'MM/dd/yyyy' },
|
|
381
|
+
},
|
|
382
|
+
};
|
|
383
|
+
const request = createBasicRequest({ properties: ['firstname'] });
|
|
384
|
+
await expect(fetchAssociations(request, options)).rejects.toThrow('Network error');
|
|
385
|
+
expect(mockFetchAssociations).toHaveBeenCalledWith(request, options);
|
|
386
|
+
});
|
|
387
|
+
it('should preserve response validation with formatting options', async () => {
|
|
388
|
+
mockFetchAssociations.mockResolvedValueOnce(createMockResponse('Invalid response')); // data should be an object
|
|
389
|
+
const options = {
|
|
390
|
+
propertiesToFormat: 'all',
|
|
391
|
+
};
|
|
392
|
+
const request = createBasicRequest({ properties: ['firstname'] });
|
|
393
|
+
await expect(fetchAssociations(request, options)).rejects.toThrow('Invalid response format');
|
|
394
|
+
});
|
|
395
|
+
});
|
|
396
|
+
describe('pagination utilities', () => {
|
|
397
|
+
describe('pageToOffset', () => {
|
|
398
|
+
it('should convert page numbers to offsets', () => {
|
|
399
|
+
expect(pageToOffset(1, 10)).toBe(0);
|
|
400
|
+
expect(pageToOffset(3, 10)).toBe(20);
|
|
401
|
+
expect(pageToOffset(2, 25)).toBe(25);
|
|
402
|
+
});
|
|
403
|
+
it('should always return 0 for page 1', () => {
|
|
404
|
+
expect(pageToOffset(1, 10)).toBe(0);
|
|
405
|
+
expect(pageToOffset(1, 50)).toBe(0);
|
|
406
|
+
});
|
|
407
|
+
});
|
|
408
|
+
describe('offsetToPage', () => {
|
|
409
|
+
it('should convert offsets to page numbers', () => {
|
|
410
|
+
expect(offsetToPage(0, 10)).toBe(1);
|
|
411
|
+
expect(offsetToPage(20, 10)).toBe(3);
|
|
412
|
+
expect(offsetToPage(15, 10)).toBe(2); // partial page
|
|
413
|
+
});
|
|
414
|
+
it('should always return 1 for offset 0', () => {
|
|
415
|
+
expect(offsetToPage(0, 10)).toBe(1);
|
|
416
|
+
expect(offsetToPage(0, 25)).toBe(1);
|
|
417
|
+
});
|
|
418
|
+
});
|
|
419
|
+
describe('calculatePaginationFlags', () => {
|
|
420
|
+
it('should calculate pagination flags correctly', () => {
|
|
421
|
+
// Page 1 scenarios
|
|
422
|
+
expect(calculatePaginationFlags(1, true)).toEqual({
|
|
423
|
+
hasNextPage: true,
|
|
424
|
+
hasPreviousPage: false,
|
|
425
|
+
});
|
|
426
|
+
expect(calculatePaginationFlags(1, false)).toEqual({
|
|
427
|
+
hasNextPage: false,
|
|
428
|
+
hasPreviousPage: false,
|
|
429
|
+
});
|
|
430
|
+
// Page 2+ scenarios
|
|
431
|
+
expect(calculatePaginationFlags(2, true)).toEqual({
|
|
432
|
+
hasNextPage: true,
|
|
433
|
+
hasPreviousPage: true,
|
|
434
|
+
});
|
|
435
|
+
expect(calculatePaginationFlags(2, false)).toEqual({
|
|
436
|
+
hasNextPage: false,
|
|
437
|
+
hasPreviousPage: true,
|
|
438
|
+
});
|
|
439
|
+
});
|
|
440
|
+
});
|
|
441
|
+
});
|
|
442
|
+
});
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|