Skip to content

fix: catch AbortErrors in storybook tests and patch @reatom/core#17

Merged
Guria merged 1 commit into
mainfrom
fix/abort-error-guard-and-reatom-patch
Jun 13, 2026
Merged

fix: catch AbortErrors in storybook tests and patch @reatom/core#17
Guria merged 1 commit into
mainfrom
fix/abort-error-guard-and-reatom-patch

Conversation

@Guria

@Guria Guria commented May 28, 2026

Copy link
Copy Markdown
Owner

Problem

Storybook tests produce many AbortError messages in the connect logger output that go undetected. These indicate issues in the routing/async lifecycle that should be tracked.

Additionally, @reatom/core has a performance issue where urlAtom proactively evaluates ALL registered route loaders on every URL change — even for non-matching routes. Combined with isSomeLoaderPending (used by GlobalLoader) reading route.loader.pending() for all routes, this forces every non-matching route loader to evaluate and produce unnecessary unmatch AbortErrors.

Patch: @reatom/core@1001.0.0

Three optimizations in patches/@reatom%2Fcore@1001.0.0.patch:

  1. urlAtom init hook — only trigger routeAtom.loader() when routeAtom() returns truthy (route matches)
  2. urlAtom set handler — same match check in the _enqueue compute loop
  3. isSomeLoaderPending — check route.match() before reading route.loader.pending(), preventing forced loader evaluation for non-matching routes

These changes are safe because:

  • Non-matching route loaders still abort correctly through the computed dependency chain when a route transitions from matching to non-matching
  • The optimizations only skip proactive pre-evaluation of loaders that would immediately abort with "unmatch"

Other fixes

  • AbortError guard (.storybook/abortErrorGuard.ts) — addGlobalExtension + withCallHook on every .onReject action to collect AbortErrors during tests. Route loader aborts are filtered (expected lifecycle).
  • conversationUnreadCountAtom — removed redundant withConnectHook that caused a concurrent abort (double-fetch on first connection)
  • Storybook URL setup — auth set BEFORE urlAtom.go() to prevent concurrent loader re-evaluations
  • Timer accessibility — added aria-label="Custom duration" to timer input
  • Mobile master-detail tests — wait for list visibility after goBack()
  • connectLogger in tests — disabled via per-task VITE_CONNECT_LOGGER=false
  • Vitest detection — fixed broken import.meta.env.VITEST (always undefined in browser context) → use __vitest_worker__

Result

  • 323/323 tests pass
  • Zero AbortErrors in test output
  • Clean ~10-line terminal output (was thousands of lines of connectLogger spam)

Summary by CodeRabbit

  • New Features

    • Enhanced test error tracking and route teardown assertions.
    • Comprehensive Storybook integration tests for Article, Chat, and Connection flows with error, retry, loading, and navigation scenarios.
    • Route-specific unread count handling in navigation.
    • Accessibility improvement: custom duration input with descriptive label.
  • Bug Fixes

    • Fixed navigation tests to wait for list visibility before subsequent interactions.
  • Tests

    • Added route loader contract tests.
  • Chores

    • Updated dependencies.
    • Reformatted Storybook story configurations.
    • Simplified atom configuration.
    • Removed deprecated blur action.

Copilot AI review requested due to automatic review settings May 28, 2026 23:39
@coderabbitai

coderabbitai Bot commented May 28, 2026

Copy link
Copy Markdown

Review Change Stack

Caution

Review failed

The pull request is closed.

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: c51c9e0a-bba3-4998-9828-211ae5660ba3

📥 Commits

Reviewing files that changed from the base of the PR and between 2170bb9 and be5311b.

⛔ Files ignored due to path filters (1)
  • bun.lock is excluded by !**/*.lock
📒 Files selected for processing (39)
  • .config/mise/conf.d/tasks-test.toml
  • .storybook/abortErrorGuard.ts
  • .storybook/preview.tsx
  • .storybook/setupStorybookUrl.ts
  • package.json
  • src/app/integration/Articles.detail.stories.tsx
  • src/app/integration/Articles.direct-url.stories.tsx
  • src/app/integration/Articles.list-request.stories.tsx
  • src/app/integration/Articles.list.stories.tsx
  • src/app/integration/Articles.navigation.stories.tsx
  • src/app/integration/Articles.stories.tsx
  • src/app/integration/Auth.stories.tsx
  • src/app/integration/Calculator.stories.tsx
  • src/app/integration/Chat.detail.stories.tsx
  • src/app/integration/Chat.direct-url.stories.tsx
  • src/app/integration/Chat.list-request.stories.tsx
  • src/app/integration/Chat.list.stories.tsx
  • src/app/integration/Chat.stories.tsx
  • src/app/integration/Connections.detail.stories.tsx
  • src/app/integration/Connections.direct-url.stories.tsx
  • src/app/integration/Connections.list-request.stories.tsx
  • src/app/integration/Connections.list.stories.tsx
  • src/app/integration/Connections.navigation.stories.tsx
  • src/app/integration/Connections.stories.tsx
  • src/app/integration/Dashboard.stories.tsx
  • src/app/integration/Items.stories.tsx
  • src/app/integration/Pricing.stories.tsx
  • src/app/integration/Settings.stories.tsx
  • src/app/integration/SidebarFooter.stories.tsx
  • src/app/integration/Timeline.stories.tsx
  • src/app/integration/Timer.stories.tsx
  • src/app/integration/Usage.stories.tsx
  • src/entities/conversation/model/unreadCount.ts
  • src/pages/chat/ui/ChatNavItem.tsx
  • src/pages/timer/testing.ts
  • src/pages/timer/ui/TimerPage.tsx
  • src/shared/test/actor.ts
  • src/shared/test/reatomRouteContracts.test.ts
  • vitest.config.ts

Walkthrough

This PR extends Storybook test infrastructure with abort-error tracking, adds comprehensive integration story coverage for Articles/Chat/Connections flows, implements route-aware optimizations, removes test utilities, and consolidates test configuration across multiple foundation layers.

Changes

Testing Infrastructure and Integration Coverage

Layer / File(s) Summary
Abort error tracking and collection
.storybook/abortErrorGuard.ts
Define DrainAbortError type and utilities for collecting reatom abort errors, aggregating by action name/message, formatting for display, and asserting expected vs. unexpected errors.
Storybook preview abort-error integration
.storybook/preview.tsx
Import abort-error helpers; clear errors before each test; conditionally configure viewport only in Vitest workers; fail tests when abort errors are drained.
Storybook navigation pre-setup callback
.storybook/setupStorybookUrl.ts
Add optional beforeNavigate callback parameter to allow test state preparation before route navigation and loader evaluation.
Articles integration test coverage
src/app/integration/Articles.*.stories.tsx
Create stories for detail error/retry/loading, direct URL navigation, list-request handling, list interactions, and navigation between articles (desktop and mobile variants).
Chat integration test coverage
src/app/integration/Chat.*.stories.tsx
Create stories for detail error/retry/loading, direct URL not-found, list-request handling, list interactions, and navigation between conversations (desktop and mobile variants).
Connections integration test coverage
src/app/integration/Connections.*.stories.tsx
Create stories for detail error/retry/loading, test/reconnect button behavior, direct URL not-found, list-request handling, list interactions, and navigation between connections (desktop and mobile variants).
Test config, route contracts, and dependencies
.config/mise/conf.d/tasks-test.toml, vitest.config.ts, src/shared/test/reatomRouteContracts.test.ts, package.json
Disable connect logger in test tasks; add Vitest unit project; introduce route-loader contract tests; update Reatom dependencies to PR versions.
Route-aware unread count and atom cleanup
src/pages/chat/ui/ChatNavItem.tsx, src/entities/conversation/model/unreadCount.ts
Make ChatNavItem unread badge conditional on route match; remove withConnectHook from conversationUnreadCountAtom, retaining only withAsyncData.
Timer accessibility and test helper refactor
src/pages/timer/ui/TimerPage.tsx, src/pages/timer/testing.ts, src/shared/test/actor.ts
Add aria-label to custom duration input; tighten test locator to labeled textbox; remove blur action from input test; remove blur() from BaseActor interface.
Storybook parameter formatting
src/app/integration/*.stories.tsx
Reformat inline Storybook parameters objects to explicit multiline form for consistency across Auth, Calculator, Chat, Dashboard, Items, Pricing, Settings, SidebarFooter, Timeline, Timer, and Usage stories.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly related PRs

  • Guria/modern-stack#12: Both PRs modify src/shared/test/actor.ts's BaseActor interface, with this PR removing the blur() method while the retrieved PR refactors actor type and method signatures.
  • Guria/modern-stack#4: This PR adds env.VITE_CONNECT_LOGGER="false" to test tasks in .config/mise/conf.d/tasks-test.toml, following prior Mise-based test task configuration work.

Poem

🐰 Stories bloom like garden rows,
Tests assert where the data flows,
Routes match before they run,
Abort errors caught—bugs undone! 🌿✨

🚥 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 describes the main changes: catching AbortErrors in Storybook tests and patching @reatom/core. It directly relates to the core objectives of fixing undetected AbortError messages and route loader evaluation issues.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ 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 fix/abort-error-guard-and-reatom-patch

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

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

@github-actions

github-actions Bot commented May 28, 2026

Copy link
Copy Markdown

Fallow audit report

No GitHub PR/MR findings.

Generated by fallow.

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Pull request overview

This PR reduces noisy AbortError output in Storybook browser tests and improves routing/loader performance by patching @reatom/core to avoid evaluating non-matching route loaders on URL changes.

Changes:

  • Add a Storybook test-time guard to collect and fail on unexpected Reatom AbortErrors, and fix Vitest detection in the browser context.
  • Patch @reatom/core@1001.0.0 to avoid proactively evaluating loaders for non-matching routes and to avoid forcing loader evaluation in isSomeLoaderPending.
  • Apply several test/stability/accessibility tweaks (timer input labeling, mobile master-detail navigation waits, disable connect logger in test tasks, simplify unread count atom).

Reviewed changes

Copilot reviewed 12 out of 13 changed files in this pull request and generated 1 comment.

Show a summary per file
File Description
src/setup.ts Enables connectLogger by default in dev unless explicitly disabled.
src/pages/timer/ui/TimerPage.tsx Adds accessible label for the custom duration input.
src/pages/timer/testing.ts Updates test locator to target the labeled timer input.
src/entities/conversation/model/unreadCount.ts Removes redundant connect hook from unread count async atom.
src/app/integration/Connections.stories.tsx Stabilizes mobile navigation test by waiting for list visibility after back navigation.
src/app/integration/Articles.stories.tsx Stabilizes mobile navigation test by waiting for list visibility after back navigation.
.storybook/setupStorybookUrl.ts Adds a pre-navigation hook so auth/setup runs before urlAtom.go().
.storybook/preview.tsx Integrates abort error guard + fixes Vitest detection + adds per-story abort error assertion.
.storybook/abortErrorGuard.ts Adds global onReject call hook to collect unexpected abort errors during Storybook tests.
patches/@reatom%2Fcore@1001.0.0.patch Patches Reatom routing internals to avoid non-matching loader evaluation and pending checks.
package.json Registers the @reatom/core patch via patchedDependencies.
bun.lock Records patchedDependencies entry for Bun.
.config/mise/conf.d/tasks-test.toml Disables connect logger in test-related tasks to reduce noisy output.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread .storybook/abortErrorGuard.ts Outdated
Comment on lines +28 to +30
// Reatom routing control flow — urlAtom proactively triggers all
// registered route loaders on every URL change, and nested loaders
// await their parents which can cause concurrent re-evaluation.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: a12c6cf666

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread patches/@reatom%2Fcore@1001.0.0.patch Outdated
if (url !== newUrl) {
_enqueue(() => {
- for (const [, routeAtom] of Object.entries(urlAtom.routes)) routeAtom.loader();
+ for (const [, routeAtom] of Object.entries(urlAtom.routes)) if (routeAtom()) routeAtom.loader();

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Preserve loader aborts when routes stop matching

When navigating away from a route with an in-flight loader, this guard skips calling that route's loader after routeAtom() becomes null. That removes the proactive recomputation that used to enter the loader, see the unmatch state, and trigger the withAbort/reject path; with the new isSomeLoaderPending short-circuit, unmatched loaders are also no longer kept subscribed via pending(). As a result, slow requests for the previous route can continue and fulfill into route.loader.data() after the route is no longer active instead of being aborted on navigation.

Useful? React with 👍 / 👎.

@Guria Guria force-pushed the fix/abort-error-guard-and-reatom-patch branch 3 times, most recently from 6598873 to 878a1f4 Compare May 29, 2026 00:00
Uses the pkg.pr.new preview build from reatom/reatom#1294 which fixes
the root cause: onReject no longer fires for abort rejections (aborts
route to onSettle instead). Drops the local @reatom/core patch.

AbortError guard (.storybook/abortErrorGuard.ts) hooks every
`.onReject` action via addGlobalExtension + withCallHook. The global
beforeEach drains collected errors after each story test and throws on
unexpected AbortErrors. assertNoRouteLoaderAbortErrors proves the fix:
navigation teardowns must NOT surface loader AbortErrors on onReject.
This is a positive regression test — fails loudly against unpatched
@reatom/core.

Other changes in this branch:
- Split Connections/Chat/Articles stories by route mode (detail,
  direct-url, list-request, list, navigation) to isolate abort
  attribution per story
- Route loader contract tests (reatomRouteContracts.test.ts)
- connectLogger: on by default in dev, off in test env
- conversationUnreadCountAtom: removed redundant withConnectHook
  causing double-fetch
- Storybook URL setup: auth before urlAtom.go() to prevent loader
  re-evaluations
- Timer: aria-label on custom duration input
- Vitest detection: __vitest_worker__ instead of broken
  import.meta.env.VITEST
- Actor: use tryTo instead of hopeThat for negative filter check
  (fixes soft-error leak); remove unused blur method

330/330 tests pass. Fallow health/dead-code/dupes clean.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@Guria Guria force-pushed the fix/abort-error-guard-and-reatom-patch branch from dfb065b to be5311b Compare June 13, 2026 09:31
@Guria Guria merged commit 306a04b into main Jun 13, 2026
0 of 2 checks passed
@Guria Guria deleted the fix/abort-error-guard-and-reatom-patch branch June 13, 2026 09:32
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.

2 participants