mirror of
https://github.com/mauriceboe/TREK.git
synced 2026-10-20 05:56:53 +00:00
6c5ab5414a
useAdminUserActions now rotates the JWT secret and resets a user's passkeys for the desktop dialogs and the phone sheets. Each shell keeps its own confirm step, and the phone sheet still stays busy until the reset settles.
82 lines
3.2 KiB
TypeScript
82 lines
3.2 KiB
TypeScript
import { act, renderHook } from '@testing-library/react';
|
|
import { beforeEach, describe, expect, it, vi } from 'vitest';
|
|
|
|
import { useAdminUserActions } from './useAdminUserActions';
|
|
|
|
// FE-HOOK-ADMUSERACT-001 to FE-HOOK-ADMUSERACT-005
|
|
|
|
const api = vi.hoisted(() => ({ rotateJwtSecret: vi.fn(), resetUserPasskeys: vi.fn() }));
|
|
vi.mock('../../api/client', () => ({ adminApi: api }));
|
|
|
|
const t = (key: string, params?: Record<string, unknown>) => (params ? `${key} ${JSON.stringify(params)}` : key);
|
|
|
|
function host() {
|
|
return {
|
|
toast: { success: vi.fn(), error: vi.fn() },
|
|
setRotatingJwt: vi.fn(),
|
|
setShowRotateJwtModal: vi.fn(),
|
|
logout: vi.fn(),
|
|
navigate: vi.fn(),
|
|
};
|
|
}
|
|
|
|
function mount(admin = host()) {
|
|
return { admin, actions: renderHook(() => useAdminUserActions(admin, t)).result.current };
|
|
}
|
|
|
|
beforeEach(() => {
|
|
api.rotateJwtSecret.mockReset().mockResolvedValue({});
|
|
api.resetUserPasskeys.mockReset().mockResolvedValue({ deleted: 2 });
|
|
});
|
|
|
|
describe('useAdminUserActions', () => {
|
|
it('FE-HOOK-ADMUSERACT-001: rotating the secret closes the dialog, logs out and goes to the login page', async () => {
|
|
const { admin, actions } = mount();
|
|
await act(() => actions.rotateJwt());
|
|
expect(admin.setRotatingJwt).toHaveBeenCalledWith(true);
|
|
expect(admin.setShowRotateJwtModal).toHaveBeenCalledWith(false);
|
|
expect(admin.logout).toHaveBeenCalledTimes(1);
|
|
expect(admin.navigate).toHaveBeenCalledWith('/login', { state: { noRedirect: true } });
|
|
expect(admin.logout.mock.invocationCallOrder[0]).toBeLessThan(admin.navigate.mock.invocationCallOrder[0]);
|
|
});
|
|
|
|
it('FE-HOOK-ADMUSERACT-002: a failed rotation toasts, stays signed in and clears the busy state', async () => {
|
|
api.rotateJwtSecret.mockRejectedValue(new Error('x'));
|
|
const { admin, actions } = mount();
|
|
await act(() => actions.rotateJwt());
|
|
expect(admin.toast.error).toHaveBeenCalledWith('common.error');
|
|
expect(admin.setRotatingJwt.mock.calls).toEqual([[true], [false]]);
|
|
expect(admin.logout).not.toHaveBeenCalled();
|
|
expect(admin.setShowRotateJwtModal).not.toHaveBeenCalled();
|
|
});
|
|
|
|
it('FE-HOOK-ADMUSERACT-003: a passkey reset toasts the count and reports success', async () => {
|
|
const { admin, actions } = mount();
|
|
let ok: boolean | undefined;
|
|
await act(async () => {
|
|
ok = await actions.resetPasskeys({ id: 7 });
|
|
});
|
|
expect(api.resetUserPasskeys).toHaveBeenCalledWith(7);
|
|
expect(admin.toast.success).toHaveBeenCalledWith('admin.passkey.resetDone {"count":2}');
|
|
expect(ok).toBe(true);
|
|
});
|
|
|
|
it('FE-HOOK-ADMUSERACT-004: an answer without a count reads as zero', async () => {
|
|
api.resetUserPasskeys.mockResolvedValue({});
|
|
const { admin, actions } = mount();
|
|
await act(() => actions.resetPasskeys({ id: 7 }));
|
|
expect(admin.toast.success).toHaveBeenCalledWith('admin.passkey.resetDone {"count":0}');
|
|
});
|
|
|
|
it('FE-HOOK-ADMUSERACT-005: a failed passkey reset toasts and reports failure', async () => {
|
|
api.resetUserPasskeys.mockRejectedValue(new Error('x'));
|
|
const { admin, actions } = mount();
|
|
let ok: boolean | undefined;
|
|
await act(async () => {
|
|
ok = await actions.resetPasskeys({ id: 7 });
|
|
});
|
|
expect(admin.toast.error).toHaveBeenCalledWith('common.error');
|
|
expect(ok).toBe(false);
|
|
});
|
|
});
|