Files
portal/app/tests/Frontend/RecoveryCodesCard.spec.ts
T
Дмитрий e280edd431 style(frontend): apply prettier --write — fix formatting drift
4 files reformatted (import list expansion, line-length wrapping).
Vitest 88/683+3sk green.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-13 13:30:51 +03:00

127 lines
4.7 KiB
TypeScript

import { describe, it, expect, beforeEach, vi } from 'vitest';
import { mount, flushPromises } from '@vue/test-utils';
import { createPinia, setActivePinia } from 'pinia';
import { createVuetify } from 'vuetify';
vi.mock('../../resources/js/api/auth', () => ({
twoFactorRegenerateRecoveryCodes: vi.fn(),
}));
import RecoveryCodesCard from '../../resources/js/components/settings/security/RecoveryCodesCard.vue';
import * as authApi from '../../resources/js/api/auth';
import { useAuthStore } from '../../resources/js/stores/auth';
const vuetify = createVuetify();
describe('RecoveryCodesCard (Q.DEFER.003 sub-B)', () => {
beforeEach(() => {
setActivePinia(createPinia());
vi.clearAllMocks();
});
const factory = (totpEnabled: boolean) => {
// Set auth state BEFORE mount — иначе computed has2fa зафиксируется false
// на первом render и кнопка не появится reactive'но (Pinia store reactivity quirk).
const auth = useAuthStore();
auth.user = {
id: 1,
email: 'test@demo.local',
first_name: 'Test',
last_name: 'User',
tenant_id: 1,
totp_enabled: totpEnabled,
last_login_at: null,
} as unknown as ReturnType<typeof useAuthStore>['user'];
const wrapper = mount(RecoveryCodesCard, {
global: { plugins: [vuetify], stubs: { VDialog: true } },
});
return wrapper;
};
it('does NOT render button when 2FA disabled (auth.user.totp_enabled=false)', () => {
const wrapper = factory(false);
expect(wrapper.find('[data-testid="regen-codes-btn"]').exists()).toBe(false);
});
it('renders button when 2FA enabled', async () => {
const wrapper = factory(true);
await flushPromises();
const btn = wrapper.find('[data-testid="regen-codes-btn"]');
expect(btn.exists()).toBe(true);
expect(btn.text()).toContain('Перегенерировать резервные коды');
});
it('clicking regen-codes-btn opens dialog (sets regenOpen)', async () => {
const wrapper = factory(true);
await flushPromises();
await wrapper.find('[data-testid="regen-codes-btn"]').trigger('click');
// VDialog is stubbed — check internal state via component instance
expect((wrapper.vm as unknown as { regenOpen: boolean }).regenOpen).toBe(true);
});
it('confirmRegen() with valid password fetches 8 codes and clears password', async () => {
const newCodes = [
'AAAA-1111',
'BBBB-2222',
'CCCC-3333',
'DDDD-4444',
'EEEE-5555',
'FFFF-6666',
'GGGG-7777',
'HHHH-8888',
];
(authApi.twoFactorRegenerateRecoveryCodes as ReturnType<typeof vi.fn>).mockResolvedValue({
recovery_codes: newCodes,
});
const wrapper = factory(true);
await flushPromises();
const vm = wrapper.vm as unknown as {
regenOpen: boolean;
regenPassword: string;
regenCodes: string[];
regenError: string;
confirmRegen: () => Promise<void>;
};
vm.regenOpen = true;
vm.regenPassword = 'correctPass';
await vm.confirmRegen();
await flushPromises();
expect(vm.regenCodes).toEqual(newCodes);
expect(vm.regenPassword).toBe('');
expect(vm.regenError).toBe('');
expect(authApi.twoFactorRegenerateRecoveryCodes).toHaveBeenCalledWith('correctPass');
});
it('confirmRegen() with invalid password sets error and keeps regenCodes empty', async () => {
(authApi.twoFactorRegenerateRecoveryCodes as ReturnType<typeof vi.fn>).mockRejectedValue(new Error('401'));
const wrapper = factory(true);
await flushPromises();
const vm = wrapper.vm as unknown as {
regenPassword: string;
regenCodes: string[];
regenError: string;
confirmRegen: () => Promise<void>;
};
vm.regenPassword = 'wrongPass';
await vm.confirmRegen();
await flushPromises();
expect(vm.regenError).toBe('Неверный пароль.');
expect(vm.regenCodes).toEqual([]);
});
it('closeRegen() clears codes and closes dialog', async () => {
const wrapper = factory(true);
await flushPromises();
const vm = wrapper.vm as unknown as {
regenOpen: boolean;
regenCodes: string[];
closeRegen: () => void;
};
vm.regenOpen = true;
vm.regenCodes = ['AAAA-1111'];
vm.closeRegen();
expect(vm.regenOpen).toBe(false);
expect(vm.regenCodes).toEqual([]);
});
});