// @vitest-environment happy-dom // // useSupervisor: an action with supervisorPolicy asks for the supervisor PIN // (ApprovalGate) and yields the grant id to send as approval_id; a capability // holder passes without a prompt; cancelling yields null (do not dispatch). import { afterEach, describe, expect, it, vi } from 'vitest' import { cleanup, fireEvent, render, screen, waitFor } from '@testing-library/react' vi.mock('react-i18next', () => ({ useTranslation: () => ({ t: (_k: string, opts?: { defaultValue?: string }) => opts?.defaultValue ?? _k, }), })) vi.mock('sonner', () => ({ toast: Object.assign(vi.fn(), { info: vi.fn(), success: vi.fn(), error: vi.fn() }) })) import { ApiProvider, type ApiClient } from '../api-context' import { PermissionsProvider } from '../permissions-context' import { ApprovalGateProvider } from '../approval-gate' import { useSupervisor, withApproval, type SupervisorAuthorization } from '../supervised-action' afterEach(() => { cleanup() vi.clearAllMocks() }) let result: SupervisorAuthorization | null | 'unset' function Probe({ policy }: { policy?: string }) { const { authorize } = useSupervisor() return ( ) } function mount(post: ApiClient['post'], caps: string[], policy?: string) { result = 'unset' const api = { get: vi.fn(), post, put: vi.fn(), delete: vi.fn() } as unknown as ApiClient return render( , ) } describe('useSupervisor', () => { it('needs nothing when the action declares no policy', async () => { const post = vi.fn() mount(post, []) fireEvent.click(screen.getByText('go')) await waitFor(() => expect(result).toEqual({})) expect(post).not.toHaveBeenCalled() }) it('a holder of general.approve_ passes without a prompt or a grant id', async () => { const post = vi.fn() mount(post, ['general.approve_cancel_cfdi'], 'cancel_cfdi') fireEvent.click(screen.getByText('go')) await waitFor(() => expect(result).toEqual({})) expect(post).not.toHaveBeenCalled() }) it('anyone else types a supervisor PIN and gets the grant id, anchored to the record', async () => { const post = vi.fn().mockResolvedValue({ data: { data: { id: 'grant-9' } } }) mount(post, [], 'cancel_cfdi') fireEvent.click(screen.getByText('go')) fireEvent.change(await screen.findByLabelText('Motivo'), { target: { value: 'error de captura' } }) fireEvent.change(screen.getByLabelText('PIN del supervisor'), { target: { value: '4321' } }) fireEvent.click(screen.getByText('Autorizar')) await waitFor(() => expect(result).toEqual({ approvalId: 'grant-9' })) expect(post).toHaveBeenCalledWith( '/approvals/pin-grant', expect.objectContaining({ policy: 'cancel_cfdi', model: 'Invoice', record_id: 'inv-1', pin: '4321' }), ) }) it('cancelling the prompt yields null', async () => { mount(vi.fn(), [], 'cancel_cfdi') fireEvent.click(screen.getByText('go')) await screen.findByLabelText('Motivo') fireEvent.click(screen.getByText('Cancelar')) await waitFor(() => expect(result).toBeNull()) }) }) describe('withApproval', () => { it('adds approval_id only when a grant was collected', () => { expect(withApproval({ a: 1 }, { approvalId: 'g' })).toEqual({ a: 1, approval_id: 'g' }) expect(withApproval({ a: 1 }, {})).toEqual({ a: 1 }) expect(withApproval({ a: 1 }, null)).toEqual({ a: 1 }) }) })