chore: add e2e test for sql editor snippets (#36293)
* chore: add e2e test for sql editor snippets * trigger build * chore: fix failing test and add rls --------- Co-authored-by: Terry Sutton <saltcod@gmail.com>
This commit is contained in:
@@ -80,6 +80,7 @@ const UtilityActions = ({
|
||||
<DropdownMenu>
|
||||
<DropdownMenuTrigger asChild>
|
||||
<Button
|
||||
data-testId="sql-editor-utility-actions"
|
||||
type="default"
|
||||
className={cn('px-1', isAiOpen ? 'block 2xl:hidden' : 'hidden')}
|
||||
icon={<MoreVertical className="text-foreground-light" />}
|
||||
|
||||
@@ -134,6 +134,7 @@ export const SQLEditorMenu = () => {
|
||||
<DropdownMenu>
|
||||
<DropdownMenuTrigger asChild>
|
||||
<Button
|
||||
data-testId="sql-editor-new-query-button"
|
||||
type="default"
|
||||
icon={<Plus className="text-foreground" />}
|
||||
className="w-[26px]"
|
||||
|
||||
@@ -4,9 +4,8 @@ import { toUrl } from '../utils/to-url'
|
||||
|
||||
test.describe('Project', async () => {
|
||||
test('Can navigate to project home page', async ({ page, ref }) => {
|
||||
console.log(page.url())
|
||||
await page.goto(toUrl(`/project/${ref}`))
|
||||
|
||||
await expect(page.getByRole('button', { name: 'Project Status' })).toBeVisible()
|
||||
await expect(page.getByRole('link', { name: 'Tables' })).toBeVisible()
|
||||
})
|
||||
})
|
||||
|
||||
@@ -34,3 +34,68 @@ test.describe('SQL Editor', () => {
|
||||
await expect(result).toBeVisible()
|
||||
})
|
||||
})
|
||||
|
||||
test.describe('SQL Snippets', () => {
|
||||
test('should create and load a new snippet', async ({ page }) => {
|
||||
await page.goto(toUrl(`/project/${env.PROJECT_REF}/sql`))
|
||||
|
||||
const addButton = page.getByTestId('sql-editor-new-query-button')
|
||||
const runButton = page.getByTestId('sql-run-button')
|
||||
await page.getByRole('button', { name: 'Favorites' }).click()
|
||||
await page.getByRole('button', { name: 'Shared' }).click()
|
||||
await expect(page.getByText('No shared queries')).toBeVisible()
|
||||
await expect(page.getByText('No favorite queries')).toBeVisible()
|
||||
|
||||
// write some sql in the editor
|
||||
await addButton.click()
|
||||
await page.getByRole('menuitem', { name: 'Create a new snippet' }).click()
|
||||
|
||||
const editor = page.getByRole('code').nth(0)
|
||||
await page.waitForTimeout(1000)
|
||||
await editor.click()
|
||||
await page.keyboard.type(`select 'hello world';`)
|
||||
await expect(page.getByText("select 'hello world';")).toBeVisible()
|
||||
await runButton.click()
|
||||
|
||||
// snippet exists
|
||||
const privateSnippet = page.getByLabel('private-snippets')
|
||||
await expect(privateSnippet).toContainText('Untitled query')
|
||||
|
||||
// favourite snippets
|
||||
await page.getByTestId('sql-editor-utility-actions').click()
|
||||
await page.getByRole('menuitem', { name: 'Add to favorites', exact: true }).click()
|
||||
const favouriteSnippetsSection = page.getByLabel('favorite-snippets')
|
||||
await expect(favouriteSnippetsSection).toContainText('Untitled query')
|
||||
|
||||
// unfavorite snippets
|
||||
await page.waitForTimeout(500)
|
||||
await page.getByTestId('sql-editor-utility-actions').click()
|
||||
await page.getByRole('menuitem', { name: 'Remove from favorites' }).click()
|
||||
await expect(favouriteSnippetsSection).not.toBeVisible()
|
||||
|
||||
// rename snippet
|
||||
await privateSnippet.getByText('Untitled query').click({ button: 'right' })
|
||||
await page.getByRole('menuitem', { name: 'Rename query', exact: true }).click()
|
||||
await expect(page.getByRole('heading', { name: 'Rename' })).toBeVisible()
|
||||
await page.getByRole('textbox', { name: 'Name' }).fill('test snippet')
|
||||
await page.getByRole('button', { name: 'Rename query', exact: true }).click()
|
||||
|
||||
const privateSnippet2 = privateSnippet.getByText('test snippet', { exact: true })
|
||||
await expect(privateSnippet2).toBeVisible()
|
||||
|
||||
// share with a team
|
||||
await privateSnippet2.click({ button: 'right' })
|
||||
await page.getByRole('menuitem', { name: 'Share query with team' }).click()
|
||||
await expect(page.getByRole('heading', { name: 'Confirm to share query: test' })).toBeVisible()
|
||||
await page.getByRole('button', { name: 'Share query', exact: true }).click()
|
||||
const sharedSnippet = await page.getByLabel('project-level-snippets')
|
||||
await expect(sharedSnippet).toContainText('test snippet')
|
||||
|
||||
// unshare a snippet
|
||||
await sharedSnippet.getByText('test snippet').click({ button: 'right' })
|
||||
await page.getByRole('menuitem', { name: 'Unshare query with team' }).click()
|
||||
await expect(page.getByRole('heading', { name: 'Confirm to unshare query:' })).toBeVisible()
|
||||
await page.getByRole('button', { name: 'Unshare query', exact: true }).click()
|
||||
await expect(sharedSnippet).not.toBeVisible()
|
||||
})
|
||||
})
|
||||
|
||||
@@ -2,9 +2,6 @@ import { expect, Page } from '@playwright/test'
|
||||
import { test } from '../utils/test'
|
||||
import { toUrl } from '../utils/to-url'
|
||||
|
||||
// Helper to generate a random table name
|
||||
const getRandomTableName = () => `pw-test-${Math.floor(Math.random() * 10000)}`
|
||||
|
||||
const getSelectors = (tableName: string) => ({
|
||||
tableButton: (page) => page.getByRole('button', { name: `View ${tableName}` }),
|
||||
newTableBtn: (page) => page.getByRole('button', { name: 'New table', exact: true }),
|
||||
@@ -41,12 +38,67 @@ const getSelectors = (tableName: string) => ({
|
||||
confirmDeleteBtn: (page) => page.getByRole('button', { name: 'Delete' }),
|
||||
rlsCheckbox: (page) => page.getByLabel('Enable Row Level Security ('),
|
||||
rlsConfirmBtn: (page) => page.getByRole('button', { name: 'Confirm' }),
|
||||
deleteTableToast: (page) => page.getByText('Successfully deleted table "'),
|
||||
deleteTableToast: (page, tableName) =>
|
||||
page.getByText(`Successfully deleted table "${tableName}"`),
|
||||
})
|
||||
|
||||
const createTable = async (page: Page, tableName: string) => {
|
||||
const s = getSelectors(tableName)
|
||||
|
||||
await s.newTableBtn(page).click()
|
||||
await s.tableNameInput(page).fill(tableName)
|
||||
|
||||
await s.createdAtExtraOptions(page).click()
|
||||
await page.getByText('Is Nullable').click()
|
||||
await s.createdAtExtraOptions(page).click({ force: true })
|
||||
|
||||
await s.addColumnBtn(page).click()
|
||||
await s.columnNameInput(page).fill('defaultValueColumn')
|
||||
await s.chooseColumnType(page).click()
|
||||
await s.signedIntOption(page).click()
|
||||
await s.defaultValueField(page).click()
|
||||
await s.defaultValueField(page).fill('2')
|
||||
|
||||
await s.saveBtn(page).click()
|
||||
|
||||
// wait till we see the success toast
|
||||
// Text: Table tableName is good to go!
|
||||
|
||||
await expect(
|
||||
page.getByText(`Table ${tableName} is good to go!`),
|
||||
'Success toast should be visible after table creation'
|
||||
).toBeVisible({
|
||||
timeout: 50000,
|
||||
})
|
||||
|
||||
await expect(
|
||||
page.getByRole('button', { name: `View ${tableName}` }),
|
||||
'Table should be visible after creation'
|
||||
).toBeVisible()
|
||||
}
|
||||
|
||||
const deleteTables = async (page: Page, tableName: string) => {
|
||||
const s = getSelectors(tableName)
|
||||
|
||||
await page.waitForTimeout(500)
|
||||
const exists = (await s.tableButton(page).count()) > 0
|
||||
if (!exists) return
|
||||
|
||||
await s.viewTableLabel(page).click()
|
||||
await s.viewTableLabel(page).getByRole('button').nth(1).click()
|
||||
await s.deleteTableBtn(page).click()
|
||||
await s.confirmDeleteBtn(page).click()
|
||||
await expect(
|
||||
s.deleteTableToast(page, tableName),
|
||||
'Delete confirmation toast should be visible'
|
||||
).toBeVisible()
|
||||
}
|
||||
|
||||
test.describe('Table Editor', () => {
|
||||
let page: Page
|
||||
let tableName: string
|
||||
const testTableName = `pw-test-table-editor`
|
||||
const tableNameRlsEnabled = `pw-test-rls-enabled`
|
||||
const tableNameRlsDisabled = `pw-test-rls-disabled`
|
||||
|
||||
test.beforeAll(async ({ browser, ref }) => {
|
||||
test.setTimeout(60000)
|
||||
@@ -55,66 +107,27 @@ test.describe('Table Editor', () => {
|
||||
* Create a new table for the tests
|
||||
*/
|
||||
page = await browser.newPage()
|
||||
|
||||
await page.goto(toUrl(`/project/${ref}/editor`))
|
||||
|
||||
tableName = getRandomTableName()
|
||||
const s = getSelectors(tableName)
|
||||
|
||||
await s.newTableBtn(page).click()
|
||||
await s.tableNameInput(page).fill(tableName)
|
||||
|
||||
await s.createdAtExtraOptions(page).click()
|
||||
await page.getByText('Is Nullable').click()
|
||||
await s.createdAtExtraOptions(page).click({ force: true })
|
||||
|
||||
await s.addColumnBtn(page).click()
|
||||
await s.columnNameInput(page).fill('defaultValueColumn')
|
||||
await s.chooseColumnType(page).click()
|
||||
await s.signedIntOption(page).click()
|
||||
await s.defaultValueField(page).click()
|
||||
await s.defaultValueField(page).fill('2')
|
||||
|
||||
await s.saveBtn(page).click()
|
||||
|
||||
// wait till we see the success toast
|
||||
// Text: Table tableName is good to go!
|
||||
|
||||
await expect(
|
||||
page.getByText(`Table ${tableName} is good to go!`),
|
||||
'Success toast should be visible after table creation'
|
||||
).toBeVisible({
|
||||
timeout: 50000,
|
||||
})
|
||||
|
||||
await expect(
|
||||
page.getByRole('button', { name: `View ${tableName}` }),
|
||||
'Table should be visible after creation'
|
||||
).toBeVisible()
|
||||
await page.waitForTimeout(2000)
|
||||
// delete table name if it exists
|
||||
await deleteTables(page, testTableName)
|
||||
await deleteTables(page, tableNameRlsEnabled)
|
||||
await deleteTables(page, tableNameRlsDisabled)
|
||||
})
|
||||
|
||||
test.afterAll(async () => {
|
||||
test.setTimeout(60000)
|
||||
/**
|
||||
* Delete the table after the tests are done
|
||||
*/
|
||||
const s = getSelectors(tableName)
|
||||
|
||||
const exists = (await s.tableButton(page).count()) > 0
|
||||
if (!exists) return
|
||||
|
||||
await s.viewTableLabel(page).click()
|
||||
await s.viewTableLabel(page).getByRole('button').nth(1).click()
|
||||
await s.deleteTableBtn(page).click()
|
||||
await s.confirmDeleteBtn(page).click()
|
||||
await expect(
|
||||
s.deleteTableToast(page),
|
||||
'Delete confirmation toast should be visible'
|
||||
).toBeVisible()
|
||||
// delete all tables related to this test
|
||||
await deleteTables(page, testTableName)
|
||||
await deleteTables(page, tableNameRlsEnabled)
|
||||
await deleteTables(page, tableNameRlsDisabled)
|
||||
})
|
||||
|
||||
test('should perform all table operations sequentially', async ({ ref }) => {
|
||||
const s = getSelectors(tableName)
|
||||
await createTable(page, testTableName)
|
||||
const s = getSelectors(testTableName)
|
||||
test.setTimeout(60000)
|
||||
|
||||
// 1. View table definition
|
||||
@@ -124,17 +137,18 @@ test.describe('Table Editor', () => {
|
||||
s.viewLines(page),
|
||||
'Table definition should contain the correct SQL'
|
||||
).toContainText(
|
||||
`CREATE TABLE public.${tableName} ( id bigint GENERATED BY DEFAULT AS IDENTITY NOT NULL, created_at timestamp with time zone NULL DEFAULT now(), "defaultValueColumn" smallint NULL DEFAULT '2'::smallint, CONSTRAINT ${tableName}_pkey PRIMARY KEY (id)) TABLESPACE pg_default;`
|
||||
`create table public.pw - test - table - editor ( id bigint generated by default as identity not null, created_at timestamp with time zone null default now(), "defaultValueColumn" smallint null default '2'::smallint, constraint pw - test - table - editor_pkey primary key (id)) TABLESPACE pg_default;
|
||||
`
|
||||
)
|
||||
|
||||
// 2. Insert test data
|
||||
await page.getByRole('button', { name: `View ${tableName}` }).click()
|
||||
await page.getByRole('button', { name: `View ${testTableName}` }).click()
|
||||
await s.insertRowBtn(page).click()
|
||||
await s.insertModal(page).click()
|
||||
await s.defaultValueInput(page).fill('100')
|
||||
await s.actionBarSaveRow(page).click()
|
||||
|
||||
await page.getByRole('button', { name: `View ${tableName}` }).click()
|
||||
await page.getByRole('button', { name: `View ${testTableName}` }).click()
|
||||
await s.insertRowBtn(page).click()
|
||||
await s.insertModal(page).click()
|
||||
await s.defaultValueInput(page).fill('4')
|
||||
@@ -151,7 +165,11 @@ test.describe('Table Editor', () => {
|
||||
await page.keyboard.down('Escape')
|
||||
|
||||
// Wait for sorting to complete
|
||||
await page.waitForResponse((response) => response.url().includes(`pg-meta/${ref}/query`))
|
||||
await page.waitForResponse(
|
||||
(response) =>
|
||||
response.url().includes(`pg-meta/${ref}/query`) ||
|
||||
response.url().includes('pg-meta/default/query')
|
||||
)
|
||||
|
||||
// give it a second to rerender
|
||||
await page.waitForTimeout(1000)
|
||||
@@ -206,5 +224,36 @@ test.describe('Table Editor', () => {
|
||||
page.getByTestId('tables-list'),
|
||||
'Tables list should be visible in public schema'
|
||||
).toBeVisible()
|
||||
|
||||
await deleteTables(page, testTableName)
|
||||
})
|
||||
|
||||
test('should show rls accordingly', async () => {
|
||||
await createTable(page, tableNameRlsEnabled)
|
||||
|
||||
// testing rls enabled
|
||||
await page.getByRole('button', { name: `View ${tableNameRlsEnabled}` }).click()
|
||||
await expect(page.getByRole('link', { name: 'Add RLS policy' })).toBeVisible()
|
||||
|
||||
// testing rls disabled
|
||||
const s2 = getSelectors(tableNameRlsDisabled)
|
||||
await s2.newTableBtn(page).click()
|
||||
await s2.tableNameInput(page).fill(tableNameRlsDisabled)
|
||||
await s2.rlsCheckbox(page).click()
|
||||
await s2.rlsConfirmBtn(page).click()
|
||||
await s2.saveBtn(page).click()
|
||||
|
||||
await expect(
|
||||
page.getByText(`Table ${tableNameRlsDisabled} is good to go!`),
|
||||
'Success toast should be visible after Rls disabled table is created.'
|
||||
).toBeVisible({
|
||||
timeout: 50000,
|
||||
})
|
||||
|
||||
await page.getByRole('button', { name: `View ${tableNameRlsDisabled}` }).click()
|
||||
await expect(page.getByRole('button', { name: 'RLS disabled' })).toBeVisible()
|
||||
|
||||
await deleteTables(page, tableNameRlsEnabled)
|
||||
await deleteTables(page, tableNameRlsDisabled)
|
||||
})
|
||||
})
|
||||
|
||||
@@ -12,7 +12,7 @@ export default defineConfig({
|
||||
testDir: './features',
|
||||
testMatch: /.*\.spec\.ts/,
|
||||
forbidOnly: IS_CI,
|
||||
retries: 3,
|
||||
retries: IS_CI ? 3 : 1,
|
||||
use: {
|
||||
baseURL: env.STUDIO_URL,
|
||||
screenshot: 'off',
|
||||
|
||||
Reference in New Issue
Block a user