All files / client/src/utils useTimeNow.ts

100% Statements 13/13
100% Branches 8/8
100% Functions 6/6
100% Lines 10/10

Press n or j to go to the next uncovered block, b, p or k for the previous block.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41                609x 13405x           13405x         609x 13349x                 609x 26269x 13344x       13344x   13344x    
import { useMemo, useSyncExternalStore } from "react";
import { config } from "shared/config";
import { getTimeNowSnapshot, subscribeToClock } from "client/utils/clock";
import { useAppSelector } from "client/utils/hooks";
 
// Real wall-clock time, never the mocked test time. Use this over useTimeNow
// only where the value is compared against something the mock doesn't move
// either, such as a timestamp the server has already written.
export const useRealTimeNow = (): Date => {
  const timeNowMs = useSyncExternalStore(subscribeToClock, getTimeNowSnapshot);
 
  // Rebuilt only when the clock ticks, so it stays referentially stable in
  // between and can be listed as a dependency. It carries no language of its
  // own: a component formatting it passes the locale it read through the locale
  // hook, so a switch invalidates the formatted string rather than this instant.
  return useMemo(() => new Date(timeNowMs), [timeNowMs]);
};
 
// Whether the mocked test time replaces the real clock. Exported so the rule can
// be tested without rendering: the whole app's sense of time depends on it.
export const shouldUseTestTime = (testTime: string | null): boolean =>
  config.client().loadedSettings !== "production" &&
  config.client().showTestValues &&
  testTime !== null &&
  testTime !== "";
 
// The current time, mocked time included. A hook rather than a plain getter so
// a component re-renders as the clock advances and when a data poll brings in a
// new mocked time. Pass the value down to pure helpers instead of letting them
// reach for the store.
export const useTimeNow = (): Date => {
  const testTime = useAppSelector((state) => state.testSettings.testTime);
  const realTimeNow = useRealTimeNow();
 
  // An unset mocked time parses to an invalid date, which the guard below keeps
  // from ever being returned
  const mockedTimeNow = useMemo(() => new Date(testTime ?? ""), [testTime]);
 
  return shouldUseTestTime(testTime) ? mockedTimeNow : realTimeNow;
};