Files
supabase/apps/studio/components/interfaces/Storage/FilesBuckets/CreateBucketModal.test.tsx
T
6ef729cb5c feat(storage): versioning bucket modals (FE-4161) (#49205)
| # | Branch | Base |
| - | ------ | ---- |
| 1 | `feat/storage-versioning-private-alpha` — merged | `master` |
| 2 | `feat/storage-versioning/002-bucket-form-fields` | `master` |
| 3 | `feat/storage-versioning/003-bucket-modals` ◀ | 2 |
| 4 | `feat/storage-versioning/004-object-versions-data` | 3 |
| 5 | `feat/storage-versioning/005-file-preview-versions` | 4 |
| 6 | `feat/storage-versioning/006-billing-storage-retention` | 5 |
| 7 | `feat/storage-versioning/007-archived-objects-data` | 6 |
| 8 | `feat/storage-versioning/008-archived-rows` | 7 |
| 9 | `feat/storage-versioning/009-archived-preview-pane` | 8 |
| 10 | `feat/storage-versioning/010-replace-file` | 9 |

## [3/10] Storage object versioning: wire into the bucket modals

**Base:** `feat/storage-versioning/002-bucket-form-fields` (PR 2)

### This PR

Mounts the object-versioning form section in the create and edit bucket
modals behind the feature preview, and saves it.

- create and edit bucket modals spread `bucketVersioningFormFields` into
their existing form schema
- lifecycle defaults  to 30 days / 10 versions
- edit adds a confirmation before suspending an actively versioned
bucket

## Enabling object versioning on a new bucket and setting lifecycle
policies


https://github.com/user-attachments/assets/194f8319-4929-432e-8a50-206f180a77a8

## Edit and suspend object-versioning


https://github.com/user-attachments/assets/f31e1d34-9840-4f5a-a269-6a911214742d

## To reproduce

1. Make sure storage versioning is enabled under feature previews >
Storage Versioning
2. Open Storage Bucket File explorer
3. create new bucket and enable Object Versioning
4. set lifecycle policy
- Noncurrent version expiration: can be either empty or >1
- Retained noncurrent versions: can be either empty or between 1 and 100
and can't exist without "Noncurrent version expiration"
5. Open new bucket with object versioning and test changing lifecycle
policies
6. Disabling object-versioning shows proper warning and updates
`versioning_status` to SUSPENDED (it can never go back to DISABLED once
it has been enabled on a bucket)

---------

Co-authored-by: Claude Sonnet 5 <noreply@anthropic.com>
Co-authored-by: Ivan Vasilov <vasilov.ivan@gmail.com>
2026-10-07 16:38:35 +02:00

140 lines
4.9 KiB
TypeScript

import { fireEvent, screen, waitFor } from '@testing-library/dom'
import userEvent from '@testing-library/user-event'
import { beforeEach, describe, expect, it, vi } from 'vitest'
import { CreateBucketModal } from './CreateBucketModal'
import { ProjectContextProvider } from '@/components/layouts/ProjectLayout/ProjectContext'
import { customRender } from '@/tests/lib/custom-render'
import { addAPIMock } from '@/tests/lib/msw'
import { routerMock } from '@/tests/lib/route-mock'
vi.mock(`hooks/misc/useCheckPermissions`, () => ({
useCheckPermissions: vi.fn(),
useAsyncCheckPermissions: vi.fn().mockImplementation(() => ({ can: true })),
}))
const { versioningEnabled } = vi.hoisted(() => ({ versioningEnabled: { current: false } }))
vi.mock(
'@/components/interfaces/App/FeaturePreview/FeaturePreviewContext',
async (importOriginal) => {
const actual = await importOriginal<Record<string, unknown>>()
return { ...actual, useIsStorageVersioningEnabled: () => versioningEnabled.current }
}
)
describe(`CreateBucketModal`, () => {
beforeEach(() => {
versioningEnabled.current = false
// useParams
routerMock.setCurrentUrl(`/project/default/storage/buckets`)
// useSelectedProject -> Project
addAPIMock({
method: `get`,
path: `/platform/projects/:ref`,
// @ts-expect-error
response: {
cloud_provider: 'localhost',
id: 1,
inserted_at: '2021-08-02T06:40:40.646Z',
name: 'Default Project',
organization_id: 1,
ref: 'default',
region: 'local',
status: 'ACTIVE_HEALTHY',
},
})
// useBucketCreateMutation
addAPIMock({
method: `post`,
path: `/platform/storage/:ref/buckets`,
})
})
it(`renders a dialog with a form`, async () => {
customRender(
<ProjectContextProvider projectRef="default">
<CreateBucketModal open={true} onOpenChange={() => {}} />
</ProjectContextProvider>,
{
nuqs: {
searchParams: {
new: 'true',
},
},
}
)
await waitFor(() => {
expect(screen.getByRole(`dialog`)).toBeInTheDocument()
})
const nameInput = screen.getByLabelText(`Bucket name`)
await userEvent.type(nameInput, `test`)
const publicToggle = screen.getByLabelText(`Public bucket`)
expect(publicToggle).not.toBeChecked()
await userEvent.click(publicToggle)
expect(publicToggle).toBeChecked()
const sizeLimitToggle = screen.getByLabelText(`Restrict file size`)
expect(sizeLimitToggle).not.toBeChecked()
await userEvent.click(sizeLimitToggle)
expect(sizeLimitToggle).toBeChecked()
const sizeLimitInput = screen.getByLabelText(`File size limit`)
expect(sizeLimitInput).toHaveValue(null)
await userEvent.type(sizeLimitInput, `25`)
const sizeLimitUnitSelect = screen.getByLabelText(`File size limit unit`)
expect(sizeLimitUnitSelect).toHaveTextContent(`MB`)
await userEvent.click(sizeLimitUnitSelect)
const bytesOption = screen.getByRole(`option`, { name: `bytes` })
await userEvent.click(bytesOption)
expect(sizeLimitUnitSelect).toHaveTextContent(`bytes`)
const mimeTypeToggle = screen.getByLabelText(`Restrict MIME types`)
expect(mimeTypeToggle).not.toBeChecked()
await userEvent.click(mimeTypeToggle)
expect(mimeTypeToggle).toBeChecked()
const mimeTypeInput = screen.getByLabelText(`Allowed MIME types`)
expect(mimeTypeInput).toHaveValue(``)
await userEvent.type(mimeTypeInput, `image/jpeg, image/png`)
const submitButton = screen.getByRole(`button`, { name: `Create` })
fireEvent.click(submitButton)
})
it('keeps the bucket it created when the retention policy is rejected', async () => {
versioningEnabled.current = true
const postBucket = vi.fn(() => Response.json({ name: 'versioned' }))
addAPIMock({ method: 'post', path: '/platform/storage/:ref/buckets', response: postBucket })
addAPIMock({
method: 'put',
path: '/platform/storage/:ref/buckets/:id/lifecycle',
response: () => Response.json({ message: 'Lifecycle is not available' }, { status: 500 }),
})
const onOpenChange = vi.fn()
customRender(
<ProjectContextProvider projectRef="default">
<CreateBucketModal open onOpenChange={onOpenChange} />
</ProjectContextProvider>
)
await userEvent.type(await screen.findByLabelText('Bucket name'), 'versioned')
await userEvent.click(screen.getByRole('switch', { name: 'Object versioning' }))
// Submitted off the form itself: jsdom doesn't associate a button with a form by
// attribute, so clicking the footer's Create submits nothing.
fireEvent.submit(document.getElementById('create-storage-bucket-form')!)
// The bucket exists, so the modal closes rather than inviting a retry that would
// only collide on the name.
await waitFor(() => expect(onOpenChange).toHaveBeenCalledWith(false))
expect(postBucket).toHaveBeenCalledTimes(1)
})
})