diff --git a/web/src/pages/audiobooks/AudiobookDetail.tsx b/web/src/pages/audiobooks/AudiobookDetail.tsx index c3973051..d3c4fa70 100644 --- a/web/src/pages/audiobooks/AudiobookDetail.tsx +++ b/web/src/pages/audiobooks/AudiobookDetail.tsx @@ -39,8 +39,9 @@ function findChapterAt( seconds: number, ): { label: string; index: number } | null { for (let i = chapters.length - 1; i >= 0; i--) { - if (seconds >= chapters[i].absoluteStart) { - return { label: chapters[i].label, index: i + 1 }; + const ch = chapters[i]; + if (ch && seconds >= ch.absoluteStart) { + return { label: ch.label, index: i + 1 }; } } return chapters[0] ? { label: chapters[0].label, index: 1 } : null; diff --git a/web/src/pages/audiobooks/components/ChaptersSection.test.tsx b/web/src/pages/audiobooks/components/ChaptersSection.test.tsx index cb62dbcf..632590b6 100644 --- a/web/src/pages/audiobooks/components/ChaptersSection.test.tsx +++ b/web/src/pages/audiobooks/components/ChaptersSection.test.tsx @@ -32,13 +32,13 @@ describe("ChaptersSection", () => { it("sort menu switches between position and longest-first orders", async () => { render(); const rowsBefore = screen.getAllByRole("button", { name: /Prologue|Memory/ }); - expect(within(rowsBefore[0]).getByText("Prologue")).toBeInTheDocument(); + expect(within(rowsBefore[0]!).getByText("Prologue")).toBeInTheDocument(); await userEvent.click(screen.getByRole("button", { name: /sort/i })); await userEvent.click(screen.getByRole("menuitem", { name: /longest first/i })); const rowsAfter = screen.getAllByRole("button", { name: /Prologue|Memory/ }); - expect(within(rowsAfter[0]).getByText("Memory")).toBeInTheDocument(); + expect(within(rowsAfter[0]!).getByText("Memory")).toBeInTheDocument(); }); it("calls onSelect with absolute start seconds when a chapter is clicked", async () => { diff --git a/web/src/pages/audiobooks/components/ChaptersSection.tsx b/web/src/pages/audiobooks/components/ChaptersSection.tsx index f6635edd..c0ea730a 100644 --- a/web/src/pages/audiobooks/components/ChaptersSection.tsx +++ b/web/src/pages/audiobooks/components/ChaptersSection.tsx @@ -68,7 +68,8 @@ export function ChaptersSection({ files, currentPositionSeconds, onSelect }: Cha const currentIndex = useMemo(() => { if (currentPositionSeconds == null) return -1; for (let i = rows.length - 1; i >= 0; i--) { - if (currentPositionSeconds >= rows[i].absoluteStart) return i; + const row = rows[i]; + if (row && currentPositionSeconds >= row.absoluteStart) return i; } return -1; }, [rows, currentPositionSeconds]); diff --git a/web/src/pages/audiobooks/player/useAudiobookPlayback.test.ts b/web/src/pages/audiobooks/player/useAudiobookPlayback.test.ts index 47e9ba67..bbe59efd 100644 --- a/web/src/pages/audiobooks/player/useAudiobookPlayback.test.ts +++ b/web/src/pages/audiobooks/player/useAudiobookPlayback.test.ts @@ -41,8 +41,8 @@ describe("useAudiobookPlayback", () => { useAudiobookPlayback({ contentId: "c", files, initialPositionSeconds: 0 }), ); expect(result.current.chapters).toHaveLength(2); - expect(result.current.chapters[0].start_seconds).toBe(0); - expect(result.current.chapters[1].start_seconds).toBe(300); + expect(result.current.chapters[0]!.start_seconds).toBe(0); + expect(result.current.chapters[1]!.start_seconds).toBe(300); }); it("computes streamUrl from the first file id", () => {