import { expect } from '@playwright/test' import path from 'path' import { test } from '../utils/test.js' import { waitForApiResponse } from '../utils/wait-for-response.js' import { createBucket, createFolder, deleteAllBuckets, deleteBucket, deleteItem, downloadFile, navigateToBucket, renameItem, uploadFile, } from '../utils/storage-helpers.js' import { dismissToastsIfAny } from '../utils/dismiss-toast.js' const bucketNamePrefix = 'pw_bucket' test.describe.serial('Storage', () => { test.beforeEach(async ({ page, ref }) => { await deleteAllBuckets(page, ref) }) test('can navigate to storage page', async ({ page, ref }) => { await expect( page.getByRole('button', { name: 'New bucket' }), 'New bucket button should be visible' ).toBeVisible() // Verify we're on the storage files page await expect(page).toHaveURL(new RegExp(`/project/${ref}/storage/files`)) }) test('can create a private bucket', async ({ page, ref }) => { const bucketName = `${bucketNamePrefix}_private` await createBucket(page, ref, bucketName, false) // Verify it's marked as private (no "Public" badge should be visible) const bucketRow = page.getByRole('row').filter({ hasText: bucketName }) await expect(bucketRow, 'Bucket row should be visible').toBeVisible() await expect( bucketRow.getByText('Public'), 'Private bucket should not have Public badge' ).not.toBeVisible() }) test('can create a public bucket', async ({ page, ref }) => { const bucketName = `${bucketNamePrefix}_public` await createBucket(page, ref, bucketName, true) // Verify it's marked as public - wait for the badge to appear const bucketRow = page.getByRole('row').filter({ hasText: bucketName }) await expect(bucketRow, 'Bucket row should be visible').toBeVisible() // The Public badge should be visible within the bucket row await expect( bucketRow.getByText('Public', { exact: true }), 'Bucket should be marked as Public' ).toBeVisible() }) test('can edit bucket settings', async ({ page, ref }) => { const bucketName = `${bucketNamePrefix}_edit` // Create a private bucket await createBucket(page, ref, bucketName, false) // Navigate to the bucket await navigateToBucket(page, ref, bucketName) // Open edit bucket dropdown await page.getByRole('button', { name: 'Edit bucket' }).click() await page.getByRole('menuitem', { name: 'Bucket settings' }).click() // Toggle public setting const publicToggle = page.getByRole('switch', { name: 'Public bucket' }) await expect(publicToggle, 'Public toggle should be visible').toBeVisible() await publicToggle.click() // Save changes const apiPromise = waitForApiResponse(page, 'storage', ref, `buckets/${bucketName}`, { method: 'PATCH', }) await page.getByRole('button', { name: 'Save' }).click() await apiPromise // Verify the bucket is now public await expect( page.getByText('Public').first(), 'Bucket should now be marked as Public' ).toBeVisible() }) test('can delete a bucket', async ({ page, ref }) => { const bucketName = `${bucketNamePrefix}_delete` // Create a bucket await createBucket(page, ref, bucketName, false) // Delete it await deleteBucket(page, ref, bucketName) // Verify it's gone await expect( page.getByRole('row').filter({ hasText: bucketName }), 'Bucket should not be visible after deletion' ).not.toBeVisible() }) test('can search for buckets', async ({ page, ref }) => { const bucketName1 = `${bucketNamePrefix}_search_1` const bucketName2 = `${bucketNamePrefix}_search_2` // Create two buckets await createBucket(page, ref, bucketName1, false) await dismissToastsIfAny(page) await createBucket(page, ref, bucketName2, false) // Search for first bucket const searchInput = page.getByPlaceholder('Search for a bucket') await searchInput.fill('search_1') // Verify only first bucket is visible await expect( page.getByRole('row').filter({ hasText: bucketName1 }), 'First bucket should be visible in search results' ).toBeVisible() await expect( page.getByRole('row').filter({ hasText: bucketName2 }), 'Second bucket should not be visible in search results' ).not.toBeVisible() // Clear search await searchInput.clear() // Verify both buckets are visible await expect( page.getByRole('row').filter({ hasText: bucketName1 }), 'First bucket should be visible after clearing search' ).toBeVisible() await expect( page.getByRole('row').filter({ hasText: bucketName2 }), 'Second bucket should be visible after clearing search' ).toBeVisible() }) test('can upload a file', async ({ page, ref }) => { const bucketName = `${bucketNamePrefix}_upload` const fileName = 'test-file.txt' // Create a bucket and navigate to it await createBucket(page, ref, bucketName, false) await navigateToBucket(page, ref, bucketName) // Upload a file const filePath = path.join(import.meta.dirname, 'files', fileName) await uploadFile(page, filePath, fileName) // Clean up await deleteBucket(page, ref, bucketName) }) test('can create a folder', async ({ page, ref }) => { const bucketName = `${bucketNamePrefix}_folder` const folderName = 'test_folder' // Create a bucket and navigate to it await createBucket(page, ref, bucketName, false) await navigateToBucket(page, ref, bucketName) // Create a folder await createFolder(page, folderName) }) test('can rename a file', async ({ page, ref }) => { const bucketName = `${bucketNamePrefix}_rename_file` const fileName = 'test-file.txt' const newFileName = 'renamed-file.txt' // Create a bucket, navigate to it, and upload a file await createBucket(page, ref, bucketName, false) await navigateToBucket(page, ref, bucketName) const filePath = path.join(import.meta.dirname, 'files', fileName) await uploadFile(page, filePath, fileName) // Rename the file await renameItem(page, fileName, newFileName) // Clean up await deleteBucket(page, ref, bucketName) }) test('can rename a folder', async ({ page, ref }) => { const bucketName = `${bucketNamePrefix}_rename_folder` const folderName = 'old_folder' const newFolderName = 'new_folder' // Create a bucket, navigate to it, and create a folder await createBucket(page, ref, bucketName, false) await navigateToBucket(page, ref, bucketName) await createFolder(page, folderName) // Rename the folder await renameItem(page, folderName, newFolderName) }) test('can delete a file', async ({ page, ref }) => { const bucketName = `${bucketNamePrefix}_delete_file` const fileName = 'test-file.txt' // Create a bucket, navigate to it, and upload a file await createBucket(page, ref, bucketName, false) await navigateToBucket(page, ref, bucketName) const filePath = path.join(import.meta.dirname, 'files', fileName) await uploadFile(page, filePath, fileName) // Delete the file await deleteItem(page, fileName) }) test('can delete a folder', async ({ page, ref }) => { const bucketName = `${bucketNamePrefix}_delete_folder` const folderName = 'test_folder' // Create a bucket, navigate to it, and create a folder await createBucket(page, ref, bucketName, false) await navigateToBucket(page, ref, bucketName) await createFolder(page, folderName) // Delete the folder await deleteItem(page, folderName) }) test('can download a file', async ({ page, ref }) => { const bucketName = `${bucketNamePrefix}_download` const fileName = 'test-file.txt' // Create a bucket, navigate to it, and upload a file await createBucket(page, ref, bucketName, false) await navigateToBucket(page, ref, bucketName) const filePath = path.join(import.meta.dirname, 'files', fileName) await uploadFile(page, filePath, fileName) // Download the file await downloadFile(page, fileName) }) })