Skip to content

test: add tests for split screen, bottom drawer, and scroll sync#381

Merged
tomusdrw merged 8 commits intomainfrom
td-split-screen-tests
Mar 31, 2026
Merged

test: add tests for split screen, bottom drawer, and scroll sync#381
tomusdrw merged 8 commits intomainfrom
td-split-screen-tests

Conversation

@tomusdrw
Copy link
Copy Markdown
Member

@tomusdrw tomusdrw commented Mar 30, 2026

Summary

  • Add 80 new component/hook-level tests covering all items listed in Add tests for split screen, bottom drawer, and scroll sync #373:
    • LocationProvider: URL hash parsing (extractSearchParams) and version resolution logic
    • SplitScreenProvider: state transitions for activate/deactivate split, compare mode, scroll linking defaults, theme persistence, shared scale
    • ScrollSync hooks: publisher/consumer behavior, feedback loop prevention via isSyncing ref
    • ZoomSyncBridge: scale propagation between panes, re-entrancy guard, negligible-diff skip
    • BottomDrawer: expand/collapse toggle, swipe gesture threshold (50px), CSS class and ARIA states
    • SidebarOverlay: open/close, Escape key dismiss, backdrop click, close button
    • Sidebar: split tab visibility based on isSplitActive and isNarrow, tab persistence, activateSplit on click
  • Set up vitest with jsdom environment, @testing-library/react, and polyfills (DOMMatrix, ResizeObserver, matchMedia, localStorage)

Closes #373

Test plan

  • All 94 tests pass (npx vitest run)
  • Biome CI passes (npm run qa)
  • CI pipeline passes

🤖 Generated with Claude Code

Summary by CodeRabbit

  • Tests

    • Added extensive Vitest + React Testing Library suites covering drawer, sidebar, overlay, split-screen, zoom-sync, location parsing, scroll-sync, and related behaviors.
  • Chores

    • Switched test runtime to jsdom, added a test setup file with DOM polyfills/cleanup, and added testing-library dev dependencies.
  • Refactor

    • Extracted location parsing and version-resolution logic into reusable utilities without changing public behavior.

Add component/hook-level tests for LocationProvider URL parsing, SplitScreenProvider
state transitions, ScrollSync publisher/consumer behavior, ZoomSyncBridge scale
propagation, BottomDrawer expand/collapse/swipe gestures, SidebarOverlay open/close/
escape dismiss, and Sidebar split tab visibility rules.

Set up vitest with jsdom environment, @testing-library/react, and polyfills for
DOMMatrix, ResizeObserver, matchMedia, and localStorage.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@netlify
Copy link
Copy Markdown

netlify bot commented Mar 30, 2026

Deploy Preview for graypaper-reader ready!

Name Link
🔨 Latest commit 64c0bce
🔍 Latest deploy log https://app.netlify.com/projects/graypaper-reader/deploys/69cbaf376706b50008548789
😎 Deploy Preview https://deploy-preview-381--graypaper-reader.netlify.app
📱 Preview on mobile
Toggle QR Code...

QR Code

Use your smartphone camera to open QR code link.

To edit notification comments on pull requests, go to your Netlify project configuration.

@coderabbitai
Copy link
Copy Markdown

coderabbitai bot commented Mar 30, 2026

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

Extracts LocationProvider helpers into utils, adds Vitest + React Testing Library tests and jsdom test setup/polyfills, updates test devDependencies, and adjusts LocationProvider to import the new utilities; no public API or runtime exports changed.

Changes

Cohort / File(s) Summary
Test config & deps
package.json, vitest.config.ts, vitest.setup.ts
Adds testing devDependencies (@testing-library/jest-dom, @testing-library/react, @testing-library/user-event, jsdom), sets Vitest env to jsdom, and installs a test setup that runs cleanup and polyfills (DOMMatrix, ResizeObserver, matchMedia, localStorage).
BottomDrawer tests
src/components/BottomDrawer/BottomDrawer.spec.tsx
New Vitest + RTL tests for expand/collapse state, aria attributes, CSS class toggles, and touch/swipe gesture thresholds.
Sidebar & Overlay tests
src/components/Sidebar/Sidebar.spec.tsx, src/components/SidebarOverlay/SidebarOverlay.spec.tsx
New suites covering split-tab visibility (responsive), tab persistence (localStorage gp-tab), interactions, and overlay open/close behavior (Escape, backdrop, close button).
Split screen & ZoomSync tests
src/components/SplitScreenProvider/SplitScreenProvider.spec.tsx, src/components/ZoomSync/ZoomSync.spec.tsx
Provider tests for split activation/deactivation, compare mode, theme/sharedScale defaults and persistence; ZoomSyncBridge tests for publishing/applying shared scale and no-op thresholds.
LocationProvider utils, provider update & tests
src/components/LocationProvider/utils/extractSearchParams.ts, src/components/LocationProvider/utils/resolveFullVersion.ts, src/components/LocationProvider/LocationProvider.tsx, src/components/LocationProvider/LocationProvider.spec.ts
Extracts extractSearchParams and resolveFullVersion into new utils, updates LocationProvider to import/use them (removing local implementations), and adds tests for parsing and version-resolution behaviors.
Scroll sync tests
src/hooks/useScrollSync.spec.tsx
Adds comprehensive tests for useScrollSyncPublisher and useScrollSyncConsumer: publishing on scroll, consumer positioning, feedback-loop prevention, listener lifecycle, and no-op/disabled cases.

Sequence Diagram(s)

(omitted)

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Possibly related PRs

Suggested reviewers

  • DrEverr
  • mateuszsikora

Poem

🐰 I hopped through code with nimble feet,
wrote tests for drawers, splits, and sheet.
Swipes and hashes, scales in tune,
polyfills snug beneath the moon.
A carrot-chuffed test — the suite's complete. 🥕

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately summarizes the main change: adding comprehensive tests for split screen, bottom drawer, and scroll sync features.
Linked Issues check ✅ Passed All objectives from issue #373 are met: LocationProvider parsing, SplitScreenProvider state transitions, ScrollSync feedback prevention, ZoomSyncBridge scale propagation, BottomDrawer gestures/ARIA, SidebarOverlay interactions, and Sidebar tab visibility are all tested.
Out of Scope Changes check ✅ Passed All changes are in scope: test files, test dependencies, test configuration, and refactored utility extraction (extractSearchParams, resolveFullVersion) directly support the testing objectives.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch td-split-screen-tests

Comment @coderabbitai help to get the list of available commands and usage tips.

@github-actions
Copy link
Copy Markdown
Contributor

github-actions bot commented Mar 30, 2026

Visual Regression Test Report ✅ Passed

Github run id: 23794854889

🔗 Artifacts: Download

Copy link
Copy Markdown

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🧹 Nitpick comments (1)
vitest.setup.ts (1)

5-7: Clear localStorage in global afterEach to avoid cross-test state leakage.

Several new tests persist values (e.g., theme); clearing storage centrally keeps suites order-independent.

💡 Suggested patch
 afterEach(() => {
   cleanup();
+  if (typeof window !== "undefined" && window.localStorage) {
+    window.localStorage.clear();
+  }
 });
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@vitest.setup.ts` around lines 5 - 7, The global afterEach currently calls
cleanup() but doesn't clear persisted browser state; update the global teardown
in the vitest setup by adding a localStorage.clear() (or sessionStorage.clear()
if needed) alongside the existing afterEach cleanup so tests don't leak theme or
other values; locate the afterEach callback that invokes cleanup() and append a
call to clear storage (e.g., localStorage.clear()) to run after every test.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@src/components/LocationProvider/LocationProvider.spec.ts`:
- Around line 12-31: The spec currently re-implements the private parsing logic
(extractSearchParams) inline which makes tests validate duplicated code instead
of the production implementation; replace the duplicated implementations in
LocationProvider.spec.ts (both occurrences around the current
extractSearchParams blocks) by moving the parsing/resolution logic into a small
shared module (e.g., export a parseHash or extractSearchParams function) that is
imported and used by LocationProvider.tsx and by this spec so tests run the
exact production code path; update imports in LocationProvider.tsx and
LocationProvider.spec.ts to use the new shared function and remove the local
copies.

In `@src/components/Sidebar/Sidebar.spec.tsx`:
- Around line 21-28: TypeScript complains because mockUseBreakpoint is declared
as a no-arg function but you call it with spread args inside the vi.mock
wrapper; either make mockUseBreakpoint accept variadic args (e.g., change its
declaration to vi.fn((...args: unknown[]) => false) or vi.fn<any, unknown[]>())
or stop forwarding args in the wrapper by replacing useBreakpoint: (...args:
unknown[]) => mockUseBreakpoint(...args) with useBreakpoint: () =>
mockUseBreakpoint(); update the mock implementation in the vi.mock block
(references: mockUseBreakpoint, useBreakpoint, vi.mock) accordingly so the types
align and the TS2556 error disappears.

---

Nitpick comments:
In `@vitest.setup.ts`:
- Around line 5-7: The global afterEach currently calls cleanup() but doesn't
clear persisted browser state; update the global teardown in the vitest setup by
adding a localStorage.clear() (or sessionStorage.clear() if needed) alongside
the existing afterEach cleanup so tests don't leak theme or other values; locate
the afterEach callback that invokes cleanup() and append a call to clear storage
(e.g., localStorage.clear()) to run after every test.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 703cca56-3b15-4daa-a328-40480640a828

📥 Commits

Reviewing files that changed from the base of the PR and between 88771a2 and ca36a76.

⛔ Files ignored due to path filters (1)
  • package-lock.json is excluded by !**/package-lock.json
📒 Files selected for processing (10)
  • package.json
  • src/components/BottomDrawer/BottomDrawer.spec.tsx
  • src/components/LocationProvider/LocationProvider.spec.ts
  • src/components/Sidebar/Sidebar.spec.tsx
  • src/components/SidebarOverlay/SidebarOverlay.spec.tsx
  • src/components/SplitScreenProvider/SplitScreenProvider.spec.tsx
  • src/components/ZoomSync/ZoomSync.spec.tsx
  • src/hooks/useScrollSync.spec.tsx
  • vitest.config.ts
  • vitest.setup.ts

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Copy link
Copy Markdown

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick comments (2)
src/components/Sidebar/Sidebar.spec.tsx (2)

122-130: Redundant localStorage.removeItem call.

Line 123 removes "gp-tab" from localStorage, but beforeEach (line 62) already does this before every test. This duplication doesn't cause harm but adds noise.

🧹 Remove redundant cleanup
   it("defaults to outline tab", () => {
-    localStorage.removeItem("gp-tab");
     const ctx = createSplitContext();
     renderSidebar(ctx);
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/components/Sidebar/Sidebar.spec.tsx` around lines 122 - 130, The test
"defaults to outline tab" in Sidebar.spec.tsx contains a redundant
localStorage.removeItem("gp-tab") call; remove that line from the it("defaults
to outline tab") test body since the beforeEach cleanup already clears "gp-tab"
for every test (keep the rest of the test, e.g., createSplitContext(),
renderSidebar(ctx), and the assertion on screen.getByTestId("tab-outline")).

13-18: Calling onSearchFinished during render is a React anti-pattern.

The Search mock invokes onSearchFinished(false) synchronously during render, which triggers a side effect (and potentially state updates) during the render phase. This can cause React warnings and unpredictable test behavior.

♻️ Proposed fix: defer the callback or use a wrapper
 vi.mock("../Search/Search", () => ({
-  Search: ({ onSearchFinished }: { onSearchFinished: (v: boolean) => void }) => {
-    onSearchFinished(false);
-    return <div data-testid="mock-search">Search</div>;
-  },
+  Search: ({ onSearchFinished }: { onSearchFinished: (v: boolean) => void }) => {
+    React.useEffect(() => {
+      onSearchFinished(false);
+    }, [onSearchFinished]);
+    return <div data-testid="mock-search">Search</div>;
+  },
 }));

You'll need to add import React from "react" at the top, or alternatively use a simpler approach if the callback isn't needed for these tests:

 vi.mock("../Search/Search", () => ({
-  Search: ({ onSearchFinished }: { onSearchFinished: (v: boolean) => void }) => {
-    onSearchFinished(false);
-    return <div data-testid="mock-search">Search</div>;
-  },
+  Search: () => <div data-testid="mock-search">Search</div>,
 }));
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/components/Sidebar/Sidebar.spec.tsx` around lines 13 - 18, The mock
Search component currently calls the onSearchFinished prop synchronously during
render (Search mock, prop onSearchFinished), which is an anti-pattern; fix it by
importing React and deferring the callback so it runs after render (e.g., wrap
the call in a useEffect inside the mock component or call it inside a setTimeout
0), ensuring the mock still returns the <div data-testid="mock-search"> but
triggers onSearchFinished(false) only after mount to avoid side effects during
render.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@src/components/Sidebar/Sidebar.spec.tsx`:
- Around line 122-130: The test "defaults to outline tab" in Sidebar.spec.tsx
contains a redundant localStorage.removeItem("gp-tab") call; remove that line
from the it("defaults to outline tab") test body since the beforeEach cleanup
already clears "gp-tab" for every test (keep the rest of the test, e.g.,
createSplitContext(), renderSidebar(ctx), and the assertion on
screen.getByTestId("tab-outline")).
- Around line 13-18: The mock Search component currently calls the
onSearchFinished prop synchronously during render (Search mock, prop
onSearchFinished), which is an anti-pattern; fix it by importing React and
deferring the callback so it runs after render (e.g., wrap the call in a
useEffect inside the mock component or call it inside a setTimeout 0), ensuring
the mock still returns the <div data-testid="mock-search"> but triggers
onSearchFinished(false) only after mount to avoid side effects during render.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 635fa690-82df-4019-9553-8f384406fe33

📥 Commits

Reviewing files that changed from the base of the PR and between ca36a76 and c0d9acb.

📒 Files selected for processing (1)
  • src/components/Sidebar/Sidebar.spec.tsx

github-actions bot and others added 3 commits March 30, 2026 18:01
…red utils

Tests now import the real production functions instead of re-implementing
them locally, ensuring tests verify actual shipped behavior.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Copy link
Copy Markdown

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🧹 Nitpick comments (1)
src/components/LocationProvider/utils/resolveFullVersion.ts (1)

14-16: Match against VersionInfo.hash, not the record key.

The helper's contract guarantees versions values carry a hash, but it doesn't guarantee the record key is the same string. Matching on keys bakes in the current storage shape and makes this utility easier to break later if metadata gets re-keyed.

♻️ Proposed refactor
   const byHash =
-    Object.keys(metadata.versions).find((version) => version.startsWith(shortVersion)) ??
+    Object.values(metadata.versions).find((version) => version.hash.startsWith(shortVersion))?.hash ??
     (metadata.nightly?.hash.startsWith(shortVersion) ? metadata.nightly.hash : null);
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/components/LocationProvider/utils/resolveFullVersion.ts` around lines 14
- 16, The current byHash lookup matches shortVersion against the record key
(Object.keys(metadata.versions)), which is fragile; change it to match against
the VersionInfo.hash property instead (e.g., iterate
Object.values(metadata.versions) or Object.entries and check
value.hash.startsWith(shortVersion)) and return the matching value.hash; keep
the nightly fallback (metadata.nightly?.hash.startsWith(shortVersion) ?
metadata.nightly.hash : null) unchanged so resolveFullVersion and any callers
use the actual VersionInfo.hash rather than the map key.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@src/components/LocationProvider/LocationProvider.spec.ts`:
- Around line 74-76: The two specs around resolveFullVersion are insufficiently
strict: update the "resolves 'nightly' literal (case-insensitive concept)" test
to call resolveFullVersion with a different-cased literal (e.g., "NightLy")
against the same metadata to assert case-insensitivity; and update the "prefers
hash prefix over friendly name" test to include a conflicting friendly-name
entry in the metadata (so there is both a friendly-name match and a hash-prefix
match) and assert that resolveFullVersion returns the hash-prefixed version
rather than the friendly name. Reference resolveFullVersion and the metadata
test fixture when making these changes so the tests actually exercise case
normalization and conflict resolution.

In `@src/components/LocationProvider/utils/extractSearchParams.ts`:
- Around line 5-23: The hash parser is brittle: query keys can overwrite the
path-derived rest, values with '=' are truncated, and malformed percent-escapes
can throw during decode; update extractSearchParams to only allow setting the
known search keys (v, search, section, split) and never overwrite rest, split
each "key=value" at the first '=' (so values may contain '='), and wrap
decodeURIComponent in a try/catch fallback (e.g., use the raw value or a safe
decode) to avoid throwing on malformed escapes before calling handleHashChange;
locate the parsing loop that iterates searchParams.split("&"), change its
splitting logic to use indexOf to separate key and value, check key against the
allowed set (exclude rest), and perform safe decoding before assigning into
result.

---

Nitpick comments:
In `@src/components/LocationProvider/utils/resolveFullVersion.ts`:
- Around line 14-16: The current byHash lookup matches shortVersion against the
record key (Object.keys(metadata.versions)), which is fragile; change it to
match against the VersionInfo.hash property instead (e.g., iterate
Object.values(metadata.versions) or Object.entries and check
value.hash.startsWith(shortVersion)) and return the matching value.hash; keep
the nightly fallback (metadata.nightly?.hash.startsWith(shortVersion) ?
metadata.nightly.hash : null) unchanged so resolveFullVersion and any callers
use the actual VersionInfo.hash rather than the map key.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 2d5038b6-3a98-496b-a6a1-0a4c811e3a78

📥 Commits

Reviewing files that changed from the base of the PR and between c0d9acb and fb6c484.

⛔ Files ignored due to path filters (5)
  • tools/snapshot-tests/tests/basic-snapshots.spec.ts-snapshots/homepage-outline-light-mode-linux.png is excluded by !**/*.png
  • tools/snapshot-tests/tests/basic-snapshots.spec.ts-snapshots/topbar-notes-dropdown-dark-mode-linux.png is excluded by !**/*.png
  • tools/snapshot-tests/tests/basic-snapshots.spec.ts-snapshots/topbar-notes-dropdown-light-mode-linux.png is excluded by !**/*.png
  • tools/snapshot-tests/tests/basic-snapshots.spec.ts-snapshots/topbar-notes-selected-on-light-mode-linux.png is excluded by !**/*.png
  • tools/snapshot-tests/tests/split-screen.spec.ts-snapshots/split-view-opened-light-mode-linux.png is excluded by !**/*.png
📒 Files selected for processing (4)
  • src/components/LocationProvider/LocationProvider.spec.ts
  • src/components/LocationProvider/LocationProvider.tsx
  • src/components/LocationProvider/utils/extractSearchParams.ts
  • src/components/LocationProvider/utils/resolveFullVersion.ts

tomusdrw and others added 2 commits March 31, 2026 12:56
…red utils

- extractSearchParams: prevent `rest` overwrite via query params, split at
  first `=` to preserve values containing `=`, wrap decodeURIComponent in
  try/catch for malformed percent-escapes
- resolveFullVersion: match against VersionInfo.hash property instead of
  record keys
- Tests: case-insensitive "NightLy" literal, conflicting hash-prefix vs
  friendly-name, rest overwrite prevention, malformed escape handling

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Copy link
Copy Markdown

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🧹 Nitpick comments (1)
src/components/LocationProvider/utils/resolveFullVersion.ts (1)

1-9: Prefer reusing canonical metadata types instead of redefining local shapes.

VersionInfo/ResolveVersionInput duplicate existing metadata contracts and can drift from IMetadata/IVersionInfo. Consider deriving this input type from the shared source type to keep refactors safe.

Proposed refactor
-interface VersionInfo {
-  hash: string;
-  name?: string;
-}
-
-interface ResolveVersionInput {
-  versions: Record<string, VersionInfo>;
-  nightly?: { hash: string } | null;
-}
+import type { IMetadata } from "../../MetadataProvider/MetadataProvider";
+
+type ResolveVersionInput = Pick<IMetadata, "versions" | "nightly">;
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/components/LocationProvider/utils/resolveFullVersion.ts` around lines 1 -
9, The local types VersionInfo and ResolveVersionInput duplicate shared metadata
shapes; update resolveFullVersion to reuse the canonical types (e.g., import
IVersionInfo and IMetadata or the existing shared types) instead of redefining
VersionInfo/ResolveVersionInput so they stay in sync with metadata contracts;
replace the local interfaces with the imported types in the function signature
and any usages within resolveFullVersion to derive nightly and versions from the
shared type definitions (keep the same property names like hash/name to preserve
logic).
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@src/components/LocationProvider/utils/resolveFullVersion.ts`:
- Around line 14-17: The prefix lookup in resolveFullVersion.ts currently picks
the first hash that startsWith(shortVersion) (byHash) which can silently choose
the wrong version when multiple hashes share the same prefix; change the logic
in the function that computes byHash to collect all matching hashes from
Object.values(metadata.versions).map(v => v.hash) and also include
metadata.nightly.hash if it startsWith(shortVersion), then if matches.length ===
1 return that single hash, otherwise (matches.length === 0 or >1) return null to
explicitly handle no-match and ambiguous-match cases; update any early-return
that used byHash to use this unique-match behavior instead.

---

Nitpick comments:
In `@src/components/LocationProvider/utils/resolveFullVersion.ts`:
- Around line 1-9: The local types VersionInfo and ResolveVersionInput duplicate
shared metadata shapes; update resolveFullVersion to reuse the canonical types
(e.g., import IVersionInfo and IMetadata or the existing shared types) instead
of redefining VersionInfo/ResolveVersionInput so they stay in sync with metadata
contracts; replace the local interfaces with the imported types in the function
signature and any usages within resolveFullVersion to derive nightly and
versions from the shared type definitions (keep the same property names like
hash/name to preserve logic).
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: f33bc62e-3290-49fc-a266-87a0dc7981be

📥 Commits

Reviewing files that changed from the base of the PR and between fb6c484 and e989d74.

⛔ Files ignored due to path filters (2)
  • tools/snapshot-tests/tests/basic-snapshots.spec.ts-snapshots/homepage-outline-light-mode-linux.png is excluded by !**/*.png
  • tools/snapshot-tests/tests/basic-snapshots.spec.ts-snapshots/topbar-notes-dropdown-light-mode-linux.png is excluded by !**/*.png
📒 Files selected for processing (3)
  • src/components/LocationProvider/LocationProvider.spec.ts
  • src/components/LocationProvider/utils/extractSearchParams.ts
  • src/components/LocationProvider/utils/resolveFullVersion.ts
✅ Files skipped from review due to trivial changes (1)
  • src/components/LocationProvider/LocationProvider.spec.ts
🚧 Files skipped from review as they are similar to previous changes (1)
  • src/components/LocationProvider/utils/extractSearchParams.ts

When multiple version hashes share the same prefix, return null instead
of silently picking the first match. This prevents resolving to the
wrong version on short/ambiguous prefixes.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Copy link
Copy Markdown

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@src/components/LocationProvider/utils/resolveFullVersion.ts`:
- Around line 14-19: The current logic builds hashMatches from metadata.versions
and metadata.nightly.hash but does not deduplicate identical hashes, so
duplicate identical entries can cause an ambiguous-length branch; update the
resolution in resolveFullVersion.ts to remove duplicate hashes (e.g., via a Set
or unique filter) after building hashMatches and before the length checks so
that if all matching hashes are identical you return that single hash; reference
the variables/values involved (hashMatches, metadata.versions,
metadata.nightly?.hash, shortVersion) and then perform the existing length
checks on the deduplicated array.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 26ae5ff4-34d6-4bb9-9ebb-4eb9b617f70a

📥 Commits

Reviewing files that changed from the base of the PR and between e989d74 and 64c0bce.

⛔ Files ignored due to path filters (1)
  • tools/snapshot-tests/tests/basic-snapshots.spec.ts-snapshots/topbar-notes-selected-on-light-mode-linux.png is excluded by !**/*.png
📒 Files selected for processing (2)
  • src/components/LocationProvider/LocationProvider.spec.ts
  • src/components/LocationProvider/utils/resolveFullVersion.ts
✅ Files skipped from review due to trivial changes (1)
  • src/components/LocationProvider/LocationProvider.spec.ts

@tomusdrw tomusdrw merged commit 2a4d10d into main Mar 31, 2026
9 checks passed
@tomusdrw tomusdrw deleted the td-split-screen-tests branch March 31, 2026 11:35
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Add tests for split screen, bottom drawer, and scroll sync

1 participant