Files
supabase/e2e/studio/features/sql-editor.spec.ts
Michael Ong 7b91d64b7e chore: add final e2e tests for table-editor and sql-editor (#36917)
* chore: add final e2e tests for table-editor and sql-editor

* chore: update tests to run in staging

* chore: minor updates

* chore: fix PR feedback

---------

Co-authored-by: Jordi Enric <37541088+jordienr@users.noreply.github.com>
2025-07-19 10:58:25 +00:00

233 lines
9.1 KiB
TypeScript

import { expect, Page } from '@playwright/test'
import { isCLI } from '../utils/is-cli'
import { test } from '../utils/test'
import { toUrl } from '../utils/to-url'
const deleteQuery = async (page: Page, queryName: string) => {
const privateSnippet = page.getByLabel('private-snippets')
await privateSnippet.getByText(queryName).first().click({ button: 'right' })
await page.getByRole('menuitem', { name: 'Delete query' }).click()
await expect(page.getByRole('heading', { name: 'Confirm to delete query' })).toBeVisible()
await page.getByRole('button', { name: 'Delete 1 query' }).click()
}
test.describe('SQL Editor', () => {
let page: Page
const pwTestQueryName = 'pw-test-query'
test.beforeAll(async ({ browser, ref }) => {
test.setTimeout(60000)
// Create a new table for the tests
page = await browser.newPage()
await page.goto(toUrl(`/project/${ref}/sql/new?skip=true`))
await page.evaluate((ref) => {
localStorage.removeItem('dashboard-history-default')
localStorage.removeItem(`dashboard-history-${ref}`)
}, ref)
// intercept AI title generation to prevent flaky tests
await page.route('**/dashboard/api/ai/sql/title-v2', async (route) => {
await route.abort()
})
})
test.beforeEach(async ({ ref }) => {
if ((await page.getByLabel('private-snippets').count()) === 0) {
return
}
// since in local, we don't have access to the supabase platform, reloading would reload all the sql snippets.
if (isCLI()) {
await page.reload()
}
// remove sql snippets for - "Untitled query" and "pw test query"
const privateSnippet = page.getByLabel('private-snippets')
let privateSnippetText = await privateSnippet.textContent()
while (privateSnippetText.includes('Untitled query')) {
deleteQuery(page, 'Untitled query')
await page.waitForResponse(
(response) =>
(response.url().includes(`projects/${ref}/content`) ||
response.url().includes('projects/default/content')) &&
response.request().method() === 'DELETE'
)
await expect(
page.getByText('Successfully deleted 1 query'),
'Delete confirmation toast should be visible'
).toBeVisible({
timeout: 50000,
})
await page.waitForTimeout(1000)
privateSnippetText =
(await page.getByLabel('private-snippets').count()) > 0
? await privateSnippet.textContent()
: ''
}
while (privateSnippetText.includes(pwTestQueryName)) {
deleteQuery(page, pwTestQueryName)
await page.waitForResponse(
(response) =>
(response.url().includes(`projects/${ref}/content`) ||
response.url().includes('projects/default/content')) &&
response.request().method() === 'DELETE'
)
await expect(
page.getByText('Successfully deleted 1 query'),
'Delete confirmation toast should be visible'
).toBeVisible({
timeout: 50000,
})
await page.waitForTimeout(1000)
privateSnippetText =
(await page.getByLabel('private-snippets').count()) > 0
? await privateSnippet.textContent()
: ''
}
})
test('should check if SQL editor can run simple commands', async () => {
await page.getByTestId('sql-editor-new-query-button').click()
await page.getByRole('menuitem', { name: 'Create a new snippet' }).click()
// write some sql in the editor
// This has to be done since the editor is not editable (input, textarea, etc.)
await page.waitForTimeout(1000)
const editor = page.getByRole('code').nth(0)
await editor.click()
await page.keyboard.press('ControlOrMeta+KeyA')
await page.keyboard.type(`select 'hello world';`)
await page.getByTestId('sql-run-button').click()
// verify the result
await expect(page.getByRole('gridcell', { name: 'hello world' })).toBeVisible({
timeout: 5000,
})
// SQL written in the editor should not be the previous query.
await page.waitForTimeout(1000)
await editor.click()
await page.keyboard.press('ControlOrMeta+KeyA')
await page.keyboard.type(`select length('hello');`)
await page.getByTestId('sql-run-button').click()
// verify the result is updated.
await expect(page.getByRole('gridcell', { name: '5' })).toBeVisible({
timeout: 5000,
})
})
test('destructive query would tripper a warning modal', async () => {
await page.getByTestId('sql-editor-new-query-button').click()
await page.getByRole('menuitem', { name: 'Create a new snippet' }).click()
// write some sql in the editor
// This has to be done since the editor is not editable (input, textarea, etc.)
await page.waitForTimeout(1000)
const editor = page.getByRole('code').nth(0)
await editor.click()
await page.keyboard.press('ControlOrMeta+KeyA')
await page.keyboard.type(`delete table 'test';`)
await page.getByTestId('sql-run-button').click()
// verify warning modal is visible
expect(page.getByRole('heading', { name: 'Potential issue detected with' })).toBeVisible()
expect(page.getByText('Query has destructive')).toBeVisible()
// reset test
await page.getByRole('button', { name: 'Cancel' }).click()
await page.waitForTimeout(500)
await editor.click()
await page.keyboard.press('ControlOrMeta+KeyA')
await page.keyboard.press('Backspace')
})
test('should create and load a new snippet', async ({ ref }) => {
const runButton = page.getByTestId('sql-run-button')
await page.getByRole('button', { name: 'Favorites' }).click()
await page.getByRole('button', { name: 'Shared' }).click()
// write some sql in the editor
await page.getByTestId('sql-editor-new-query-button').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(pwTestQueryName)
await page.getByRole('button', { name: 'Rename query', exact: true }).click()
await page.waitForResponse(
(response) =>
(response.url().includes(`projects/${ref}/content`) ||
response.url().includes('projects/default/content')) &&
response.request().method() === 'PUT' &&
response.status().toString().startsWith('2')
)
await expect(privateSnippet.getByText(pwTestQueryName, { exact: true })).toBeVisible({
timeout: 50000,
})
const privateSnippet2 = await privateSnippet.getByText(pwTestQueryName, { exact: true })
// 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' })).toBeVisible()
await page.getByRole('button', { name: 'Share query', exact: true }).click()
await page.waitForResponse(
(response) =>
(response.url().includes(`projects/${ref}/content`) ||
response.url().includes('projects/default/content')) &&
response.request().method() === 'PUT' &&
response.status().toString().startsWith('2')
)
const sharedSnippet = await page.getByLabel('project-level-snippets')
await expect(sharedSnippet).toContainText(pwTestQueryName)
// unshare a snippet
await sharedSnippet.getByText(pwTestQueryName).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()
// delete snippet (for non-local environment)
if (!isCLI()) {
deleteQuery(page, pwTestQueryName)
await expect(
page.getByText('Successfully deleted 1 query'),
'Delete confirmation toast should be visible'
).toBeVisible({
timeout: 50000,
})
}
})
})