eebcaf1912
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
135 lines
5.8 KiB
TypeScript
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();
|
|
});
|
|
});
|