From c6b0202be7a7fdabc6918d1fc2744a5406b8798a Mon Sep 17 00:00:00 2001 From: Andrew Kirkegaard Date: Wed, 25 Oct 2023 23:28:51 -0400 Subject: [PATCH] check --- README.md | 5 +- app/App.svelte | 21 ++- app/App.tsx | 129 ------------------- app/{cutout/Cutout.module.css => Cutout.css} | 14 +- app/Cutout.svelte | 6 + app/PeriodSelector.svelte.d.ts | 4 + app/api/index.ts | 42 +++--- app/chart/Line.tsx | 4 +- app/chart/Plot.tsx | 11 -- app/chart/chart.css | 18 --- app/cutout/index.tsx | 2 +- app/cutout/store.ts | 72 +++++------ app/dom.ts | 26 ---- app/index.html | 4 +- app/index.ts | 2 +- app/ui/Axis.css | 17 +++ app/ui/BottomAxis.d.ts | 9 ++ app/ui/BottomAxis.svelte | 21 +++ app/ui/ButtonGroup.d.ts | 8 -- app/ui/ButtonGroup.svelte | 6 +- app/ui/ButtonGroup.svelte.d.ts | 12 ++ app/ui/LineChart.css | 8 ++ app/ui/LineChart.d.ts | 14 ++ app/ui/LineChart.svelte | 50 +++++++ app/ui/Marker.css | 9 ++ app/ui/Marker.d.ts | 8 ++ app/ui/Marker.svelte | 16 +++ app/ui/Path.svelte | 7 + app/ui/RightAxis.d.ts | 9 ++ app/ui/RightAxis.svelte | 20 +++ app/ui/index.d.ts | 6 + test/app/api.spec.ts | 2 +- test/app/cutout/store.spec.ts | 21 +-- 33 files changed, 320 insertions(+), 283 deletions(-) delete mode 100644 app/App.tsx rename app/{cutout/Cutout.module.css => Cutout.css} (76%) create mode 100644 app/Cutout.svelte create mode 100644 app/PeriodSelector.svelte.d.ts delete mode 100644 app/chart/Plot.tsx delete mode 100644 app/dom.ts create mode 100644 app/ui/Axis.css create mode 100644 app/ui/BottomAxis.d.ts create mode 100644 app/ui/BottomAxis.svelte delete mode 100644 app/ui/ButtonGroup.d.ts create mode 100644 app/ui/ButtonGroup.svelte.d.ts create mode 100644 app/ui/LineChart.css create mode 100644 app/ui/LineChart.d.ts create mode 100644 app/ui/LineChart.svelte create mode 100644 app/ui/Marker.css create mode 100644 app/ui/Marker.d.ts create mode 100644 app/ui/Marker.svelte create mode 100644 app/ui/Path.svelte create mode 100644 app/ui/RightAxis.d.ts create mode 100644 app/ui/RightAxis.svelte create mode 100644 app/ui/index.d.ts diff --git a/README.md b/README.md index 577cc014..5f359c75 100644 --- a/README.md +++ b/README.md @@ -45,15 +45,14 @@ yarn fix ``` ### Testing - | Command | Description | |-------------------------------|--------------------------------------------------------------------| | `yarn workspace test unit` | Run unit and integration tests. Does not require app to be running | | `yarn workspace test watch` | Run unit and integration tests in watch mode | -| `yarn workspace test ui` | Open storybook | | `yarn workspace test e2e` | Run end-to-end acceptance tests. App must be running in dev mode | -| `yarn workspace test cypress` | Open cypress for end-to-end acceptance tests | | `yarn workspace test smoke` | Run smoke tests. App must be running locally in production mode | +| `yarn workspace test cypress` | Open cypress for end-to-end acceptance tests | +| `yarn workspace test ui` | Open storybook | ### CI/CD Workflows Add git hooks with `yarn prepare` diff --git a/app/App.svelte b/app/App.svelte index c947f0d5..aaf704a4 100644 --- a/app/App.svelte +++ b/app/App.svelte @@ -3,15 +3,30 @@

Time Period

- +
-
-
Reports
+
+ {#await $data} +
Loading...
+ {:then data} + + + + + {:catch error} +
{error.message}
+ {/await}
diff --git a/app/App.tsx b/app/App.tsx deleted file mode 100644 index e94cc788..00000000 --- a/app/App.tsx +++ /dev/null @@ -1,129 +0,0 @@ -import type { JSXElement } from "solid-js"; -import { createMemo, createResource, createSignal, Match, Switch } from "solid-js"; -import { format } from "d3-format"; -import { bisector } from "d3-array"; -import Week from "lib/Week"; -import type { CutoutIndex } from "lib/CutoutIndex"; -import cutoutIndex from "lib/CutoutIndex"; -import type Observation from "lib/Observation"; -import Period from "lib/Period"; -import { dropWhile } from "lib/itertools/drop"; -import type { CashIndex } from "lib/CashIndex"; -import cashIndex from "lib/CashIndex"; -import type Cutout from "lib/cutout"; -import type Purchase from "lib/purchases"; -import cutout from "./api/cutout"; -import slaughter from "./api/slaughter"; -import purchases from "./api/purchases"; -import type { Data } from "./chart"; -import TimePicker from "./timepicker"; -import CashIndexChart from "./cash"; -import CutoutChart from "./cutout"; -import PrimalChart from "./primal"; -import PurchasesChart from "./purchases"; -import styles from "./App.module.css"; - -interface Resources { - cutout: Cutout[]; - cutoutIndex: CutoutIndex[]; - cashIndex: CashIndex[]; - purchases: Purchase[]; -} - -interface DateRange { - start: Date; - end: Date; -} - -const formatNumber = format(".2f"); - -const { center: bisectDate } = bisector(observation => observation.date); - -const getObservation = (data: Data[], date: Date): Data => - data[bisectDate(data, date)]; - -function fetch({ start, end }: DateRange): Promise { - start = Week.with(start).previous.start; - - return Promise.all([ - cutout.query(start, end), - slaughter.query(start, end), - purchases.query(start, end) - ]).then(([cutout, slaughter, purchases]) => ({ - cutout: Array.from(dropWhile(cutout, record => record.date < start)), - cutoutIndex: Array.from(dropWhile(cutoutIndex(cutout), record => record.date < start)), - cashIndex: Array.from(dropWhile(cashIndex(slaughter), record => record.date < start)), - purchases: Array.from(dropWhile(purchases, record => record.date < start)) - })); -} - -function App(): JSXElement { - const [period, setPeriod] = createSignal(Period.ThreeMonths); - - const range = createMemo(function() { - const end = import.meta.env.PROD ? new Date() : new Date(2021, 11, 23); - return { start: period().from(end), end }; - }); - - const [data] = createResource(range, fetch, { - initialValue: { - cutout: [], - cutoutIndex: [], - cashIndex: [], - purchases: [] - } - }); - - const [date, setDate] = createSignal(range().end); - - function selectDate(event: CustomEvent): void { - setDate(event.detail); - } - - function selectPeriod(event: CustomEvent): void { - setPeriod(event.detail); - setDate(range().end); - } - - return
-
- -
- - - - - - - - - -
- }> - -
- - - -
Error
-
- -
; -} - -export default App; - -export { formatNumber, getObservation }; diff --git a/app/cutout/Cutout.module.css b/app/Cutout.css similarity index 76% rename from app/cutout/Cutout.module.css rename to app/Cutout.css index 0f4e4a55..61cd06c6 100644 --- a/app/cutout/Cutout.module.css +++ b/app/Cutout.css @@ -5,7 +5,7 @@ @apply xl:px-10; } -.cutout :global(.chart) { +.cutout .chart { @apply px-4; @apply md:px-10; } @@ -46,29 +46,29 @@ @apply text-xs; } -.cutout :global(.plot) g:first-child path { +.cutout .plot g:first-child path { @apply stroke-blue-400; stroke-width: 4; } -.cutout :global(.plot) g:nth-child(2) path { +.cutout .plot g:nth-child(2) path { @apply stroke-blue-400; @apply opacity-25; stroke-width: 4; } -.cutout :global(.plot) g:first-child circle { +.cutout .plot g:first-child circle { @apply fill-blue-400; } -.cutout :global(.plot) g:nth-child(2) circle { +.cutout .plot g:nth-child(2) circle { @apply hidden; } -.cutout :global(.marker) rect { +.cutout .marker rect { @apply fill-blue-400; } -.cutout :global(.marker) text { +.cutout .marker text { @apply fill-white; } diff --git a/app/Cutout.svelte b/app/Cutout.svelte new file mode 100644 index 00000000..f1a5be84 --- /dev/null +++ b/app/Cutout.svelte @@ -0,0 +1,6 @@ + + + diff --git a/app/PeriodSelector.svelte.d.ts b/app/PeriodSelector.svelte.d.ts new file mode 100644 index 00000000..a2dd34e0 --- /dev/null +++ b/app/PeriodSelector.svelte.d.ts @@ -0,0 +1,4 @@ +import ButtonGroup from "ui/ButtonGroup.svelte"; +import type Period from "lib/Period"; + +export default class extends ButtonGroup {} diff --git a/app/api/index.ts b/app/api/index.ts index a3fee485..61267926 100644 --- a/app/api/index.ts +++ b/app/api/index.ts @@ -1,7 +1,7 @@ -import { writable, type Readable } from "svelte/store"; +import { get, writable, derived, type Readable } from "svelte/store"; import Week from "lib/Week"; import Result from "lib/Result"; -import type Period from "lib/Period"; +import Period from "lib/Period"; import type Cutout from "lib/cutout"; import type Purchase from "lib/purchases"; import type Slaughter from "lib/slaughter"; @@ -9,6 +9,7 @@ import cutout from "./cutout"; import slaughter from "./slaughter"; import purchases from "./purchases"; import { today } from "./lib"; +import result from "lib/Result"; export interface Resources { period: Period; @@ -18,26 +19,33 @@ export interface Resources { } interface ApiStore extends Readable> { + period: Period; fetch(period: Period): void; } -const { subscribe, set } = writable>(); +const period = writable(Period.ThreeMonths); +const resources = writable>(); + +period.subscribe(function(period) { + const { start } = Week.with(period.from(today)).previous; + + resources.set(Result.Loading()); + + Promise.all([ + cutout.query(start, today), + slaughter.query(start, today), + purchases.query(start, today) + ]).then(([cutout, slaughter, purchases]) => + resources.set(Result.Success({ period, cutout, slaughter, purchases })) + ).catch(error => + resources.set(Result.Failure(error))); +}); const store: ApiStore = { - subscribe, - fetch(period: Period) { - const { start } = Week.with(period.from(today)).previous; - - set(Result.Loading()); - - Promise.all([ - cutout.query(start, today), - slaughter.query(start, today), - purchases.query(start, today) - ]).then(([cutout, slaughter, purchases]) => - set(Result.Success({ period, cutout, slaughter, purchases })) - ).catch(error => - set(Result.Failure(error))); + subscribe: resources.subscribe, + fetch: period.set, + get period(): Period { + return get(period); } }; diff --git a/app/chart/Line.tsx b/app/chart/Line.tsx index c9d50384..3826979c 100644 --- a/app/chart/Line.tsx +++ b/app/chart/Line.tsx @@ -4,8 +4,6 @@ import type { Offset } from "."; type Props = Partial; const Line = (props: Props): JSXElement => - ; + export default Line; diff --git a/app/chart/Plot.tsx b/app/chart/Plot.tsx deleted file mode 100644 index b2f34fe6..00000000 --- a/app/chart/Plot.tsx +++ /dev/null @@ -1,11 +0,0 @@ -import type { JSXElement, PropsWithChildren } from "solid-js"; -import type { Offset } from "."; - -type Props = PropsWithChildren & Partial; - -const Plot = (props: Props): JSXElement => - - {props.children} - ; - -export default Plot; diff --git a/app/chart/chart.css b/app/chart/chart.css index 8d51e5ee..e69de29b 100644 --- a/app/chart/chart.css +++ b/app/chart/chart.css @@ -1,18 +0,0 @@ -svg { - @apply w-full; - @apply h-full; -} - -.chart { - @apply touch-none; -} - -.chart .marker { - @apply fill-white; - @apply text-xs; - @apply font-bold; -} - -.chart .marker-line { - @apply stroke-gray-200; -} diff --git a/app/cutout/index.tsx b/app/cutout/index.tsx index 8c3e70de..fd1c194a 100644 --- a/app/cutout/index.tsx +++ b/app/cutout/index.tsx @@ -1,6 +1,6 @@ import type { JSXElement } from "solid-js"; import { createEffect, createMemo, createSignal, Index } from "solid-js"; -import styles from "./Cutout.module.css"; +import styles from "../Cutout.module.css"; import type { CutoutIndex } from "lib/CutoutIndex"; import type { Data } from "../chart"; import LineChart from "../chart/LineChart"; diff --git a/app/cutout/store.ts b/app/cutout/store.ts index 90085645..66aab61a 100644 --- a/app/cutout/store.ts +++ b/app/cutout/store.ts @@ -1,63 +1,53 @@ import { derived, writable, type Readable } from "svelte/store"; -import Result from "lib/Result"; import cutoutIndex from "lib/CutoutIndex"; import { dropWhile } from "lib/itertools/drop"; import map from "lib/itertools/map"; -import type { Resources } from "../api"; import { formatNumber, getObservation, today } from "../api/lib"; import type { Series } from "../ui/LineChart"; +import type Cutout from "lib/cutout"; +import api from "../api"; interface Stat { label: string; value: string; } -interface DataSeries { - cutout: Series; - index: Series; -} - -export interface CutoutViewModel extends DataSeries { +interface CutoutViewModel { date: Date; stats: Stat[]; + cutout: Series; + index: Series; } -interface CutoutStore extends Readable> { +interface CutoutStore extends Readable { select: (date: Date) => void; } -function store(api: Readable>): CutoutStore { +function store(response: Cutout[]): CutoutStore { + const { start } = api.period; const date = writable(today); - - const series = derived(api, result => - Result.map(result, data => { - const start = data.period.from(today); - const records = dropWhile(cutoutIndex(data.cutout), record => record.date < start); - - const index = Array.from(map(records, ({ date, indexPrice: value }) => ({ - date, value - }))); - - const cutout = Array.from(map(records, ({ date, carcassPrice: value }) => ({ - date, value - }))); - - return { cutout, index }; - })); - - const { subscribe } = derived([series, date], ([result, date]) => - Result.map(result, ({ cutout, index }) => ({ - date, - cutout, - index, - stats: [{ - label: "Cutout", - value: formatNumber(getObservation(cutout, date).value) - }, { - label: "Index", - value: formatNumber(getObservation(index, date).value) - }] - }))); + const records = dropWhile(cutoutIndex(response), record => record.date < start); + + const index = Array.from(map(records, ({ date, indexPrice: value }) => ({ + date, value + }))); + + const cutout = Array.from(map(records, ({ date, carcassPrice: value }) => ({ + date, value + }))); + + const { subscribe } = derived(date, date => ({ + date, + cutout, + index, + stats: [{ + label: "Cutout", + value: formatNumber(getObservation(cutout, date).value) + }, { + label: "Index", + value: formatNumber(getObservation(index, date).value) + }] + })); return { subscribe, @@ -66,3 +56,5 @@ function store(api: Readable>): CutoutStore { } export default store; + +export type { CutoutViewModel, CutoutStore }; diff --git a/app/dom.ts b/app/dom.ts deleted file mode 100644 index ffde3bcb..00000000 --- a/app/dom.ts +++ /dev/null @@ -1,26 +0,0 @@ -import type { Optional, UnaryOperator } from "lib"; - -function getElement(selector: string): UnaryOperator> { - let element: Optional; - - return function(parent: ParentNode): Optional { - if (element !== undefined) return element; - - const selected = parent.querySelector(selector); - - if (selected != null) { - element = selected as T; - } - - return element; - }; -} - -function dispatch(this: EventTarget, name: string, detail: T, options?: EventInit): void { - this.dispatchEvent(new CustomEvent(name, Object.assign({ detail }, options ?? { - bubbles: true, - cancelable: true - }))); -} - -export { getElement, dispatch }; diff --git a/app/index.html b/app/index.html index 1f199268..fcb663c1 100644 --- a/app/index.html +++ b/app/index.html @@ -9,7 +9,7 @@ -
- +
+ diff --git a/app/index.ts b/app/index.ts index 350dd460..91e5a599 100644 --- a/app/index.ts +++ b/app/index.ts @@ -2,7 +2,7 @@ import "./index.css"; import App from "./App.svelte"; const app = new App({ - target: document.getElementById("main") as HTMLElement + target: document.body.querySelector("main") as HTMLElement }); export default app; diff --git a/app/ui/Axis.css b/app/ui/Axis.css new file mode 100644 index 00000000..83d2bd83 --- /dev/null +++ b/app/ui/Axis.css @@ -0,0 +1,17 @@ +.axis .tick > line { + @apply stroke-gray-200; +} + +.axis .tick > text { + @apply text-gray-500; + @apply text-xs; +} + +.axis .domain { + @apply hidden; +} + +.y.axis .tick > text { + transform: translate(-14px, -8px); + text-anchor: end; +} diff --git a/app/ui/BottomAxis.d.ts b/app/ui/BottomAxis.d.ts new file mode 100644 index 00000000..19c53920 --- /dev/null +++ b/app/ui/BottomAxis.d.ts @@ -0,0 +1,9 @@ +import { type SvelteComponent } from "svelte"; + +export interface BottomAxisProps { + scale: AxisScale; +} + +type BottomAxis = SvelteComponent; + +export default BottomAxis; diff --git a/app/ui/BottomAxis.svelte b/app/ui/BottomAxis.svelte new file mode 100644 index 00000000..ff9c2cbc --- /dev/null +++ b/app/ui/BottomAxis.svelte @@ -0,0 +1,21 @@ + + + + + diff --git a/app/ui/ButtonGroup.d.ts b/app/ui/ButtonGroup.d.ts deleted file mode 100644 index 50b92d2c..00000000 --- a/app/ui/ButtonGroup.d.ts +++ /dev/null @@ -1,8 +0,0 @@ -import { type SvelteComponent } from "svelte"; - -interface ButtonGroupProps { - items: T[]; - selected?: T; -} - -export type ButtonGroup = SvelteComponent>; diff --git a/app/ui/ButtonGroup.svelte b/app/ui/ButtonGroup.svelte index bd9e7c14..3578d63c 100644 --- a/app/ui/ButtonGroup.svelte +++ b/app/ui/ButtonGroup.svelte @@ -6,13 +6,13 @@ import { createEventDispatcher } from "svelte"; type T = $$Generic; - interface SelectEvent { + interface Events { select: T; } - export let items: T[]; + export let items: readonly T[]; export let selected: T = items[0]; - const dispatch = createEventDispatcher(); + const dispatch = createEventDispatcher(); const select = (item: T) => function(event: MouseEvent & { currentTarget: Element }): void { diff --git a/app/ui/ButtonGroup.svelte.d.ts b/app/ui/ButtonGroup.svelte.d.ts new file mode 100644 index 00000000..108a52ff --- /dev/null +++ b/app/ui/ButtonGroup.svelte.d.ts @@ -0,0 +1,12 @@ +import { type SvelteComponentTyped } from "svelte"; + +declare interface Props { + items: T[]; + selected?: T; +} + +declare interface Events { + select: T; +} + +export default class extends SvelteComponentTyped, Events, never> {} diff --git a/app/ui/LineChart.css b/app/ui/LineChart.css new file mode 100644 index 00000000..e17b9937 --- /dev/null +++ b/app/ui/LineChart.css @@ -0,0 +1,8 @@ +svg { + @apply w-full; + @apply h-full; +} + +.chart { + @apply touch-none; +} diff --git a/app/ui/LineChart.d.ts b/app/ui/LineChart.d.ts new file mode 100644 index 00000000..7e31448d --- /dev/null +++ b/app/ui/LineChart.d.ts @@ -0,0 +1,14 @@ +export interface Data { + date: Date; + value: number; +} + +export type Series = Data[]; + +export interface ChartProps { + data: Series[]; +} + +type Chart = SvelteComponent; + +export default Chart; diff --git a/app/ui/LineChart.svelte b/app/ui/LineChart.svelte new file mode 100644 index 00000000..1a7e8141 --- /dev/null +++ b/app/ui/LineChart.svelte @@ -0,0 +1,50 @@ + + +
+ + + + + {#each data as series} + + {/each} + + +
diff --git a/app/ui/Marker.css b/app/ui/Marker.css new file mode 100644 index 00000000..abe44d4a --- /dev/null +++ b/app/ui/Marker.css @@ -0,0 +1,9 @@ +.marker text { + @apply fill-white; + @apply text-xs; + @apply font-bold; +} + +.marker line { + @apply stroke-gray-200; +} diff --git a/app/ui/Marker.d.ts b/app/ui/Marker.d.ts new file mode 100644 index 00000000..1f9017fe --- /dev/null +++ b/app/ui/Marker.d.ts @@ -0,0 +1,8 @@ +import { type SvelteComponent } from "svelte"; + +export interface MarkerProps { +} + +type Marker = SvelteComponent; + +export default Marker; diff --git a/app/ui/Marker.svelte b/app/ui/Marker.svelte new file mode 100644 index 00000000..a40cd699 --- /dev/null +++ b/app/ui/Marker.svelte @@ -0,0 +1,16 @@ + + + + + + + + + + ; + +; diff --git a/app/ui/Path.svelte b/app/ui/Path.svelte new file mode 100644 index 00000000..e419a262 --- /dev/null +++ b/app/ui/Path.svelte @@ -0,0 +1,7 @@ + + + + +; diff --git a/app/ui/RightAxis.d.ts b/app/ui/RightAxis.d.ts new file mode 100644 index 00000000..9e21645c --- /dev/null +++ b/app/ui/RightAxis.d.ts @@ -0,0 +1,9 @@ +import { type SvelteComponent } from "svelte"; + +export interface RightAxisProps { + scale: AxisScale; +} + +type RightAxis = SvelteComponent; + +export default RightAxis; diff --git a/app/ui/RightAxis.svelte b/app/ui/RightAxis.svelte new file mode 100644 index 00000000..a10393f0 --- /dev/null +++ b/app/ui/RightAxis.svelte @@ -0,0 +1,20 @@ + + + + + diff --git a/app/ui/index.d.ts b/app/ui/index.d.ts new file mode 100644 index 00000000..9112049e --- /dev/null +++ b/app/ui/index.d.ts @@ -0,0 +1,6 @@ +import type ButtonGroup from "./ButtonGroup"; +import type LineChart from "./LineChart"; +import type RightAxis from "./RightAxis"; +import type BottomAxis from "./BottomAxis"; +import type Marker from "./Marker"; +export type { ButtonGroup, LineChart, RightAxis, BottomAxis, Marker }; diff --git a/test/app/api.spec.ts b/test/app/api.spec.ts index 258ee28f..3010c5c6 100644 --- a/test/app/api.spec.ts +++ b/test/app/api.spec.ts @@ -3,7 +3,7 @@ import { get } from "svelte/store"; import Period from "lib/Period"; import Result from "lib/Result"; import api from "app/api"; -import { promisify } from "./index"; +import { promisify } from "."; describe("fetch periods", () => { test("fetch one month of data", async () => { diff --git a/test/app/cutout/store.spec.ts b/test/app/cutout/store.spec.ts index e846989d..be8a116c 100644 --- a/test/app/cutout/store.spec.ts +++ b/test/app/cutout/store.spec.ts @@ -1,19 +1,22 @@ import { describe, test, expect, beforeEach } from "vitest"; +import { get } from "svelte/store"; import Period from "lib/Period"; -import store, { type CutoutViewModel } from "app/cutout/store"; +import type { CutoutStore } from "app/cutout/store"; +import cutoutStore from "app/cutout/store"; import api from "app/api"; import { promisify } from ".."; -describe("derive Cutout ViewModel from api", () => { - const cutout = store(api); - let result: CutoutViewModel; +describe("derive Cutout ViewModel from cutout", () => { + let store: CutoutStore; beforeEach(async () => { api.fetch(Period.OneMonth); - result = await promisify(cutout); + const { cutout } = await promisify(api); + store = cutoutStore(cutout); }); test("initial state from api", () => { + const result = get(store); expect(result.date).toEqual(new Date(2021, 11, 23)); expect(result.cutout.slice(-5)).toEqual([{ @@ -25,11 +28,9 @@ describe("derive Cutout ViewModel from api", () => { }, { date: new Date(2021, 11, 21), value: 84.91 - }, { date: new Date(2021, 11, 22), value: 84.67 - }, { date: new Date(2021, 11, 23), value: 91.47 @@ -47,9 +48,9 @@ describe("derive Cutout ViewModel from api", () => { }); test("selecting a date updates the stats", async () => { - expect.hasAssertions(); - cutout.select(new Date(2021, 11, 17)); - const result = await promisify(cutout); + const result = get(store); + expect(result.date).toEqual(new Date(2021, 11, 23)); + store.select(new Date(2021, 11, 17)); expect(result.date).toEqual(new Date(2021, 11, 17));