diff --git a/apps/studio/components/grid/components/grid/Grid.tsx b/apps/studio/components/grid/components/grid/Grid.tsx index 04cad32dd0b..d5ba2b094b7 100644 --- a/apps/studio/components/grid/components/grid/Grid.tsx +++ b/apps/studio/components/grid/components/grid/Grid.tsx @@ -89,11 +89,12 @@ export const Grid = memo( const tableEntityType = snap.originalTable?.entity_type const isForeignTable = tableEntityType === ENTITY_TYPE.FOREIGN_TABLE const isTableEmpty = (rows ?? []).length === 0 + const canImportData = snap.editable && !isForeignTable const track = useTrack() const { isDraggedOver, onDragOver, onFileDrop } = useCsvFileDrop({ - enabled: isTableEmpty && !isForeignTable, + enabled: isTableEmpty && canImportData, onFileDropped: (file) => tableEditorSnap.onImportData(valtioRef(file)), onTelemetryEvent: (eventName) => track(eventName), }) @@ -322,7 +323,7 @@ export const Grid = memo( started.

- ) : ( + ) : canImportData ? (
- )} + ) : null} ) : (
diff --git a/e2e/studio/features/table-editor.spec.ts b/e2e/studio/features/table-editor.spec.ts index e149f30fd3d..0d1688e34ca 100644 --- a/e2e/studio/features/table-editor.spec.ts +++ b/e2e/studio/features/table-editor.spec.ts @@ -1,7 +1,5 @@ import fs from 'fs' import path from 'path' -import { expect, Page } from '@playwright/test' - import { env } from '../env.config.js' import { expectClipboardValue } from '../utils/clipboard.js' import { dropTable, query } from '../utils/db/index.js' @@ -16,6 +14,7 @@ import { waitForGridDataToLoad, waitForTableToLoad, } from '../utils/wait-for-response.js' +import { expect, Page } from '@playwright/test' const deleteTable = async (page: Page, ref: string, tableName: string) => { const viewLocator = page.getByLabel(`View ${tableName}`) @@ -155,6 +154,37 @@ testRunner('table editor', () => { await expect(page.getByLabel(`View ${authTableMfa}`)).toBeVisible() }) + test('protected schema empty tables do not expose CSV import actions', async ({ page, ref }) => { + const emptyAuthTables = await query<{ relname: string }>(` + select relname + from pg_stat_user_tables + where schemaname = 'auth' + and n_live_tup = 0 + and relname <> 'schema_migrations' + order by relname + limit 1; + `) + test.skip(emptyAuthTables.length === 0, 'Requires an empty auth table in the test dataset') + const [{ relname: tableName }] = emptyAuthTables + + await page.goto(toUrl(`/project/${ref}/editor?schema=public`)) + + await page.getByTestId('schema-selector').click() + await page.getByPlaceholder('Find schema...').fill('auth') + + const tableLoadPromise = waitForTableToLoad(page, ref, 'auth') + await page.getByRole('option', { name: 'auth' }).click() + await tableLoadPromise + + await expect(page.getByRole('button', { name: `View ${tableName}`, exact: true })).toBeVisible() + await page.getByRole('button', { name: `View ${tableName}`, exact: true }).click() + await page.waitForURL(/\/editor\/\d+\?schema=auth$/) + + await expect(page.getByText('This table is empty')).toBeVisible() + await expect(page.getByRole('button', { name: 'Import data from CSV' })).not.toBeVisible() + await expect(page.getByText('or drag and drop a CSV file here')).not.toBeVisible() + }) + test('should show rls accordingly', async ({ page, ref }) => { const tableNameRlsEnabled = 'pw_table_rls_enabled' const tableNameRlsDisabled = 'pw_table_rls_disabled'