@htlkg/data 0.0.1 → 0.0.3
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/README.md +53 -0
- package/dist/client/index.d.ts +117 -31
- package/dist/client/index.js +74 -22
- package/dist/client/index.js.map +1 -1
- package/dist/content-collections/index.js +20 -24
- package/dist/content-collections/index.js.map +1 -1
- package/dist/hooks/index.d.ts +113 -5
- package/dist/hooks/index.js +165 -182
- package/dist/hooks/index.js.map +1 -1
- package/dist/index.d.ts +8 -4
- package/dist/index.js +305 -182
- package/dist/index.js.map +1 -1
- package/dist/queries/index.d.ts +78 -1
- package/dist/queries/index.js +47 -0
- package/dist/queries/index.js.map +1 -1
- package/dist/stores/index.d.ts +106 -0
- package/dist/stores/index.js +108 -0
- package/dist/stores/index.js.map +1 -0
- package/package.json +60 -37
- package/src/client/__tests__/server.test.ts +100 -0
- package/src/client/client.md +91 -0
- package/src/client/index.test.ts +232 -0
- package/src/client/index.ts +145 -0
- package/src/client/server.ts +118 -0
- package/src/content-collections/content-collections.md +87 -0
- package/src/content-collections/generator.ts +314 -0
- package/src/content-collections/index.ts +32 -0
- package/src/content-collections/schemas.ts +75 -0
- package/src/content-collections/sync.ts +139 -0
- package/src/hooks/README.md +293 -0
- package/src/hooks/createDataHook.ts +208 -0
- package/src/hooks/data-hook-errors.property.test.ts +270 -0
- package/src/hooks/data-hook-filters.property.test.ts +263 -0
- package/src/hooks/data-hooks.property.test.ts +190 -0
- package/src/hooks/hooks.test.ts +76 -0
- package/src/hooks/index.ts +21 -0
- package/src/hooks/useAccounts.ts +66 -0
- package/src/hooks/useBrands.ts +95 -0
- package/src/hooks/useProducts.ts +88 -0
- package/src/hooks/useUsers.ts +89 -0
- package/src/index.ts +32 -0
- package/src/mutations/accounts.ts +127 -0
- package/src/mutations/brands.ts +133 -0
- package/src/mutations/index.ts +32 -0
- package/src/mutations/mutations.md +96 -0
- package/src/mutations/users.ts +136 -0
- package/src/queries/accounts.ts +121 -0
- package/src/queries/brands.ts +176 -0
- package/src/queries/index.ts +45 -0
- package/src/queries/products.ts +282 -0
- package/src/queries/queries.md +88 -0
- package/src/queries/server-helpers.ts +114 -0
- package/src/queries/users.ts +199 -0
- package/src/stores/createStores.ts +148 -0
- package/src/stores/index.ts +15 -0
- package/src/stores/stores.md +104 -0
|
@@ -0,0 +1,270 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Property-Based Tests for Data Hook Error Handling
|
|
3
|
+
*
|
|
4
|
+
* Feature: htlkg-modular-architecture, Property 7: Data hook errors are handled
|
|
5
|
+
* Validates: Requirements 11.5
|
|
6
|
+
*
|
|
7
|
+
* Tests that data hooks correctly handle errors during data fetching.
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
import { describe, it, expect, vi, beforeEach } from "vitest";
|
|
11
|
+
import * as fc from "fast-check";
|
|
12
|
+
import { useBrands } from "./useBrands";
|
|
13
|
+
import { useAccounts } from "./useAccounts";
|
|
14
|
+
import { useUsers } from "./useUsers";
|
|
15
|
+
import { useProducts } from "./useProducts";
|
|
16
|
+
|
|
17
|
+
// Create separate mock functions for each model
|
|
18
|
+
const mockBrandList = vi.fn();
|
|
19
|
+
const mockAccountList = vi.fn();
|
|
20
|
+
const mockUserList = vi.fn();
|
|
21
|
+
const mockProductList = vi.fn();
|
|
22
|
+
|
|
23
|
+
// Mock console.error to verify error logging
|
|
24
|
+
const mockConsoleError = vi.spyOn(console, "error").mockImplementation(() => {});
|
|
25
|
+
|
|
26
|
+
vi.mock("../client", () => ({
|
|
27
|
+
generateClient: vi.fn(() => ({
|
|
28
|
+
models: {
|
|
29
|
+
Brand: { list: mockBrandList },
|
|
30
|
+
Account: { list: mockAccountList },
|
|
31
|
+
User: { list: mockUserList },
|
|
32
|
+
Product: { list: mockProductList },
|
|
33
|
+
},
|
|
34
|
+
})),
|
|
35
|
+
}));
|
|
36
|
+
|
|
37
|
+
describe("Data Hook Error Handling - Property 7: Data hook errors are handled", () => {
|
|
38
|
+
beforeEach(() => {
|
|
39
|
+
vi.clearAllMocks();
|
|
40
|
+
mockConsoleError.mockClear();
|
|
41
|
+
});
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* Property: For any error during fetch, the error state should be set
|
|
45
|
+
* and the error should be logged
|
|
46
|
+
*/
|
|
47
|
+
it("useBrands handles fetch errors", async () => {
|
|
48
|
+
await fc.assert(
|
|
49
|
+
fc.asyncProperty(fc.string({ minLength: 1 }), async (errorMessage) => {
|
|
50
|
+
// Reset mocks
|
|
51
|
+
mockBrandList.mockClear();
|
|
52
|
+
mockConsoleError.mockClear();
|
|
53
|
+
|
|
54
|
+
// Setup mock to throw an error
|
|
55
|
+
const testError = new Error(errorMessage);
|
|
56
|
+
mockBrandList.mockRejectedValue(testError);
|
|
57
|
+
|
|
58
|
+
const { error, loading, refetch } = useBrands({ autoFetch: false });
|
|
59
|
+
|
|
60
|
+
// Initially no error
|
|
61
|
+
expect(error.value).toBeNull();
|
|
62
|
+
|
|
63
|
+
// Trigger fetch
|
|
64
|
+
await refetch();
|
|
65
|
+
|
|
66
|
+
// Wait for async operations
|
|
67
|
+
await new Promise((resolve) => setTimeout(resolve, 10));
|
|
68
|
+
|
|
69
|
+
// Error state should be set
|
|
70
|
+
expect(error.value).toBeInstanceOf(Error);
|
|
71
|
+
expect(error.value?.message).toBe(errorMessage);
|
|
72
|
+
|
|
73
|
+
// Loading should be false after error
|
|
74
|
+
expect(loading.value).toBe(false);
|
|
75
|
+
|
|
76
|
+
// Error should be logged
|
|
77
|
+
expect(mockConsoleError).toHaveBeenCalled();
|
|
78
|
+
expect(mockConsoleError.mock.calls[0][0]).toContain("[useBrands]");
|
|
79
|
+
}),
|
|
80
|
+
{ numRuns: 50 },
|
|
81
|
+
);
|
|
82
|
+
});
|
|
83
|
+
|
|
84
|
+
/**
|
|
85
|
+
* Property: For any GraphQL errors array, the first error message should be used
|
|
86
|
+
*/
|
|
87
|
+
it("useBrands handles GraphQL errors", async () => {
|
|
88
|
+
await fc.assert(
|
|
89
|
+
fc.asyncProperty(fc.string({ minLength: 1 }), async (errorMessage) => {
|
|
90
|
+
// Reset mocks
|
|
91
|
+
mockBrandList.mockClear();
|
|
92
|
+
mockConsoleError.mockClear();
|
|
93
|
+
|
|
94
|
+
// Setup mock to return GraphQL errors
|
|
95
|
+
mockBrandList.mockResolvedValue({
|
|
96
|
+
data: null,
|
|
97
|
+
errors: [{ message: errorMessage }],
|
|
98
|
+
});
|
|
99
|
+
|
|
100
|
+
const { error, loading, refetch } = useBrands({ autoFetch: false });
|
|
101
|
+
|
|
102
|
+
// Trigger fetch
|
|
103
|
+
await refetch();
|
|
104
|
+
|
|
105
|
+
// Wait for async operations
|
|
106
|
+
await new Promise((resolve) => setTimeout(resolve, 10));
|
|
107
|
+
|
|
108
|
+
// Error state should be set with the GraphQL error message
|
|
109
|
+
expect(error.value).toBeInstanceOf(Error);
|
|
110
|
+
expect(error.value?.message).toBe(errorMessage);
|
|
111
|
+
|
|
112
|
+
// Loading should be false after error
|
|
113
|
+
expect(loading.value).toBe(false);
|
|
114
|
+
|
|
115
|
+
// Error should be logged
|
|
116
|
+
expect(mockConsoleError).toHaveBeenCalled();
|
|
117
|
+
}),
|
|
118
|
+
{ numRuns: 50 },
|
|
119
|
+
);
|
|
120
|
+
});
|
|
121
|
+
|
|
122
|
+
/**
|
|
123
|
+
* Property: For any error during useAccounts fetch, error handling should work
|
|
124
|
+
*/
|
|
125
|
+
it("useAccounts handles fetch errors", async () => {
|
|
126
|
+
await fc.assert(
|
|
127
|
+
fc.asyncProperty(fc.string({ minLength: 1 }), async (errorMessage) => {
|
|
128
|
+
// Reset mocks
|
|
129
|
+
mockAccountList.mockClear();
|
|
130
|
+
mockConsoleError.mockClear();
|
|
131
|
+
|
|
132
|
+
// Setup mock to throw an error
|
|
133
|
+
const testError = new Error(errorMessage);
|
|
134
|
+
mockAccountList.mockRejectedValue(testError);
|
|
135
|
+
|
|
136
|
+
const { error, loading, refetch } = useAccounts({ autoFetch: false });
|
|
137
|
+
|
|
138
|
+
// Trigger fetch
|
|
139
|
+
await refetch();
|
|
140
|
+
|
|
141
|
+
// Wait for async operations
|
|
142
|
+
await new Promise((resolve) => setTimeout(resolve, 10));
|
|
143
|
+
|
|
144
|
+
// Error state should be set
|
|
145
|
+
expect(error.value).toBeInstanceOf(Error);
|
|
146
|
+
expect(error.value?.message).toBe(errorMessage);
|
|
147
|
+
|
|
148
|
+
// Loading should be false after error
|
|
149
|
+
expect(loading.value).toBe(false);
|
|
150
|
+
|
|
151
|
+
// Error should be logged
|
|
152
|
+
expect(mockConsoleError).toHaveBeenCalled();
|
|
153
|
+
expect(mockConsoleError.mock.calls[0][0]).toContain("[useAccounts]");
|
|
154
|
+
}),
|
|
155
|
+
{ numRuns: 50 },
|
|
156
|
+
);
|
|
157
|
+
});
|
|
158
|
+
|
|
159
|
+
/**
|
|
160
|
+
* Property: For any error during useUsers fetch, error handling should work
|
|
161
|
+
*/
|
|
162
|
+
it("useUsers handles fetch errors", async () => {
|
|
163
|
+
await fc.assert(
|
|
164
|
+
fc.asyncProperty(fc.string({ minLength: 1 }), async (errorMessage) => {
|
|
165
|
+
// Reset mocks
|
|
166
|
+
mockUserList.mockClear();
|
|
167
|
+
mockConsoleError.mockClear();
|
|
168
|
+
|
|
169
|
+
// Setup mock to throw an error
|
|
170
|
+
const testError = new Error(errorMessage);
|
|
171
|
+
mockUserList.mockRejectedValue(testError);
|
|
172
|
+
|
|
173
|
+
const { error, loading, refetch } = useUsers({ autoFetch: false });
|
|
174
|
+
|
|
175
|
+
// Trigger fetch
|
|
176
|
+
await refetch();
|
|
177
|
+
|
|
178
|
+
// Wait for async operations
|
|
179
|
+
await new Promise((resolve) => setTimeout(resolve, 10));
|
|
180
|
+
|
|
181
|
+
// Error state should be set
|
|
182
|
+
expect(error.value).toBeInstanceOf(Error);
|
|
183
|
+
expect(error.value?.message).toBe(errorMessage);
|
|
184
|
+
|
|
185
|
+
// Loading should be false after error
|
|
186
|
+
expect(loading.value).toBe(false);
|
|
187
|
+
|
|
188
|
+
// Error should be logged
|
|
189
|
+
expect(mockConsoleError).toHaveBeenCalled();
|
|
190
|
+
expect(mockConsoleError.mock.calls[0][0]).toContain("[useUsers]");
|
|
191
|
+
}),
|
|
192
|
+
{ numRuns: 50 },
|
|
193
|
+
);
|
|
194
|
+
});
|
|
195
|
+
|
|
196
|
+
/**
|
|
197
|
+
* Property: For any error during useProducts fetch, error handling should work
|
|
198
|
+
*/
|
|
199
|
+
it("useProducts handles fetch errors", async () => {
|
|
200
|
+
await fc.assert(
|
|
201
|
+
fc.asyncProperty(fc.string({ minLength: 1 }), async (errorMessage) => {
|
|
202
|
+
// Reset mocks
|
|
203
|
+
mockProductList.mockClear();
|
|
204
|
+
mockConsoleError.mockClear();
|
|
205
|
+
|
|
206
|
+
// Setup mock to throw an error
|
|
207
|
+
const testError = new Error(errorMessage);
|
|
208
|
+
mockProductList.mockRejectedValue(testError);
|
|
209
|
+
|
|
210
|
+
const { error, loading, refetch } = useProducts({ autoFetch: false });
|
|
211
|
+
|
|
212
|
+
// Trigger fetch
|
|
213
|
+
await refetch();
|
|
214
|
+
|
|
215
|
+
// Wait for async operations
|
|
216
|
+
await new Promise((resolve) => setTimeout(resolve, 10));
|
|
217
|
+
|
|
218
|
+
// Error state should be set
|
|
219
|
+
expect(error.value).toBeInstanceOf(Error);
|
|
220
|
+
expect(error.value?.message).toBe(errorMessage);
|
|
221
|
+
|
|
222
|
+
// Loading should be false after error
|
|
223
|
+
expect(loading.value).toBe(false);
|
|
224
|
+
|
|
225
|
+
// Error should be logged
|
|
226
|
+
expect(mockConsoleError).toHaveBeenCalled();
|
|
227
|
+
expect(mockConsoleError.mock.calls[0][0]).toContain("[useProducts]");
|
|
228
|
+
}),
|
|
229
|
+
{ numRuns: 50 },
|
|
230
|
+
);
|
|
231
|
+
});
|
|
232
|
+
|
|
233
|
+
/**
|
|
234
|
+
* Property: After an error, refetch should clear the error and try again
|
|
235
|
+
*/
|
|
236
|
+
it("refetch clears previous errors", async () => {
|
|
237
|
+
await fc.assert(
|
|
238
|
+
fc.asyncProperty(
|
|
239
|
+
fc.string({ minLength: 1 }),
|
|
240
|
+
fc.array(fc.record({ id: fc.string(), name: fc.string() })),
|
|
241
|
+
async (errorMessage, successData) => {
|
|
242
|
+
// Reset mocks
|
|
243
|
+
mockBrandList.mockClear();
|
|
244
|
+
mockConsoleError.mockClear();
|
|
245
|
+
|
|
246
|
+
// First call fails
|
|
247
|
+
mockBrandList.mockRejectedValueOnce(new Error(errorMessage));
|
|
248
|
+
// Second call succeeds
|
|
249
|
+
mockBrandList.mockResolvedValueOnce({ data: successData, errors: null });
|
|
250
|
+
|
|
251
|
+
const { error, brands, refetch } = useBrands({ autoFetch: false });
|
|
252
|
+
|
|
253
|
+
// First fetch - should error
|
|
254
|
+
await refetch();
|
|
255
|
+
await new Promise((resolve) => setTimeout(resolve, 10));
|
|
256
|
+
|
|
257
|
+
expect(error.value).toBeInstanceOf(Error);
|
|
258
|
+
|
|
259
|
+
// Second fetch - should succeed and clear error
|
|
260
|
+
await refetch();
|
|
261
|
+
await new Promise((resolve) => setTimeout(resolve, 10));
|
|
262
|
+
|
|
263
|
+
expect(error.value).toBeNull();
|
|
264
|
+
expect(brands.value).toEqual(successData);
|
|
265
|
+
},
|
|
266
|
+
),
|
|
267
|
+
{ numRuns: 30 },
|
|
268
|
+
);
|
|
269
|
+
});
|
|
270
|
+
});
|
|
@@ -0,0 +1,263 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Property-Based Tests for Data Hook Filters
|
|
3
|
+
*
|
|
4
|
+
* Feature: htlkg-modular-architecture, Property 6: Data hook filters are applied
|
|
5
|
+
* Validates: Requirements 11.4
|
|
6
|
+
*
|
|
7
|
+
* Tests that data hooks correctly apply filter options to GraphQL queries.
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
import { describe, it, expect, vi, beforeEach, afterEach } from "vitest";
|
|
11
|
+
import * as fc from "fast-check";
|
|
12
|
+
import { useBrands } from "./useBrands";
|
|
13
|
+
import { useAccounts } from "./useAccounts";
|
|
14
|
+
import { useUsers } from "./useUsers";
|
|
15
|
+
import { useProducts } from "./useProducts";
|
|
16
|
+
import type { Brand, Account, Product } from "@htlkg/core/types";
|
|
17
|
+
|
|
18
|
+
// Create separate mock functions for each model
|
|
19
|
+
const mockBrandList = vi.fn();
|
|
20
|
+
const mockAccountList = vi.fn();
|
|
21
|
+
const mockUserList = vi.fn();
|
|
22
|
+
const mockProductList = vi.fn();
|
|
23
|
+
|
|
24
|
+
vi.mock("../client", () => ({
|
|
25
|
+
generateClient: vi.fn(() => ({
|
|
26
|
+
models: {
|
|
27
|
+
Brand: { list: mockBrandList },
|
|
28
|
+
Account: { list: mockAccountList },
|
|
29
|
+
User: { list: mockUserList },
|
|
30
|
+
Product: { list: mockProductList },
|
|
31
|
+
},
|
|
32
|
+
})),
|
|
33
|
+
}));
|
|
34
|
+
|
|
35
|
+
describe("Data Hook Filters - Property 6: Data hook filters are applied", () => {
|
|
36
|
+
beforeEach(() => {
|
|
37
|
+
vi.clearAllMocks();
|
|
38
|
+
mockBrandList.mockResolvedValue({ data: [], errors: null });
|
|
39
|
+
mockAccountList.mockResolvedValue({ data: [], errors: null });
|
|
40
|
+
mockUserList.mockResolvedValue({ data: [], errors: null });
|
|
41
|
+
mockProductList.mockResolvedValue({ data: [], errors: null });
|
|
42
|
+
});
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* Property: For any filter options, the hook should pass those filters
|
|
46
|
+
* to the GraphQL query
|
|
47
|
+
*/
|
|
48
|
+
it("useBrands applies filter options to GraphQL query", async () => {
|
|
49
|
+
await fc.assert(
|
|
50
|
+
fc.asyncProperty(
|
|
51
|
+
fc.record({
|
|
52
|
+
accountId: fc.option(fc.string({ minLength: 1 }), { nil: undefined }),
|
|
53
|
+
activeOnly: fc.boolean(),
|
|
54
|
+
limit: fc.option(fc.integer({ min: 1, max: 100 }), { nil: undefined }),
|
|
55
|
+
}),
|
|
56
|
+
async (options) => {
|
|
57
|
+
// Reset mock before each property test iteration
|
|
58
|
+
mockBrandList.mockClear();
|
|
59
|
+
|
|
60
|
+
// Call hook with filter options
|
|
61
|
+
const { refetch } = useBrands({ ...options, autoFetch: false });
|
|
62
|
+
await refetch();
|
|
63
|
+
|
|
64
|
+
// Verify the mock was called
|
|
65
|
+
expect(mockBrandList).toHaveBeenCalledTimes(1);
|
|
66
|
+
|
|
67
|
+
const callArgs = mockBrandList.mock.calls[0][0];
|
|
68
|
+
|
|
69
|
+
// Check that limit is passed correctly
|
|
70
|
+
if (options.limit !== undefined) {
|
|
71
|
+
expect(callArgs.limit).toBe(options.limit);
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
// Check that filters are built correctly
|
|
75
|
+
if (callArgs.filter) {
|
|
76
|
+
if (options.accountId) {
|
|
77
|
+
expect(callArgs.filter.accountId).toEqual({ eq: options.accountId });
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
if (options.activeOnly) {
|
|
81
|
+
expect(callArgs.filter.status).toEqual({ eq: "active" });
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
},
|
|
85
|
+
),
|
|
86
|
+
{ numRuns: 50 },
|
|
87
|
+
);
|
|
88
|
+
});
|
|
89
|
+
|
|
90
|
+
/**
|
|
91
|
+
* Property: For any filter, the hook should only return items matching the filter
|
|
92
|
+
*/
|
|
93
|
+
it("useBrands returns only filtered brands", async () => {
|
|
94
|
+
await fc.assert(
|
|
95
|
+
fc.asyncProperty(
|
|
96
|
+
fc.array(
|
|
97
|
+
fc.record({
|
|
98
|
+
id: fc.string(),
|
|
99
|
+
name: fc.string(),
|
|
100
|
+
accountId: fc.string(),
|
|
101
|
+
status: fc.constantFrom("active", "inactive", "maintenance", "suspended"),
|
|
102
|
+
timezone: fc.string(),
|
|
103
|
+
settings: fc.constant({}),
|
|
104
|
+
}),
|
|
105
|
+
{ minLength: 0, maxLength: 20 },
|
|
106
|
+
),
|
|
107
|
+
fc.constantFrom("active", "inactive", "maintenance", "suspended"),
|
|
108
|
+
async (mockBrands, filterStatus) => {
|
|
109
|
+
// Reset mock before each property test iteration
|
|
110
|
+
mockBrandList.mockClear();
|
|
111
|
+
mockBrandList.mockResolvedValue({ data: mockBrands, errors: null });
|
|
112
|
+
|
|
113
|
+
// Call hook with activeOnly filter
|
|
114
|
+
const { brands, refetch } = useBrands({
|
|
115
|
+
activeOnly: filterStatus === "active",
|
|
116
|
+
autoFetch: false,
|
|
117
|
+
});
|
|
118
|
+
|
|
119
|
+
await refetch();
|
|
120
|
+
|
|
121
|
+
// Wait for the hook to update
|
|
122
|
+
await new Promise((resolve) => setTimeout(resolve, 10));
|
|
123
|
+
|
|
124
|
+
// If activeOnly is true, verify all returned brands are active
|
|
125
|
+
if (filterStatus === "active") {
|
|
126
|
+
// The hook should have requested active brands
|
|
127
|
+
const callArgs = mockBrandList.mock.calls[0][0];
|
|
128
|
+
if (callArgs.filter) {
|
|
129
|
+
expect(callArgs.filter.status).toEqual({ eq: "active" });
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
},
|
|
133
|
+
),
|
|
134
|
+
{ numRuns: 50 },
|
|
135
|
+
);
|
|
136
|
+
});
|
|
137
|
+
|
|
138
|
+
/**
|
|
139
|
+
* Property: For any accountId filter, only brands from that account should be requested
|
|
140
|
+
*/
|
|
141
|
+
it("useBrands filters by accountId", async () => {
|
|
142
|
+
await fc.assert(
|
|
143
|
+
fc.asyncProperty(
|
|
144
|
+
fc.string({ minLength: 1 }),
|
|
145
|
+
async (accountId) => {
|
|
146
|
+
// Reset mock before each property test iteration
|
|
147
|
+
mockBrandList.mockClear();
|
|
148
|
+
|
|
149
|
+
const { refetch } = useBrands({ accountId, autoFetch: false });
|
|
150
|
+
await refetch();
|
|
151
|
+
|
|
152
|
+
expect(mockBrandList).toHaveBeenCalledTimes(1);
|
|
153
|
+
const callArgs = mockBrandList.mock.calls[0][0];
|
|
154
|
+
|
|
155
|
+
// Verify accountId filter is applied
|
|
156
|
+
expect(callArgs.filter).toBeDefined();
|
|
157
|
+
expect(callArgs.filter.accountId).toEqual({ eq: accountId });
|
|
158
|
+
},
|
|
159
|
+
),
|
|
160
|
+
{ numRuns: 50 },
|
|
161
|
+
);
|
|
162
|
+
});
|
|
163
|
+
|
|
164
|
+
/**
|
|
165
|
+
* Property: For any filter options on useAccounts, filters should be passed to query
|
|
166
|
+
*/
|
|
167
|
+
it("useAccounts applies filter options to GraphQL query", async () => {
|
|
168
|
+
await fc.assert(
|
|
169
|
+
fc.asyncProperty(
|
|
170
|
+
fc.record({
|
|
171
|
+
filter: fc.option(
|
|
172
|
+
fc.record({
|
|
173
|
+
status: fc.record({ eq: fc.string() }),
|
|
174
|
+
}),
|
|
175
|
+
{ nil: undefined },
|
|
176
|
+
),
|
|
177
|
+
limit: fc.option(fc.integer({ min: 1, max: 100 }), { nil: undefined }),
|
|
178
|
+
}),
|
|
179
|
+
async (options) => {
|
|
180
|
+
// Reset mock before each property test iteration
|
|
181
|
+
mockAccountList.mockClear();
|
|
182
|
+
|
|
183
|
+
const { refetch } = useAccounts({ ...options, autoFetch: false });
|
|
184
|
+
await refetch();
|
|
185
|
+
|
|
186
|
+
expect(mockAccountList).toHaveBeenCalledTimes(1);
|
|
187
|
+
const callArgs = mockAccountList.mock.calls[0][0];
|
|
188
|
+
|
|
189
|
+
// Check that limit is passed correctly
|
|
190
|
+
if (options.limit !== undefined) {
|
|
191
|
+
expect(callArgs.limit).toBe(options.limit);
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
// Check that filter is passed correctly
|
|
195
|
+
if (options.filter !== undefined) {
|
|
196
|
+
expect(callArgs.filter).toEqual(options.filter);
|
|
197
|
+
}
|
|
198
|
+
},
|
|
199
|
+
),
|
|
200
|
+
{ numRuns: 50 },
|
|
201
|
+
);
|
|
202
|
+
});
|
|
203
|
+
|
|
204
|
+
/**
|
|
205
|
+
* Property: For any brandId or accountId filter on useUsers, filters should be applied
|
|
206
|
+
*/
|
|
207
|
+
it("useUsers applies brandId and accountId filters", async () => {
|
|
208
|
+
await fc.assert(
|
|
209
|
+
fc.asyncProperty(
|
|
210
|
+
fc.record({
|
|
211
|
+
brandId: fc.option(fc.string({ minLength: 1 }), { nil: undefined }),
|
|
212
|
+
accountId: fc.option(fc.string({ minLength: 1 }), { nil: undefined }),
|
|
213
|
+
}),
|
|
214
|
+
async (options) => {
|
|
215
|
+
// Reset mock before each property test iteration
|
|
216
|
+
mockUserList.mockClear();
|
|
217
|
+
|
|
218
|
+
const { refetch } = useUsers({ ...options, autoFetch: false });
|
|
219
|
+
await refetch();
|
|
220
|
+
|
|
221
|
+
expect(mockUserList).toHaveBeenCalledTimes(1);
|
|
222
|
+
const callArgs = mockUserList.mock.calls[0][0];
|
|
223
|
+
|
|
224
|
+
// Check that filters are built correctly
|
|
225
|
+
if (callArgs.filter) {
|
|
226
|
+
if (options.brandId) {
|
|
227
|
+
expect(callArgs.filter.brandIds).toEqual({ contains: options.brandId });
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
if (options.accountId) {
|
|
231
|
+
expect(callArgs.filter.accountIds).toEqual({ contains: options.accountId });
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
},
|
|
235
|
+
),
|
|
236
|
+
{ numRuns: 50 },
|
|
237
|
+
);
|
|
238
|
+
});
|
|
239
|
+
|
|
240
|
+
/**
|
|
241
|
+
* Property: For activeOnly filter on useProducts, only active products should be requested
|
|
242
|
+
*/
|
|
243
|
+
it("useProducts applies activeOnly filter", async () => {
|
|
244
|
+
await fc.assert(
|
|
245
|
+
fc.asyncProperty(fc.boolean(), async (activeOnly) => {
|
|
246
|
+
// Reset mock before each property test iteration
|
|
247
|
+
mockProductList.mockClear();
|
|
248
|
+
|
|
249
|
+
const { refetch } = useProducts({ activeOnly, autoFetch: false });
|
|
250
|
+
await refetch();
|
|
251
|
+
|
|
252
|
+
expect(mockProductList).toHaveBeenCalledTimes(1);
|
|
253
|
+
const callArgs = mockProductList.mock.calls[0][0];
|
|
254
|
+
|
|
255
|
+
// If activeOnly is true, verify filter is applied
|
|
256
|
+
if (activeOnly && callArgs.filter) {
|
|
257
|
+
expect(callArgs.filter.isActive).toEqual({ eq: true });
|
|
258
|
+
}
|
|
259
|
+
}),
|
|
260
|
+
{ numRuns: 50 },
|
|
261
|
+
);
|
|
262
|
+
});
|
|
263
|
+
});
|