fix: useAdminUsers null guard, unmount cleanup, split test
- Guard setUsers against null data with nullish coalescing (data ?? []) - Add isMounted flag in useEffect to prevent setState after unmount - Split combined inviteUser+deleteUser test into two independent it blocks Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -48,14 +48,13 @@ describe('useAdminUsers', () => {
|
||||
expect(result.current.users).toEqual([]);
|
||||
});
|
||||
|
||||
it('inviteUser appends new user to state; deleteUser removes user from state', async () => {
|
||||
it('inviteUser appends a user to state', async () => {
|
||||
const existingUser = { id: '1', email: 'a@example.com', created_at: '2024-01-01', last_sign_in_at: null };
|
||||
const newUser = { id: '2', email: 'b@example.com', created_at: '2024-01-03', last_sign_in_at: null };
|
||||
|
||||
mockInvoke
|
||||
.mockResolvedValueOnce({ data: [existingUser], error: null })
|
||||
.mockResolvedValueOnce({ data: newUser, error: null })
|
||||
.mockResolvedValueOnce({ data: {}, error: null });
|
||||
.mockResolvedValueOnce({ data: newUser, error: null });
|
||||
|
||||
const { result } = renderHook(() => useAdminUsers());
|
||||
await act(async () => {});
|
||||
@@ -65,6 +64,19 @@ describe('useAdminUsers', () => {
|
||||
await result.current.inviteUser('b@example.com');
|
||||
});
|
||||
expect(result.current.users).toEqual([existingUser, newUser]);
|
||||
});
|
||||
|
||||
it('deleteUser removes a user from state', async () => {
|
||||
const existingUser = { id: '1', email: 'a@example.com', created_at: '2024-01-01', last_sign_in_at: null };
|
||||
const newUser = { id: '2', email: 'b@example.com', created_at: '2024-01-03', last_sign_in_at: null };
|
||||
|
||||
mockInvoke
|
||||
.mockResolvedValueOnce({ data: [existingUser, newUser], error: null })
|
||||
.mockResolvedValueOnce({ data: {}, error: null });
|
||||
|
||||
const { result } = renderHook(() => useAdminUsers());
|
||||
await act(async () => {});
|
||||
expect(result.current.users).toEqual([existingUser, newUser]);
|
||||
|
||||
await act(async () => {
|
||||
await result.current.deleteUser('1');
|
||||
|
||||
@@ -6,19 +6,25 @@ export function useAdminUsers() {
|
||||
const [loading, setLoading] = useState(true);
|
||||
const [error, setError] = useState(null);
|
||||
|
||||
const fetchUsers = useCallback(async () => {
|
||||
const fetchUsers = useCallback(async (isMounted) => {
|
||||
const { data, error } = await supabase.functions.invoke('list-admin-users');
|
||||
|
||||
if (!isMounted) return;
|
||||
|
||||
if (error) {
|
||||
setError(error.message);
|
||||
} else {
|
||||
setUsers(data);
|
||||
setUsers(data ?? []);
|
||||
}
|
||||
setLoading(false);
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
fetchUsers();
|
||||
let isMounted = true;
|
||||
fetchUsers(isMounted);
|
||||
return () => {
|
||||
isMounted = false;
|
||||
};
|
||||
}, [fetchUsers]);
|
||||
|
||||
const inviteUser = async (email) => {
|
||||
|
||||
Reference in New Issue
Block a user