Files
supabase/apps/studio/data/content/content-delete-mutation.test.ts
Jordi Enric 4c011cf9c0 feat(reports): add optimistic delete for custom reports (#46803)
## Problem

Deleting a custom report waited for the API round trip before updating
the UI. The confirmation modal showed a loading spinner, the report
stayed visible in the sidebar until the request resolved, and the
interaction felt sluggish.

## Fix

The delete now applies optimistically. On confirm, the report is removed
from the sidebar immediately and the user is navigated away. The actual
delete runs in the background. If it fails, the cached list is rolled
back to its previous state and an error toast is shown.

The optimistic behavior lives inside `useContentDeleteMutation` (via
`onMutate` snapshot + `onError` rollback), so any current or future
caller of that hook gets it for free, no per-call wiring required.

## How to test

- Open a project with at least one custom report
- Click the kebab menu on a report and choose Delete report, then
confirm
- Expected result: the report disappears from the sidebar instantly and
a success toast appears
- To test rollback: throttle/offline the network or force the delete
endpoint to fail, then delete again
- Expected result: the report reappears in the sidebar and an error
toast is shown

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

* **Improvements**
* Deletion flows now provide explicit loading, success and error
feedback; UI updates immediately on delete and will restore if the
action fails.

* **Removals**
* Removed the reports menu and individual report menu item UI components
(affects report-level rename/delete dropdowns and related menu
navigation).

* **Tests**
* Added tests covering content deletion behavior, multiple-deletion
cases, and data integrity after removals.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->

---------

Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-15 09:57:19 +02:00

38 lines
1.3 KiB
TypeScript

import { describe, expect, it } from 'vitest'
import { removeContentFromList } from './content-delete-mutation'
import type { ContentData } from './content-query'
const makeListData = (ids: string[]): ContentData => ({
cursor: 'next-cursor',
content: ids.map((id) => ({ id, name: id, type: 'report' })) as ContentData['content'],
})
describe('removeContentFromList', () => {
it('removes a single matching id', () => {
const result = removeContentFromList(makeListData(['a', 'b', 'c']), ['b'])
expect(result.content.map((item) => item.id)).toEqual(['a', 'c'])
})
it('removes multiple matching ids', () => {
const result = removeContentFromList(makeListData(['a', 'b', 'c']), ['a', 'c'])
expect(result.content.map((item) => item.id)).toEqual(['b'])
})
it('leaves the list unchanged when no ids match', () => {
const result = removeContentFromList(makeListData(['a', 'b']), ['x'])
expect(result.content.map((item) => item.id)).toEqual(['a', 'b'])
})
it('preserves sibling fields such as cursor', () => {
const result = removeContentFromList(makeListData(['a']), ['a'])
expect(result.cursor).toBe('next-cursor')
})
it('does not mutate the input data', () => {
const input = makeListData(['a', 'b'])
removeContentFromList(input, ['a'])
expect(input.content.map((item) => item.id)).toEqual(['a', 'b'])
})
})