Files
portal/app/tests/Frontend/ImpersonationBanner.spec.ts
T
2026-05-16 10:09:29 +03:00

135 lines
5.8 KiB
TypeScript

import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest';
import { mount, flushPromises } from '@vue/test-utils';
import { createVuetify } from 'vuetify';
import ImpersonationBanner from '../../resources/js/components/admin/ImpersonationBanner.vue';
import type { ImpersonationActiveSession } from '../../resources/js/api/admin';
vi.mock('../../resources/js/api/admin', async (importOriginal) => {
const orig = await importOriginal<typeof import('../../resources/js/api/admin')>();
return { ...orig, impersonationActive: vi.fn() };
});
const adminApi = await import('../../resources/js/api/admin');
function makeSession(overrides: Partial<ImpersonationActiveSession> = {}): ImpersonationActiveSession {
return {
token_id: 1,
tenant_id: 10,
tenant_name: 'ООО Ромашка',
requested_by: 7,
reason: 'Диагностика проблемы с балансом по обращению клиента',
sent_to_email: 'client@romashka.ru',
used_at: '2026-05-16T08:00:00Z',
expires_at: '2026-05-16T08:15:00Z',
...overrides,
};
}
const mountBanner = () =>
mount(ImpersonationBanner, {
global: {
plugins: [createVuetify()],
stubs: {
RouterLink: {
props: ['to'],
inheritAttrs: false,
template: '<a v-bind="$attrs" :href="to"><slot /></a>',
},
},
},
});
beforeEach(() => vi.clearAllMocks());
afterEach(() => vi.useRealTimers());
describe('ImpersonationBanner', () => {
it('вызывает impersonationActive на mount', async () => {
vi.mocked(adminApi.impersonationActive).mockResolvedValueOnce([]);
const wrapper = mountBanner();
await flushPromises();
expect(adminApi.impersonationActive).toHaveBeenCalledTimes(1);
wrapper.unmount();
});
it('0 активных сессий — баннер не рендерится', async () => {
vi.mocked(adminApi.impersonationActive).mockResolvedValueOnce([]);
const wrapper = mountBanner();
await flushPromises();
expect(wrapper.find('[data-testid="impersonation-banner"]').exists()).toBe(false);
wrapper.unmount();
});
it('1 активная сессия — баннер с именем тенанта + ссылка на /admin/impersonation', async () => {
vi.mocked(adminApi.impersonationActive).mockResolvedValueOnce([makeSession({ tenant_name: 'ООО Ромашка' })]);
const wrapper = mountBanner();
await flushPromises();
const banner = wrapper.find('[data-testid="impersonation-banner"]');
expect(banner.exists()).toBe(true);
expect(banner.text()).toContain('Активна impersonation-сессия');
expect(banner.text()).toContain('ООО Ромашка');
expect(wrapper.find('[data-testid="impersonation-banner-link"]').attributes('href')).toBe(
'/admin/impersonation',
);
wrapper.unmount();
});
it('несколько активных сессий — баннер показывает счётчик', async () => {
vi.mocked(adminApi.impersonationActive).mockResolvedValueOnce([
makeSession({ token_id: 1 }),
makeSession({ token_id: 2 }),
makeSession({ token_id: 3 }),
]);
const wrapper = mountBanner();
await flushPromises();
expect(wrapper.find('[data-testid="impersonation-banner"]').text()).toContain(
'Активны impersonation-сессии: 3',
);
wrapper.unmount();
});
it('tenant_name=null — fallback на «тенант #id»', async () => {
vi.mocked(adminApi.impersonationActive).mockResolvedValueOnce([
makeSession({ tenant_name: null, tenant_id: 42 }),
]);
const wrapper = mountBanner();
await flushPromises();
expect(wrapper.find('[data-testid="impersonation-banner"]').text()).toContain('тенант #42');
wrapper.unmount();
});
it('ошибка impersonationActive — баннер не падает и остаётся скрыт', async () => {
vi.mocked(adminApi.impersonationActive).mockRejectedValueOnce(new Error('500'));
const wrapper = mountBanner();
await flushPromises();
expect(wrapper.find('[data-testid="impersonation-banner"]').exists()).toBe(false);
wrapper.unmount();
});
it('polling — impersonationActive вызывается повторно через 30 с', async () => {
vi.useFakeTimers();
vi.mocked(adminApi.impersonationActive).mockResolvedValue([]);
const wrapper = mountBanner();
await vi.advanceTimersByTimeAsync(0);
expect(adminApi.impersonationActive).toHaveBeenCalledTimes(1);
await vi.advanceTimersByTimeAsync(30_000);
expect(adminApi.impersonationActive).toHaveBeenCalledTimes(2);
wrapper.unmount();
});
it('polling — данные второго опроса обновляют баннер', async () => {
vi.useFakeTimers();
vi.mocked(adminApi.impersonationActive)
.mockResolvedValueOnce([])
.mockResolvedValue([makeSession({ tenant_name: 'ООО Ромашка' })]);
const wrapper = mountBanner();
await vi.advanceTimersByTimeAsync(0);
expect(wrapper.find('[data-testid="impersonation-banner"]').exists()).toBe(false);
await vi.advanceTimersByTimeAsync(30_000);
await wrapper.vm.$nextTick();
const banner = wrapper.find('[data-testid="impersonation-banner"]');
expect(banner.exists()).toBe(true);
expect(banner.text()).toContain('ООО Ромашка');
wrapper.unmount();
});
});