mirror of
https://github.com/supabase/supabase.git
synced 2026-11-01 01:27:16 +08:00
The close button on the Auth users detail panel (and the queue message panel) is absolutely positioned, but `TabsList` is `position: relative` and comes later in tree order — so it painted on top of the button and swallowed every click. The button looked fine and did nothing. Stacks the button above the tab list, adds an accessible name to the users panel button, and covers it with an E2E test (Playwright does real hit-testing, so it fails without the fix). Fixes FE-4519
169 lines
5.7 KiB
TypeScript
169 lines
5.7 KiB
TypeScript
import { expect, Page } from '@playwright/test'
|
|
|
|
import { createUserViaUI, deleteUserViaUI, navigateToAuthUsers } from '../utils/auth-helpers.js'
|
|
import { test } from '../utils/test.js'
|
|
import { toUrl } from '../utils/to-url.js'
|
|
import { waitForApiResponse } from '../utils/wait-for-response.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 - this verifies the user appears in the table
|
|
await createUserViaUI(page, ref, testEmail, testPassword)
|
|
|
|
// Verify the user details are correct
|
|
const userRow = page.getByRole('row').filter({ hasText: testEmail })
|
|
await expect(userRow.getByText(testEmail)).toBeVisible()
|
|
await expect(userRow.getByText('Email')).toBeVisible()
|
|
|
|
// Clean up: delete the user - this verifies the user is removed from the table
|
|
await deleteUserViaUI(page, ref, testEmail)
|
|
})
|
|
|
|
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 - each creation verifies the user appears in the table
|
|
for (const user of testUsers) {
|
|
await createUserViaUI(page, ref, user.email, user.password)
|
|
}
|
|
|
|
// Clean up: delete all test users - each deletion verifies the user is removed
|
|
for (const user of testUsers) {
|
|
await deleteUserViaUI(page, ref, user.email)
|
|
}
|
|
})
|
|
})
|
|
|
|
test('should show web3 users as enabled when the matching web3 provider is enabled', async ({
|
|
page,
|
|
ref,
|
|
}) => {
|
|
test.skip(
|
|
process.env.IS_PLATFORM !== 'true',
|
|
'Provider enabled status is only resolved in platform mode'
|
|
)
|
|
|
|
const userId = '11111111-1111-4111-8111-111111111111'
|
|
|
|
await page.route('**/platform/pg-meta/*/query**', async (route) => {
|
|
const key = new URL(route.request().url()).searchParams.get('key')
|
|
if (key !== `user-${userId}`) return route.continue()
|
|
|
|
await route.fulfill({
|
|
status: 200,
|
|
contentType: 'application/json',
|
|
body: JSON.stringify([
|
|
{
|
|
id: userId,
|
|
email: 'web3-user@example.com',
|
|
phone: null,
|
|
role: 'authenticated',
|
|
aud: 'authenticated',
|
|
created_at: '2025-10-15T10:00:00.000Z',
|
|
updated_at: '2025-10-15T10:05:00.000Z',
|
|
confirmed_at: '2025-10-15T10:01:00.000Z',
|
|
invited_at: null,
|
|
confirmation_sent_at: null,
|
|
last_sign_in_at: '2025-10-15T10:06:00.000Z',
|
|
banned_until: null,
|
|
is_sso_user: false,
|
|
is_anonymous: false,
|
|
providers: ['web3'],
|
|
raw_app_meta_data: {
|
|
provider: 'web3',
|
|
providers: ['web3'],
|
|
},
|
|
raw_user_meta_data: {
|
|
custom_claims: {
|
|
chain: 'solana',
|
|
},
|
|
},
|
|
user_metadata: {},
|
|
},
|
|
]),
|
|
})
|
|
})
|
|
|
|
await page.route('**/platform/auth/*/config**', async (route) => {
|
|
await route.fulfill({
|
|
status: 200,
|
|
contentType: 'application/json',
|
|
body: JSON.stringify({
|
|
EXTERNAL_WEB3_SOLANA_ENABLED: true,
|
|
INDEX_WORKER_ENSURE_USER_SEARCH_INDEXES_EXIST: false,
|
|
MAILER_OTP_EXP: 0,
|
|
}),
|
|
})
|
|
})
|
|
|
|
await page.goto(toUrl(`/project/${ref}/auth/users?show=${userId}`))
|
|
|
|
const providerRow = page
|
|
.getByRole('link', { name: 'Configure web3 provider' })
|
|
.locator("xpath=ancestor::div[contains(@class, 'bg-surface-100')][1]")
|
|
|
|
await expect(providerRow).toContainText('Enabled')
|
|
await expect(providerRow).not.toContainText('Disabled')
|
|
})
|
|
|
|
test('should close the user panel from its close button', async ({ page, ref }) => {
|
|
const userId = '22222222-2222-4222-8222-222222222222'
|
|
|
|
await page.route('**/platform/pg-meta/*/query**', async (route) => {
|
|
const key = new URL(route.request().url()).searchParams.get('key')
|
|
if (key !== `user-${userId}`) return route.continue()
|
|
|
|
await route.fulfill({
|
|
status: 200,
|
|
contentType: 'application/json',
|
|
body: JSON.stringify([
|
|
{
|
|
id: userId,
|
|
email: 'close-panel@example.com',
|
|
phone: null,
|
|
role: 'authenticated',
|
|
aud: 'authenticated',
|
|
created_at: '2025-10-15T10:00:00.000Z',
|
|
updated_at: '2025-10-15T10:05:00.000Z',
|
|
confirmed_at: '2025-10-15T10:01:00.000Z',
|
|
invited_at: null,
|
|
confirmation_sent_at: null,
|
|
last_sign_in_at: '2025-10-15T10:06:00.000Z',
|
|
banned_until: null,
|
|
is_sso_user: false,
|
|
is_anonymous: false,
|
|
providers: ['email'],
|
|
raw_app_meta_data: { provider: 'email', providers: ['email'] },
|
|
raw_user_meta_data: {},
|
|
user_metadata: {},
|
|
},
|
|
]),
|
|
})
|
|
})
|
|
|
|
await page.goto(toUrl(`/project/${ref}/auth/users?show=${userId}`))
|
|
|
|
await expect(page.getByRole('tab', { name: 'Overview' })).toBeVisible()
|
|
|
|
// The tab list is a positioned sibling that spans the panel's full width, so it
|
|
// will intercept this click unless the button is stacked above it.
|
|
await page.getByRole('button', { name: 'Close panel' }).click()
|
|
|
|
await expect(page.getByRole('tab', { name: 'Overview' })).toBeHidden()
|
|
await expect.poll(() => new URL(page.url()).searchParams.get('show')).toBeNull()
|
|
})
|