Make table editor tests setup faster

This commit is contained in:
Gildas Garcia committed 2026-03-03 12:01:33 +01:00
1 parent ec3b6238ba
commit 33fbdbbe8a
3 files changed
+195 -247

No files matched your search

+151 -247
View File
@@ -3,10 +3,10 @@ import path from 'path'
import { expect, Page } from '@playwright/test'
import { env } from '../env.config.js'
import { createTable as dbCreateTable, dropTable } from '../utils/db/index.js'
import { releaseFileOnceCleanup, withFileOnceSetup } from '../utils/once-per-file.js'
import { dropTable } from '../utils/db/index.js'
import { createTable, createTableWithRLS } from '../utils/db/queries.js'
import { resetLocalStorage } from '../utils/reset-local-storage.js'
import { test } from '../utils/test.js'
import { test, withSetupCleanup } from '../utils/test.js'
import { toUrl } from '../utils/to-url.js'
import { waitForApiResponseWithTimeout } from '../utils/wait-for-response-with-timeout.js'
import {
@@ -16,67 +16,6 @@ import {
waitForTableToLoad,
} from '../utils/wait-for-response.js'
const tableNamePrefix = 'pw_table'
const columnName = 'pw_column'
const dismissToastsIfAny = async (page: Page) => {
const closeButtons = page.getByRole('button', { name: 'Close toast' })
const count = await closeButtons.count()
for (let i = 0; i < count; i++) {
await closeButtons.nth(i).click()
}
}
const createTable = async (page: Page, ref: string, tableName: string) => {
// Ensure no toast overlays block the dialog trigger
await dismissToastsIfAny(page)
const newTableBtn = page.getByRole('button', { name: 'New table', exact: true })
await expect(newTableBtn).toBeVisible()
await newTableBtn.click()
// Check if we're in the unable to find table error
// If so, click Close tab or Head back first
const closeTabBtn = page.getByRole('button', { name: 'Close tab' })
const headBackBtn = page.getByRole('button', { name: 'Head back' })
if ((await closeTabBtn.count()) > 0) {
await closeTabBtn.click()
}
if ((await headBackBtn.count()) > 0) {
await headBackBtn.click()
}
const nameInput = page.getByTestId('table-name-input')
await expect(nameInput).toBeVisible()
await nameInput.fill(tableName)
await expect(nameInput).toHaveValue(tableName)
await page.getByTestId('created_at-extra-options').click()
await page.getByRole('checkbox', { name: 'Is Nullable' }).click()
await page.getByTestId('created_at-extra-options').click({ force: true })
await page.getByRole('button', { name: 'Add column' }).click()
await page.getByRole('textbox', { name: 'column_name' }).fill(columnName)
await page.getByText('Choose a column type...').click()
await page.getByRole('option', { name: 'text Variable-length' }).click()
const createTablePromise = waitForApiResponseWithTimeout(page, (response) =>
response.url().includes('query?key=table-create')
)
// Wait specifically for tables list refresh instead of generic networkidle
const tablesPromise = waitForApiResponseWithTimeout(page, (response) =>
response.url().includes('tables?include_columns=true&included_schemas=public')
)
// wait for tables to load, we don't need to wait here cause this response may complete before the table creation.
const entitiesPromise = waitForApiResponseWithTimeout(page, (response) =>
response.url().includes('query?key=entity-types-public-')
)
await page.getByRole('button', { name: 'Save' }).click()
await Promise.all([createTablePromise, tablesPromise, entitiesPromise])
await page.waitForSelector('[data-testid="table-editor-side-panel"]', { state: 'detached' })
await expect(
page.getByRole('button', { name: `View ${tableName}`, exact: true }),
'Table should be visible after creation'
).toBeVisible()
}
const deleteTable = async (page: Page, ref: string, tableName: string) => {
const viewLocator = page.getByLabel(`View ${tableName}`)
if ((await viewLocator.count()) === 0) return
@@ -119,56 +58,22 @@ const deleteEnumIfExist = async (page: Page, ref: string, enumName: string) => {
// Due to rate API rate limits run this test in serial mode on platform.
const testRunner = env.IS_PLATFORM ? test.describe.serial : test.describe
testRunner('table editor', () => {
test.beforeAll(async ({ browser, ref }) => {
await withFileOnceSetup(import.meta.url, async () => {
const ctx = await browser.newContext()
const page = await ctx.newPage()
const loadPromise = waitForTableToLoad(page, ref)
await page.goto(toUrl(`/project/${ref}/editor?schema=public`))
await loadPromise
const viewButtons = page.getByRole('button', { name: /^View / })
const names = await Promise.all(
(await viewButtons.all()).map(async (btn) => {
const ariaLabel = await btn.getAttribute('aria-label')
const name = ariaLabel ? ariaLabel.replace(/^View\s+/, '').trim() : ''
return name
})
)
const tablesToDelete = names.filter((tableName) => tableName.startsWith(tableNamePrefix))
for (const tableName of tablesToDelete) {
await deleteTable(page, ref, tableName)
await expect
.poll(async () => {
return await page.getByLabel(`View ${tableName}`, { exact: true }).count()
})
.toBe(0)
}
})
})
test.beforeEach(async ({ page, ref }) => {
const loadPromise = waitForTableToLoad(page, ref)
page.goto(toUrl(`/project/${ref}/editor?schema=public`))
await loadPromise
})
test.afterAll(async () => {
await releaseFileOnceCleanup(import.meta.url)
})
test('sidebar actions works as expected', async ({ page, ref }) => {
const tableNameActions = 'pw_table_actions'
const tableNameActionsDuplicate = 'pw_table_actions_duplicate'
// create table + verify that this exists.
await createTable(page, ref, tableNameActions)
await using _ = await withSetupCleanup(
() => createTableWithRLS(tableNameActions, 'pw_column'),
async () => {
await dropTable(tableNameActions)
await dropTable(tableNameActionsDuplicate)
}
)
await page.goto(toUrl(`/project/${ref}/editor?schema=public`))
// copies table name to clipboard when copy table name is clicked
await page.getByRole('button', { name: `View ${tableNameActions}`, exact: true }).click()
await page.waitForURL(/\/editor\/\d+\?schema=public$/)
await page
.getByRole('button', { name: `View ${tableNameActions}`, exact: true })
.getByRole('button')
@@ -218,6 +123,7 @@ testRunner('table editor', () => {
test('switching schemas work as expected', async ({ page, ref }) => {
const authTableSso = 'identities'
const authTableMfa = 'mfa_factors'
await page.goto(toUrl(`/project/${ref}/editor?schema=public`))
// change schema from public to auth
await page.getByTestId('schema-selector').click()
@@ -246,29 +152,21 @@ testRunner('table editor', () => {
const tableNameRlsDisabled = 'pw_table_rls_disabled'
// create table with RLS enabled and verify
await createTable(page, ref, tableNameRlsEnabled)
await using _ = await withSetupCleanup(
async () => {
await createTableWithRLS(tableNameRlsEnabled, 'pw_column')
await createTable(tableNameRlsDisabled, 'pw_column')
},
async () => {
await dropTable(tableNameRlsEnabled)
await dropTable(tableNameRlsDisabled)
}
)
await page.goto(toUrl(`/project/${ref}/editor?schema=public`))
await page.getByRole('button', { name: `View ${tableNameRlsEnabled}` }).click()
await expect(page.getByRole('link', { name: 'Add RLS policy' })).toBeVisible()
// create table with RLS disabled and verify
await page.getByRole('button', { name: 'New table', exact: true }).click()
await page.getByTestId('table-name-input').fill(tableNameRlsDisabled)
await page.getByLabel('Enable Row Level Security (').click()
await page.getByRole('button', { name: 'Confirm' }).click()
// Wait for table creation
const apiPromise = waitForApiResponse(
page,
'pg-meta',
ref,
'tables?include_columns=false&included_schemas=public'
)
// Wait for lints refresh
const lintsPromise = waitForApiResponse(page, 'projects', ref, 'run-lints')
await page.getByRole('button', { name: 'Save' }).click()
await apiPromise
await lintsPromise
await page.getByRole('button', { name: `View ${tableNameRlsDisabled}` }).click()
await expect(page.getByRole('button', { name: 'RLS disabled' })).toBeVisible()
})
@@ -357,7 +255,16 @@ testRunner('table editor', () => {
const columnNameUpdated = 'pw_column_updated'
// create a new table
await createTable(page, ref, tableNameGridEditor)
await using _ = await withSetupCleanup(
async () => {
await createTableWithRLS(tableNameGridEditor, 'pw_column')
},
async () => {
await dropTable(tableNameUpdated)
}
)
await page.goto(toUrl(`/project/${ref}/editor?schema=public`))
await page.getByRole('button', { name: `View ${tableNameGridEditor}`, exact: true }).click()
await page.waitForURL(/\/editor\/\d+\?schema=public$/)
@@ -397,7 +304,7 @@ testRunner('table editor', () => {
await expect(page.getByLabel(`View ${tableNameGridEditor}`, { exact: true })).not.toBeVisible()
await expect(page.getByRole('columnheader', { name: columnNameUpdated })).toBeVisible()
await expect(
page.getByRole('columnheader', { name: columnName, exact: true })
page.getByRole('columnheader', { name: 'pw_column', exact: true })
).not.toBeVisible()
// test export data via csv
@@ -495,12 +402,15 @@ testRunner('table editor', () => {
const tableName = 'pw_table_filtering'
const colName = 'pw_column'
if (!page.url().includes('/editor')) {
await page.goto(toUrl(`/project/${ref}/editor?schema=public`))
await waitForTableToLoad(page, ref)
}
await createTable(page, ref, tableName)
await using _ = await withSetupCleanup(
async () => {
await createTableWithRLS(tableName, 'pw_column')
},
async () => {
await dropTable(tableName)
}
)
await page.goto(toUrl(`/project/${ref}/editor?schema=public`))
await page.getByRole('button', { name: `View ${tableName}`, exact: true }).click()
await page.waitForURL(/\/editor\/\d+\?schema=public$/)
@@ -531,44 +441,45 @@ testRunner('table editor', () => {
await expect(page.getByRole('gridcell', { name: '789' })).toBeVisible()
await expect(page.getByRole('gridcell', { name: '456' })).not.toBeVisible()
await expect(page.getByRole('gridcell', { name: '123' })).not.toBeVisible()
await deleteTable(page, ref, tableName)
})
test('view table definition works as expected', async ({ page, ref }) => {
const tableName = 'pw_table_definition'
const colName = 'pw_column'
if (!page.url().includes('/editor')) {
const tableLoadPromise = waitForTableToLoad(page, ref)
await page.goto(toUrl(`/project/${ref}/editor?schema=public`))
await tableLoadPromise
}
await createTable(page, ref, tableName)
await using _ = await withSetupCleanup(
async () => {
await createTableWithRLS(tableName, 'pw_column')
},
async () => {
await dropTable(tableName)
}
)
await page.goto(toUrl(`/project/${ref}/editor?schema=public`))
await page.getByRole('button', { name: `View ${tableName}`, exact: true }).click()
await page.waitForURL(/\/editor\/\d+\?schema=public$/)
const apiPromise = waitForApiResponse(page, 'pg-meta', ref, 'query?key=table-definition-')
await page.getByText('definition', { exact: true }).click()
await apiPromise
await expect(page.locator('.view-lines')).toContainText(
`create table public.${tableName} ( id bigint generated by default as identity not null, created_at timestamp with time zone null default now(), ${colName} text null, constraint ${tableName}_pkey primary key (id)) TABLESPACE pg_default;`
)
await deleteTable(page, ref, tableName)
})
test('sorting rows works as expected', async ({ page, ref }) => {
const tableName = 'pw_table_sorting'
const colName = 'pw_column'
// Ensure we're on editor
if (!page.url().includes('/editor')) {
await page.goto(toUrl(`/project/${ref}/editor?schema=public`))
await waitForTableToLoad(page, ref)
}
// Create a small table and three rows
await createTable(page, ref, tableName)
await using _ = await withSetupCleanup(
async () => {
await createTableWithRLS(tableName, 'pw_column')
},
async () => {
await dropTable(tableName)
}
)
await page.goto(toUrl(`/project/${ref}/editor?schema=public`))
await page.getByRole('button', { name: `View ${tableName}`, exact: true }).click()
await page.waitForURL(/\/editor\/\d+\?schema=public$/)
for (const value of ['789', '456', '123']) {
await page.getByTestId('table-editor-insert-new-row').click()
@@ -600,23 +511,22 @@ testRunner('table editor', () => {
expect(await page.getByRole('gridcell').nth(3).textContent()).toBe('123')
expect(await page.getByRole('gridcell').nth(8).textContent()).toBe('456')
expect(await page.getByRole('gridcell').nth(13).textContent()).toBe('789')
// Cleanup
await deleteTable(page, ref, tableName)
})
test('column actions works as expected', async ({ page, ref }) => {
const tableName = 'pw_table_column_menu'
const colName = 'pw_column'
// Ensure we're on editor
if (!page.url().includes('/editor')) {
await page.goto(toUrl(`/project/${ref}/editor?schema=public`))
await waitForTableToLoad(page, ref)
}
// Create a small table and three rows
await createTable(page, ref, tableName)
await using _ = await withSetupCleanup(
async () => {
await createTableWithRLS(tableName, 'pw_column')
},
async () => {
await dropTable(tableName)
}
)
await page.goto(toUrl(`/project/${ref}/editor?schema=public`))
await page.getByRole('button', { name: `View ${tableName}`, exact: true }).click()
await page.waitForURL(/\/editor\/\d+\?schema=public$/)
@@ -627,9 +537,6 @@ testRunner('table editor', () => {
await page.waitForTimeout(500)
const copiedTableResult = await page.evaluate(() => navigator.clipboard.readText())
expect(copiedTableResult).toBe(colName)
// Cleanup
await deleteTable(page, ref, tableName)
})
test('importing, pagination and large data actions works as expected', async ({ page, ref }) => {
@@ -637,7 +544,15 @@ testRunner('table editor', () => {
const tableNameDataActions = 'pw_table_data'
// create table
await createTable(page, ref, tableNameDataActions)
await using _ = await withSetupCleanup(
async () => {
await createTableWithRLS(tableNameDataActions, 'pw_column')
},
async () => {
await dropTable(tableNameDataActions)
}
)
await page.goto(toUrl(`/project/${ref}/editor?schema=public`))
await page.getByRole('button', { name: `View ${tableNameDataActions}`, exact: true }).click()
await page.waitForURL(/\/editor\/\d+\?schema=public$/)
@@ -681,7 +596,7 @@ testRunner('table editor', () => {
await expect(page.getByRole('gridcell', { name: 'value 101', exact: true })).toBeVisible()
// change pagination size (100 -> 500)
await page.getByRole('button', { name: 'rows' }).click()
await page.getByRole('button', { name: '100 rows' }).click()
const waitForPaginationChange = createApiResponseWaiter(
page,
'pg-meta',
@@ -769,7 +684,7 @@ testRunner('table editor', () => {
return columns[2].trim()
})
const expectedDefaultColumnValues = [
columnName,
'pw_column',
'value 4 to export',
'value 5 to export',
'value 6 to export',
@@ -797,22 +712,22 @@ testRunner('table editor', () => {
await waitForGridDataToLoad(page, ref) // retrieve rows data
await expect(page.getByRole('gridcell', { name: 'value 7' })).not.toBeVisible()
await expect(page.getByRole('gridcell', { name: 'value 101' })).not.toBeVisible()
await deleteTable(page, ref, tableNameDataActions)
})
test('copying cell values from first and second row works', async ({ page, ref }) => {
const tableName = 'pw_table_copy_rows'
const colName = 'pw_column'
// Ensure we're on editor
if (!page.url().includes('/editor')) {
await page.goto(toUrl(`/project/${ref}/editor?schema=public`))
await waitForTableToLoad(page, ref)
}
// Create table and add two rows
await createTable(page, ref, tableName)
await using _ = await withSetupCleanup(
async () => {
await createTableWithRLS(tableName, 'pw_column')
},
async () => {
await dropTable(tableName)
}
)
await page.goto(toUrl(`/project/${ref}/editor?schema=public`))
await page.getByRole('button', { name: `View ${tableName}`, exact: true }).click()
await page.waitForURL(/\/editor\/\d+\?schema=public$/)
@@ -864,28 +779,29 @@ testRunner('table editor', () => {
// Verify second row value was copied
const secondCopiedValue = await page.evaluate(() => navigator.clipboard.readText())
expect(secondCopiedValue).toBe('second_row_value')
// Cleanup
await deleteTable(page, ref, tableName)
})
test('boolean fields can be edited correctly', async ({ page, ref }) => {
const tableName = 'pw_table_boolean_edits'
const boolColName = 'is_active'
if (!page.url().includes('/editor')) {
await page.goto(toUrl(`/project/${ref}/editor?schema=public`))
await waitForTableToLoad(page, ref)
}
await using _ = await withSetupCleanup(
async () => {
await createTableWithRLS(tableName, 'pw_column')
},
async () => {
await dropTable(tableName)
}
)
await dismissToastsIfAny(page)
// Create a simple table with a boolean column
await page.getByRole('button', { name: 'New table', exact: true }).click()
await page.getByTestId('table-name-input').fill(tableName)
await page.getByTestId('created_at-extra-options').click()
await page.getByRole('checkbox', { name: 'Is Nullable' }).click()
await page.getByTestId('created_at-extra-options').click({ force: true })
await page.goto(toUrl(`/project/${ref}/editor?schema=public`))
await page.getByRole('button', { name: `View ${tableName}`, exact: true }).click()
await page
.getByRole('button', { name: `View ${tableName}`, exact: true })
.getByRole('button')
.nth(2)
.click()
await page.getByRole('menuitem', { name: 'Edit table' }).click()
// Add boolean column
await page.getByRole('button', { name: 'Add column' }).click()
@@ -896,15 +812,10 @@ testRunner('table editor', () => {
await page.getByRole('button', { name: 'Save' }).click()
await expect(
page.getByText(`Table ${tableName} is good to go!`),
'Success toast should be visible after table creation'
page.getByText(`Successfully updated ${tableName}!`),
'Success toast should be visible after table update'
).toBeVisible({ timeout: 50000 })
await expect(
page.getByRole('button', { name: `View ${tableName}`, exact: true }),
'Table should be visible after creation'
).toBeVisible()
// Navigate to the table
await page.getByRole('button', { name: `View ${tableName}`, exact: true }).click()
await page.waitForURL(/\/editor\/\d+\?schema=public$/)
@@ -983,28 +894,28 @@ testRunner('table editor', () => {
falseCells.first(),
'FALSE value should be preserved and not become NULL after inline edit'
).toBeVisible()
// Cleanup
await deleteTable(page, ref, tableName)
})
test('nullable boolean fields support NULL values', async ({ page, ref }) => {
const tableName = 'pw_table_boolean_nullable'
const boolColName = 'is_enabled'
if (!page.url().includes('/editor')) {
await page.goto(toUrl(`/project/${ref}/editor?schema=public`))
await waitForTableToLoad(page, ref)
}
await dismissToastsIfAny(page)
// Create a table with a nullable boolean column
await page.getByRole('button', { name: 'New table', exact: true }).click()
await page.getByTestId('table-name-input').fill(tableName)
await page.getByTestId('created_at-extra-options').click()
await page.getByRole('checkbox', { name: 'Is Nullable' }).click()
await page.getByTestId('created_at-extra-options').click({ force: true })
await using _ = await withSetupCleanup(
async () => {
await createTableWithRLS(tableName, 'pw_column')
},
async () => {
await dropTable(tableName)
}
)
await page.goto(toUrl(`/project/${ref}/editor?schema=public`))
await page.getByRole('button', { name: `View ${tableName}`, exact: true }).click()
await page
.getByRole('button', { name: `View ${tableName}`, exact: true })
.getByRole('button')
.nth(2)
.click()
await page.getByRole('menuitem', { name: 'Edit table' }).click()
// Add nullable boolean column
await page.getByRole('button', { name: 'Add column' }).click()
@@ -1015,13 +926,13 @@ testRunner('table editor', () => {
await page.getByRole('button', { name: 'Save' }).click()
await expect(
page.getByText(`Table ${tableName} is good to go!`),
'Success toast should be visible after table creation'
page.getByText(`Successfully updated ${tableName}!`),
'Success toast should be visible after table update'
).toBeVisible({ timeout: 50000 })
await expect(
page.getByRole('button', { name: `View ${tableName}`, exact: true }),
'Table should be visible after creation'
'Table should be visible after update'
).toBeVisible()
// Navigate to the table
@@ -1075,11 +986,11 @@ testRunner('table editor', () => {
await updateNullResponse
// Verify value changed to NULL on the second row
const nullCells = page.getByRole('gridcell', { name: 'NULL' })
const nullCells = page.getByRole('gridcell', { name: 'NULL' }).nth(2)
await expect(nullCells, 'FALSE should change to NULL after inline edit').toBeVisible()
// Edit NULL to FALSE using inline editor
const nullCellToFalse = page.getByRole('gridcell', { name: 'NULL' })
const nullCellToFalse = page.getByRole('gridcell', { name: 'NULL' }).nth(2)
await nullCellToFalse.dblclick()
const updateFalseResponse = waitForApiResponse(page, 'pg-meta', ref, 'query?key=', {
@@ -1093,27 +1004,24 @@ testRunner('table editor', () => {
page.getByRole('gridcell', { name: 'FALSE' }),
'NULL should change to FALSE after inline edit'
).toBeVisible()
// Cleanup
await deleteTable(page, ref, tableName)
})
test('can create and remove foreign key with column selection', async ({ page, ref }) => {
const sourceTableName = 'pw_table_fk_source'
const targetTableName = 'pw_table_fk_target'
if (!page.url().includes('/editor')) {
await page.goto(toUrl(`/project/${ref}/editor?schema=public`))
await waitForTableToLoad(page, ref)
}
await using _ = await withSetupCleanup(
async () => {
await createTableWithRLS(targetTableName, 'pw_column')
await createTableWithRLS(sourceTableName, 'pw_column')
},
async () => {
await dropTable(sourceTableName)
await dropTable(targetTableName)
}
)
await page.goto(toUrl(`/project/${ref}/editor?schema=public`))
await dismissToastsIfAny(page)
// Create target table first (will be referenced)
await createTable(page, ref, targetTableName)
// Create source table (will have the foreign key)
await createTable(page, ref, sourceTableName)
await page.getByRole('button', { name: `View ${sourceTableName}`, exact: true }).click()
await page.waitForURL(/\/editor\/\d+\?schema=public$/)
@@ -1232,22 +1140,20 @@ testRunner('table editor', () => {
// Close the edit table dialog
await page.getByRole('button', { name: 'Cancel' }).click()
await expect(page.getByTestId('table-editor-side-panel')).not.toBeVisible()
// Clean up
await deleteTable(page, ref, sourceTableName)
await deleteTable(page, ref, targetTableName)
})
test('CSV drag and drop imports data on empty table', async ({ page, ref }) => {
const tableName = 'pw_table_csv_drag_drop'
await dropTable(tableName)
await dbCreateTable(tableName, columnName)
const loadPromise = waitForTableToLoad(page, ref)
await using _ = await withSetupCleanup(
async () => {
await createTableWithRLS(tableName, 'pw_column')
},
async () => {
await dropTable(tableName)
}
)
await page.goto(toUrl(`/project/${ref}/editor?schema=public`))
await loadPromise
await page.getByRole('button', { name: `View ${tableName}`, exact: true }).click()
await page.waitForURL(/\/editor\/\d+\?schema=public$/)
@@ -1298,7 +1204,5 @@ testRunner('table editor', () => {
'Table should show 3 records after drag and drop import'
).toBeVisible()
await expect(page.getByRole('gridcell', { name: 'drag drop value 1' })).toBeVisible()
await dropTable(tableName)
})
})
+17
View File
@@ -33,6 +33,7 @@ export async function createTable(
await query(
`CREATE TABLE IF NOT EXISTS ${tableName} (
id bigint generated by default as identity primary key,
created_at timestamp with time zone null default now(),
${columnName} text
)`
)
@@ -44,6 +45,22 @@ export async function createTable(
}
}
/**
* Create a table with a single text column with RLS enabled (as if created through UI) and optionally insert initial rows.
*
* @param tableName - The table name to create
* @param columnName - The name of the text column
* @param initialData - Optional array of rows to insert (each row is a key-value map)
*/
export async function createTableWithRLS(
tableName: string,
columnName: string,
initialData?: Array<Record<string, string>>
) {
await createTable(tableName, columnName, initialData)
await query(`alter table public.${tableName} enable row level security;`)
}
/**
* Drop a table if it exists.
*
+27
View File
@@ -27,3 +27,30 @@ export const test = base.extend<TestOptions>({
await use(page)
},
})
/**
* A function that returns a disposable object. Calling it with using keyword ensures that the cleanup function
* will be called whether the test succeeded or not.
*
* @example
* await using _ = await withSetupCleanup(
* () => createTableWithRLS('pw_table', 'pw_column'),
* async () => {
* await dropTable('pw_table')
* }
* )
* @param setup The setup function (create tables, etc.)
* @param cleanup The cleanup function (remove tables, etc.)
* @returns A disposable object
*/
export const withSetupCleanup = async (
setup: () => Promise<void>,
cleanup: () => Promise<void>
) => {
await setup()
return {
async [Symbol.asyncDispose]() {
await cleanup()
},
}
}