Files
Charis d453e57086 test(sql-editor): characterization tests for SQLEditor (decompose 1/6) (#47820)
## Summary

Add some tests for the SQL editor so I can refactor it without
regressions. Tests are not best practice because they are intended to be
temporary and improving them would require refactoring first (currently
they are over-mocking and asserting on internal details).

Stacked on top of #47792 (`charislam/sql-editor-top-bar-controls`).

## What this adds

`apps/studio/tests/components/SQLEditor/SQLEditor.test.tsx` (11 tests):

- Run success → `addResult` + Results tab; EXPLAIN-shaped result
auto-switches to the explain tab; a non-EXPLAIN run switches back.
- Run error with `position` → error-highlight line math +
`deltaDecorations` + `revealLineInCenter`; the next run clears the
highlight.
- Run button refocuses the editor; disabled + short-circuits while a
diff is open.
- Diff request queued before mount drains exactly once (one-shot; no
re-apply on remount).
- Ask-AI widget renders only while the prompt is open (render-time
`editorRef.current` read).
- Destructive query → warning modal → confirm forces the re-run;
confirm-with-RLS appends enable-RLS statements.

## Test approach

Real Monaco / DiffEditor are replaced with lightweight fakes exposing a
controllable editor; child panels + orthogonal context hooks are
stubbed; the execute mutation runs for real against an MSW-mocked
`/platform/pg-meta/:ref/query`. Tests assert on public behavior so they
survive the internal refactor unchanged.

## Verification

- `pnpm --filter studio exec vitest run
tests/components/SQLEditor/SQLEditor.test.tsx` — 11/11 pass (stable
across repeated runs)
- `pnpm --filter studio typecheck` — clean
- `eslint` — 0 errors

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

## Summary by CodeRabbit

* **Tests**
* Added comprehensive coverage for SQL editor behavior, including query
execution, result and explain views, error highlighting, editor focus,
and diff mode.
* Added validation for destructive-query confirmations, including RLS
confirmation flows.
* Added coverage for queued diff requests and conditional AI prompt
display.

<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2026-07-10 10:47:45 -04:00
..

UI Testing Notes

Rules

  • All tests should be run consistently (avoid situations whereby tests fails "sometimes")

  • Group tests in folders based on the feature they are testing. Avoid file/folder based folder names since those can change and we will forget to update the tests.

Examples: /logs /reports /projects /database-settings /auth

Custom Render and Custom Render Hook

customRender and customRenderHook are wrappers around render and renderHook that add some necessary providers like QueryClientProvider, TooltipProvider and NuqsTestingAdapter.

Generally use those instead of the default render and renderHook functions.

import { customRender, customRenderHook } from 'tests/lib/custom-render'

customRender(<MyComponent />)
customRenderHook(() => useMyHook())

Mocking API Requests

To mock API requests, we use the msw library.

Global mocks can be found in tests/lib/msw-global-api-mocks.ts.

To mock an endpoint you can use the addAPIMock function. Make sure to add the mock in the beforeEach hook. It won't work with beforeAll if you have many tests.

beforeEach(() => {
  addAPIMock({
    method: 'get',
    path: '/api/my-endpoint',
    response: {
      data: { foo: 'bar' },
    },
  })
})

API Mocking Tips:

  • Keep mocks in the same folder as the tests that use them
  • Add a test to verify the mock is working

This will make debugging and updating the mocks easier.

test('mock is working', async () => {
  const response = await fetch('/api/my-endpoint')
  expect(response.json()).resolves.toEqual({ data: { foo: 'bar' } })
})

Mocking Nuqs URL Parameters

To render a component that uses Nuqs with some predefined query parameters, you can use customRender with the nuqs prop.


customRender(<MyComponent />, {
  nuqs: {
    searchParams: {
      search: 'hello world',
    },
  },
})

<Popover> vs <Dropdown>

When simulating clicks on these components, do the following:

// for Popovers
import userEvent from '@testing-library/user-event'
await userEvent.click('Hello world')

// for Dropdowns
import clickDropdown from 'tests/helpers'
clickDropdown('Hello world')