feat(frontend): manage Cognito app clients and users
Adds a users panel under the selected user pool: copyable app client ids, a create-user form that follows the pool's sign-in method, and the user list with delete. Maps the userpools icon key to the Cognito artwork.
This commit is contained in:
@@ -0,0 +1,89 @@
|
||||
import {QueryClient, QueryClientProvider} from '@tanstack/react-query'
|
||||
import {render, screen} from '@testing-library/react'
|
||||
import userEvent from '@testing-library/user-event'
|
||||
import {beforeEach, expect, test, vi} from 'vitest'
|
||||
import {CognitoUsersPanel} from './CognitoUsersPanel'
|
||||
import type {CloudResource} from '@/types/resource'
|
||||
|
||||
const api = vi.hoisted(() => ({
|
||||
deleteFlatItem: vi.fn(),
|
||||
getCloudResource: vi.fn(),
|
||||
listFlatItems: vi.fn(),
|
||||
putFlatItem: vi.fn(),
|
||||
}))
|
||||
|
||||
vi.mock('@/api/cloudProxyClient', () => api)
|
||||
vi.mock('@/lib/accountStore', () => ({useAccountId: () => '000000000000'}))
|
||||
|
||||
const summary: CloudResource = {
|
||||
id: 'us-east-1_8e65d229a', name: 'orders-users', cloud: 'aws', service: 'userpools', type: 'user-pool',
|
||||
region: 'us-east-1', createdAt: null, metadata: {},
|
||||
}
|
||||
|
||||
function pool(signInWith: 'email' | 'username'): CloudResource {
|
||||
return {...summary, metadata: {signInWith, appClients: [{clientId: 'a8e5c7d38d3b4a1a97c9d822de', clientName: 'orders-users-client'}]}}
|
||||
}
|
||||
|
||||
const alice = {
|
||||
id: 'aa36ecc1-ed8a-43b5-84e9-988398c17436', collectionId: null, timestamp: '2026-10-23T16:01:22.000Z', metadata: {},
|
||||
body: {username: 'aa36ecc1-ed8a-43b5-84e9-988398c17436', email: 'alice@example.com', status: 'CONFIRMED', enabled: true},
|
||||
}
|
||||
|
||||
function renderPanel() {
|
||||
const client = new QueryClient({defaultOptions: {queries: {retry: false}}})
|
||||
return render(<QueryClientProvider client={client}><CognitoUsersPanel resource={summary} runtimeReachable/></QueryClientProvider>)
|
||||
}
|
||||
|
||||
beforeEach(() => {
|
||||
vi.clearAllMocks()
|
||||
api.listFlatItems.mockResolvedValue({items: [alice], nextCursor: null})
|
||||
api.putFlatItem.mockResolvedValue(alice)
|
||||
api.deleteFlatItem.mockResolvedValue(undefined)
|
||||
})
|
||||
|
||||
test('shows the app client id and the pool users', async () => {
|
||||
api.getCloudResource.mockResolvedValue(pool('email'))
|
||||
renderPanel()
|
||||
|
||||
expect(await screen.findByText('a8e5c7d38d3b4a1a97c9d822de')).toBeInTheDocument()
|
||||
expect(screen.getByRole('button', {name: 'Copy Client ID'})).toBeInTheDocument()
|
||||
expect(await screen.findByText('alice@example.com')).toBeInTheDocument()
|
||||
expect(screen.getByText('CONFIRMED')).toBeInTheDocument()
|
||||
expect(api.getCloudResource).toHaveBeenCalledWith('aws', 'userpools', summary.id, expect.anything())
|
||||
})
|
||||
|
||||
test('creates an email user who can sign in immediately', async () => {
|
||||
const user = userEvent.setup()
|
||||
api.getCloudResource.mockResolvedValue(pool('email'))
|
||||
renderPanel()
|
||||
|
||||
await screen.findByText('a8e5c7d38d3b4a1a97c9d822de')
|
||||
await user.type(screen.getByLabelText('Email'), 'bob@example.com')
|
||||
await user.type(screen.getByLabelText('Password'), 'Passw0rd!')
|
||||
await user.click(screen.getByRole('button', {name: 'Create user'}))
|
||||
|
||||
expect(api.putFlatItem).toHaveBeenCalledWith('aws', 'userpools', summary.id, {email: 'bob@example.com', password: 'Passw0rd!', permanent: true})
|
||||
})
|
||||
|
||||
test('creates a username user with a temporary password when asked', async () => {
|
||||
const user = userEvent.setup()
|
||||
api.getCloudResource.mockResolvedValue(pool('username'))
|
||||
renderPanel()
|
||||
|
||||
await user.type(await screen.findByLabelText('Username'), 'bob')
|
||||
await user.type(screen.getByLabelText('Password'), 'Passw0rd!')
|
||||
await user.click(screen.getByLabelText(/User can sign in now/))
|
||||
await user.click(screen.getByRole('button', {name: 'Create user'}))
|
||||
|
||||
expect(api.putFlatItem).toHaveBeenCalledWith('aws', 'userpools', summary.id, {username: 'bob', password: 'Passw0rd!', permanent: false})
|
||||
})
|
||||
|
||||
test('deletes a user by its stored username', async () => {
|
||||
const user = userEvent.setup()
|
||||
api.getCloudResource.mockResolvedValue(pool('email'))
|
||||
renderPanel()
|
||||
|
||||
await user.click(await screen.findByRole('button', {name: 'Delete user alice@example.com'}))
|
||||
|
||||
expect(api.deleteFlatItem).toHaveBeenCalledWith('aws', 'userpools', summary.id, alice.id)
|
||||
})
|
||||
Reference in New Issue
Block a user