Skip to content

Commit 7b91d64

Browse files
Michaelomhjordienr
andauthored
chore: add final e2e tests for table-editor and sql-editor (supabase#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 <[email protected]>
1 parent 43c0c25 commit 7b91d64

File tree

4 files changed

+356
-52
lines changed

4 files changed

+356
-52
lines changed

e2e/studio/README.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,8 @@ Edit the `.env.local` file with your credentials and environment.
1010

1111
### Install the playwright browser
1212

13+
⚠️ This should be done in the `e2e/studio` directory
14+
1315
```bash
1416
pnpm exec playwright install
1517
```
Lines changed: 164 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -1,55 +1,159 @@
1-
import { expect } from '@playwright/test'
2-
import { env } from '../env.config'
1+
import { expect, Page } from '@playwright/test'
2+
import { isCLI } from '../utils/is-cli'
33
import { test } from '../utils/test'
44
import { toUrl } from '../utils/to-url'
55

6+
const deleteQuery = async (page: Page, queryName: string) => {
7+
const privateSnippet = page.getByLabel('private-snippets')
8+
await privateSnippet.getByText(queryName).first().click({ button: 'right' })
9+
await page.getByRole('menuitem', { name: 'Delete query' }).click()
10+
await expect(page.getByRole('heading', { name: 'Confirm to delete query' })).toBeVisible()
11+
await page.getByRole('button', { name: 'Delete 1 query' }).click()
12+
}
13+
614
test.describe('SQL Editor', () => {
7-
test('should check if SQL editor can run simple commands', async ({ page }) => {
8-
await page.goto(toUrl(`/project/${env.PROJECT_REF}/sql/new?skip=true`))
15+
let page: Page
16+
const pwTestQueryName = 'pw-test-query'
917

10-
const editor = page.getByRole('code').nth(0)
18+
test.beforeAll(async ({ browser, ref }) => {
19+
test.setTimeout(60000)
20+
21+
// Create a new table for the tests
22+
page = await browser.newPage()
23+
await page.goto(toUrl(`/project/${ref}/sql/new?skip=true`))
24+
25+
await page.evaluate((ref) => {
26+
localStorage.removeItem('dashboard-history-default')
27+
localStorage.removeItem(`dashboard-history-${ref}`)
28+
}, ref)
29+
30+
// intercept AI title generation to prevent flaky tests
31+
await page.route('**/dashboard/api/ai/sql/title-v2', async (route) => {
32+
await route.abort()
33+
})
34+
})
35+
36+
test.beforeEach(async ({ ref }) => {
37+
if ((await page.getByLabel('private-snippets').count()) === 0) {
38+
return
39+
}
40+
41+
// since in local, we don't have access to the supabase platform, reloading would reload all the sql snippets.
42+
if (isCLI()) {
43+
await page.reload()
44+
}
45+
46+
// remove sql snippets for - "Untitled query" and "pw test query"
47+
const privateSnippet = page.getByLabel('private-snippets')
48+
let privateSnippetText = await privateSnippet.textContent()
49+
while (privateSnippetText.includes('Untitled query')) {
50+
deleteQuery(page, 'Untitled query')
51+
52+
await page.waitForResponse(
53+
(response) =>
54+
(response.url().includes(`projects/${ref}/content`) ||
55+
response.url().includes('projects/default/content')) &&
56+
response.request().method() === 'DELETE'
57+
)
58+
await expect(
59+
page.getByText('Successfully deleted 1 query'),
60+
'Delete confirmation toast should be visible'
61+
).toBeVisible({
62+
timeout: 50000,
63+
})
64+
await page.waitForTimeout(1000)
65+
privateSnippetText =
66+
(await page.getByLabel('private-snippets').count()) > 0
67+
? await privateSnippet.textContent()
68+
: ''
69+
}
70+
71+
while (privateSnippetText.includes(pwTestQueryName)) {
72+
deleteQuery(page, pwTestQueryName)
73+
await page.waitForResponse(
74+
(response) =>
75+
(response.url().includes(`projects/${ref}/content`) ||
76+
response.url().includes('projects/default/content')) &&
77+
response.request().method() === 'DELETE'
78+
)
79+
await expect(
80+
page.getByText('Successfully deleted 1 query'),
81+
'Delete confirmation toast should be visible'
82+
).toBeVisible({
83+
timeout: 50000,
84+
})
85+
await page.waitForTimeout(1000)
86+
privateSnippetText =
87+
(await page.getByLabel('private-snippets').count()) > 0
88+
? await privateSnippet.textContent()
89+
: ''
90+
}
91+
})
92+
93+
test('should check if SQL editor can run simple commands', async () => {
94+
await page.getByTestId('sql-editor-new-query-button').click()
95+
await page.getByRole('menuitem', { name: 'Create a new snippet' }).click()
1196

1297
// write some sql in the editor
1398
// This has to be done since the editor is not editable (input, textarea, etc.)
14-
await editor.click()
99+
await page.waitForTimeout(1000)
100+
const editor = page.getByRole('code').nth(0)
15101
await editor.click()
16102
await page.keyboard.press('ControlOrMeta+KeyA')
17103
await page.keyboard.type(`select 'hello world';`)
104+
await page.getByTestId('sql-run-button').click()
18105

19-
await page.getByRole('button', { name: /^Run( CTRL)?$/, exact: false }).click()
106+
// verify the result
107+
await expect(page.getByRole('gridcell', { name: 'hello world' })).toBeVisible({
108+
timeout: 5000,
109+
})
20110

21-
// Should say "Running..."
22-
await expect(page.getByText('Running...')).toBeVisible()
111+
// SQL written in the editor should not be the previous query.
112+
await page.waitForTimeout(1000)
113+
await editor.click()
114+
await page.keyboard.press('ControlOrMeta+KeyA')
115+
await page.keyboard.type(`select length('hello');`)
116+
await page.getByTestId('sql-run-button').click()
23117

24-
// Wait until Running... is not visible
25-
await expect(page.getByText('Running...')).not.toBeVisible()
118+
// verify the result is updated.
119+
await expect(page.getByRole('gridcell', { name: '5' })).toBeVisible({
120+
timeout: 5000,
121+
})
122+
})
123+
124+
test('destructive query would tripper a warning modal', async () => {
125+
await page.getByTestId('sql-editor-new-query-button').click()
126+
await page.getByRole('menuitem', { name: 'Create a new snippet' }).click()
26127

27-
// clear the editor
128+
// write some sql in the editor
129+
// This has to be done since the editor is not editable (input, textarea, etc.)
130+
await page.waitForTimeout(1000)
131+
const editor = page.getByRole('code').nth(0)
28132
await editor.click()
29133
await page.keyboard.press('ControlOrMeta+KeyA')
30-
await page.keyboard.press('Backspace')
134+
await page.keyboard.type(`delete table 'test';`)
135+
await page.getByTestId('sql-run-button').click()
31136

32-
// verify the result
33-
const result = page.getByRole('gridcell', { name: 'hello world' })
34-
await expect(result).toBeVisible()
35-
})
36-
})
137+
// verify warning modal is visible
138+
expect(page.getByRole('heading', { name: 'Potential issue detected with' })).toBeVisible()
139+
expect(page.getByText('Query has destructive')).toBeVisible()
37140

38-
test.describe('SQL Snippets', () => {
39-
test('should create and load a new snippet', async ({ page }) => {
40-
await page.goto(toUrl(`/project/${env.PROJECT_REF}/sql`))
141+
// reset test
142+
await page.getByRole('button', { name: 'Cancel' }).click()
143+
await page.waitForTimeout(500)
144+
await editor.click()
145+
await page.keyboard.press('ControlOrMeta+KeyA')
146+
await page.keyboard.press('Backspace')
147+
})
41148

42-
const addButton = page.getByTestId('sql-editor-new-query-button')
149+
test('should create and load a new snippet', async ({ ref }) => {
43150
const runButton = page.getByTestId('sql-run-button')
44151
await page.getByRole('button', { name: 'Favorites' }).click()
45152
await page.getByRole('button', { name: 'Shared' }).click()
46-
await expect(page.getByText('No shared queries')).toBeVisible()
47-
await expect(page.getByText('No favorite queries')).toBeVisible()
48153

49154
// write some sql in the editor
50-
await addButton.click()
155+
await page.getByTestId('sql-editor-new-query-button').click()
51156
await page.getByRole('menuitem', { name: 'Create a new snippet' }).click()
52-
53157
const editor = page.getByRole('code').nth(0)
54158
await page.waitForTimeout(1000)
55159
await editor.click()
@@ -77,25 +181,52 @@ test.describe('SQL Snippets', () => {
77181
await privateSnippet.getByText('Untitled query').click({ button: 'right' })
78182
await page.getByRole('menuitem', { name: 'Rename query', exact: true }).click()
79183
await expect(page.getByRole('heading', { name: 'Rename' })).toBeVisible()
80-
await page.getByRole('textbox', { name: 'Name' }).fill('test snippet')
184+
await page.getByRole('textbox', { name: 'Name' }).fill(pwTestQueryName)
81185
await page.getByRole('button', { name: 'Rename query', exact: true }).click()
82-
83-
const privateSnippet2 = privateSnippet.getByText('test snippet', { exact: true })
84-
await expect(privateSnippet2).toBeVisible()
186+
await page.waitForResponse(
187+
(response) =>
188+
(response.url().includes(`projects/${ref}/content`) ||
189+
response.url().includes('projects/default/content')) &&
190+
response.request().method() === 'PUT' &&
191+
response.status().toString().startsWith('2')
192+
)
193+
await expect(privateSnippet.getByText(pwTestQueryName, { exact: true })).toBeVisible({
194+
timeout: 50000,
195+
})
196+
const privateSnippet2 = await privateSnippet.getByText(pwTestQueryName, { exact: true })
85197

86198
// share with a team
87199
await privateSnippet2.click({ button: 'right' })
88200
await page.getByRole('menuitem', { name: 'Share query with team' }).click()
89-
await expect(page.getByRole('heading', { name: 'Confirm to share query: test' })).toBeVisible()
201+
await expect(page.getByRole('heading', { name: 'Confirm to share query' })).toBeVisible()
90202
await page.getByRole('button', { name: 'Share query', exact: true }).click()
203+
await page.waitForResponse(
204+
(response) =>
205+
(response.url().includes(`projects/${ref}/content`) ||
206+
response.url().includes('projects/default/content')) &&
207+
response.request().method() === 'PUT' &&
208+
response.status().toString().startsWith('2')
209+
)
91210
const sharedSnippet = await page.getByLabel('project-level-snippets')
92-
await expect(sharedSnippet).toContainText('test snippet')
211+
await expect(sharedSnippet).toContainText(pwTestQueryName)
93212

94213
// unshare a snippet
95-
await sharedSnippet.getByText('test snippet').click({ button: 'right' })
214+
await sharedSnippet.getByText(pwTestQueryName).click({ button: 'right' })
96215
await page.getByRole('menuitem', { name: 'Unshare query with team' }).click()
97216
await expect(page.getByRole('heading', { name: 'Confirm to unshare query:' })).toBeVisible()
98217
await page.getByRole('button', { name: 'Unshare query', exact: true }).click()
99218
await expect(sharedSnippet).not.toBeVisible()
219+
220+
// delete snippet (for non-local environment)
221+
if (!isCLI()) {
222+
deleteQuery(page, pwTestQueryName)
223+
224+
await expect(
225+
page.getByText('Successfully deleted 1 query'),
226+
'Delete confirmation toast should be visible'
227+
).toBeVisible({
228+
timeout: 50000,
229+
})
230+
}
100231
})
101232
})

0 commit comments

Comments
 (0)