mirror of
https://github.com/supabase/supabase.git
synced 2026-05-07 06:27:16 +08:00
chore: added tests for users and minor refactor (#41279)
* added tests for users and minor refactor * fix helpers * addressed PR feedback * remove before await * missed the await
This commit is contained in:
74
e2e/studio/features/auth-users.spec.ts
Normal file
74
e2e/studio/features/auth-users.spec.ts
Normal file
@@ -0,0 +1,74 @@
|
||||
import { expect, Page } from '@playwright/test'
|
||||
import { test } from '../utils/test.js'
|
||||
import { toUrl } from '../utils/to-url.js'
|
||||
import { waitForApiResponse } from '../utils/wait-for-response.js'
|
||||
import { createUserViaUI, deleteUserViaUI, navigateToAuthUsers } from '../utils/auth-helpers.js'
|
||||
|
||||
test.describe('auth users list refresh', () => {
|
||||
test.beforeEach(async ({ page, ref }) => {
|
||||
await navigateToAuthUsers(page, ref)
|
||||
})
|
||||
|
||||
test('should automatically refresh users list after creating a user', async ({ page, ref }) => {
|
||||
const testEmail = `test-create-${Date.now()}@example.com`
|
||||
const testPassword = 'testpassword123'
|
||||
|
||||
// Create user via UI
|
||||
await createUserViaUI(page, ref, testEmail, testPassword)
|
||||
|
||||
// Verify the user appears in the table WITHOUT manually refreshing the page
|
||||
const userRow = page.getByRole('row').filter({ hasText: testEmail })
|
||||
await expect(
|
||||
userRow,
|
||||
'User should appear in the table immediately after creation without manual refresh'
|
||||
).toBeVisible({ timeout: 10_000 })
|
||||
|
||||
// Verify the user details are correct
|
||||
await expect(userRow.getByText(testEmail)).toBeVisible()
|
||||
await expect(userRow.getByText('Email')).toBeVisible()
|
||||
|
||||
// Clean up: delete the user
|
||||
await deleteUserViaUI(page, ref, testEmail)
|
||||
|
||||
// Verify the user is removed from the table
|
||||
await expect
|
||||
.poll(async () => {
|
||||
return await page.getByRole('row').filter({ hasText: testEmail }).count()
|
||||
}, 'User should be removed from the table after deletion')
|
||||
.toBe(0)
|
||||
})
|
||||
|
||||
test('should automatically refresh users list after creating multiple users', async ({
|
||||
page,
|
||||
ref,
|
||||
}) => {
|
||||
const testUsers = [
|
||||
{ email: `test-multi-1-${Date.now()}@example.com`, password: 'testpassword123' },
|
||||
{ email: `test-multi-2-${Date.now()}@example.com`, password: 'testpassword123' },
|
||||
{ email: `test-multi-3-${Date.now()}@example.com`, password: 'testpassword123' },
|
||||
]
|
||||
|
||||
// Create multiple users
|
||||
for (const user of testUsers) {
|
||||
await createUserViaUI(page, ref, user.email, user.password)
|
||||
|
||||
// Verify each user appears in the table
|
||||
await expect(
|
||||
page.getByRole('row').filter({ hasText: user.email }),
|
||||
`User ${user.email} should appear in the table after creation`
|
||||
).toBeVisible()
|
||||
}
|
||||
|
||||
// Clean up: delete all test users
|
||||
for (const user of testUsers) {
|
||||
await deleteUserViaUI(page, ref, user.email)
|
||||
|
||||
// Verify each user is removed
|
||||
await expect
|
||||
.poll(async () => {
|
||||
return await page.getByRole('row').filter({ hasText: user.email }).count()
|
||||
}, `User ${user.email} should be removed from the table after deletion`)
|
||||
.toBe(0)
|
||||
}
|
||||
})
|
||||
})
|
||||
@@ -8,12 +8,12 @@ import {
|
||||
deleteAllBuckets,
|
||||
deleteBucket,
|
||||
deleteItem,
|
||||
dismissToastsIfAny,
|
||||
downloadFile,
|
||||
navigateToBucket,
|
||||
renameItem,
|
||||
uploadFile,
|
||||
} from '../utils/storage-helpers.js'
|
||||
import { dismissToastsIfAny } from '../utils/dismiss-toast.js'
|
||||
|
||||
const bucketNamePrefix = 'pw_bucket'
|
||||
|
||||
|
||||
89
e2e/studio/utils/auth-helpers.ts
Normal file
89
e2e/studio/utils/auth-helpers.ts
Normal file
@@ -0,0 +1,89 @@
|
||||
import { expect, Page } from '@playwright/test'
|
||||
import { waitForApiResponse } from './wait-for-response.js'
|
||||
import { dismissToastsIfAny } from './dismiss-toast.js'
|
||||
import { toUrl } from './to-url.js'
|
||||
|
||||
export const createUserViaUI = async (page: Page, ref: string, email: string, password: string) => {
|
||||
await dismissToastsIfAny(page)
|
||||
|
||||
// Open the Add user dropdown
|
||||
await page.getByRole('button', { name: 'Add user' }).click()
|
||||
|
||||
// Click "Create new user"
|
||||
await page.getByRole('menuitem', { name: 'Create new user' }).click()
|
||||
|
||||
// Wait for dialog to be visible
|
||||
await expect(page.getByRole('dialog', { name: 'Create a new user' })).toBeVisible()
|
||||
|
||||
// Fill in email
|
||||
await page.getByRole('textbox', { name: 'user@example.com' }).fill(email)
|
||||
|
||||
// Fill in password
|
||||
await page.getByRole('textbox', { name: '••••••••' }).fill(password)
|
||||
|
||||
// Verify that "Auto Confirm User?" is checked by default
|
||||
await expect(page.getByRole('checkbox', { name: 'Auto Confirm User?' })).toBeChecked()
|
||||
|
||||
// Set up API waiters BEFORE clicking the button to avoid race conditions
|
||||
const createUserPromise = waitForApiResponse(page, 'platform/auth', ref, 'users', {
|
||||
method: 'POST',
|
||||
})
|
||||
const usersListPromise = waitForApiResponse(page, 'platform/pg-meta', ref, 'query?key=')
|
||||
|
||||
// Click "Create user"
|
||||
await page.getByRole('button', { name: 'Create user' }).click()
|
||||
|
||||
// Wait for both API calls to complete
|
||||
await Promise.all([createUserPromise, usersListPromise])
|
||||
|
||||
// Wait for success toast
|
||||
await expect(
|
||||
page.getByText(`Successfully created user: ${email}`),
|
||||
'Success toast should be visible after user creation'
|
||||
).toBeVisible({ timeout: 10_000 })
|
||||
}
|
||||
|
||||
export const deleteUserViaUI = async (page: Page, ref: string, email: string) => {
|
||||
await dismissToastsIfAny(page)
|
||||
|
||||
// Find the user row by email and click the checkbox
|
||||
const userRow = page.getByRole('row').filter({ hasText: email })
|
||||
await expect(userRow, `User row with email ${email} should be visible`).toBeVisible()
|
||||
|
||||
// Click the checkbox to select the user
|
||||
await userRow.getByRole('checkbox').first().click()
|
||||
|
||||
// Click "Delete 1 users" button
|
||||
await page.getByRole('button', { name: 'Delete 1 users' }).click()
|
||||
|
||||
// Wait for confirmation dialog
|
||||
await expect(page.getByRole('dialog', { name: 'Confirm to delete 1 user' })).toBeVisible()
|
||||
|
||||
// Set up API waiters BEFORE clicking the delete button
|
||||
const deleteUserPromise = waitForApiResponse(page, 'platform/auth', ref, 'users/', {
|
||||
method: 'DELETE',
|
||||
})
|
||||
const usersListPromise = waitForApiResponse(page, 'platform/pg-meta', ref, 'query?key=')
|
||||
|
||||
// Confirm deletion
|
||||
await page.getByRole('button', { name: 'Delete' }).click()
|
||||
|
||||
// Wait for both API calls to complete
|
||||
await Promise.all([deleteUserPromise, usersListPromise])
|
||||
|
||||
// Wait for success toast
|
||||
await expect(
|
||||
page.getByText('Successfully deleted the selected 1 user'),
|
||||
'Success toast should be visible after user deletion'
|
||||
).toBeVisible({ timeout: 10_000 })
|
||||
}
|
||||
|
||||
export const navigateToAuthUsers = async (page: Page, ref: string) => {
|
||||
await page.goto(toUrl(`/project/${ref}/auth/users`))
|
||||
|
||||
// Wait for the page to load by checking for the "Users" heading
|
||||
await expect(page.getByRole('heading', { name: 'Users', level: 3 })).toBeVisible()
|
||||
|
||||
// Wait for initial users list to load
|
||||
await waitForApiResponse(page, 'platform/pg-meta', ref, 'query?key=')
|
||||
}
|
||||
@@ -9,3 +9,11 @@ export const dismissToast = async (page: Page) => {
|
||||
}
|
||||
|
||||
export const toKebabCase = (str: string) => str.replace(/([A-Z])/g, '-$1').toLowerCase()
|
||||
|
||||
export 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()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,17 +1,7 @@
|
||||
import { expect, Page } from '@playwright/test'
|
||||
import { waitForApiResponse } from './wait-for-response.js'
|
||||
import { toUrl } from './to-url.js'
|
||||
|
||||
/**
|
||||
* Dismisses any visible toast notifications
|
||||
*/
|
||||
export 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()
|
||||
}
|
||||
}
|
||||
import { dismissToastsIfAny } from './dismiss-toast.js'
|
||||
|
||||
/**
|
||||
* Navigates to a the storage home view
|
||||
|
||||
Reference in New Issue
Block a user