clock.ts 1.3 KB

1234567891011121314151617181920212223242526272829303132333435363738
  1. import { Effect } from "effect"
  2. import { TestClock } from "effect/testing"
  3. // Defers on a real macrotask so pubsub delivery, fiber hops, and filesystem
  4. // work can complete between TestClock advances.
  5. const settle = Effect.promise(() => new Promise((resolve) => setTimeout(resolve, 1)))
  6. // One advance step: let pending real work finish, then fire due TestClock timers.
  7. const tick = Effect.gen(function* () {
  8. yield* settle
  9. yield* TestClock.adjust("500 millis")
  10. })
  11. /**
  12. * Drives every pending TestClock timer to completion: stream debounces and
  13. * State's 500ms reload debounce register their sleeps from separate fiber hops
  14. * that a single adjust can miss, so the loop alternates real-macrotask settles
  15. * with adjusts until the condition holds. Extra adjusts are harmless when
  16. * nothing is pending.
  17. */
  18. export const advance = Effect.fnUntraced(function* (condition: () => boolean) {
  19. for (let attempt = 0; attempt < 100; attempt++) {
  20. if (condition()) return
  21. yield* tick
  22. }
  23. return yield* Effect.die(new Error("condition never became true after 100 advances"))
  24. })
  25. /**
  26. * Advances far enough that any pending debounced work would have completed,
  27. * so a no-op assertion afterwards is meaningful.
  28. */
  29. export const drain = Effect.gen(function* () {
  30. for (let round = 0; round < 4; round++) {
  31. yield* tick
  32. }
  33. yield* settle
  34. })