Files
portal/app/tests/Frontend/ProfileTab.spec.ts
T
Дмитрий f94552d452 WIP чекпойнт: lead-region/supplier бэкенд + фронт-редизайн + Pint + тесты
92 файла одной пачкой. Исключены чужие зоны: CLAUDE.md, .claude/settings.json, docs/observer/.pii-counters.json.
gitleaks staged: no leaks found. Не верифицировано тестами - сохранение труда в историю.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-17 05:17:12 +03:00

109 lines
4.0 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import { describe, it, expect, beforeEach, vi } from 'vitest';
import { mount } from '@vue/test-utils';
import { createPinia, setActivePinia } from 'pinia';
import { createVuetify } from 'vuetify';
vi.mock('../../resources/js/api/auth', () => ({
updateProfile: vi.fn(),
}));
vi.mock('../../resources/js/api/client', () => ({
apiClient: {},
ensureCsrfCookie: vi.fn(),
extractValidationErrors: vi.fn(() => null),
extractErrorMessage: vi.fn((_e, fallback) => fallback ?? 'Произошла ошибка.'),
extractRateLimitRetry: vi.fn(() => null),
}));
import * as authApi from '../../resources/js/api/auth';
import ProfileTab from '../../resources/js/views/settings/ProfileTab.vue';
import { useAuthStore } from '../../resources/js/stores/auth';
import type { AuthUser } from '../../resources/js/api/auth';
const vuetify = createVuetify();
const mockUser: AuthUser = {
id: 1,
email: 'ivan@example.ru',
first_name: 'Иван',
last_name: 'Петров',
phone: '+7 916 000-00-00',
timezone: 'Europe/Moscow',
tenant_id: 1,
totp_enabled: false,
last_login_at: null,
};
const factory = (user: AuthUser | null = mockUser) => {
setActivePinia(createPinia());
const auth = useAuthStore();
auth.user = user;
return mount(ProfileTab, { global: { plugins: [vuetify] } });
};
describe('ProfileTab.vue', () => {
beforeEach(() => {
vi.clearAllMocks();
});
it('подставляет имя/фамилию/телефон/тайм-зону из auth-store', () => {
const wrapper = factory();
// eslint-disable-next-line @typescript-eslint/no-explicit-any
const vm = wrapper.vm as any;
expect(vm.firstName).toBe('Иван');
expect(vm.lastName).toBe('Петров');
expect(vm.phone).toBe('+7 916 000-00-00');
expect(vm.timezone).toBe('Europe/Moscow');
});
it('save() вызывает updateProfile с payload и показывает успех', async () => {
(authApi.updateProfile as ReturnType<typeof vi.fn>).mockResolvedValue({
...mockUser,
first_name: 'Пётр',
});
const wrapper = factory();
// eslint-disable-next-line @typescript-eslint/no-explicit-any
const vm = wrapper.vm as any;
vm.firstName = 'Пётр';
await vm.save();
expect(authApi.updateProfile).toHaveBeenCalledWith({
first_name: 'Пётр',
last_name: 'Петров',
phone: '+7 916 000-00-00',
timezone: 'Europe/Moscow',
});
expect(vm.saveSuccess).toBe(true);
expect(vm.saveError).toBe(null);
});
it('save() с пустым phone отправляет null', async () => {
(authApi.updateProfile as ReturnType<typeof vi.fn>).mockResolvedValue(mockUser);
const wrapper = factory();
// eslint-disable-next-line @typescript-eslint/no-explicit-any
const vm = wrapper.vm as any;
vm.phone = ' ';
await vm.save();
expect(authApi.updateProfile).toHaveBeenCalledWith(expect.objectContaining({ phone: null }));
});
it('save() показывает ошибку при reject', async () => {
(authApi.updateProfile as ReturnType<typeof vi.fn>).mockRejectedValue(new Error('boom'));
const wrapper = factory();
// eslint-disable-next-line @typescript-eslint/no-explicit-any
const vm = wrapper.vm as any;
await vm.save();
expect(vm.saveError).toBe('Не удалось сохранить профиль.');
expect(vm.saveSuccess).toBe(false);
expect(vm.saving).toBe(false);
});
it('resetForm() возвращает поля к значениям из store', async () => {
const wrapper = factory();
// eslint-disable-next-line @typescript-eslint/no-explicit-any
const vm = wrapper.vm as any;
vm.firstName = 'Изменено';
vm.resetForm();
expect(vm.firstName).toBe('Иван');
});
});