@aiquants/daily-report 0.10.0 → 0.11.0

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.
@@ -0,0 +1,383 @@
1
+ /**
2
+ * Scroll position restoration guards for the tab-unmounted list views.
3
+ * タブアンマウントをまたぐスクロール位置復元のガード。
4
+ *
5
+ * 実際に起きた欠陥 (両方とも本番ビルド + 実機ブラウザで再現・計測済み):
6
+ * 1. 初期表示が 6px (ITEM_VERTICAL_PADDING) スクロールされて見える —
7
+ * 親が ref の「値」をレンダー時に読んで props へ焼き込み、`initialScrollOffset: 0` (数値) が
8
+ * 旧 virtualscroll の `toPanePositionWithInset(0, top) = top` に落ちてペイン 6px で開始した。
9
+ * 2. タブ往復でスクロール位置がリセットされる (実測 3206 → 6) — タブは非制御で親は
10
+ * タブ切替時に再レンダーされないため、復帰マウントには memo 計算時点の古い焼き込み値が渡った。
11
+ *
12
+ * 対策の契約 (ここで固定する):
13
+ * - 親は ref **オブジェクト**を渡し、子が**マウント時に一度だけ**値を読む (レンダー時読取の排除)。
14
+ * - 保存座標は onScroll が配信する**論理座標** (getScrollPosition のペイン座標ではない)。
15
+ * - DetailList はアンカー (記録時の選択日報) が一致するときだけ復元し、
16
+ * 復元マウントでは初回の「選択日報へのスクロール」を 1 回だけスキップする。
17
+ */
18
+
19
+ import { act, fireEvent, render, screen } from "@testing-library/react"
20
+ import type { ReactNode } from "react"
21
+ import { beforeEach, describe, expect, it, vi } from "vitest"
22
+ import type { DailyReportDetail, DailyReportItem } from "../../shared/types"
23
+
24
+ /** `VirtualScroll` 代役が記録する props (マウント/再レンダーごとに 1 件)。 */
25
+ type CapturedProps = {
26
+ testId?: string
27
+ initialScrollOffset?: number
28
+ onScroll?: (position: number, totalHeight: number) => void
29
+ getItemHeight?: (index: number) => number
30
+ }
31
+
32
+ const captured: CapturedProps[] = []
33
+ /** 代役の命令ハンドル経由で観測した scrollToIndex 呼び出し。 */
34
+ const scrollToIndexCalls: Array<{ testId?: string; index: number }> = []
35
+
36
+ /**
37
+ * Stand-in for VirtualScroll that renders every item and records scroll wiring.
38
+ * スクロール結線を記録しつつ全アイテムを描画する `VirtualScroll` の代役。
39
+ */
40
+ vi.mock("@aiquants/virtualscroll", async () => {
41
+ const { forwardRef: fwd, useImperativeHandle: useHandle } = await import("react")
42
+ return {
43
+ tapScrollCircleSampleVisual: () => null,
44
+ VirtualScroll: fwd(
45
+ (
46
+ props: {
47
+ itemCount: number
48
+ getItem: (index: number) => unknown
49
+ children: (item: unknown, index: number) => ReactNode
50
+ contentProps?: Record<string, unknown>
51
+ } & CapturedProps,
52
+ ref: React.Ref<unknown>,
53
+ ) => {
54
+ captured.push({ testId: props.testId, initialScrollOffset: props.initialScrollOffset, onScroll: props.onScroll, getItemHeight: (props as { getItemHeight?: (index: number) => number }).getItemHeight })
55
+ useHandle(ref, () => ({
56
+ scrollTo: () => 0,
57
+ scrollToIndex: (index: number) => {
58
+ scrollToIndexCalls.push({ testId: props.testId, index })
59
+ },
60
+ getScrollPosition: () => 0,
61
+ getContentSize: () => 0,
62
+ getViewportSize: () => 0,
63
+ updateItemSize: () => {},
64
+ focusItemAtIndex: () => {},
65
+ }))
66
+ return (
67
+ <div data-testid={props.testId} {...props.contentProps}>
68
+ {Array.from({ length: props.itemCount }, (_, index) => (
69
+ // biome-ignore lint/suspicious/noArrayIndexKey: 代役なので index キーで十分
70
+ <div key={index}>{props.children(props.getItem(index), index)}</div>
71
+ ))}
72
+ </div>
73
+ )
74
+ },
75
+ ),
76
+ }
77
+ })
78
+
79
+ /** 統合テストでアクションコンテキストが返すアイテム (テストごとに差し替える)。 */
80
+ const contextItemsRef: { current: DailyReportItem[] } = { current: [] }
81
+
82
+ /** 日報アクションはネットワーク/ルーターに依存するため、呼び出しを受け止めるだけの代役にする。 */
83
+ vi.mock("../contexts/daily-report-action-context", async (importOriginal) => {
84
+ const actual = await importOriginal<typeof import("../contexts/daily-report-action-context")>()
85
+ const noop = async () => {}
86
+ return {
87
+ ...actual,
88
+ useDailyReportActionContext: () => ({
89
+ pendingAddCommentIds: { current: new Map() },
90
+ pendingDeleteCommentIds: { current: new Map() },
91
+ pendingReadUpdates: { current: new Map() },
92
+ pendingStarUpdates: { current: new Map() },
93
+ resolvedIdMap: { current: new Map() },
94
+ version: 0,
95
+ items: contextItemsRef.current,
96
+ user: null,
97
+ isSseEnabled: false,
98
+ toggleSse: () => {},
99
+ addComment: noop,
100
+ deleteComment: noop,
101
+ toggleStar: noop,
102
+ toggleRead: noop,
103
+ createReport: async () => 0,
104
+ updateReport: noop,
105
+ publishReport: noop,
106
+ deleteReport: noop,
107
+ removeStaleItem: () => {},
108
+ refetchData: noop,
109
+ clearCacheAndRefetch: noop,
110
+ }),
111
+ }
112
+ })
113
+
114
+ /** 詳細取得はキャッシュ層ごと差し替え、常に用意した 1 件を返す。 */
115
+ vi.mock("../hooks/use-daily-report", async (importOriginal) => {
116
+ const actual = await importOriginal<typeof import("../hooks/use-daily-report")>()
117
+ return {
118
+ ...actual,
119
+ useDailyReportDetail: () => ({ report: buildDetail(), error: null, isLoading: false, isRefetching: false }),
120
+ useDailyReportPrefetch: () => () => {},
121
+ }
122
+ })
123
+
124
+ /** resolved-content が使う useSearchParams をルーター無しで満たす。 */
125
+ vi.mock("react-router", async (importOriginal) => {
126
+ const actual = await importOriginal<typeof import("react-router")>()
127
+ return { ...actual, useSearchParams: () => [new URLSearchParams(), () => {}] as const }
128
+ })
129
+
130
+ const { DailyReportList } = await import("./daily-report-list")
131
+ const { DailyReportDetailList } = await import("./daily-report-detail-list")
132
+ const { DailyReportResolvedContent } = await import("./daily-report-resolved-content")
133
+ const { DailyReportConfigProvider } = await import("../config-context")
134
+
135
+ const buildItem = (reportHubId: number): DailyReportItem => ({ reportHubId, businessDate: "2026-07-31" })
136
+
137
+ const buildDetail = (): DailyReportDetail => ({
138
+ reportHubId: 1,
139
+ date: "2026-07-31",
140
+ interviewers: [],
141
+ comments: [],
142
+ isRead: false,
143
+ isStarred: false,
144
+ labels: [],
145
+ commentItems: [],
146
+ attachments: [],
147
+ })
148
+
149
+ /** 設定コンテキストを与えて描画する。 */
150
+ const renderView = (node: ReactNode) =>
151
+ render(
152
+ <DailyReportConfigProvider
153
+ config={{
154
+ apiBasePath: "/daily_report/api",
155
+ fieldLabels: { attachments: "添付ファイル", attachmentMissing: "実体が見つかりません", attachmentDownload: "ダウンロード" },
156
+ }}>
157
+ {node}
158
+ </DailyReportConfigProvider>,
159
+ )
160
+
161
+ /** 指定 testId の最新キャプチャ (再レンダーで積み直されるため末尾を返す)。 */
162
+ const latestOf = (testId: string) => {
163
+ const props = [...captured].reverse().find((entry) => entry.testId === testId)
164
+ if (!props) throw new Error(`VirtualScroll props for ${testId} were not captured`)
165
+ return props
166
+ }
167
+
168
+ /** 指定 testId の「マウント初回」キャプチャ列 (initialScrollOffset の変遷検査用)。 */
169
+ const allOf = (testId: string) => captured.filter((entry) => entry.testId === testId)
170
+
171
+ beforeEach(() => {
172
+ captured.length = 0
173
+ scrollToIndexCalls.length = 0
174
+ contextItemsRef.current = []
175
+ vi.stubGlobal(
176
+ "ResizeObserver",
177
+ class {
178
+ observe() {}
179
+ unobserve() {}
180
+ disconnect() {}
181
+ },
182
+ )
183
+ vi.stubGlobal(
184
+ "matchMedia",
185
+ vi.fn((query: string) => ({ matches: false, media: query, onchange: null, addEventListener: vi.fn(), removeEventListener: vi.fn(), addListener: vi.fn(), removeListener: vi.fn(), dispatchEvent: vi.fn() })),
186
+ )
187
+ })
188
+
189
+ describe("DailyReportList のスクロール共有スロット", () => {
190
+ it("マウント時に一度だけ ref を読み、その後の再レンダーで値を読み直さない (焼き込み排除)", () => {
191
+ const items = [buildItem(1), buildItem(2)]
192
+ const scrollOffsetRef = { current: 1234 }
193
+ const view = renderView(<DailyReportList dailyReportItems={items} selectedReportHubId={1} onSelectItem={() => {}} scrollOffsetRef={scrollOffsetRef} />)
194
+ expect(latestOf("daily-report-list").initialScrollOffset).toBe(1234)
195
+
196
+ // 外部で ref が動いても、同一マウント中の initialScrollOffset は変化しない
197
+ scrollOffsetRef.current = 9999
198
+ view.rerender(
199
+ <DailyReportConfigProvider config={{ apiBasePath: "/daily_report/api", fieldLabels: { attachments: "添付ファイル", attachmentMissing: "実体が見つかりません", attachmentDownload: "ダウンロード" } }}>
200
+ <DailyReportList dailyReportItems={items} selectedReportHubId={2} onSelectItem={() => {}} scrollOffsetRef={scrollOffsetRef} />
201
+ </DailyReportConfigProvider>,
202
+ )
203
+ expect(latestOf("daily-report-list").initialScrollOffset).toBe(1234)
204
+ })
205
+
206
+ it("onScroll が配信する論理座標をそのまま ref へ書き戻す", () => {
207
+ const scrollOffsetRef = { current: 0 }
208
+ renderView(<DailyReportList dailyReportItems={[buildItem(1)]} selectedReportHubId={null} onSelectItem={() => {}} scrollOffsetRef={scrollOffsetRef} />)
209
+ act(() => latestOf("daily-report-list").onScroll?.(3206, 100000))
210
+ expect(scrollOffsetRef.current).toBe(3206)
211
+ })
212
+
213
+ it("ref 未指定でも 0 で開始し例外にならない (スロットは opt-in)", () => {
214
+ renderView(<DailyReportList dailyReportItems={[buildItem(1)]} selectedReportHubId={null} onSelectItem={() => {}} />)
215
+ expect(latestOf("daily-report-list").initialScrollOffset).toBe(0)
216
+ act(() => latestOf("daily-report-list").onScroll?.(10, 100000))
217
+ })
218
+ })
219
+
220
+ describe("DailyReportDetailList のアンカー付き復元", () => {
221
+ it("アンカーが現在の選択と一致すれば復元し、初回の選択スクロールはスキップする", () => {
222
+ const restoreRef = { current: { offset: 900, anchorReportHubId: 42, measuredHeights: new Map<number, number>() } }
223
+ renderView(<DailyReportDetailList dailyReportItems={[buildItem(41), buildItem(42)]} selectedItemId={42} scrollRestoreRef={restoreRef} />)
224
+ expect(latestOf("daily-report-detail-list").initialScrollOffset).toBe(900)
225
+ // 復元マウントでは scrollToIndex(選択) が走らない (復元位置の上書き防止)
226
+ expect(scrollToIndexCalls.filter((c) => c.testId === "daily-report-detail-list")).toEqual([])
227
+ })
228
+
229
+ it("復元マウント後の選択変更では従来どおり選択日報へスクロールする (スキップは 1 回だけ)", () => {
230
+ const restoreRef = { current: { offset: 900, anchorReportHubId: 42, measuredHeights: new Map<number, number>() } }
231
+ const items = [buildItem(41), buildItem(42)]
232
+ const view = renderView(<DailyReportDetailList dailyReportItems={items} selectedItemId={42} scrollRestoreRef={restoreRef} />)
233
+ view.rerender(
234
+ <DailyReportConfigProvider config={{ apiBasePath: "/daily_report/api", fieldLabels: { attachments: "添付ファイル", attachmentMissing: "実体が見つかりません", attachmentDownload: "ダウンロード" } }}>
235
+ <DailyReportDetailList dailyReportItems={items} selectedItemId={41} scrollRestoreRef={restoreRef} />
236
+ </DailyReportConfigProvider>,
237
+ )
238
+ expect(scrollToIndexCalls.filter((c) => c.testId === "daily-report-detail-list")).toEqual([{ testId: "daily-report-detail-list", index: 0 }])
239
+ // アンカー同期の副作用が選択変更を追従する
240
+ expect(restoreRef.current.anchorReportHubId).toBe(41)
241
+ })
242
+
243
+ it("アンカーが選択と不一致なら復元せず、従来どおり選択日報へスクロールする", () => {
244
+ const restoreRef = { current: { offset: 900, anchorReportHubId: 41, measuredHeights: new Map<number, number>() } }
245
+ renderView(<DailyReportDetailList dailyReportItems={[buildItem(41), buildItem(42)]} selectedItemId={42} scrollRestoreRef={restoreRef} />)
246
+ expect(latestOf("daily-report-detail-list").initialScrollOffset).toBe(0)
247
+ expect(scrollToIndexCalls.filter((c) => c.testId === "daily-report-detail-list")).toEqual([{ testId: "daily-report-detail-list", index: 1 }])
248
+ })
249
+
250
+ it("アンカー一致かつ offset 0 の復元マウントでも初回の選択スクロールをスキップする (先頭復元の上書き防止)", () => {
251
+ // 復元判定は「アンカー一致」であって「offset > 0」ではない: 先頭まで戻してから
252
+ // タブを離れた場合も復元マウントであり、選択日報へ跳んではならない
253
+ const restoreRef = { current: { offset: 0, anchorReportHubId: 42, measuredHeights: new Map<number, number>() } }
254
+ renderView(<DailyReportDetailList dailyReportItems={[buildItem(41), buildItem(42)]} selectedItemId={42} scrollRestoreRef={restoreRef} />)
255
+ expect(latestOf("daily-report-detail-list").initialScrollOffset).toBe(0)
256
+ expect(scrollToIndexCalls.filter((c) => c.testId === "daily-report-detail-list")).toEqual([])
257
+ })
258
+
259
+ it("アンカー日報がアンマウント中に消えていても、次の正当な選択変更のスクロールを飲み込まない", () => {
260
+ // 武装したままの skip フラグが「解決できないアンカー」で残留し、その後の選択変更
261
+ // (新規作成ボタン等) を 1 回無言で飲み込む退行の検知。武装解除して通常スクロールする
262
+ const restoreRef = { current: { offset: 900, anchorReportHubId: 42, measuredHeights: new Map<number, number>() } }
263
+ const items = [buildItem(41), buildItem(43)] // 42 は削除済み
264
+ const view = renderView(<DailyReportDetailList dailyReportItems={items} selectedItemId={42} scrollRestoreRef={restoreRef} />)
265
+ expect(latestOf("daily-report-detail-list").initialScrollOffset).toBe(900)
266
+ expect(scrollToIndexCalls.filter((c) => c.testId === "daily-report-detail-list")).toEqual([])
267
+
268
+ view.rerender(
269
+ <DailyReportConfigProvider config={{ apiBasePath: "/daily_report/api", fieldLabels: { attachments: "添付ファイル", attachmentMissing: "実体が見つかりません", attachmentDownload: "ダウンロード" } }}>
270
+ <DailyReportDetailList dailyReportItems={items} selectedItemId={43} scrollRestoreRef={restoreRef} />
271
+ </DailyReportConfigProvider>,
272
+ )
273
+ expect(scrollToIndexCalls.filter((c) => c.testId === "daily-report-detail-list")).toEqual([{ testId: "daily-report-detail-list", index: 1 }])
274
+ })
275
+
276
+ it("null アンカーの復元マウントでも、最初の選択確定のスクロールを飲み込まない", () => {
277
+ const restoreRef = { current: { offset: 900, anchorReportHubId: null, measuredHeights: new Map<number, number>() } }
278
+ const items = [buildItem(41), buildItem(42)]
279
+ const view = renderView(<DailyReportDetailList dailyReportItems={items} selectedItemId={null} scrollRestoreRef={restoreRef} />)
280
+ view.rerender(
281
+ <DailyReportConfigProvider config={{ apiBasePath: "/daily_report/api", fieldLabels: { attachments: "添付ファイル", attachmentMissing: "実体が見つかりません", attachmentDownload: "ダウンロード" } }}>
282
+ <DailyReportDetailList dailyReportItems={items} selectedItemId={41} scrollRestoreRef={restoreRef} />
283
+ </DailyReportConfigProvider>,
284
+ )
285
+ expect(scrollToIndexCalls.filter((c) => c.testId === "daily-report-detail-list")).toEqual([{ testId: "daily-report-detail-list", index: 0 }])
286
+ })
287
+
288
+ it("復元スキップの消費は「スクロール済み」として記録され、直後の items 参照更新で復元が壊れない", () => {
289
+ // skip 消費時に lastScrolledRef を記録しないと、次の items 配列更新 (SSE チャンク等) で
290
+ // 効果が再実行され、消費済みフラグの保護が無いまま選択スクロールが復元位置を上書きする
291
+ const restoreRef = { current: { offset: 900, anchorReportHubId: 42, measuredHeights: new Map<number, number>() } }
292
+ const view = renderView(<DailyReportDetailList dailyReportItems={[buildItem(41), buildItem(42)]} selectedItemId={42} scrollRestoreRef={restoreRef} />)
293
+ expect(scrollToIndexCalls.filter((c) => c.testId === "daily-report-detail-list")).toEqual([])
294
+
295
+ // 同一内容・新しい配列 identity で再レンダー (ストリーム更新の最小モデル)
296
+ view.rerender(
297
+ <DailyReportConfigProvider config={{ apiBasePath: "/daily_report/api", fieldLabels: { attachments: "添付ファイル", attachmentMissing: "実体が見つかりません", attachmentDownload: "ダウンロード" } }}>
298
+ <DailyReportDetailList dailyReportItems={[buildItem(41), buildItem(42)]} selectedItemId={42} scrollRestoreRef={restoreRef} />
299
+ </DailyReportConfigProvider>,
300
+ )
301
+ expect(scrollToIndexCalls.filter((c) => c.testId === "daily-report-detail-list")).toEqual([])
302
+ })
303
+
304
+ it("不一致経路の武装解除は恒久: 後からマウント時と同じ選択へ戻っても飲み込まない", () => {
305
+ // 武装解除を「一致分岐の中」へ動かす退行 (不一致では解除されない) の検知。
306
+ // 不一致で解除されないと、後でマウント時選択と同じ値へ選択が戻ったときに
307
+ // 残留フラグが消費され、正当なスクロールを 1 回飲み込む
308
+ const restoreRef = { current: { offset: 900, anchorReportHubId: 42, measuredHeights: new Map<number, number>() } }
309
+ const config = { apiBasePath: "/daily_report/api", fieldLabels: { attachments: "添付ファイル", attachmentMissing: "実体が見つかりません", attachmentDownload: "ダウンロード" } }
310
+ const view = renderView(<DailyReportDetailList dailyReportItems={[buildItem(41), buildItem(43)]} selectedItemId={42} scrollRestoreRef={restoreRef} />)
311
+
312
+ // 不一致の選択変更 → 武装解除してスクロール (既存テストと同じ前段)
313
+ view.rerender(
314
+ <DailyReportConfigProvider config={config}>
315
+ <DailyReportDetailList dailyReportItems={[buildItem(41), buildItem(43)]} selectedItemId={43} scrollRestoreRef={restoreRef} />
316
+ </DailyReportConfigProvider>,
317
+ )
318
+ expect(scrollToIndexCalls.filter((c) => c.testId === "daily-report-detail-list")).toEqual([{ testId: "daily-report-detail-list", index: 1 }])
319
+
320
+ // アンカー日報が復活し、マウント時と同じ 42 を選択 → 解除済みなので通常どおりスクロールする
321
+ const revived = [buildItem(41), buildItem(43), buildItem(42)]
322
+ view.rerender(
323
+ <DailyReportConfigProvider config={config}>
324
+ <DailyReportDetailList dailyReportItems={revived} selectedItemId={42} scrollRestoreRef={restoreRef} />
325
+ </DailyReportConfigProvider>,
326
+ )
327
+ expect(scrollToIndexCalls.filter((c) => c.testId === "daily-report-detail-list")).toEqual([
328
+ { testId: "daily-report-detail-list", index: 1 },
329
+ { testId: "daily-report-detail-list", index: 2 },
330
+ ])
331
+ })
332
+
333
+ it("実測カード高さは共有スロットの Map をそのまま使い、アンマウント越しに座標空間を保存する", () => {
334
+ // ピクセル復元は「保存時と同じ高さ空間」でだけ正しい。スロットの Map が
335
+ // getItemHeight へ直結していること (= 再マウントで既定高さ推定に戻らないこと) を固定する
336
+ const measuredHeights = new Map<number, number>([[42, 777]])
337
+ const restoreRef = { current: { offset: 900, anchorReportHubId: 42, measuredHeights } }
338
+ renderView(<DailyReportDetailList dailyReportItems={[buildItem(41), buildItem(42)]} selectedItemId={42} scrollRestoreRef={restoreRef} />)
339
+ const getItemHeight = latestOf("daily-report-detail-list").getItemHeight
340
+ expect(getItemHeight?.(1)).toBe(777) // index 1 = reportHubId 42 の実測値
341
+ expect(getItemHeight?.(0)).not.toBe(777) // 未実測カードは既定推定
342
+ })
343
+
344
+ it("onScroll は offset のみ書き、アンカーは選択同期の副作用が管理する", () => {
345
+ const restoreRef = { current: { offset: 0, anchorReportHubId: null as number | null, measuredHeights: new Map<number, number>() } }
346
+ renderView(<DailyReportDetailList dailyReportItems={[buildItem(42)]} selectedItemId={42} scrollRestoreRef={restoreRef} />)
347
+ act(() => latestOf("daily-report-detail-list").onScroll?.(555, 100000))
348
+ expect(restoreRef.current).toEqual({ offset: 555, anchorReportHubId: 42, measuredHeights: expect.any(Map) })
349
+ })
350
+ })
351
+
352
+ describe("タブ往復の統合 (実 Radix Tabs + 実リスト)", () => {
353
+ /** Radix Tabs のトリガーを実ブラウザ相当のイベント列で押す。 */
354
+ const clickTab = (name: RegExp) => {
355
+ const trigger = screen.getByRole("tab", { name })
356
+ fireEvent.mouseDown(trigger)
357
+ fireEvent.focus(trigger)
358
+ fireEvent.click(trigger)
359
+ }
360
+
361
+ it("スクロール → 他タブ → 復帰で、復帰マウントの initialScrollOffset が最新のスクロール位置になる", () => {
362
+ contextItemsRef.current = [buildItem(1), buildItem(2), buildItem(3)]
363
+ renderView(<DailyReportResolvedContent userId="ENC1" />)
364
+
365
+ // 初回マウントは 0 (数値 0 は virtualscroll 側でペイン 0 = 本当の先頭に写像される)
366
+ const firstMount = allOf("daily-report-list")[0]
367
+ expect(firstMount?.initialScrollOffset).toBe(0)
368
+
369
+ // ユーザーのスクロールを模す (論理座標 3206)
370
+ act(() => latestOf("daily-report-list").onScroll?.(3206, 100000))
371
+
372
+ // DetailList タブへ → List がアンマウントされる
373
+ clickTab(/DetailList/)
374
+ expect(screen.queryByTestId("daily-report-list")).toBeNull()
375
+
376
+ // List タブへ復帰 → 復帰マウントは最新位置で開始する (旧実装は焼き込み値へリセットされた)
377
+ const capturedBefore = allOf("daily-report-list").length
378
+ clickTab(/^📄 List$/)
379
+ const returnedMounts = allOf("daily-report-list").slice(capturedBefore)
380
+ expect(returnedMounts.length).toBeGreaterThan(0)
381
+ expect(returnedMounts[0]?.initialScrollOffset).toBe(3206)
382
+ })
383
+ })