mirror of
https://github.com/QuantumNous/new-api.git
synced 2026-08-31 02:41:34 +00:00
fix(web): restore admin unbinding for built-in providers (#6987)
* fix(web): align admin binding types Refs #6985 * test(web): restore animation mock
This commit is contained in:
parent
e468b73915
commit
692e8d6ee6
@ -0,0 +1,148 @@
|
||||
/*
|
||||
Copyright (C) 2023-2026 QuantumNous
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU Affero General Public License as
|
||||
published by the Free Software Foundation, either version 3 of the
|
||||
License, or (at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU Affero General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Affero General Public License
|
||||
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
For commercial licensing, please contact support@quantumnous.com
|
||||
*/
|
||||
import { fireEvent, render, screen, waitFor } from '@testing-library/react'
|
||||
import { afterAll, afterEach, beforeAll, describe, expect, test } from 'vitest'
|
||||
|
||||
import { api } from '@/lib/api'
|
||||
|
||||
import { UserBindingDialog } from '../user-binding-dialog'
|
||||
|
||||
type ApiMethod = (url: string) => Promise<{ data: unknown }>
|
||||
type MockableApi = {
|
||||
get: ApiMethod
|
||||
delete: ApiMethod
|
||||
}
|
||||
|
||||
const apiClient = api as unknown as MockableApi
|
||||
const originalGet = apiClient.get
|
||||
const originalDelete = apiClient.delete
|
||||
const originalGetAnimations = Object.getOwnPropertyDescriptor(
|
||||
HTMLElement.prototype,
|
||||
'getAnimations'
|
||||
)
|
||||
|
||||
const user = {
|
||||
id: 7,
|
||||
username: 'bound-user',
|
||||
email: 'user@example.com',
|
||||
github_id: 'github-user',
|
||||
discord_id: 'discord-user',
|
||||
wechat_id: 'wechat-user',
|
||||
oidc_id: 'oidc-user',
|
||||
telegram_id: 'telegram-user',
|
||||
linux_do_id: 'linuxdo-user',
|
||||
}
|
||||
|
||||
function findUnbindButton(provider: string): HTMLButtonElement {
|
||||
let container = screen.getByText(provider).parentElement
|
||||
while (container && !container.querySelector('button')) {
|
||||
container = container.parentElement
|
||||
}
|
||||
const button = container?.querySelector<HTMLButtonElement>('button')
|
||||
if (!button) {
|
||||
throw new Error(`Expected unbind button for ${provider}`)
|
||||
}
|
||||
return button
|
||||
}
|
||||
|
||||
beforeAll(() => {
|
||||
Object.defineProperty(HTMLElement.prototype, 'getAnimations', {
|
||||
configurable: true,
|
||||
value: () => [],
|
||||
})
|
||||
})
|
||||
|
||||
afterAll(() => {
|
||||
if (originalGetAnimations) {
|
||||
Object.defineProperty(
|
||||
HTMLElement.prototype,
|
||||
'getAnimations',
|
||||
originalGetAnimations
|
||||
)
|
||||
return
|
||||
}
|
||||
Reflect.deleteProperty(HTMLElement.prototype, 'getAnimations')
|
||||
})
|
||||
|
||||
afterEach(() => {
|
||||
apiClient.get = originalGet
|
||||
apiClient.delete = originalDelete
|
||||
})
|
||||
|
||||
describe('UserBindingDialog built-in bindings', () => {
|
||||
test('submits every built-in provider type accepted by the backend', async () => {
|
||||
const deletedUrls: string[] = []
|
||||
apiClient.get = async (url) => {
|
||||
switch (url) {
|
||||
case '/api/user/7':
|
||||
return { data: { success: true, data: user } }
|
||||
case '/api/user/7/oauth/bindings':
|
||||
return { data: { success: true, data: [] } }
|
||||
case '/api/status':
|
||||
return {
|
||||
data: {
|
||||
success: true,
|
||||
data: {
|
||||
github_oauth: true,
|
||||
discord_oauth: true,
|
||||
wechat_login: true,
|
||||
oidc_enabled: true,
|
||||
telegram_oauth: true,
|
||||
linuxdo_oauth: true,
|
||||
},
|
||||
},
|
||||
}
|
||||
default:
|
||||
throw new Error(`Unexpected GET ${url}`)
|
||||
}
|
||||
}
|
||||
apiClient.delete = async (url) => {
|
||||
deletedUrls.push(url)
|
||||
return { data: { success: true, message: 'success' } }
|
||||
}
|
||||
|
||||
render(<UserBindingDialog open userId={7} onOpenChange={() => undefined} />)
|
||||
|
||||
const expectedBindings = [
|
||||
['Email', 'email'],
|
||||
['GitHub', 'github'],
|
||||
['Discord', 'discord'],
|
||||
['WeChat', 'wechat'],
|
||||
['OIDC', 'oidc'],
|
||||
['Telegram', 'telegram'],
|
||||
['LinuxDO', 'linuxdo'],
|
||||
] as const
|
||||
|
||||
await screen.findByText('bound-user (ID: 7)')
|
||||
for (const [provider, bindingType] of expectedBindings) {
|
||||
fireEvent.click(findUnbindButton(provider))
|
||||
fireEvent.click(screen.getByRole('button', { name: 'Confirm Unbind' }))
|
||||
await waitFor(() => {
|
||||
expect(deletedUrls.at(-1)).toBe(`/api/user/7/bindings/${bindingType}`)
|
||||
})
|
||||
await waitFor(() => {
|
||||
expect(
|
||||
screen.queryByRole('button', { name: 'Confirm Unbind' })
|
||||
).not.toBeInTheDocument()
|
||||
})
|
||||
}
|
||||
|
||||
expect(deletedUrls).toHaveLength(expectedBindings.length)
|
||||
})
|
||||
})
|
||||
@ -102,42 +102,42 @@ const BUILTIN_BINDINGS: ReadonlyArray<{
|
||||
statusKey: null,
|
||||
},
|
||||
{
|
||||
key: 'github_id',
|
||||
key: 'github',
|
||||
field: 'github_id',
|
||||
label: 'GitHub',
|
||||
icon: <SiGithub className='h-4 w-4' />,
|
||||
statusKey: 'github_oauth',
|
||||
},
|
||||
{
|
||||
key: 'discord_id',
|
||||
key: 'discord',
|
||||
field: 'discord_id',
|
||||
label: 'Discord',
|
||||
icon: <SiDiscord className='h-4 w-4' />,
|
||||
statusKey: 'discord_oauth',
|
||||
},
|
||||
{
|
||||
key: 'wechat_id',
|
||||
key: 'wechat',
|
||||
field: 'wechat_id',
|
||||
label: 'WeChat',
|
||||
icon: <MessageCircle className='h-4 w-4' />,
|
||||
statusKey: 'wechat_login',
|
||||
},
|
||||
{
|
||||
key: 'oidc_id',
|
||||
key: 'oidc',
|
||||
field: 'oidc_id',
|
||||
label: 'OIDC',
|
||||
icon: <Globe className='h-4 w-4' />,
|
||||
statusKey: 'oidc_enabled',
|
||||
},
|
||||
{
|
||||
key: 'telegram_id',
|
||||
key: 'telegram',
|
||||
field: 'telegram_id',
|
||||
label: 'Telegram',
|
||||
icon: <Send className='h-4 w-4' />,
|
||||
statusKey: 'telegram_oauth',
|
||||
},
|
||||
{
|
||||
key: 'linux_do_id',
|
||||
key: 'linuxdo',
|
||||
field: 'linux_do_id',
|
||||
label: 'LinuxDO',
|
||||
icon: <Globe className='h-4 w-4' />,
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user