diff --git a/app/pages/project/instances/instance/tabs/StorageTab.tsx b/app/pages/project/instances/instance/tabs/StorageTab.tsx index dccfbae66..64d3eff51 100644 --- a/app/pages/project/instances/instance/tabs/StorageTab.tsx +++ b/app/pages/project/instances/instance/tabs/StorageTab.tsx @@ -25,7 +25,8 @@ import { Button, EmptyMessage, Storage24Icon } from '@oxide/ui' import { DiskStatusBadge } from 'app/components/StatusBadge' import AttachDiskSideModalForm from 'app/forms/disk-attach' import { CreateDiskSideModalForm } from 'app/forms/disk-create' -import { getInstanceSelector, useInstanceSelector, useToast } from 'app/hooks' +import { getInstanceSelector, useInstanceSelector } from 'app/hooks' +import { addToast } from 'app/stores/toast' import { fancifyStates } from './common' @@ -53,7 +54,6 @@ export function StorageTab() { const [showDiskCreate, setShowDiskCreate] = useState(false) const [showDiskAttach, setShowDiskAttach] = useState(false) - const addToast = useToast() const queryClient = useApiQueryClient() const { instance: instanceName, project } = useInstanceSelector() const instancePathQuery = useMemo( @@ -61,11 +61,30 @@ export function StorageTab() { [instanceName, project] ) - const detachDisk = useApiMutation('instanceDiskDetach', {}) + const detachDisk = useApiMutation('instanceDiskDetach', { + onSuccess() { + queryClient.invalidateQueries('instanceDiskList') + addToast({ content: 'Disk detached' }) + }, + onError(err) { + addToast({ + title: 'Failed to detach disk', + content: err.message, + variant: 'error', + }) + }, + }) const createSnapshot = useApiMutation('snapshotCreate', { onSuccess() { queryClient.invalidateQueries('snapshotList') - addToast({ content: 'Snapshot successfully created' }) + addToast({ content: 'Snapshot created' }) + }, + onError(err) { + addToast({ + title: 'Failed to create snapshot', + content: err.message, + variant: 'error', + }) }, }) @@ -97,18 +116,11 @@ export function StorageTab() { <>Instance must be in state {detachableStates} before disk can be detached ), onActivate() { - detachDisk.mutate( - { body: { disk: disk.name }, ...instancePathQuery }, - { - onSuccess: () => { - queryClient.invalidateQueries('instanceDiskList') - }, - } - ) + detachDisk.mutate({ body: { disk: disk.name }, ...instancePathQuery }) }, }, ], - [detachDisk, instance, queryClient, instancePathQuery, createSnapshot, project] + [detachDisk, instance, instancePathQuery, createSnapshot, project] ) const attachDisk = useApiMutation('instanceDiskAttach', { diff --git a/app/test/e2e/instance/disks.e2e.ts b/app/test/e2e/instance/disks.e2e.ts index 99d08a3d0..e1a3fa221 100644 --- a/app/test/e2e/instance/disks.e2e.ts +++ b/app/test/e2e/instance/disks.e2e.ts @@ -66,11 +66,27 @@ test('Attach disk', async ({ page }) => { await expectVisible(page, ['role=cell[name="disk-3"]']) }) +test('Detach disk', async ({ page }) => { + await page.goto('/projects/mock-project/instances/db1') + + // Have to stop instance to edit disks + await stopInstance(page) + + const successMsg = page.getByText('Disk detached').nth(0) + const row = page.getByRole('row', { name: 'disk-1' }) + await expect(row).toBeVisible() + await expect(successMsg).toBeHidden() + + await clickRowAction(page, 'disk-1', 'Detach') + await expect(successMsg).toBeVisible() + await expect(row).toBeHidden() // disk row goes away +}) + test('Snapshot disk', async ({ page }) => { await page.goto('/projects/mock-project/instances/db1') // have to use nth with toasts because the text shows up in multiple spots - const successMsg = page.getByText('Snapshot successfully created').nth(0) + const successMsg = page.getByText('Snapshot created').nth(0) await expect(successMsg).toBeHidden() await clickRowAction(page, 'disk-2', 'Snapshot') diff --git a/libs/api-mocks/msw/handlers.ts b/libs/api-mocks/msw/handlers.ts index 6433690f8..acac0a89b 100644 --- a/libs/api-mocks/msw/handlers.ts +++ b/libs/api-mocks/msw/handlers.ts @@ -438,7 +438,7 @@ export const handlers = makeHandlers({ instanceDiskDetach({ body, path, query: projectParams }) { const instance = lookup.instance({ ...path, ...projectParams }) if (instance.run_state !== 'stopped') { - throw 'Cannot detach disk to instance that is not stopped' + throw 'Cannot detach disk from instance that is not stopped' } const disk = lookup.disk({ ...projectParams, disk: body.disk }) disk.state = { state: 'detached' }