Files
supabase/apps/studio/components/interfaces/ErrorHandling
1966209483 chore(deps): upgrade vitest to v5 (#49994)
Upgrades Vitest from 4.1.4 to 5.0.0 across the monorepo, fixes the
handful of things v5 turned into hard errors, and drops the
`vi.clearAllMocks()` boilerplate that v5's `clearMocks` default makes
redundant.

**Changed:**
- `vitest`, `@vitest/ui`, `@vitest/coverage-v8` 4.1.4 → 5.0.0 (catalog)
- `vi.mock` calls that lived inside `beforeAll`/`beforeEach`/test bodies
moved to module scope (v5 throws on nested calls). Affects the Studio
and docs setup files and four Studio tests.
- `detectBrowser` test restores `navigator` via `vi.unstubAllGlobals()`
instead of assigning `global.navigator`, which now reaches jsdom's
getter-only property.
- `RowEditor.utils.test.ts` restores its `JSON.stringify` spy. It used
to leak a throwing mock for the rest of the file, which v5's coverage
provider now trips over. A later test in the same file had been
asserting the leak's side effect (valid JSON reported as invalid) and
now asserts the correct behavior.
- `@testing-library/jest-dom` 6.6 → 7.0.1. Its vitest type augmentation
resolves through a peer now, so it lands on each package's own `vitest`
instead of whichever copy pnpm hoisted. Fixes `toBeInTheDocument` type
errors in dev-tools after the reshuffle.
- `@testing-library/react` 16.0.0 → 16.3.3 for the React 19 peer range.
- `vite: catalog:` added to dev-tools, www, and common. Without it they
resolved a newer vite than the catalog pin, which forked a second vitest
instance in the lockfile. There's now one.
- ai-commands custom matcher types use v5's `Matchers<R, T>` form.
- 110 test files: `vi.clearAllMocks()` removed from
`beforeEach`/`afterEach` hooks, along with hooks that only did that and
the imports they left unused. Calls that also reset/restore mocks are
untouched. Second commit, mechanical.

**Added:**
- `.vitest/` to the root gitignore (v5 writes JSON/JUnit/HTML reporter
output there)

**Removed:**
- `vite-tsconfig-paths` catalog entry and deps. Vitest 5 resolves
tsconfig paths itself.

Release-age note: this sat in draft with a temporary
`minimumReleaseAgeExclude` entry for `vitest` and `@vitest/*` while
5.0.0 was inside the workspace's 3-day `minimumReleaseAge` window. That
window has closed, so the exclusion is gone and nothing bypasses the
release-age gate.

**Perf** (local, medians of 3 runs, same machine):

| Suite | v4.1.4 | v5.0.0 |
|---|---|---|
| studio | 144.1s | 141.7s (-2%) |
| studio `--coverage` | 156.9s | 146.4s (-7%) |
| ui-patterns | 6.27s | 5.07s (-19%) |
| ui `--coverage` | 3.35s | 2.14s (-36%) |
| www | 0.89s | 0.47s (-47%) |

Studio is dominated by jsdom environment setup per file, which v5
doesn't change. `vitest doctor` recommends keeping the current pool
config: the vm pools and `isolate: false` all break tests.

## To test

- `pnpm install --frozen-lockfile` succeeds with no
`minimumReleaseAgeExclude` entry for vitest.
- CI: Studio unit tests, ui, ui-patterns, www, docs, and typecheck/lint
should all be green. The lint ratchet was checked locally: warning
counts on touched Studio files are identical to master.
- `pnpm test:studio` locally passes with coverage (588 files, 6240
tests).
- Open a Studio test that uses `toBeInTheDocument` in your editor and
confirm no type errors on jest-dom matchers, in Studio and in
`packages/dev-tools`.
- Known pre-existing failures unrelated to this PR: one dev-tools test
(`getEventCountBadge` capped pill) fails on master too.


<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit

## Tests
- Improved test coverage for JSON validation and mobile navigation
behavior.
- Updated test setup, cleanup, environment configuration, and matcher
support across application and shared package suites.
- Removed obsolete coverage for alternate MCP transport selection.

## Chores
- Streamlined TypeScript path resolution and Vitest reporter output
handling.
- Updated testing libraries and Vitest tooling across documentation,
Studio, website, and shared packages.
- Added Vitest reporter output to ignored files.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->

---------

Co-authored-by: Alaister Young <10985857+alaister@users.noreply.github.com>
Co-authored-by: Ivan Vasilov <vasilov.ivan@gmail.com>
2026-09-10 16:45:54 +08:00
..

Error Handling

ErrorMatcher displays a typed API error. If the error was classified by handleError (i.e. it is an instance of a known error class), it shows matching troubleshooting steps. Otherwise it shows a generic error card.

Classification happens in the data layer — handleError in data/fetchers.ts matches the error message against patterns and throws the appropriate error subclass (e.g. ConnectionTimeoutError). The component never does regex matching itself.

The title always comes from the caller — the same error type can appear on different pages with different titles.

Usage

import { ErrorMatcher } from 'components/interfaces/ErrorHandling/ErrorMatcher'

{
  isError && (
    <ErrorMatcher title="Failed to load tables" error={error} supportFormParams={{ projectRef }} />
  )
}

Pass the full error object from React Query — not error.message. This lets ErrorMatcher check the error class and show the right troubleshooting steps.

Props

Prop Type Description
title string Displayed in the error card header. Set by the caller.
error string | { message: string } The error from React Query (pass the full object, not .message).
supportFormParams Partial<SupportFormUrlKeys> Typed params for the support form URL (projectRef, category…).
className string? Extra classes on the card.

supportFormParams is typed as Partial<SupportFormUrlKeys> — autocomplete shows all available fields (projectRef, orgSlug, category, subject, message, error, sid). The URL is built by createSupportFormUrl() from SupportForm.utils.tsx.

Adding a new error mapping

1. Add the error class to types/api-errors.ts

export type KnownErrorType = 'connection-timeout' | 'your-error'

export class YourError extends ResponseError {
  readonly errorType = 'your-error' as const
}

export type ClassifiedError = ConnectionTimeoutError | FailedToRetrieveProjectsError | YourError

2. Add a pattern entry to data/error-patterns.ts

import { YourError } from 'types/api-errors'

export const ERROR_PATTERNS: ErrorPattern[] = [
  // existing...
  {
    pattern: /YOUR_ERROR_PATTERN/i,
    ErrorClass: YourError,
  },
]

handleError picks this up automatically — any matching API error will be thrown as a YourError instance.

3. Create errorMappings/YourError.tsx

import { SIDEBAR_KEYS } from 'components/layouts/ProjectLayout/LayoutSidebar/LayoutSidebarProvider'
import { useAiAssistantStateSnapshot } from 'state/ai-assistant-state'
import { useSidebarManagerSnapshot } from 'state/sidebar-manager-state'

import { TroubleshootingAccordion } from '../TroubleshootingAccordion'
import {
  FixWithAITroubleshootingSection,
  TroubleshootingGuideSection,
} from '../TroubleshootingSections'
import { DOCS_URL } from '@/lib/constants'

const ERROR_TYPE = 'your-error'
const BUILD_PROMPT = () => `Describe the issue for the AI assistant.`

export function YourErrorTroubleshooting() {
  const { openSidebar } = useSidebarManagerSnapshot()
  const aiSnap = useAiAssistantStateSnapshot()

  return (
    <TroubleshootingAccordion
      errorType={ERROR_TYPE}
      stepTitles={{ 1: 'Troubleshooting guide', 2: 'Debug with AI' }}
    >
      <TroubleshootingGuideSection
        number={1}
        errorType={ERROR_TYPE}
        href={`${DOCS_URL}/guides/...`}
      />
      <FixWithAITroubleshootingSection
        number={2}
        errorType={ERROR_TYPE}
        buildPrompt={BUILD_PROMPT}
        onDebugWithAI={(prompt) => {
          openSidebar(SIDEBAR_KEYS.AI_ASSISTANT)
          aiSnap.newChat({ initialMessage: prompt })
        }}
      />
    </TroubleshootingAccordion>
  )
}

4. Add it to error-mappings.tsx

import { YourErrorTroubleshooting } from './errorMappings/YourError'

export const ERROR_MAPPINGS: Record<KnownErrorType, ErrorMapping> = {
  // existing...
  'your-error': {
    id: 'your-error',
    Troubleshooting: YourErrorTroubleshooting,
  },
}

That's it. ErrorMatcher picks it up automatically.

Available section components

Component Props
RestartDatabaseTroubleshootingSection number, errorType, onRestartProject?
TroubleshootingGuideSection number, errorType, href, title?, description?
FixWithAITroubleshootingSection number, errorType, buildPrompt, onDebugWithAI?