@sylphx/lens-solid 1.2.22 → 1.2.28
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/package.json +3 -3
- package/src/primitives.test.ts +455 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@sylphx/lens-solid",
|
|
3
|
-
"version": "1.2.
|
|
3
|
+
"version": "1.2.28",
|
|
4
4
|
"description": "SolidJS bindings for Lens API framework",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.js",
|
|
@@ -12,7 +12,7 @@
|
|
|
12
12
|
}
|
|
13
13
|
},
|
|
14
14
|
"scripts": {
|
|
15
|
-
"build": "bun build ./src/index.ts --outdir ./dist --target browser --external solid-js && tsc --emitDeclarationOnly --declaration --outDir ./dist",
|
|
15
|
+
"build": "bun build ./src/index.ts --outdir ./dist --target browser --external solid-js && bunx tsc --emitDeclarationOnly --declaration --outDir ./dist",
|
|
16
16
|
"typecheck": "tsc --noEmit",
|
|
17
17
|
"test": "bun test",
|
|
18
18
|
"prepack": "[ -d dist ] || bun run build"
|
|
@@ -31,7 +31,7 @@
|
|
|
31
31
|
"author": "SylphxAI",
|
|
32
32
|
"license": "MIT",
|
|
33
33
|
"dependencies": {
|
|
34
|
-
"@sylphx/lens-client": "^1.
|
|
34
|
+
"@sylphx/lens-client": "^1.15.0"
|
|
35
35
|
},
|
|
36
36
|
"peerDependencies": {
|
|
37
37
|
"solid-js": ">=1.8.0"
|
package/src/primitives.test.ts
CHANGED
|
@@ -182,3 +182,458 @@ describe("primitive functions", () => {
|
|
|
182
182
|
expect(query.loading()).toBe(false);
|
|
183
183
|
});
|
|
184
184
|
});
|
|
185
|
+
|
|
186
|
+
// =============================================================================
|
|
187
|
+
// Tests: createQuery - Comprehensive Coverage
|
|
188
|
+
// =============================================================================
|
|
189
|
+
|
|
190
|
+
describe("createQuery comprehensive", () => {
|
|
191
|
+
test("createQuery handles successful query with subscribe and then", async () => {
|
|
192
|
+
let _subscribeCallback: ((value: string) => void) | null = null;
|
|
193
|
+
let thenResolve: ((value: string) => void) | null = null;
|
|
194
|
+
|
|
195
|
+
const mockQueryResult = {
|
|
196
|
+
subscribe: (callback: (value: string) => void) => {
|
|
197
|
+
_subscribeCallback = callback;
|
|
198
|
+
return () => {
|
|
199
|
+
_subscribeCallback = null;
|
|
200
|
+
};
|
|
201
|
+
},
|
|
202
|
+
then: (resolve: (value: string) => void) => {
|
|
203
|
+
thenResolve = resolve;
|
|
204
|
+
},
|
|
205
|
+
};
|
|
206
|
+
|
|
207
|
+
const query = createQuery(mockQueryResult as any);
|
|
208
|
+
|
|
209
|
+
// Initially loading
|
|
210
|
+
expect(query.loading()).toBe(true);
|
|
211
|
+
expect(query.data()).toBe(null);
|
|
212
|
+
|
|
213
|
+
// Simulate promise resolution
|
|
214
|
+
if (thenResolve) {
|
|
215
|
+
thenResolve("test-data");
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
// Wait for next tick
|
|
219
|
+
await new Promise((resolve) => setTimeout(resolve, 0));
|
|
220
|
+
|
|
221
|
+
expect(query.data()).toBe("test-data");
|
|
222
|
+
expect(query.loading()).toBe(false);
|
|
223
|
+
expect(query.error()).toBe(null);
|
|
224
|
+
});
|
|
225
|
+
|
|
226
|
+
test("createQuery handles query error with non-Error object", async () => {
|
|
227
|
+
let thenReject: ((err: any) => void) | null = null;
|
|
228
|
+
|
|
229
|
+
const mockQueryResult = {
|
|
230
|
+
subscribe: () => () => {},
|
|
231
|
+
then: (_resolve: any, reject: (err: any) => void) => {
|
|
232
|
+
thenReject = reject;
|
|
233
|
+
},
|
|
234
|
+
};
|
|
235
|
+
|
|
236
|
+
const query = createQuery(mockQueryResult as any);
|
|
237
|
+
|
|
238
|
+
// Simulate promise rejection with non-Error
|
|
239
|
+
if (thenReject) {
|
|
240
|
+
thenReject("string error");
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
// Wait for next tick
|
|
244
|
+
await new Promise((resolve) => setTimeout(resolve, 0));
|
|
245
|
+
|
|
246
|
+
expect(query.loading()).toBe(false);
|
|
247
|
+
expect(query.error()).toBeInstanceOf(Error);
|
|
248
|
+
expect(query.error()?.message).toBe("string error");
|
|
249
|
+
});
|
|
250
|
+
|
|
251
|
+
test("createQuery handles query error with Error object", async () => {
|
|
252
|
+
let thenReject: ((err: any) => void) | null = null;
|
|
253
|
+
|
|
254
|
+
const mockQueryResult = {
|
|
255
|
+
subscribe: () => () => {},
|
|
256
|
+
then: (_resolve: any, reject: (err: any) => void) => {
|
|
257
|
+
thenReject = reject;
|
|
258
|
+
},
|
|
259
|
+
};
|
|
260
|
+
|
|
261
|
+
const query = createQuery(mockQueryResult as any);
|
|
262
|
+
|
|
263
|
+
const testError = new Error("test error");
|
|
264
|
+
if (thenReject) {
|
|
265
|
+
thenReject(testError);
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
// Wait for next tick
|
|
269
|
+
await new Promise((resolve) => setTimeout(resolve, 0));
|
|
270
|
+
|
|
271
|
+
expect(query.loading()).toBe(false);
|
|
272
|
+
expect(query.error()).toBe(testError);
|
|
273
|
+
});
|
|
274
|
+
|
|
275
|
+
test("createQuery refetch re-executes query", async () => {
|
|
276
|
+
let subscribeCount = 0;
|
|
277
|
+
let unsubscribeCalled = false;
|
|
278
|
+
let _thenResolve: ((value: string) => void) | null = null;
|
|
279
|
+
|
|
280
|
+
const mockQueryResult = {
|
|
281
|
+
subscribe: (callback: (value: string) => void) => {
|
|
282
|
+
subscribeCount++;
|
|
283
|
+
setTimeout(() => callback(`data-${subscribeCount}`), 0);
|
|
284
|
+
return () => {
|
|
285
|
+
unsubscribeCalled = true;
|
|
286
|
+
};
|
|
287
|
+
},
|
|
288
|
+
then: (resolve: (value: string) => void) => {
|
|
289
|
+
_thenResolve = resolve;
|
|
290
|
+
setTimeout(() => resolve(`then-${subscribeCount}`), 0);
|
|
291
|
+
},
|
|
292
|
+
};
|
|
293
|
+
|
|
294
|
+
const query = createQuery(mockQueryResult as any);
|
|
295
|
+
|
|
296
|
+
// Wait for initial load
|
|
297
|
+
await new Promise((resolve) => setTimeout(resolve, 10));
|
|
298
|
+
|
|
299
|
+
expect(subscribeCount).toBe(1);
|
|
300
|
+
|
|
301
|
+
// Call refetch
|
|
302
|
+
query.refetch();
|
|
303
|
+
|
|
304
|
+
expect(query.loading()).toBe(true);
|
|
305
|
+
expect(query.error()).toBe(null);
|
|
306
|
+
expect(unsubscribeCalled).toBe(true);
|
|
307
|
+
|
|
308
|
+
// Wait for refetch to complete
|
|
309
|
+
await new Promise((resolve) => setTimeout(resolve, 10));
|
|
310
|
+
|
|
311
|
+
expect(subscribeCount).toBe(2);
|
|
312
|
+
});
|
|
313
|
+
|
|
314
|
+
test("createQuery with skip option does not execute", () => {
|
|
315
|
+
let subscribed = false;
|
|
316
|
+
|
|
317
|
+
const mockQueryResult = {
|
|
318
|
+
subscribe: () => {
|
|
319
|
+
subscribed = true;
|
|
320
|
+
return () => {};
|
|
321
|
+
},
|
|
322
|
+
then: () => {},
|
|
323
|
+
};
|
|
324
|
+
|
|
325
|
+
const query = createQuery(mockQueryResult as any, { skip: true });
|
|
326
|
+
|
|
327
|
+
expect(query.loading()).toBe(false);
|
|
328
|
+
expect(query.data()).toBe(null);
|
|
329
|
+
expect(query.error()).toBe(null);
|
|
330
|
+
expect(subscribed).toBe(false);
|
|
331
|
+
});
|
|
332
|
+
|
|
333
|
+
test("createQuery with null query does not execute", () => {
|
|
334
|
+
const query = createQuery(null);
|
|
335
|
+
|
|
336
|
+
expect(query.loading()).toBe(false);
|
|
337
|
+
expect(query.data()).toBe(null);
|
|
338
|
+
expect(query.error()).toBe(null);
|
|
339
|
+
});
|
|
340
|
+
|
|
341
|
+
test("createQuery with undefined query does not execute", () => {
|
|
342
|
+
const query = createQuery(undefined);
|
|
343
|
+
|
|
344
|
+
expect(query.loading()).toBe(false);
|
|
345
|
+
expect(query.data()).toBe(null);
|
|
346
|
+
expect(query.error()).toBe(null);
|
|
347
|
+
});
|
|
348
|
+
|
|
349
|
+
test("createQuery with accessor function returning null", () => {
|
|
350
|
+
const query = createQuery(() => null);
|
|
351
|
+
|
|
352
|
+
expect(query.loading()).toBe(false);
|
|
353
|
+
expect(query.data()).toBe(null);
|
|
354
|
+
expect(query.error()).toBe(null);
|
|
355
|
+
});
|
|
356
|
+
|
|
357
|
+
test("createQuery with accessor function returning query", async () => {
|
|
358
|
+
let thenResolve: ((value: string) => void) | null = null;
|
|
359
|
+
|
|
360
|
+
const mockQueryResult = {
|
|
361
|
+
subscribe: () => () => {},
|
|
362
|
+
then: (resolve: (value: string) => void) => {
|
|
363
|
+
thenResolve = resolve;
|
|
364
|
+
},
|
|
365
|
+
};
|
|
366
|
+
|
|
367
|
+
const query = createQuery(() => mockQueryResult as any);
|
|
368
|
+
|
|
369
|
+
if (thenResolve) {
|
|
370
|
+
thenResolve("accessor-data");
|
|
371
|
+
}
|
|
372
|
+
|
|
373
|
+
await new Promise((resolve) => setTimeout(resolve, 0));
|
|
374
|
+
|
|
375
|
+
expect(query.data()).toBe("accessor-data");
|
|
376
|
+
});
|
|
377
|
+
|
|
378
|
+
test("createQuery sets up unsubscribe on query execution", () => {
|
|
379
|
+
let unsubscribeCalled = false;
|
|
380
|
+
|
|
381
|
+
const mockQueryResult = {
|
|
382
|
+
subscribe: () => {
|
|
383
|
+
return () => {
|
|
384
|
+
unsubscribeCalled = true;
|
|
385
|
+
};
|
|
386
|
+
},
|
|
387
|
+
then: () => {},
|
|
388
|
+
};
|
|
389
|
+
|
|
390
|
+
// Create query which will call subscribe and store unsubscribe
|
|
391
|
+
createQuery(mockQueryResult as any);
|
|
392
|
+
|
|
393
|
+
// The unsubscribe function exists and will be called on cleanup
|
|
394
|
+
// We can't directly test onCleanup without a full SolidJS environment
|
|
395
|
+
// but we verify the subscribe function was called and returns an unsubscribe
|
|
396
|
+
expect(unsubscribeCalled).toBe(false); // Not called yet until cleanup
|
|
397
|
+
});
|
|
398
|
+
});
|
|
399
|
+
|
|
400
|
+
// =============================================================================
|
|
401
|
+
// Tests: createMutation - Comprehensive Coverage
|
|
402
|
+
// =============================================================================
|
|
403
|
+
|
|
404
|
+
describe("createMutation comprehensive", () => {
|
|
405
|
+
test("createMutation handles successful mutation", async () => {
|
|
406
|
+
const mockMutation = async (input: { name: string }) => ({
|
|
407
|
+
data: { id: "123", name: input.name },
|
|
408
|
+
});
|
|
409
|
+
|
|
410
|
+
const mutation = createMutation(mockMutation);
|
|
411
|
+
|
|
412
|
+
expect(mutation.loading()).toBe(false);
|
|
413
|
+
|
|
414
|
+
const promise = mutation.mutate({ name: "test" });
|
|
415
|
+
|
|
416
|
+
expect(mutation.loading()).toBe(true);
|
|
417
|
+
|
|
418
|
+
const result = await promise;
|
|
419
|
+
|
|
420
|
+
expect(result.data.id).toBe("123");
|
|
421
|
+
expect(result.data.name).toBe("test");
|
|
422
|
+
expect(mutation.data()?.id).toBe("123");
|
|
423
|
+
expect(mutation.loading()).toBe(false);
|
|
424
|
+
expect(mutation.error()).toBe(null);
|
|
425
|
+
});
|
|
426
|
+
|
|
427
|
+
test("createMutation handles Error rejection", async () => {
|
|
428
|
+
const testError = new Error("mutation failed");
|
|
429
|
+
const mockMutation = async () => {
|
|
430
|
+
throw testError;
|
|
431
|
+
};
|
|
432
|
+
|
|
433
|
+
const mutation = createMutation(mockMutation);
|
|
434
|
+
|
|
435
|
+
try {
|
|
436
|
+
await mutation.mutate({} as any);
|
|
437
|
+
expect(true).toBe(false); // Should not reach
|
|
438
|
+
} catch (err) {
|
|
439
|
+
expect(err).toBe(testError);
|
|
440
|
+
expect(mutation.error()).toBe(testError);
|
|
441
|
+
expect(mutation.loading()).toBe(false);
|
|
442
|
+
}
|
|
443
|
+
});
|
|
444
|
+
|
|
445
|
+
test("createMutation handles non-Error rejection", async () => {
|
|
446
|
+
const mockMutation = async () => {
|
|
447
|
+
throw "string error";
|
|
448
|
+
};
|
|
449
|
+
|
|
450
|
+
const mutation = createMutation(mockMutation);
|
|
451
|
+
|
|
452
|
+
try {
|
|
453
|
+
await mutation.mutate({} as any);
|
|
454
|
+
expect(true).toBe(false); // Should not reach
|
|
455
|
+
} catch (err) {
|
|
456
|
+
expect(err).toBeInstanceOf(Error);
|
|
457
|
+
expect((err as Error).message).toBe("string error");
|
|
458
|
+
expect(mutation.error()?.message).toBe("string error");
|
|
459
|
+
expect(mutation.loading()).toBe(false);
|
|
460
|
+
}
|
|
461
|
+
});
|
|
462
|
+
|
|
463
|
+
test("createMutation reset clears all state after mutation", async () => {
|
|
464
|
+
const mockMutation = async (input: { name: string }) => ({
|
|
465
|
+
data: { id: "123", name: input.name },
|
|
466
|
+
});
|
|
467
|
+
|
|
468
|
+
const mutation = createMutation(mockMutation);
|
|
469
|
+
|
|
470
|
+
await mutation.mutate({ name: "test" });
|
|
471
|
+
|
|
472
|
+
expect(mutation.data()).not.toBe(null);
|
|
473
|
+
|
|
474
|
+
mutation.reset();
|
|
475
|
+
|
|
476
|
+
expect(mutation.data()).toBe(null);
|
|
477
|
+
expect(mutation.loading()).toBe(false);
|
|
478
|
+
expect(mutation.error()).toBe(null);
|
|
479
|
+
});
|
|
480
|
+
|
|
481
|
+
test("createMutation clears error on new mutation", async () => {
|
|
482
|
+
let shouldFail = true;
|
|
483
|
+
const mockMutation = async (input: { name: string }) => {
|
|
484
|
+
if (shouldFail) {
|
|
485
|
+
throw new Error("first error");
|
|
486
|
+
}
|
|
487
|
+
return { data: { id: "123", name: input.name } };
|
|
488
|
+
};
|
|
489
|
+
|
|
490
|
+
const mutation = createMutation(mockMutation);
|
|
491
|
+
|
|
492
|
+
// First call fails
|
|
493
|
+
try {
|
|
494
|
+
await mutation.mutate({ name: "test" });
|
|
495
|
+
} catch {}
|
|
496
|
+
|
|
497
|
+
expect(mutation.error()).not.toBe(null);
|
|
498
|
+
|
|
499
|
+
// Second call succeeds
|
|
500
|
+
shouldFail = false;
|
|
501
|
+
await mutation.mutate({ name: "test" });
|
|
502
|
+
|
|
503
|
+
expect(mutation.error()).toBe(null);
|
|
504
|
+
expect(mutation.data()?.id).toBe("123");
|
|
505
|
+
});
|
|
506
|
+
});
|
|
507
|
+
|
|
508
|
+
// =============================================================================
|
|
509
|
+
// Tests: createLazyQuery - Comprehensive Coverage
|
|
510
|
+
// =============================================================================
|
|
511
|
+
|
|
512
|
+
describe("createLazyQuery comprehensive", () => {
|
|
513
|
+
test("createLazyQuery handles successful execution", async () => {
|
|
514
|
+
const mockQueryResult = Promise.resolve("test-data");
|
|
515
|
+
|
|
516
|
+
const query = createLazyQuery(mockQueryResult as any);
|
|
517
|
+
|
|
518
|
+
expect(query.loading()).toBe(false);
|
|
519
|
+
expect(query.data()).toBe(null);
|
|
520
|
+
|
|
521
|
+
const result = await query.execute();
|
|
522
|
+
|
|
523
|
+
expect(result).toBe("test-data");
|
|
524
|
+
expect(query.data()).toBe("test-data");
|
|
525
|
+
expect(query.loading()).toBe(false);
|
|
526
|
+
expect(query.error()).toBe(null);
|
|
527
|
+
});
|
|
528
|
+
|
|
529
|
+
test("createLazyQuery handles Error rejection", async () => {
|
|
530
|
+
const testError = new Error("query failed");
|
|
531
|
+
const mockQueryResult = Promise.reject(testError);
|
|
532
|
+
|
|
533
|
+
const query = createLazyQuery(mockQueryResult as any);
|
|
534
|
+
|
|
535
|
+
try {
|
|
536
|
+
await query.execute();
|
|
537
|
+
expect(true).toBe(false); // Should not reach
|
|
538
|
+
} catch (err) {
|
|
539
|
+
expect(err).toBe(testError);
|
|
540
|
+
expect(query.error()).toBe(testError);
|
|
541
|
+
expect(query.loading()).toBe(false);
|
|
542
|
+
}
|
|
543
|
+
});
|
|
544
|
+
|
|
545
|
+
test("createLazyQuery handles non-Error rejection", async () => {
|
|
546
|
+
const mockQueryResult = Promise.reject("string error");
|
|
547
|
+
|
|
548
|
+
const query = createLazyQuery(mockQueryResult as any);
|
|
549
|
+
|
|
550
|
+
try {
|
|
551
|
+
await query.execute();
|
|
552
|
+
expect(true).toBe(false); // Should not reach
|
|
553
|
+
} catch (err) {
|
|
554
|
+
expect(err).toBeInstanceOf(Error);
|
|
555
|
+
expect((err as Error).message).toBe("string error");
|
|
556
|
+
expect(query.error()?.message).toBe("string error");
|
|
557
|
+
expect(query.loading()).toBe(false);
|
|
558
|
+
}
|
|
559
|
+
});
|
|
560
|
+
|
|
561
|
+
test("createLazyQuery with accessor function", async () => {
|
|
562
|
+
const mockQueryResult = Promise.resolve("accessor-data");
|
|
563
|
+
const query = createLazyQuery(() => mockQueryResult as any);
|
|
564
|
+
|
|
565
|
+
const result = await query.execute();
|
|
566
|
+
|
|
567
|
+
expect(result).toBe("accessor-data");
|
|
568
|
+
expect(query.data()).toBe("accessor-data");
|
|
569
|
+
});
|
|
570
|
+
|
|
571
|
+
test("createLazyQuery with accessor returning null", async () => {
|
|
572
|
+
const query = createLazyQuery(() => null);
|
|
573
|
+
|
|
574
|
+
const result = await query.execute();
|
|
575
|
+
|
|
576
|
+
expect(result).toBe(null);
|
|
577
|
+
expect(query.data()).toBe(null);
|
|
578
|
+
expect(query.loading()).toBe(false);
|
|
579
|
+
});
|
|
580
|
+
|
|
581
|
+
test("createLazyQuery with undefined", async () => {
|
|
582
|
+
const query = createLazyQuery(undefined);
|
|
583
|
+
|
|
584
|
+
const result = await query.execute();
|
|
585
|
+
|
|
586
|
+
expect(result).toBe(null);
|
|
587
|
+
expect(query.data()).toBe(null);
|
|
588
|
+
});
|
|
589
|
+
|
|
590
|
+
test("createLazyQuery reset after execution", async () => {
|
|
591
|
+
const mockQueryResult = Promise.resolve("test-data");
|
|
592
|
+
const query = createLazyQuery(mockQueryResult as any);
|
|
593
|
+
|
|
594
|
+
await query.execute();
|
|
595
|
+
|
|
596
|
+
expect(query.data()).toBe("test-data");
|
|
597
|
+
|
|
598
|
+
query.reset();
|
|
599
|
+
|
|
600
|
+
expect(query.data()).toBe(null);
|
|
601
|
+
expect(query.loading()).toBe(false);
|
|
602
|
+
expect(query.error()).toBe(null);
|
|
603
|
+
});
|
|
604
|
+
|
|
605
|
+
test("createLazyQuery reset after error", async () => {
|
|
606
|
+
const mockQueryResult = Promise.reject(new Error("test error"));
|
|
607
|
+
const query = createLazyQuery(mockQueryResult as any);
|
|
608
|
+
|
|
609
|
+
try {
|
|
610
|
+
await query.execute();
|
|
611
|
+
} catch {}
|
|
612
|
+
|
|
613
|
+
expect(query.error()).not.toBe(null);
|
|
614
|
+
|
|
615
|
+
query.reset();
|
|
616
|
+
|
|
617
|
+
expect(query.data()).toBe(null);
|
|
618
|
+
expect(query.loading()).toBe(false);
|
|
619
|
+
expect(query.error()).toBe(null);
|
|
620
|
+
});
|
|
621
|
+
|
|
622
|
+
test("createLazyQuery multiple executions", async () => {
|
|
623
|
+
let executeCount = 0;
|
|
624
|
+
const mockQueryFn = () => {
|
|
625
|
+
executeCount++;
|
|
626
|
+
return Promise.resolve(`data-${executeCount}`) as any;
|
|
627
|
+
};
|
|
628
|
+
|
|
629
|
+
const query = createLazyQuery(mockQueryFn);
|
|
630
|
+
|
|
631
|
+
const result1 = await query.execute();
|
|
632
|
+
expect(result1).toBe("data-1");
|
|
633
|
+
|
|
634
|
+
const result2 = await query.execute();
|
|
635
|
+
expect(result2).toBe("data-2");
|
|
636
|
+
|
|
637
|
+
expect(executeCount).toBe(2);
|
|
638
|
+
});
|
|
639
|
+
});
|